Google Maps API v3: Markers not being removed - google-maps

I'm creating a map that loads & destroys markers based on both the bounding box and zoom level. I'm having a real problem getting markers to properly be removed, it seems to work sometimes for certain situations.
I have an object that contains the marker info, which also contains the google maps marker object. My code detects if the market should be deleted based on the bounding box or zoom-level. I set the marker object to "setMap(null);" and using firebug I can see that its being set, I then remove the parent object entirely and the objects data length is updated properly.
I output to the firebug console when a marker is supposedly deleted, seems to be working and I can see that the marker isn't being re-crated from the ajax call for markers on the boundingbox change.
Yet if I zoom around the map I can sometimes see that the markers are being removed, if I zoom away then pan back holding the mouse down. Or sometimes the markers will all be removed if I zoomout the first time, but if I zoom in again then back out they are not removed.
I must be doing something wrong with the logic of my code, I'm stumped.
You can view the source of
http://www.trailforks.com/map/test.php?lat=49.352247&lon=-123.202413
the JS is
http://www.trailforks.com/map/includes/map.js
the code for deleting a marker is at the bottom
function clearMarkerMemory(mapItem, i) {
google.maps.event.removeListener(mapItem.lis); // remove stored listener
mapper.data[i].obj.setMap(null); // remove marker
mapper.data.splice(i, 1);
console.log("removed marker "+mapItem.icon+":"+mapItem.nid+' '+mapItem.name);
};
I added some more debug into to the console, going to a simple area of the map with only 2 markers http://www.trailforks.com/map/test.php?lat=49.43210641783767&lon=-123.49878636730955&z=14
I can see the markers created, then move the map a bit and see that the markers weren't re-created because they were detected in the marker object. I then move the viewport so one of the markers is off the screen and I can see that the marker is removed and the marker object length updates. But if I pan the map back over the marker is still on the map.

I was struggling with similar problem for a long while until I realized that the map marker's setMap-method is asynchronous. When you call that and immediately remove any references to that marker object, the browser's garbage collector steps in and cleans it up from the memory and thus prevents the actual remove operation from happening.
Try it out by just commenting out the line with the splice call and see if that helps. If it does help, you should consider delaying the removal of the object, or alternatively storing the reference to the marker object until it's really removed. How to detect if it's really removed? I have no idea.
I hope this helps!

Instead of doing:
google.maps.event.addListener(map, 'dragend', function() { refreshMarkers(); }); //refresh markers when user moves map
google.maps.event.addListener(map, 'zoom_changed', function() { refreshMarkers(); }); //refresh markers when user moves map
change it to:
EDIT, (after comments):
To prevent multiple instances of the event handler occurring simultaneously, a global variable could be used, as follows:
google.maps.event.addListener(map, 'bounds_changed', function() {
if (processing) { // var processing is global
return;
}
processing = true;
refreshMarkers();
processing = false;
}); //refresh markers when user moves map
That should cover both situations. As it is now, with two different event listeners, the AJAX calls could be conflicting, and you maybe firing a second call before the first one has completed.

Related

Wrong positioning of Google Maps infoWindow

I have a website at http://arquitectospelomundo.com which displays several markers, combined by the markerclusterer function, which display, when clicked, an infowindow with some info. When clicking directly on the map, the info window appears on the right spot; but when clicking on the sidebar (on one of the pictures), the info window goes out of bounds.
This was working correctly and suddenly changed; I am trying to figure it out but so far with no success. I would appreciate any help pointing me in the right direction.
Thank you.
As it seems the issue is related to the MarkerClusterer.
When the marker where the click has been triggered is inside a cluster the map-property of the marker is null and you get the wrong position.
Possible solution:
When the marker is within a cluster use a hidden marker(hidden via visible) as anchor for the infoWindow:
google.maps.event.addListener(marker, 'click', function() {
//create the dummy-marker on first click
if(!map.get('dummy')){
map.set('dummy',new google.maps.Marker({map:map,visible:false}))
}
var latLng = marker.getPosition();
//when the marker is within a cluster
if(!marker.getMap()){
//set position of the dummy
map.get('dummy').setPosition(latLng);
//use dummy as infowindow-anchor
marker= map.get('dummy');
}
map.panTo(latLng);
infowindow.setContent(contentString);
infowindow.open(map,marker);
});
I had the same problem with Marker clusterer and outside clicks to the map, opening infowindows. The solution by #Dr.Molle with dummy markers is a nice direction, but the problem is that you can use the dummy to pan to, but the listener triggers a click on the marker which is not on the map. The correct infowindow opens, but the autopan feature has no way of knowing where to pan to, so it stays where it was after the last click. This is a problem with repeated clicks from outside the map. The first click goes okay, but the second or third one goes wrong, depening on if it was in a cluster or not.
The solution I found tests if the marker is in a cluster, if yes, attaches it to the map, and removes it after the click (not sure if that is neccessary, maybe to prevent the map from getting slow). I put it in the function that is called with each click from outside. But maybe you can also build it into the event listener. This works perfectly for me:
function openInfoWindow(id){
googlemap.setZoom(13);
var marker = markers[id];
var latLng = marker.getPosition();
if(!marker.getMap()){ //if the clicked marker is in a cluster, so it is not attached to a map
marker.setMap(googlemap); //attach it to the map
google.maps.event.trigger(marker, 'click'); // the standard trigger, opens infowindow
googlemap.panTo(latLng);
marker.setMap(null); //detach it from the map (not sure if necessary
} else {
google.maps.event.trigger(marker, 'click');
googlemap.panTo(latLng);
}
return false;
}

Positioning a draggable map marker at top-left of map window

I have a Google map that I am using to allow people to suggest locations. Currently I position a draggable marker in the centre of the map using a LatLng created with
myPosition = frmMap.getCenter();
What I would like to do though is place it initially somewhere to the edge of the map, perhaps directly under the zoom control (not unlike the way you see the yellow street view man above the zoom control).
I've searched for a solution but am not coming up with anything. My only idea was to do some maths based on the Center and NortEast but I'd rather have an absolute position based on pixels if that's possible?
As I mentioned in the comments, computing a latlng value for a marker to position under the zoom controls is not only cumbersome, but might not be feasible if the user starts panning/zooming around in the map (as the marker will move wherever the latlng takes it).
My suggestion would be to use the Drawing Library provided by the Maps API. This basically gives you a drawing control, to add markers to the map (other overlays are possible too: cirlce, polygon, polyline, rectangle). And like any control google maps provides, you can strictly position them anywhere you'd like - by setting it in the options. The snippet below describes how you initialize the drawing library:
var drawingManager = new google.maps.drawing.DrawingManager({
drawingControlOptions: {
position: google.maps.ControlPosition.LEFT_BOTTOM,
drawingModes: [
google.maps.drawing.OverlayType.MARKER
]
}
});
drawingManager.setMap(map);
This gives you your drawing control, with the mode for Marker enabled, and binds it to your map object.
You can then listen to when a marker's been added by adding a listener on the drawingManager variable for the markercomplete event. Then in the call back you can get the position of the added marker, the snippet below demonstrates this:
google.maps.event.addListener(drawingManager, 'markercomplete', function (marker) {
var position = marker.getPosition();
});
I put together a small jsfiddle with this example if you'd like to see it in action. Also, click here for full reference of the Drawing Library for the maps api.
EDIT: (start mode in marker add on map load, hide drawing controls after marker added, maker marker draggable)
To start the drawing mode to add marker on map load, simply set the drawingMode option in your drawingManager variable declaration:
`drawingMode : google.maps.drawing.OverlayType.MARKER`
You can hide the drawing controls in the markercomplete event listener:
// To hide:
drawingManager.setOptions({
drawingControl: false //changes UI back to regular map interactions
});
drawingManager.setDrawingMode(null); //hides controls
Alternatively, if you're never going to need the drawing controls again later in your client interactions you can remove it from the map complete via:
drawingManager.setMap(null);
Then to make the marker draggable, just set the option in the listener as well (because the marker in the callback function is a google maps marker object anyway - which references the marker that's added to your map).
marker.setOptions({
draggable: true
});
You can then add a listener on the marker object for the dragend event to track changes to the location.
Here's in updated fiddle: http://jsfiddle.net/svigna/J5zMg/3/

Google map event after all pins are loaded(api3)

Question is simple. On page load I have map without pins. There are control on map that enable to user to place pins on map. Sometimes number of pins that need to be placed are over 2000 and this is process that need time. My question about this is there any google map event similar to jQuery document.ready that could wait until all map pins are placed on map. I need to display loading overlay until all pins are placed on map and after that hide it.
Just like function for map
window.google.maps.event.addListener(map, 'idle', function () {
// do something
});
I try with idle but if map don't change zoom this event is not executed(this situation is possible when you add pins on map).
To answer your question, no there is no google maps event for when all your pins are placed. However you do not need one. Just write your code so that it does these actions in this order when you load your pins:
apply your loading overlay
loop through the creation of all your pins and add them to the map
remove you loading overlay
If code helps you more:
function loadMyPins(pinDataCollection){
addMyOverlay();
for(var i=0; i<pinDataCollection.length; i++){
//create new pin
}
removeMyOverlay();
}

Google maps: Can't click marker which is on top of another

I have a map with about hundred location points (markers). Which are grouped with ClusterMarker http://googlemapsapi.martinpearman.co.uk/clustermarker
And then there's one "main" marker, which is draggable.
If main marker location is overlapping with other marker then it becomes undraggable. Or in another words: clicking takes action on marker which is behind the main marker.
Weird, because I'm setting zIndexProcess when creating the main marker, which brings it to the front of others (by default it's not like that).
So.. I don't know what's the problem and how to ask but I would try like this: How to get clickable area to the front if I have brought to the front main marker icon already? (at least it is visible on top of others)
P.S. I have GPolygon drawn on the base of main marker (radius, a circle) and this circle is drawn behind all markers. Don't know if this is relevant. Oh, and I can't bring this polygon to the front (seems like zIndexProcess does not work on this)
I had a similar situation and what you can do is pass the event through to another object. In my example here if the condition is met then I pass the event (by triggering it) on the map itself. You could do a similar check to see if the event point is inside a visible polygon (I won't put that code here as it should be relatively easily doable) and pass the click event onto the polygon.
Here is the code (lightly modified) that I use:
google.maps.event.addListener(this.intLineObj ,"click", function(latlng) {
if (<condition>)
{
//pass the event down to the map
google.maps.event.trigger(map, "click", latlng);
} else {
//do stuff
}
});
I hope that helps.

Snap to nearest street

You guys have been helping out solving some of my problems with a Google Map lately, and thank you for that.
I am almost done with this - only one problem is left. When I place the first marker on the map, it snaps to the nearest street (which is fine!) but when I drag the first marker to another place, directions suddenly mess up. And the markers get mixes.
You can see an example on http://dev.korebogen.dk/gmap/
I need to make it possible to move the first marker (still snapping) and when I place the second marker, the directions first load. But in order to make the first marker snap again, I have to load the directions.
I hope some of you have a solution. Thanks in advance.
Blackpool Community Church Javascript Team has an excellent example of exactly this (direct link to the fourth example). Check out their other examples as well.
(disclaimer: I'm not affiliated with them, but have learned a lot about GMaps from their examples)
Edit:
I suspect the map events fire somewhat like this (pseudocode, for real event names etc. check the GMaps docs):
map click: mousedown, mouseup, click:{set red marker}
drag red marker: mousedown, dragstart{red marker}, mouseup, click:{set marker b} (mousedown+mouseup), dragend
both markers are set? Yes, get directions
What I'd suggest: in red-marker and marker-A dragstart functions, set some flag "dragging a marker", reset it in dragend function; in the Set marker B function, only set marker if we're currently NOT dragging something (flag is not set).
The code I gave you previously listened for the first two clicks, and added a marker for each. The problem is that when you drag the first marker, it's calling the "click" event again - and thus adding another marker at the same location.
Fortunately, the click event lets you know whether an overlay was clicked. So only execute the code that adds a new marker if overlay is null. Note that overlay is not a boolean.
var listener = GEvent.addListener(map, "click", function(overlay, latlng) {
if (overlay == null) {
// code to add new marker
}
});