MvcMaps Preview 1 – A Unified Bing/Google Maps API for ASP.NET MVC

Nov 2, 2009  • Open Source  • .NET  • Mapping

I spent some time lately working on bringing some of the concepts of Web.Maps.VE to ASP.NET MVC. The concepts I’m referring to are Simplicity and Ease of Development in making the implementation of mapping within ASP.NET MVC applications as simple as possible along with Flexibility and Customizability of the Base Mapping API itself. Then I thought, Since I’m building an abstraction layer to simplify Bing Maps development, why not implement it in a flexible manor as to be able to support other Mapping API’s as well?

The result of such an effort in a nice Unified API that allows virtually the same code to be written when implementing either Bing Maps or Google Maps. In fact, all you need to do to change your application over to using one mapping provider instead of the other is to just change a single line of code.

Sound too good to be true?

I introduce you to the all new MvcMaps project, and I’m releasing it as Open Source under the Microsoft Public License.

Preview Release?

This initial release is just a Preview release and isn’t really meant for production use, although there’s absolutely nothing stopping you from using it in such an environment.

Introduction

The above source code download link contains the full source code for the component, plus a very basic Interactive SDK style website demonstrating some basic examples of using it.

The following snippet is the most basic example of how to add both a Bing Map and a Google Map within a View:

<%@ Import Namespace="MvcMaps" %>

<style type="text/javascript">
.BingMap
{
    width: 600px;
    height: 400px;
    border: solid 1px black;
}
.GoogleMap
{
    width: 600px;
    height: 400px;
    border: solid 1px black;
}
</style>

## Bing Map
<% Ajax.BingMap()     // Create a Bing Map
    .CssClass("BingMap") // Define the CSS Style to use. These specify the Maps Size
    .Render();           // Render all the HTML / JavaScript necessary to create the Map to Server.Response
    %>

## Google Map
<% Ajax.GoogleMap()      // Create a Google Map
    .CssClass("GoogleMap")  // Define the CSS Style to use. These specify the Maps Size
    .Render();              // Render all the HTML / JavaScript necessary to create the Map to Server.Response
    %>

Notice that the code is identical, except for the BingMap and GoogleMap parts. That is how you define the specific mapping provider to use, but the rest of the code is the same.

Plotting Pushpins, Polylines and Polygons

Adding some Pushpins, Polylines and Polygons is extremely simple, and its the same code for both Bing Maps and Google Maps!

Here’s a basic example of adding one of each:

<% Ajax.BingMap().CssClass("BingMap")
    // Add Pushpin Shape
    .AddPushpin(
        new Pushpin(39.9097362345372, -97.470703125,
             "Some Pushpin Title", "Some Pushpin Description")
        )
    
    // Add Polyline Shape
    .AddPolyline(
        new Polyline(new List() {
            new LatLng(48.166085, -121.11328),
            new LatLng(34.270835, -118.34472),
            new LatLng(43.041543, -87.901954),
            new LatLng(38.889546, -77.035338)
        }) {
            LineColor = "#0000FF",
            LineWeight = 6
        }
    )
    
    // Add Polygon Shape
    .AddPolygon(
        new Polygon(new List() {
            new LatLng(34.270835, -118.34472),
            new LatLng(43.041543, -87.901954),
            new LatLng(38.889546, -77.035338)
        }) {
            FillColor = "#00ff00",
            FillOpacity = 0.5,
            LineWeight = 2,
            LineColor = "#FF0000"
        }
    )
    
    // Render Map (HTML and JavaScript) to the Page
    .Render()
%></pre>
``
 
## Dynamic / Interactive Style Map

One of the **coolest** features I've built into the component so far is the ability to extremely easily add Dynamic / Interactive Style Map.

You probably wouldn't believe me if I explained in words how simple it is to add a dynamic style map, so instead I'll just show you the most basic code to get it working.

Here's the code to add the Map to the Page. In this example you just tell the Map what Controller and Action to call to get the Map data to be displayed.

```html
<% Ajax.BingMap()
    .CssClass("BingMap")
    .DynamicMap( new { controller = "DynamicMap", action = "SchoolDistricts" })
    .Render();
%>

Then you define your Controller Action, make it accept the Map View (or Map Bounds) which are essentially the Min and Max Lat/Lng values of the visible area on the client, and then you just return a MapDataResult object that contains the Pushpins, Polylines and Polygons to be plotted.

The following example demonstrates searching an XML file for School Districts within the Maps Viewable Area, and then plots them on the Map using Pushpins. As of MvcMaps Preview 1, only Pushpins are supported with the MapDataResult object.

public class DynamicMapController : Controller
{
    public ActionResult SchoolDistricts(double minLat, double maxLat, double minLng, double maxLng)
    {
        // Query and Get all School Districts within the passed in "Map View".
        var doc = XDocument.Load(Server.MapPath("~/App_Data/WISchoolDistricts.xml"));
        var schooldistricts = (from d in doc.Element("schooldistricts").Elements("schooldistrict")
                        where double.Parse(d.Attribute("latitude").Value) >= minLat
                        && double.Parse(d.Attribute("latitude").Value) <= maxLat
                        && double.Parse(d.Attribute("longitude").Value) >= minLng
                        && double.Parse(d.Attribute("longitude").Value) <= maxLng
                        select d
                    );

        // Generate "Pushpin" objects for each School District to be Plotted on the Map.
        var pushpins = (from d in schooldistricts
                        select(new Pushpin(
                            double.Parse(d.Attribute("latitude").Value),
                            double.Parse(d.Attribute("longitude").Value)
                        ){
                            Title = d.Attribute("name").Value,
                            Description = d.Attribute("address").Value
                        }));

        // Return a "MapDataResult" object that contains all the data that is to be Plotted on the Map.
        return new MapDataResult()
        {
            Pushpins = pushpins
        };
    }
}

Yes it really is that simple!!

How to Customize the Dynamic Map

The Dynamic Map example above is nice and simple, but if you need to customize the client-side map behavior somehow, don’t worry, there are override hooks built in to the component that allow you to override the complete behavior of the map in how it displays the data, or just execute some custom code on the returned data after it’s already been plotted.

You can use the DynamicMap DataLoad option to specify a JavaScript function to get called every time map data is automatically loaded. The following example displayed the total number of Pushpins currently plotted in a SPAN tag above the Map. The data parameter on the function is the data object that is returned from the MapDataResult passed down from the controller action method.

<span id='lblPushpinCount'></span><br />
<% Ajax.BingMap()
    .CssClass("BingMap")
    .DynamicMap(
        new { controller = "DynamicMap", action = "SchoolDistricts" },
        new DynamicMapOptions() {
            DataLoaded = "function(data) { $('#lblPushpinCount').html(data.pushpins.length); }"
        })
    .Render();
%>

Also, you can use the DynamicMap DisplayData option to completely override the maps default behavior of plotting the data. Just in case you need to completely customize it, and the default behavior just doesn’t cut it. The following example plots the Pushpins returned and displays the total number of pushpins in a SPAN tag above the Map:

<span id='lblPushpinCount'></span>
<% Ajax.BingMap()
    .CssClass("BingMap")
    .DynamicMap(
        new { controller = "DynamicMap", action = "SchoolDistricts" },
        new DynamicMapOptions() {
            DisplayData = "DynamicMap_DisplayData_Handler"
        })
    .Render();
%>

<script type="text/javascript">
function DynamicMap_DisplayData_Handler(data) {
    // Method gets called with "this" equaling the Mvc.Maps JavaScript Map Object
    
    // Clear All Currently Plotted Data
    this.clearDynamicMapData();
    
    // Plot New Pushpins that were Loaded
    this.plotPushpins(data.pushpins);

    // Display Pushpin Count
    $('#lblPushpinCount').html(data.pushpins.length);
}
</script>

Map Load Event

In case you need to execute some code on the Page as soon as the Map has finished loading, you can specify any JavaScript code you need to be executed.

Pass in JavaScript as String
<% Ajax.GoogleMap()
        .Load("alert('Map Loaded!');")
        .Render();
        %>
        
Use Lambda Expression to define JavaScript code
<% Ajax.GoogleMap()
        .Load( () => { %>
            alert('Map Loaded!');
            var map = this.mapObject;  // get ref to underlying map providers object
        <% })
        .Render();
        %>

When specifying code to execute on the Load event, the context of the this keyword will be a reference to the client-side MvcMaps Map Object. To get a reference to the underlying Map Providers object (VEMap or GMap2), just access it’s mapObject property like so:

var map = this.mapObject;

Conclusion

The MvcMaps project really makes it dead simple to implement mapping in an ASP.NET MVC Web Application. There’s no need to worry about all the tedious work that you used to have to do on every page.

I plan on building out a few more features into the component, and getting it to the point of a Stable release soon. I just wanted to share what I’ve done so far, so others can provide feedback.

Also, don’t forget to download the code and the Interactive SDK from the link at the top of this post.