Google Maps API Marker Clusterer and Ajax - google-maps-api-3

I am running multiple ajax calls to download a large number of google maps icons. When I try to increment the Marker Clusterer, however, the map clears all markers. I believe this is because I am calling var markerCluster = new MarkerCluster(map); in each AJAX call.
Can anyone tell me how to correctly implement this?
var populateMapByIncident = function(incident, page) {
var run_again = false;
$.getJSON(
"/public_map_ajax_handler",
{"shortname" : incident, "page": page},
function(sites_list) {
if (sites_list.length > 2) {
run_again = true;
}
var mapOptions = {
zoom: 8,
center: new google.maps.LatLng(40.6501038, -73.8495823),
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
var markers = [];
var i = 0;
for (var i = 0; i < sites_list.length; i++) {
var latLng = new google.maps.LatLng(sites_list[i].latitude, sites_list[i].longitude);
var marker = new google.maps.Marker({'position': latLng,
'icon': getMarkerIcon(sites_list[i]),
'site_id': sites_list[i].id,
'case_number': sites_list[i].case_number,
'work_type': sites_list[i].work_type,
'floors_affected': sites_list[i].floors_affected,
'status': sites_list[i].status});
markers.push(marker);
var site_id = sites_list[i].id;
google.maps.event.addListener(marker, "click", function() {
new Messi('<p>Name, Address, Phone Number are removed from the public map</p><p>Details: work type: '
+ this.work_type+ ', floors affected: ' + this.floors_affected + '</p>' + '<p>Status: ' + this.status + '</p>',
{title: 'Case Number: ' + this.case_number, titleClass: 'info',
buttons: [
{id: 0, label: 'Printer Friendly', val: "On the live version, this would send all of this site's data to a printer friendly page." },
{id: 1, label: 'Change Status', val: "On the live version, you would be able to change the site's status here."},
{id: 2, label: 'Edit', val: "On the live version, you would be able to edit the site's info, as new details come in."},
{id: 3, label: 'Claim', val: "On the live version, clicking this button would 'Claim' the site for your organization, letting other organizations know that you intend to work on that site"},
{id: 4, label: 'Close', val: 'None'}], callback: function(val) { if (val != "None") {Messi.alert(val);} }});
});
}
var markerCluster = new MarkerClusterer(map);
markerCluster.addMarkers(markers);
if (run_again == true) {
populateMapByIncident(incident, page + 1, markers);
} else {
markerCluster.addMarkers(markers);
}
}
);
}

I am running multiple ajax calls to download a large number of google maps icons. When I try to increment the Marker Clusterer, however, the map clears all markers. I believe this is because I am calling var markerCluster = new MarkerCluster(map); in each AJAX call.
Can anyone tell me how to correctly implement this?
Don't do that. Create the MarkerClusterer one time in the global scope (outside of any function), and add markers to it when you receive them from the server (assuming you aren't sending any duplicates).
See the documentation
Looks like you are already adding arrays of markers to the MarkerClusterer:
addMarkers(markers:Array., opt_nodraw:boolean) | None | Add an array of markers to the clusterer.
All you really need to do is move where you create the MarkerClusterer to the global scope. One suggestion below.
var markerCluster = new MarkerClusterer(map); // <------------- add this
var populateMapByIncident = function(incident, page) {
var run_again = false;
$.getJSON(
// ----- existing code ------- //
// leave as is
// ----- modification -------- //
// var markerCluster = new MarkerClusterer(map); <----------- remove this
markerCluster.addMarkers(markers);
if (run_again == true) {
populateMapByIncident(incident, page + 1, markers);
} else {
markerCluster.addMarkers(markers);
}

Related

I wish to show route of 'n' number of drivers (single route for each driver)on Gmap. Its a case of dynamic drivers

Currently i am able to display 3 route of 3 drivers respectively(one for each) on a single Gmap.
Now, I wish to show route of 'n' number of drivers (single route for each driver)on Gmap. Its a case of dynamic drivers. I can get data from db for 'n' number of drivers that i need to display on a single map.
My Code is below for single driver please help me for dynamic craetion of routes:
var lat_lng1 = [];
var latlngbounds1 = "";
lat_lng1 = new Array();
var value1 = markers1.length;
//Intialize the Path Array
var path1 = new google.maps.MVCArray();
//Intialize the Direction Service
var service1 = new google.maps.DirectionsService();
var lineSymbol1 = { path1: google.maps.SymbolPath.CIRCLE };
//Set the Path Stroke Color
for (i = 0; i < markers1.length; i++) {
var data1 = markers1[i]
var myLatlng1 = new google.maps.LatLng(data1.lat, data1.lng);
lat_lng1.push(myLatlng1);
var marker1 = new google.maps.Marker({position: myLatlng1,map: map,icon: icon1});
(function (marker1, data1) {
// Attaching a click event to the current marker
google.maps.event.addListener(marker1, "click", function (e) {
if (i == 0){
infoWindow.setContent(data1.Person);infoWindow.open(map, marker1);}
if(i=(markers2.length -1)){
infoWindow.setContent(data1.Person);infoWindow.open(map, marker1);}
else{
infoWindow.setContent(data1.Title);
infoWindow.open(map, marker1);}
});
})(marker1, data1);
}
poly = new google.maps.Polyline({
// path: lineCoordinates,
strokeColor: '#BC456F',
icons: [{
icon: {
path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,strokeColor: '#009900',fillColor: '#009900',fillOpacity: 1},
repeat: '100px',
path1: []
}],
map: map
});
well I solved this issue the very next day by calling ajax loops by driver id

Google Map: Link to the map according to feature type

I have a google Map with markers on a webpage.
Each marker has a unique feature position and type
This is the scenario I would like to put in place:
On another webpage I have static links to different markers of the map.
If you click on one of those links, you are directed to the map in which, one of these markers is centered (and its info window open).
But the markers latitude and longitude might change while the links will never change.
This means, I need the links not to use latitude and longitude info but markers feature type instead (which are remain the same).
How can I do that?
Here is my sample google Map script so far:
<script>
function initMap() {
var mapDiv = document.getElementById('map');
var map = new google.maps.Map(mapDiv, {
center: {lat: 48.85639, lng: 2.33625}, // default centering
zoom: 18,
styles:
[
{featureType: 'poi',stylers: [{ visibility: 'off' }]},
{featureType: 'transit.station',stylers: [{ visibility: "off" }]}
]
});
var features = [
{position: new google.maps.LatLng(48.85659, 2.33555),type: 'markerone'},
{position: new google.maps.LatLng(48.85619, 2.33695),type: 'markertwo'}
];
var icons = {
'markerone': {icon: 'icon_one.png'},
'markertwo': {icon: 'icon_two.png'}
};
var contents= {
'markerone': {text: 'Content 1'},
'markertwo': {text: 'Content 2'}
};
for (var i = 0, feature; feature = features[i]; i++)
{
var marker = new google.maps.Marker({
position: feature.position,
icon: icons[feature.type].icon,
map: map
});
var content = contents[feature.type].text;
var infowindow = new google.maps.InfoWindow()
google.maps.event.addListener(marker,'mouseover', (function(marker,content,infowindow){
return function() {
infowindow.setContent(content);
infowindow.open(map,marker);
};
})(marker,content,infowindow));
google.maps.event.addListener(marker,'mouseout', (function(marker,content,infowindow){
return function() {
infowindow.close(map,marker);
};
})(marker,content,infowindow));
}
}
</script>
In this sample, I have to markers.
One has a feature type of "markerone" and the second is "markertwo".
How can I set my links to redirect and center the map around a specific marker in this kind of fashion:
http://www.mywebsite.com/mymap.php?myvariable=markertwo
Thank you.
First you would have to get the parameters. The example below gets all parameters and put them into an array. There you can search for your certain paramater like "markerType" and check if it's given or not. If not you have to perform a default action, otherwise you can handle the certain markerType like finding the correct marker, setting the map center to it and open the corrosponding infoWindow.
You just have to call the focusMarkerType-method onload of your page.
function getSearchParameters() {
var prmstr = window.location.search.substr(1);
return prmstr != null && prmstr != "" ? transformToAssocArray(prmstr) : {};
}
function transformToAssocArray( prmstr ) {
var params = {};
var prmarr = prmstr.split("&");
for ( var i = 0; i < prmarr.length; i++) {
var tmparr = prmarr[i].split("=");
params[tmparr[0]] = tmparr[1];
}
return params;
}
function focusMarkerType(){
var params = getSearchParameters();
if(params.markerType!=null){
//Handling the certain marker type
var found = false;
for (var i = 0, feature; feature = features[i]; i++) {
if (feature.type == params.markerType) {
found = true;
map.setCenter(feature.position);
//more...
break;
}
}
if (!found) {
console.log("unknown type")
}
}else{
//Handling default behaviour if no marker type is given
}
}

Google Maps v3 MarkerManager v1.1 LatLngToPixel undefined

I’m having problems upgrading to Google Maps v3 from v2. In particular with MarkerManager. I haven’t worked with maps before and am helping out someone whose developer abandoned them who developed the maps in v2.
The map is to show a maximum of 100 markers that when clicked on show an infobox. (There is also a telerik RadGrid bound to with the results.) It all seems to work until it gets into DisplayAllMarkers() mgr.addMarkers() (see below). It fails in ProjectionHelperOverlay.prototype.LatLngToPixel with Uncaught TypeError: Cannot call method ‘lng’ of undefined. I’ve tried putting in alerts in AddMarker if lat or lng are undefined, but none were alerted. The previous calls to MarkerManager.prototype.addMarkerBatch_ set mPoint to undefined which was then passed to getTilePoint which calls LatLngToPixel where it fails.
I’m thinking it’s a problem with listeners, but have tried various listeners without success. I would really appreciate some help with this as I have been unable to make progress for some time and am letting down the client and don’t know what to try next.
Select SearchResult.js snippet of code:
$(document).ready(function () {
InitializeMap(originalScale);
});
var map;
var batch = [];
var mgr;
var geocoder = null;
function InitializeMap(scale) {
map = new google.maps.Map(
document.getElementById('MapContainer'), {
center: new google.maps.LatLng(Latitude, Longitude),
zoom: scale,
mapTypeId: google.maps.MapTypeId.ROADMAP,
scrollwheel: false
});
var listener = google.maps.event.addListener(map, "bounds_changed", function () {
MapMoved();
google.maps.event.removeListener(listener);
});
}
var south, west, north, east, zoom;
function MapMoved() {
zoom = map.getZoom();
//zoom = map.setZoom();
var bounds = map.getBounds();
var sw = bounds.getSouthWest();
var ne = bounds.getNorthEast();
south = sw.lat();
west = sw.lng();
north = ne.lat();
east = ne.lng();
Search();
}
The Search(); loads up the search parameters (including the north, south, east, west co-ordinates) and calls a telerik ajax request.
C# snippet:
if (Properties.Count >= 1 || zoom == "0") //display properties
{
RadAjaxManager1.ResponseScripts.Add("ClearAll();");
Properties.ForEach(p =>
{
string script = "AddMarker(" + p.ID.ToString() + "," + p.LatitudeShort.ToString() + "," + p.LongitudeShort.ToString() + ");";
RadAjaxManager1.ResponseScripts.Add(script);
});
RadAjaxManager1.ResponseScripts.Add("DisplayAllMarkers();");
}
else //zoom out map – which will start the search process all over again.
{
string delta = "1";
RadAjaxManager1.ResponseScripts.Add("ZoomOut(" + delta + ");");
}
SearchResult.js snippet continued:
function ClearAll() {
batch = [];
}
function AddMarker(id, lat, lng) {
batch.push(CreatePropertyMarker(lat, lng, id));
}
function DisplayAllMarkers() {
mgr = new MarkerManager(map, { trackMarkers: true });
google.maps.event.addListener(mgr, 'loaded', function () {
mgr.clearMarkers();
mgr.addMarkers(batch, 3, 17); // **Failing in here!!!**
mgr.refresh();
});
}
function CreatePropertyMarker(lat, lng, id) {
var redIcon3 = {
url: '../images/HouseIcon_ForGoogleMap.png',
size: new google.maps.Size(22, 22),
origin: new google.maps.Point(0, 0),
anchor: new google.maps.Point(0, 22)
};
var marker3 = new google.maps.Marker(new google.maps.LatLng(lat, lng), redIcon3);
google.maps.event.addListener(marker3, 'click', function () {
var data = "{'Id': '" + id + "'}";
$.ajax({
type: "POST",
dataType: "json",
contentType: "application/json",
url: 'searchresult.aspx/GetPropertyPopOut',
data: data,
success: function (data) {
var popout = data.d;
marker3.openExtInfoWindow(map, "custom_info_window_red", popout, { beakOffset: -1 });
},
error: function (result) {
alert('status:' + result.status + ' statusText:' + result.statusText);
}
});
});
return marker3;
}
Script order and versions:
jquery-ui-1.9.1.custom.min.css
Google Maps: v3 (I have also tried specifying particular version 3 numbers e.g. v=3.12 http://maps.googleapis.com/maps/api/js?key=MY_KEY&sensor=false
jquery-1.8.2.js
jquery-ui-1.9.1.custom.min.js
extinfowindow.js v2.0
MarkerManager.js v1.1
SearchResult.js
jquery-ui-1.8.16.custom.min.js
jquery-1.6.2.min.js
After tying a load of different things I eventually found out the problem was when creating the marker it needed position set. I changed the line in CreatePropertyMarker from:
var marker3 = new google.maps.Marker(new google.maps.LatLng(lat, lng), redIcon3);
to:
var marker3 = new google.maps.Marker({
position: new google.maps.LatLng(lat, lng),
icon: redIcon3
});

Cluster google map pins when using an object of markers instead of an array

I'm managing this website: http://www.gprprojecten.nl
Basically what I'm doing with is is getting a stream of data from a webservice and chaching this. Afterwards I do all kinds of things to this data, including displaying all the projects on a google map.
Problem is, that as the projectnumber grows, the (custom)pins start to overlap (not visible, not clickable).
So I want to cluster my pins.
I started working on a google maps clusterer library using A32.blog as my starting point.
But now a problem arises. As I have chosen a client side filter system called filter.js, I depend on that way of using google maps, which is with objects rather then an array of markers as used in the clusterer library.
Here is the code I use to generate my map in combination with filter.js:
var googleMap = {
center_lat_lng: [52.14, 5.3],
map: null,
markers: {},
addMarker: function (project) {
var that = this;
var image = new google.maps.MarkerImage('#Url.Content("~/Content/img/marker.png")',
new google.maps.Size(63, 27),
new google.maps.Point(0, 0),
new google.maps.Point(23, 27));
var marker = new google.maps.Marker({
position: new google.maps.LatLng(project.loclat, project.loclong),
map: this.map,
title: project.gebouwnaam,
icon: image
});
marker.info_window_content = "<div class=\"gmapcontentwindow\"><h4>" + project.gebouwnaam + "</h4><img src=\"" + project.thumburl + "\" alt=\"" + project.gebouwnaam + "\" /><div class=\"gebouwdata\"><span class=\"date\">" + project.publicatiedatum + "</span><h5>" + project.plaats + "</h5><span>" + project.gebruiksfunctie + " | " + project.gebouwcategorie + "</span><span>" + project.licentiehouder + "</span><span class=\"stars " + project.rating + "\"></span>"
if (project.akkoorddoorexpert == "True") {
marker.info_window_content += "<span class=\"expert\"></span>"
}
var forwardUrl = '#Url.Content("~/Home/Details/")';
marker.info_window_content += "<span class=\"forward\">Lees meer >></b></span></div></div>"
this.markers[project.id] = marker
google.maps.event.addListener(marker, 'click', function () {
that.infowindow.setContent(marker.info_window_content)
that.infowindow.open(that.map, marker);
});
},
updateMarkers: function (filtering_result) {
var google_map = this;
$.each(google_map.markers, function () { this.setMap(null); })
$.each(filtering_result, function () {
google_map.markers[this.id].setMap(google_map.map);
});
},
init: function () {
var options = {
center: new google.maps.LatLng(this.center_lat_lng[0], this.center_lat_lng[1]),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
this.map = new google.maps.Map(document.getElementById("gmap"), options)
this.infowindow = new google.maps.InfoWindow({
});
//var markersarray = $(this.markers).toArray();
//console.log(markersarray);
//doet het nog niet???
//http://a32.me/2012/05/handling-huge-amount-of-markers-on-google-maps-with-clusters/
//var markerCluster = new MarkerClusterer(this.map, markersarray, {
// gridSize: 10,
// minimumClusterSize: 2
//});
}
};
project is an object that is passed to the init function from filter.js
You need to add the Marker Clusterer to the global scope or keep a reference to it in your object.

Google Maps API with backbone, how to bind an event

I've seen several other posts about this, but the answers from those responses don't work for me.
The other responses:
How do I bind a google maps geocoder.geocode() callback function
Backbone.js with Google Maps - problems with this and listeners
My code:
var ns = namespace('camelcase.geomanager.map');
ns.Site = Backbone.Model.extend({
url: '/site'
});
ns.Sites = Backbone.Collection.extend({
model: ns.Site
});
ns.MapView = Backbone.View.extend({
initialize: function() {
this.markers = new Array();
// Create the Google Map
var mapOptions = {
center: new google.maps.LatLng(-34.397, 150.644),
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
this.googleMap = new google.maps.Map(this.$(".mapCanvas")[0], mapOptions);
// Register events
this.collection.on('add', this.addSite, this);
this.collection.on('remove', this.removeSite, this);
},
addSite: function(model) {
// Get model attributes
var elementId = model.get('elementId');
var latitude = model.get('latitude');
var longitude = model.get('longitude');
var id = model.get('id');
var notes = model.get('notes');
var title = ""+id;
// Create icon and marker
var icon = '/resources/img/elements/' + elementId + '_marker.png';
var latLng = new google.maps.LatLng(latitude, longitude);
var marker = new google.maps.Marker({
position: latLng,
title: title,
map: this.googleMap,
icon: icon
});
// Load info window
var siteBubbleTemplate = _.template($('#siteBubbleTemplate').html());
var siteContent = $(siteBubbleTemplate({
siteId: id,
siteNotes: notes
}))[0];
var infoWindow = new google.maps.InfoWindow({
content: siteContent
});
// Show info window when clicking on marker
_.bindAll(this, this.openSite);
google.maps.event.addListener(marker, 'click', this.openSite(id));
this.markers.push({
id: id,
marker: marker,
infoWindow: infoWindow
});
},
openSite: function(id) {
var marker;
for (var c=0; c<this.markers.length; c++) {
marker = this.markers[c];
// Open the appropriate marker info window
if (marker.id == id) {
marker.infoWindow.open(googleMap, marker.marker);
}
// Close the rest
else {
marker.infoWindow.close();
}
}
}
});
The offending line:
google.maps.event.addListener(marker, 'click', this.openSite(id));
The error being reported in firebug:
TypeError: func is undefined
underscore.js (line 482)
I suspect this.marker is the problem, since you should be able to just refer to it by name.
google.maps.event.addListener(marker, 'click', this.openSite(id));
Looks like it was a scoping issue. I solved my problem with the following code:
// Show info window when clicking on marker
_.bindAll(this);
var _self = this;
var doSomething = function(event) {
_self.openSite({
event: event
});
};
google.maps.event.addListener(marker, 'click', doSomething);
I'll give the answer to whoever can best explain why this works.
In a model/collection event handler, Backbone sets 'this' to the model/collection which raised the event. If you call _.bindAll(this) in your view's initialize(), 'this' will be set to the view in your event handlers. Check out this jsfiddle: http://jsfiddle.net/9cvVv/339/ and see what happens when you uncomment _.bindAll(this);
var MyView = Backbone.View.extend({
initialize: function() {
// TODO: uncomment this line
// _.bindAll(this);
this.collection.bind('myEvent', this.onDoSomething);
},
updateCollection: function() {
this.collection.doSomething();
},
onDoSomething: function() {
if (this.models && typeof this.models.length === 'number') {
alert('"this" is a collection.');
}
else if (this.collection) {
alert('"this" is the view.');
}
else {
alert('"this" is something else.');
}
}
});

Resources