Google Map freezes after repainting MarkerClusterPlus - javascript

I've got a map with a few MarkerWithLabel objects on it (http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerwithlabel/1.1.9/). The labels, in this case, are integers.
I also have a MarkerClustererPlus (http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerclustererplus/2.1.2/) which also works fine.
However, I want to change the text on Clusters to show the sum of those integers on labels for every MarkerWithLabel inside a Cluster.
I did that by binding this function to the end of clustering:
function calculateClusterLabels() {
$.each(markerCluster.clusters_, function(i, cluster){
var sum = 0;
var cluster_markers = cluster.getMarkers();
$.each(cluster_markers, function(j, marker) {
sum += marker.labelContent;
});
cluster.clusterIcon_.sums_['text'] = sum;
cluster.updateIcon(); // also tried cluster.repaint();
});
}
And that works - at least for the Cluster text. But now we are getting to the real problem: it freezes the whole Map. Raven.js catches this: Uncaught TypeError: undefined is not a function. But nothing clearer than this.
Any ideas?
EDIT:
Some more code. Data is fetched with ajax and then the markers are set in a loop:
$.each(us_data, function(k, v) {
var markerPosition = new google.maps.LatLng(us_data[k]['lat'], us_data[k]['lon']);
var marker = new MarkerWithLabel({
position: markerPosition,
draggable: false,
map: map,
labelContent: us_data[k]['count'],
labelAnchor: anchor,
labelClass: "marker-with-label"
});
markers.push(marker);
});
And then I make the Clusters and bind the event:
markerCluster = new MarkerClusterer(map, markers, {imagePath: 'https://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclustererplus/images/m'});
google.maps.event.addListener(markerCluster, 'clusteringend', function() {
calculateClusterLabels();
});
This all happens inside the .done() of ajax, but markerCluster and markers are visible outside.

To answer myself: well, it was fairly simple after all:
function calculateClusterLabels() {
$.each(markerCluster.clusters_, function(i, cluster){
var sum = 0;
var cluster_markers = cluster.getMarkers();
$.each(cluster_markers, function(j, marker) {
sum += marker.labelContent;
});
if (cluster.clusterIcon_.sums_ != null) {
cluster.clusterIcon_.sums_['text'] = sum;
}
});
}
What I did - I added a simple if statement to check whether the .sums_ object is not null (because it wasn't null only on the visible clusters that had markers inside) and I omitted the .updateIcon call and everything works perfectly, no errors.
An even better solution, for my case, was to simply change the markerclusterer.js source file:
MarkerClusterer.CALCULATOR = function (markers, numStyles) {
var index = 0;
var title = "";
var count = 0;
if (typeof markers[0].labelContent != 'undefined') {
var sum = 0;
var i;
for (i = 0; i < markers.length; ++i) {
if (!isNaN(markers[i].labelContent) {
sum += markers[i].labelContent;
} else {
// whatever we need, perhaps we want to calculate it differently
}
}
count = sum.toString();
} else {
count = markers.length.toString();
}
var dv = count;
while (dv !== 0) {
dv = parseInt(dv / 10, 10);
index++;
}
index = Math.min(index, numStyles);
return {
text: count,
index: index,
title: title
};
};
This was even better because it updated Cluster styles, as well and it works both with regular Marker and MarkerWithLabel objects.

Related

Cant get my marker's latLng to use in L.Routing.control

guys
I been trying to get my markers latlon when user double click on it but still don't get any results. Been trying other methods but i think this is the most accurate since i dont get any error when executing js
Any recommendation pls
var places = [
["LOCATION_1", 8.9856146341374, -79.51102268985925],
["LOCATION_2", 8.984640842221594, -79.51383510471848],
["LOCATION_3", 8.972080043026754, -79.5529245611453],
["LOCATION_4", 9.052896045979661, -79.4515923525883],
["LOCATION_5", 9.053366385577624, -79.50832832626823]
];
var map = L.map('map', {
center: [9.352867999999996, -79.689331],//[35.791188, -78.636755],
zoom: 9,
layers:L.tileLayer('http://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{
maxZoom: 20,
subdomains:['mt0','mt1','mt2','mt3']
})
});
for (var i = 0; i < places.length; i++) {
marker = new L.marker([places[i][1], places[i][2]])
.bindPopup(places[i][0])
.addTo(map);
}
function getdest(){
L.marker.on('dblclick',function(e){
var latlng_dest=e.latlng() });
console.log(latlng_dest)
return latlng_dest
}
navigator.geolocation.getCurrentPosition(function(location) {
var latlng_orig = new L.LatLng(location.coords.latitude, location.coords.longitude);
L.Routing.control({
waypoints: [
//L.latLng(9.10607301250145, -79.34754531445351),
L.latLng(latlng_orig)
//,L.latLng(latlng_dest)
//,L.latLng(9.100769244670843, -79.35099352767948)
,L.latLng(getdest())
]
}).addTo(map)
});
You have many common things wrong:
e.latlng() is not a function it is a property e.latlng
L.marker.on('dblclick',function(e){ this makes no sense. You creating a new instance of a Marker without coords and then adding a listener to it.
You can't return a value in a function from a listener. The listener is not called at the moment you return the value L.marker.on('dblclick',function(e){ var latlng_dest=e.latlng() }); return latlng_dest
Your code should look like that:
for (var i = 0; i < places.length; i++) {
marker = new L.marker([places[i][1], places[i][2]])
.bindPopup(places[i][0])
.addTo(map)
.on('dblclick', function(e) {
waypoints.push(e.latlng);
routeControl.setWaypoints(waypoints);
});
}
var routeControl = L.Routing.control({
waypoints: [],
}).addTo(map);
var waypoints = [];
navigator.geolocation.getCurrentPosition(function(location) {
var latlng_orig = new L.LatLng(location.coords.latitude, location.coords.longitude);
waypoints.push(latlng_orig);
});

javascript event function being called multiple times

I am integrating maps on a webpage with Overlapping Marker Spiderfier on google maps. I added a click listener on the marker as below.
$scope.setMarkers = function() {
for (var i = 0; i < $scope.markers.length; i++) {
$scope.markers[i].setMap($scope.map);
$scope.oms.addMarker($scope.markers[i]);
var marker = $scope.markers[i];
var iw = new google.maps.InfoWindow({
content: ""
});
$scope.oms.addListener('click', function(marker) {
iw.setContent(marker.desc);
iw.open($scope.map, marker);
});
}
};
and it works fine but jshint is giving me error for making function inside loop. So i changed it to.
$scope.setMarkers = function() {
for (var i = 0; i < $scope.markers.length; i++) {
$scope.markers[i].setMap($scope.map);
$scope.oms.addMarker($scope.markers[i]);
$scope.addMarkerEventListener(i);
}
};
$scope.addMarkerEventListener = function(i) {
var marker = $scope.markers[i];
var iw = new google.maps.InfoWindow({
content: ""
});
$scope.oms.addListener('click', function(marker) {
iw.setContent(marker.desc);
iw.open($scope.map, marker);
});
};
now when I am clicking on the marker its opening upto 90 info windows one behind another(i have 90 markers in an array). What am i missing.
you add the same listener each time you add a marker(Note that a listener will not overwrite previously added listeners).
It's sufficient to add 1 listener and to use 1 InfoWindow(except you want to have multiple InfoWindow's open at the same time).
Your first attempt works as expected because you overwrite iw inside the loop.
Possible solution:
//create a single InfoWindow-instance
$scope.iw = new google.maps.InfoWindow();
//add a single click-listener
$scope.oms.addListener('click', function (marker) {
$scope.iw.close();
$scope.iw.setContent(marker.desc);
$scope.iw.open($scope.map, marker);
});
//the loop
for (var i = 0; i < $scope.markers.length; ++i) {
$scope.markers[i].setMap($scope.map);
$scope.oms.addMarker($scope.markers[i]);
}
Demo: http://jsfiddle.net/doktormolle/qoko4425/

Remove markers out of viewport

I have to manage a map of about 80.000 markers concentrated in France.
To do that, I decided to get the bounds of the viewport and call a dynamic-JSON (with PHP) which contains the markers inside the viewport. And this on the "idle" event.
I faced a problem with this solution. Indeed, the markers which already exist was re-plotted (at the same position), which consequently weigh the map for nothing...
To solve it, the markers list before and after the JSON query are compared (thanks to jQuery), in order to plot only the new markers. And it works!
Now, I would want to remove the markers which are not currently shown on the map. Or a list of markers (I get it thanks to jQuery) designated by an ID which is also the title of the marker. So, how can a delete markers like that ? I specify that I am using MarkerManager.
Otherwise, you guess that if I do not remove these markers, they will be re-plotted in some cases... For example, you are viewing the city A, you move the map to view the city B, and you get back to the city A...
Here is the code:
var map;
var mgr;
var markers = [];
function initialize(){
var mapOptions = {
zoom: 6,
center: new google.maps.LatLng(46.679594, 2.109375)
};
map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);
var mgrOptions = { borderPadding: 50, maxZoom: 15, trackMarkers: false };
mgr = new MarkerManager(map, mgrOptions);
google.maps.event.addListener(map, 'idle', function() {
mapEvent();
});
}
function mapEvent(){
if( map.getZoom() >= 8 ){
var bounds = map.getBounds();
getSupports(bounds.getNorthEast(), bounds.getSouthWest());
} else {
// Todo
}
}
var markerslistID = new Array();
var markerslistData = {};
function getSupports(ne, sw){
newMarkerslistID = new Array();
newMarkerslistData = {};
// Getting the markers of the current view
$.getJSON('./markerslist.php?nelat='+ne.lat()+'&nelng='+ne.lng()+'&swlat='+sw.lat()+'&swlng='+sw.lng(), function(data) {
for (var i = 0; i < data.points.length; i++) {
var val = data.points[i];
newMarkerslistID.push(val.id);
newMarkerslistData[val.id] = new Array(val.lat, val.lng, val.icon);
}
// List of New Markers TO PLOT
var diffNewMarkers = $(newMarkerslistID).not(markerslistID).get();
// List of Old markers TO REMOVE
var diffOldMarkers = $(markerslistID).not(newMarkerslistID).get();
// Plotting the NEW MARKERS
for( var i = 0; i < diffNewMarkers.length; i++ ){
var marker = new google.maps.Marker({
position: new google.maps.LatLng(newMarkerslistData[diffNewMarkers[i]][0], newMarkerslistData[diffNewMarkers[i]][1]),
title : diffNewMarkers[i],
icon : './images/'+newMarkerslistData[diffNewMarkers[i]][2]+'.png'
});
mgr.addMarker(marker, 0);
}
/*****************************************
HERE WE HAVE TO REMOVE
THE MARKERS CONTAINED IN diffOldMarkers
*****************************************/
mgr.refresh();
// Switching the new list to the old, for the next event
markerslistID = newMarkerslistID;
markerslistData = newMarkerslistData;
});
}
Thank you for your help.
A one-liner to hide all markers that ar not in the current viewport.
!map.getBounds().contains(marker.getPosition()) && marker.setVisible(false);
Or,
if (map.getBounds().contains(marker.getPosition()) && !marker.getVisible()) {
marker.setVisible(true);
}
else if (!map.getBounds().contains(marker.getPosition()) && marker.getVisible()) {
marker.setVisible(false);
}

Show a moving marker on the map

I am trying to make a marker move(not disappear and appear again) on the map as a vehicle moves on the road.
I have two values of latLng and I want to move the marker between the two till the next point is sent by the vehicle. And then repeat the process again.
What I tried:[This is not a very efficient way, I know]
My thought was to implement the above using the technique in points below:
1) Draw a line between the two.
2) Get the latLng of each point on 1/10th fraction of the polyline.
3) Mark the 10 points on the map along with the polyline.
Here is my Code:
var isOpen = false;
var deviceID;
var accountID;
var displayNameOfVehicle;
var maps = {};
var lt_markers = {};
var lt_polyLine = {};
function drawMap(jsonData, mapObj, device, deleteMarker) {
var oldposition = null;
var oldimage = null;
var arrayOflatLng = [];
var lat = jsonData[0].latitude;
var lng = jsonData[0].longitude;
//alert(jsonData[0].imagePath);
var myLatLng = new google.maps.LatLng(lat, lng);
if (deleteMarker == true) {
if (lt_markers["marker" + device] != null) {
oldimage = lt_markers["marker" + device].getIcon().url;
oldposition = lt_markers["marker" + device].getPosition();
lt_markers["marker" + device].setMap(null);
lt_markers["marker" + device] = null;
}
else {
console.log('marker is null');
oldimage = new google.maps.MarkerImage(jsonData[0].imagePath,
null,
null,
new google.maps.Point(5, 17), //(15,27),
new google.maps.Size(30, 30));
oldposition = myLatLng;
}
}
var image = new google.maps.MarkerImage(jsonData[0].imagePath,
null,
null,
new google.maps.Point(5, 17), //(15,27),
new google.maps.Size(30, 30));
lt_markers["marker" + device] = new google.maps.Marker({
position: myLatLng,
icon: image,
title: jsonData[0].address
});
if (oldposition == myLatLng) {
alert('it is same');
lt_markers["marker" + device].setMap(mapObj);
mapObj.panTo(myLatLng);
}
else {
alert('it is not same');
var markMarker = null;
var i = 10;
for (i = 10; i <= 100; i + 10) {
//-------
// setTimeout(function() {
if (markMarker != null) {
markMarker.setMap(null);
markMarker = null;
}
alert('inside the loop');
var intermediatelatlng = mercatorInterpolate(mapObj, oldposition, myLatLng, i / 100);
alert('Intermediate Latlng is :' + intermediatelatlng);
arrayOflatLng.push(intermediatelatlng);
var flightPath = new google.maps.Polyline({
path: arrayOflatLng,
strokeColor: "#FFFFFF",
strokeOpacity: 1.0,
strokeWeight: 1
});
flightPath.setMap(mapObj);
if (i != 100) {
markMarker = new google.maps.Marker({
position: intermediatelatlng,
icon: image,
title: jsonData[0].address,
map: mapObj
});
}
else {
markMarker = new google.maps.Marker({
position: intermediatelatlng,
icon: oldimage,
title: jsonData[0].address,
map: mapObj
});
}
mapObj.panTo(intermediatelatlng);
//--------
// }, 1000);
}
}
}
function mercatorInterpolate(map, latLngFrom, latLngTo, fraction) {
// Get projected points
var projection = map.getProjection();
var pointFrom = projection.fromLatLngToPoint(latLngFrom);
var pointTo = projection.fromLatLngToPoint(latLngTo);
// Adjust for lines that cross the 180 meridian
if (Math.abs(pointTo.x - pointFrom.x) > 128) {
if (pointTo.x > pointFrom.x)
pointTo.x -= 256;
else
pointTo.x += 256;
}
// Calculate point between
var x = pointFrom.x + (pointTo.x - pointFrom.x) * fraction;
var y = pointFrom.y + (pointTo.y - pointFrom.y) * fraction;
var pointBetween = new google.maps.Point(x, y);
// Project back to lat/lng
var latLngBetween = projection.fromPointToLatLng(pointBetween);
return latLngBetween;
}
Problems Faced:
1) The marker is not showing up on the map because the process of plotting and removal of marker is so fast that the marker is not visisble on screen. I've tried setTimeOut, and It does not help at all.
2) if I alow the browser to run this code for more than 5 minutes, the browser crashes.
Note: The Above function is called every 10 seconds using setInterval.
What Can be a better solution? Please Help..
For the marker to move relatively smoothly, you need to
Update more than every 1/10 fraction of the polyline (at least every few pixels)
Call the update method more frequently
Don't delete and re-add the marker
For example, something like:
var counter = 0;
interval = window.setInterval(function() {
counter++;
// just pretend you were doing a real calculation of
// new position along the complex path
var pos = new google.maps.LatLng(35, -110 + counter / 100);
marker.setPosition(pos);
if (counter >= 1000) {
window.clearInterval(interval);
}
}, 10);
I made a simple example at http://jsfiddle.net/bmSbU/2/ which shows a marker moving along a straight path. If this is what you want, most of your code above regarding where along the line you are can be reused (or check out http://broady.github.io/maps-examples/points-along-line/along-directions.html )
You can use marker-animate-unobtrusive library to make markers
smoothly transition from one location to another (instead of reappearing).
You could initialize your marker like that:
var marker = new SlidingMarker({
//your original marker options
});
Just call marker.setPosition() each time new vehicle's coordinate arrive.
P.S. I'm the author of the library.
Why not keep the existing Marker/ MarkerImage and call setPosition() to move it, either on a timer or as the position changes?
Deleting it & recreating it is what causes it to flash/ flicker and eventually crash. If you keep the same instance but just move it, you should do much better.
See: Marker.setPosition()
https://developers.google.com/maps/documentation/javascript/reference#Marker

google maps v3: Invalid value for constructor parameter 0 while drawing polylines

I have a problem when constructing a polygon. The error message says something like:
Invalid value for constructor parameter 0: (49.27862248020283, -122.79301448410035),(49.277964542440955, -122.79370112960816),(49.278524490028595, -122.7950207764435)
It must be something ridiculously simple, but I just can't see it. Any tips you have are useful.
I'm basically painting a map inside an iframe on a modal window (with wicket). Everything is ok, but when I'm trying show a polygon (the points are loaded from a database and sent by webservice) I get the error message.
iframe code: (only the relevant)
/**
* Draws the polygon.
*/
function drawPolygon() {
if (order >= 3) {
deleteMarkers();
// Construct the polygon
// Note that we don't specify an array or arrays, but instead just
// a simple array of LatLngs in the paths property
polygonObject = new google.maps.Polygon({
paths: polygonCoords,
strokeColor: "#FF0000",
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: "#FF0000",
fillOpacity: 0.35
});
polygonObject.setMap(map);
isPolygonDrawed = true;
//After we create the polygon send the points to wicket
parent.sendPoints();
//Change the message on the top label
controlText.style.color = '#ADAAAA';
controlText.innerHTML = polygonCreated;
//With this we make sure no other markers are created after the polygon is drawed.
//Is assigned (order - 1) because when this code is called the order has already been added 1.
MAX_POLYGON_VERTEX = order - 1;
//Disable the create polygon button.
enable = false;
createControlText.style.color = '#ADAAAA';
}
else alert(alertMessage);
}
Now the code on the parent (the modal window)
/**
* Show the polygon on map.
*/
function showPolygon(zoneId) {
var url = applicationRootUrl + 'zonePointsOnMap?zoneId=' + zoneId;
$.getJSON(url, function(data) {
if(data.length == 0) {
return false;
}
frames['zoneMapIFrame'].order = parseInt(data.length);
alert(data.length);
$.each(data, function(i, item) {
if(item != null) {
if(item.latitude != null && item.longitude != null) {
var lat = parseFloat(item.latitude);
var lng = parseFloat(item.longitude);
var latlng = new google.maps.LatLng(lat, lng);
var pointOrder = item.order;
frames['zoneMapIFrame'].polygonCoords[pointOrder] = latlng;
alert(item.order + " point " + latlng);
frames['zoneMapIFrame'].bounds.extend(latlng);
}
}
});
});
setTimeout("frames['zoneMapIFrame'].drawPolygon()", 200);
setTimeout("frames['zoneMapIFrame'].fitMapZoomPolygon()", 300);
}
I can see that the points are loaded ok with alerts, but I keep getting the error message.
Help me!
I was having the same problem.
Don't know if its the best solution, probably not, but it worked for me.
The problem was that the Latlng weren't being recognized. So I recreated the array.
var lats = [];
var lat_size = steps[step].lat_lngs.length;
for (var t=0; t <lat_size; t++) {
lats.push(new google.maps.LatLng(steps[step].lat_lngs[t].lat(), steps[step].lat_lngs[t].lng()))
}
var polylineOptions = {
map: map,
path: lats
}
new google.maps.Polyline(polylineOptions);

Categories

Resources