I have a simple web application displaying places of interest. A page displaying the place of interest will use Bing maps to display a map. Some users will be able edit the place of interest.
Getting started...
Maps Interactive SDK is a great place to get started with Bing maps. Starting with the "I want to show a specific map" example, the output is:
1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2: <html>
3: <head>
4: <title></title>
5: <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6: <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>
7: <script type="text/javascript">
8: var map = null;
9: 10: function GetMap()
11: {12: map = new VEMap('myMap');
13: map.LoadMap(new VELatLong(47.6, -122.33), 10 ,'h' ,false);
14: } 15: </script>
16: </head>
17: <body onload="GetMap();">
18: <div id='myMap' style="position:relative; width:400px; height:400px;"></div>
19: </body>
20: </html>
This is my starting point.
Adding jQuery and JSON to the mix
I'm going to modify the JavaScript GetMap method to initialise the map using jQuery and to represent the map using JSON.
1: $(function() { 2: var mapping = {"Lat":-41.229314,"Long":174.879084,"Zoom":18}; 3: var map = new VEMap('myMap'); 4: var latLong = new VELatLong(mapping.Lat, mapping.Long);5:
6: map.LoadMap(latLong, mapping.Zoom, 'h', false); 7: });The domain and the database...
Now that I can express my map as a JSON object I can look at storing this in the database. There are some design factors I want to consider:
- Right now I want to display a basic map, but as the app evolves I'll probably want to do more.
- I don't know the full feature-set of Bing maps and I don't know what it will be capable off in the future.
Naively, my domain model for places of interest will look like this:
1: public class PlaceOfInterest {
2: public int Id {get; set;}
3: public string Title {get; set;}
4: public string Mapping {get; set;}
5: public double Latitude {get; set;}
6: public double Longitude {get; set;}
7: }Mapping the mapping to a map object...
So I have a all the mapping information as JSON, but I like to work with real objects. I'm going to use a lightweight JSON serializer and deserializer to get an object I can work with and get some type-safety in my app.
My map class is:
1: [DataContract] public class Map {
2: [DataMember(Name="Lat")]
3: public double Latitude {get; set;}
4: [DataMember(Name="Long")]
5: public double Longitude {get; set;}
6: [DataMember]7: public int Zoom {get; set;}
8: }Now for the heavy(ish)-lifting class for this app:
1: public class Json where T: class, new()
2: {3: public string Serialize(T contract)
4: {5: var serializer = new DataContractJsonSerializer(typeof (T));
6: var stream = new MemoryStream();
7: serializer.WriteObject(stream, contract);8: return Encoding.Default.GetString(stream.ToArray());
9: }10:
11: public T Deserialize(string json)
12: {13: if (String.IsNullOrEmpty(json))
14: return new T();
15: var serializer = new DataContractJsonSerializer(typeof(T));
16: var stream = new MemoryStream(Encoding.Unicode.GetBytes(json));
17: return serializer.ReadObject(stream) as T;
18: } 19: } 1: PlaceOfInterest thePlace = PlaceRepository.GetARandomPlaceOfInterest();2: Map theMap = new Json().Deserialize(thePlace.Mapping); // De-serializing
3: string json = new Json().Serialize(theMap); // Serializing
1: <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<PlaceOfInterest>" %>
2:
3: ...4:
5: <asp:Content ContentPlaceHolderID="ScriptPlaceHolder" runat="server">
6: <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>
7: <script type="text/javascript">
8: $(function() {
9: var mapping = <%= Model.Mapping %>;
10: var map = new VEMap('myMap');
11: var latLong = new VELatLong(mapping.Lat, mapping.Long);
12: 13: map.LoadMap(latLong, mapping.Zoom, 'h');
14: });15: </script>
16: </asp:Content>
Because the PlaceOfInterest model doesn't natively have a map model I'm going to use a view model for my Edit action to create the Map object.
1: public class Edit
2: {3: public Edit() {}
4:
5: public Edit(PlaceOfInterest placeOfInterest)
6: { 7: PlaceOfInterest = placeOfInterest;8: Map = new Json().Deserialize(placeOfInterest.Mapping);
9: }10:
11: public PlaceOfInterest PlaceOfInterest { get; set; }
12: public Map Map { get; set; }
13: } 1: [AcceptVerbs(HttpVerbs.Get)]2: public ActionResult Edit(int id)
3: {4: var model = new Edit(Repository.Get(id));
5: return View(model);
6: }7:
8: [AcceptVerbs(HttpVerbs.Post)]9: public ActionResult Edit(
10: int id,
11: [Bind(Prefix = "")]PlaceOfInterest placeOfInterest,
12: [Bind(Prefix = "Mapping")]Domain.Map map)
13: { 14: var thePlaceOfInterest = Repository.Get(id); 15: UpdateModel(thePlaceOfInterest);16:
17: thePlaceOfInterest.Latitude = map.Latitude; 18: thePlaceOfInterest.Longitude = map.Longitude;19:
20: thePlaceOfInterest.Mapping = new Json().Serialize(map); Repository.Save();
21: return View(new Edit(Repository.Get(id)));
22: }The partial control for the Map...
1: <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<map>" %>
2:
3: <fieldset>
4: <legend>Map</legend>
5: <p>
6: <label for="Latitude">Latitude:</label>
7: <%= Html.TextBox("Mapping.Latitude", Model.Latitude) %>
8: <%= Html.ValidationMessage("Mapping.Latitude", "*")%>
9: </p>
10: <p>
11: <label for="Longitude">Longitude:</label>
12: <%= Html.TextBox("Mapping.Longitude", Model.Longitude)%>
13: <%= Html.ValidationMessage("Mapping.Longitude", "*")%>
14: </p>
15: <p>
16: <label for="Zoom">Zoom:</label>
17: <%= Html.TextBox("Mapping.Zoom", Model.Zoom)%>
18: <%= Html.ValidationMessage("Mapping.Zoom", "*")%>
19: </p>
20: </fieldset>
And that's pretty much it. In summary...
- I'm using JSON to store all the data necessary to render a Bing map.
- I use a lightweight bit of JavaScript to consume the JSON map representation.
- For editing the map I deserialize to a domain model and then use a type-safe partial page in ASP.NET MVC.
- As my needs for mapping increase I don't have the overhead of having to change my database schema. I do have to update the domain model but then my partial view has to be kept up to date.
