I\'m incorporating Google Maps into my MVC 4 application. Fairly straightforward to do. However, I have a question concerning the best way to add multiple markers dynamically. M
Use the web API and handle it all asynchronously. Pass a JSON object back to the client, parse out your info, remove your old markers and add the new ones.
EDIT after your comment:
If you post some of what you've tried and not been able to get to work then we can help you with the problems you're running into. If you're just getting started:
http://www.asp.net/web-api - Great tutorials on using the Web API http://angularjs.org/ - Check out Angular for the GET request (http://docs.angularjs.org/api/ng.$http) and binding your results to the UI
In the project I'm working on right now, this is how we handle it:
Let the main ViewModel be called FooViewModel
.
ViewModels:
public class FooViewModel
{
// Your search results:
public IEnumerable<FooWithLocationViewModel> Foos { get; set; }
// Properties for your search filter:
public decimal? MaxPrice { get; set; }
public CityEnum? City { get; set; }
...
}
public class FooWithLocationViewModel
{
public string Name { get; set; }
public decimal Price { get; set; }
...
public double Latidude { get; set; }
public double Longitude { get; set; }
...
}
In the view, there is a <div>
for each FooWithLocationViewModel
. We will make use of data-*
attributes which are valid in HTML5:
View:
@model FooViewModel
...
@foreach(FooWithLocationViewModel foo in Model.Foos)
{
<div class="foo" data-latitude="@(foo.Latitude)"
data-longitude="@(foo.Longitude)">
<span>@foo.Name</span>
<span>@foo.Price</span>
...
</div>
}
<div id="map-canvas"></div>
Now, it is time for JavaScript, I am assuming that you are using JQuery:
Initializing the map, adding the markers and storing them in markers
array:
function initialize() {
var foos = $(".foo");
var markers = new Array();
var mapOptions = {
...
}};
var mapCanvas = document.getElementById('map-canvas');
if (mapCanvas != null) {
map = new google.maps.Map(mapCanvas, mapOptions);
$.each(foos, function (key, value) {
markers[key] = new google.maps.Marker({
map: map,
draggable: false,
animation: google.maps.Animation.DROP,
position: new google.maps.LatLng(
Number($(value).attr("data-latitude")),
Number($(value).attr("data-longitude")
));
});
google.maps.event.addListener(markers[key], 'click', function () {
// If you need this...
});
});
}
}
What's good about this approach is, everything is done in the function that initializes your Google Map. Thus your script does not have to be embedded in your View. We are using an HTML5 friendly approach. And it is simple.
Would like to suggest an approach for this requirement:
On search button click - be it (POST request or AJAX POST), send the request to server.
Perform the search on server 'search()' action.
There are two responsibilities of search() action - 1. Show search result. 2. Show corresponding Markers on Map
Search result will be the data and representation, be it 'div'/'table/tr/td'. This representation is generated on server without using any client resources and client side processing time. So the 'SearchView' can return both data and representation.
Another responsibility is showing the markers corresponding to each search result. This is totally client responsibility. So lets keep it separate from the first responsibility. Using the JSON serialization library inject the serialized 'markers' json array in the 'SearchView' instead of mixing it with search result. The only time you might want to have the markers details with tightly bind to each search result is when you want to highlight the markers on click of individual search results. The use case could be initially show all the search results and show all the markers. Then when user clicks/hovers over (considering this is not targeted for mobile as hover cannot work there) the individual search result you can reposition the marker or add label or more information to selected search result.
Also you might want to have the script in a separate file to avoid loading it on each search action. And from SearchView you can just call the 'InitializeMap()' JS function passing it the JSON serialized Markers and the initial map position.
Using the JSON would be better approach and it provides the future flexibility to convert the application to AJAX easily. Also it is faster than parsing the HTML using Jquery for getting the marker details.
function InitializeMap(initialMapPosition,markers){
var latlng = new google.maps.LatLng(initialMapPosition.latitude,initialMapPosition.longitude);
var options = {
zoom: 15,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP // this could be as per your requirement.
};
var map = new google.maps.Map($('#map-canvas')[0], options);
// Place markers on map
for( i = 0; i < markers.length; i++) {
var latLng = new google.maps.LatLng(markers[i].lat, markers[i].lng);
var marker = new google.maps.Marker({
position: latLng,
map: map
});
}
}
Here are my thoughts on the subject.
Recently, I did a project which was a single page, AJAX-driven web application (I know you are not looking for an AJAX based solution but stay with me).
I integrated Google maps in that application that was used to show location of different entities.
When the user searched for something, instead of doing Geo-Coding in the browser (which was, at times, very slow), I sent the request to server. On server, the incoming text was Geo-coded (using Google API) and result was cached in local DB for later use. If, the same request came again, I fetch the Geo-cordinates from the DB and send it back to client.
The return type was pretty simple. i.e.
public class Marker
{
public double Lat{set;get;}
public double Lon{set;get;}
public string Title{set;get;}
}
On client, I would simply iterate over the List and plot the markers on the map (plotting markers is faster than requesting Geo-coding).
Now, the same can be done in post back. All you have to do is, in your initialize function call
var markers = @Html.Action("GetMarkers")
this will fill var markers with list of all your markers that you can then iterate.
Remember, the return type of GetMarkers
is
public JsonResult GetMarkers()
{
...
// returns List<Markers>
}