Snap to nearest marker - google-maps

i am using GoogleMaps and i have 2 or more markers and they are draggable.
I want to snap 2 markers if they are near and merge them into 1.
is this possible ?
Can someone give me pointers .. how i can realize that ?

You need to handle the drag event on the GMarker object. The trick is what do you do when you detect that you are near enough to another marker to snap them together. I played around a little with this and thought maybe hiding the currently dragged marker might be a good way to go.
GEvent.addListener(marker, "drag", function(point) {
// iterate over your points and for each otherPoint...
if (near (point, otherPoint))
{
// hide this marker
marker.hide ();
// move nearby marker to indicate merge?
// then delete the dragged marker on the dragend (if it was merged)
}
}
Not an entirely elegant solution, but it might suit your purposes.
Edit: I wondered if you were looking for the code to check nearby points, so I updated my example to do that:
function near (point1, point2)
{
sw = new GLatLng(point2.lat() - 0.005, point2.lng() - 0.005);
ne = new GLatLng(point2.lat() + 0.005, point2.lng() + 0.005);
var bounds = new GLatLngBounds(sw, ne);
if (bounds.contains (point1))
return true;
return false;
}

Related

why setMap(null) is not working google maps api v3?

I am using google maps api 3.9 .In app user can add marker or delete marker.when user click on map an Infowindow will be displayed.in which user can enter name,lat,long and click the save image as follows:
google.maps.event.addListener(map, 'click', function(event) {
point = new google.maps.Marker({
position: event.latLng
, map: map
, icon: 'resource/image/mapIcons/point.png'
, id: id
, type:"point"
});
type = point.type;
newPoint = true;
existingPoint = false;
markerObj = this;
inputInfowindow.setContent("<table style='width:92%;' id='inputTable'>" +
"<tr> <td>point</td> </tr>" +
"<tr> <td><input class='infoInput' type='text' id='name' placeholder='name'/> </td> </tr>" +
"<tr> <td><input class='infoInput'type='text' id='lat' placeholder='latitude'/></td> </tr>" +
"<tr> <td><input class='infoInput'type='text' id='lon' placeholder='longitude'/></td> </tr>" +
"<tr><td><input type='image' src='resource/image/mapIcons/save.png' onclick='save()' class='saveImage' alt='save'/> </td></tr>");
event1 = event.latLng;
currentMarker = point;
inputInfowindow.open(map,point);
});
marker saved in DB.
when user cliclks on delete button follwing method ll be called:
function deleteMarker(id,rev) {
var marker = markerObj;
markerObj = undefined;
var x = confirm("are you sure to delete marker?");
if(x){
deleteLocations(id,rev);//removes marker details from DB
if(marker){
console.log(marker);
marker.setMap(null);
}
}
}
but at marker.setMap(null); marker is removed from map still its on map.I checked with console.log(marker); marker object coming properly,no errors on console.i went through lot of googling but no result.Please help about this.
From the documentation-
To remove an overlay from a map, call the overlay's setMap() method,
passing null. Note that calling this method does not delete the
overlay; it simply removes the overlay from the map. If instead you
wish to delete the overlay, you should remove it from the map, and
then set the overlay itself to null.
so after the marker.setMap(null) you should also write marker=null
Update1-
function deleteMarker(id,rev) {
var x = confirm("are you sure to delete marker?");
if(x)
{
deleteLocations(id,rev);//removes marker details from DB
if(markerObj)
{
console.log(markerObj);
markerObj.setMap(null);
markerObj=null;
}
}
}
Update 2-
Here is a simple demo that works. See the code and check where your code is wrong. Probably some variable scope issue exists in your code.
WORKING DEMO
marker.setMap(null) does not delete the object, it only hides it. To delete it do marker = null;
I had same problem. You should call these methods before markers[index].setMapp(null) :
map.setCenter(desMarker[index].getPosition());
desMarker[index].setPosition(null);
after these call:
markers[index].setMapp(null)
In the map click event you assign this to the markerObj. Though this refers to the map object and not the marker object.
Change it to
markerObj = point;
and it should work as expected.
I had a similar error, I'm not sure if my solution applies to your case, nevertheless.. I set up my code so that when my page loaded the map would be filled up with any markers from coordinates denoted in my database. Then I allowed the user to add more points to the database and then added a marker to the user's selected location on the map.
What I didn't realize is that any time a coordinate was changed or created in my database my code was re-adding markers to all the coordinates on my map. So anytime I created a point I was both manually adding a marker to the coordinates and my database was adding a marker to the coordinates. So when I thought my code was broken what was really happening is I was deleting one of two points in the same location.
So I don't know exactly how you're pulling in coordinates and markers from you database but it's worth looking into.
For future people, using "marker.setVisible(false);" works for me
marker.setVisible(false);//this line works
marker.setMap(null);
marker.setPosition(null);
marker = null;

Need to create a custom filter for markers on Google Maps - where do I start

I'm in a bit of a pickle here. I've set up a google maps object that shows airport locations using markers. I've even enabled clustering to a certain extent. The thing is that I need to include a filter which would allow users to:
Filter and show certain types of airports i.e by clicking on a corresponding check box
SHow markers within a certain distance from a central point. Like show all markers within a radius of # miles. A user can enter a value in a text field to see this or use a slider control.
I'm quite stuck with respect on starting this out - I need some help on this.
What you can do is draw your circle (in response to user input). Then draw the markers that fall within that bounds. Each time you redraw the circle, also redraw all the markers.
// draw a circle of appropriate radius
var circleOptions = {
center: destinationLatlng,
radius: 500, // or value from some formfield, in metres
fillColor: "#FF0000",
fillOpacity: 0.2,
strokeColor: "#FF0000",
strokeOpacity: 0.8,
strokeWeight: 1
};
var circle = new google.maps.Circle(circleOptions);
var bounds = circle.getBounds();
// loop over your markers, and only draw the ones that are within these bounds
for (var i = 0; i < arrMarkers.length; i++) {
if (bounds.contains(arrMarkers[i].getPosition())) {
// only do setMap if the marker wasn't already visible
if (arrMarkers[i].getVisible() != true) {
arrMarkers[i].setMap(map);
arrMarkers[i].setVisible(true);
}
} else {
// remove the ones that are not within the circle's bounds
arrMarkers[i].setMap(null);
arrMarkers[i].setVisible(false);
}
}
You'll notice I do both setMap and setVisible. This is so that I can then use getVisible to determine if I need to redo setMap (so avoiding unnecessary function calls to setMap - I think I had an issue with flickering).
All this should be within a function that happens in response to user input, e.g. when they submit the form that asks for the radius (or as they slide the slider). This should also maybe be called from within your initialize function (if you want to draw a circle at the very start as well).
Of course this assumes you actually want to display a circle on your map showing that radius; I find this useful. However if you don't, you can use exactly the same message, but just set the fillOpacity and strokeOpacity to 0.0.
Organize references to markers into categories when you add it to map:
var markers = { cat1: [...markers...], cat2: [...markers...] }
When user selects cirtain type - just set or unset map for that markers in markers.catN

google maps api v3 + infoBubble in markerClusterer

I was trying to add an infoBubble to a markerCluster in the 'clusterclick' event but the infoBubble.Open method ask for a 'marker' parameter to bind with. The problem is that a markerCluster is not a google.maps.Point so it's not posible to bind the infoBubble to it.
I assigned the possition of the markerCluster to the infoBubble but the infoBubble redraws in the new position moving the marker from its possition.
Has anyone had the same problem? Is there a solution without modifing the original infoBubble code?
http://google-maps-utility-library-v3.googlecode.com/svn/trunk/infobubble/
Solution problem 1:
The marker parameter was optional If I just simply never assign it, the problem is solved.
Use:
infoBubble.setPossition(latLng);
infoBubble.open(map);
Not:
infoBubble.open(map, marker);
Problem 2: But now the infoBubble appears over the market, is there a way to move it up??
Solution problem 2:
I modified the InfoBubble sourceCode to contain a offsetParameter and then add the pixels in the draw function:
InfoBubble.prototype.PIXEL_OFFSET = 0
...
var top = pos.y - (height + arrowSize); if (anchorHeight) { top -= anchorHeight; } top -= this.PIXEL_OFFSET
Just in case someone had the same problem
Add this to line 93 (beneath the other option fields)
if (options['pixelOffset'] == undefined) {
options['pixelOffset'] = this.PIXEL_OFFSET_;
}
Around line 182, add this
InfoBubble.prototype.PIXEL_OFFSET_ = [0.0];
Around line 908, add this:
top -= this.get('pixelOffset')[1]; // Add offset Y.
left -= this.get('pixelOffset')[0]; // Add offset X.
Above lines should be placed above:
this.bubble_.style['top'] = this.px(top);
this.bubble_.style['left'] = this.px(left);
Now in your construction for options you could do
var popupWindowOptions = {
backgroundColor: '#2B2B2B',
arrowStyle: 0,
pixelOffset: [0,16]
};
this.popupWindow = new InfoBubble(popupWindowOptions);

How to access Google Maps API v3 marker's DIV and its pixel position?

Instead of Google Maps API's default info window, I'm going to use other jQuery tooltip plugin over marker. So I need to get marker's DIV and its pixel position.
But couldn't get it because there are no id or class for certain marker. Only I can access map canvas div from marker object and undocumented pixelBounds object.
How can I access marker's DIV?
Where can I get DIV's pixel position? Can I convert lat-lng position to pixel values?
==
appended:
I also tried with below code, but it doesn't change when I scroll the map.
var marker = new google.maps.Marker({...});
google.maps.event.addListener(marker, 'click', function() {
var px = this.getMap().getProjection().fromLatLngToPoint(this.getPosition());
console.log("(" + px.x + "," + px.y + ")");
});
I don't really get why would you want to get specific div for marker? If you want to display tooltip then all you need is pixel position of markers anchor (and knowledge about size of marker and placement of anchor), not div element. You can always trigger opening and closing tooltip by hand when event occurs on google.maps side.
For getting pixel position of anchor of given marker you can use this code:
var scale = Math.pow(2, map.getZoom());
var nw = new google.maps.LatLng(
map.getBounds().getNorthEast().lat(),
map.getBounds().getSouthWest().lng()
);
var worldCoordinateNW = map.getProjection().fromLatLngToPoint(nw);
var worldCoordinate = map.getProjection().fromLatLngToPoint(marker.getPosition());
var pixelOffset = new google.maps.Point(
Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale),
Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale)
);
In pixelDistance you get offset of specific marker anchor counted from left upper corner of the map (and you can get it's position from map.getDiv() div). Why it works like this (or is there a better way?) you can read in documentation of google maps overlays.
var overlay = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.setMap(map);
var proj = overlay.getProjection();
var pos = marker.getPosition();
var p = proj.fromLatLngToContainerPixel(pos);
You can now access your pixel coordinates through p.x and p.y.
FOLLOWING ADDED POST COMMENT:
The downfall of the overlay projection is that until it your map canvas finishes loading it isn't initialized. I have the following listener that will force whatever method I need to trigger when the map does finish loading.
google.maps.event.addListener(map, 'idle', functionName());
In the mean time I use the following check to avoid any errors before it does draw.
if(overlay.getProjection()) {
// code here
}
One thing to remember when using MBO's code:
When the map tiles are repeated, map.getBounds().getSouthWest() returns "-180" independent of the map's position. A fallback I'm using in this case is calculating the pixel distance to the center instead of the upper left corner, since map.getCenter() seems to return the currently centered point in any case. E.g. (using jQuery):
// Calculate marker position in pixels form upper left corner
var pixelCoordsCenter = map.getProjection().fromLatLngToPoint(map.getCenter());
pixelOffset = new google.maps.Point(
Math.floor((pixelCoordsMarker.x - pixelCoordsCenter.x) * scale + $(container).width()/2),
Math.floor((pixelCoordsMarker.y - pixelCoordsCenter.y) * scale + $(container).height()/2)
);
anyone still looking for an answer to this, have a look here: http://code.google.com/p/google-maps-utility-library-v3/wiki/Libraries
among some other useful google maps stuff there's RichMarker which allows you to add DOM elements of your choice as draggable markers. just add class/id to handle with jQuery.
Rene's answer only gives you the "world coordinates" (that is, coords independent of zoom level and viewport). MBO's answer seems right, though, so that's the one you should accept and vote up (I can't as I just registered) as the solution might easily be overlooked otherwise.
As for an "easier" version, you can use the methods in MapCanvasProjection instead, but that means you'll have to make your own overlay. See here for an example. :P
MapCanvasProjection's fromLatLngToContainerPixel() is probably what the author's after. It will give you the pixel offset relative to the map's container. I did some experiments and found the "simplest" working solution. (I wish Google makes this feature more accessible!)
First you declare a subclass of OverlayView somewhere like so:
function CanvasProjectionOverlay() {}
CanvasProjectionOverlay.prototype = new google.maps.OverlayView();
CanvasProjectionOverlay.prototype.constructor = CanvasProjectionOverlay;
CanvasProjectionOverlay.prototype.onAdd = function(){};
CanvasProjectionOverlay.prototype.draw = function(){};
CanvasProjectionOverlay.prototype.onRemove = function(){};
Then somewhere else in your code where you instantiate the map, you also instantiate this OverlayView and set its map, like so:
var map = new google.maps.Map(document.getElementById('google-map'), mapOptions);
// Add canvas projection overlay so we can use the LatLng to pixel converter
var canvasProjectionOverlay = new CanvasProjectionOverlay();
canvasProjectionOverlay.setMap(map);
Then, whenever you need to use fromLatLngToContainerPixel, you just do this:
canvasProjectionOverlay.getProjection().fromLatLngToContainerPixel(myLatLng);
Note that because the MapCanvasProjection object will only be available once draw() is called, which is sometime before the map's idle, I suggest creating a boolean "mapInitialized" flag, set it to true on the first map idle callback. And then do what you need to do only after that.
Well, if you MUST access the DIV, here's some code. Beware that this will only work with the standard marker (20x34px), and it'll find all markers. You might want to improve this hack to suit your needs...
BEWARE! THIS IS A HACK
var a=document.getElementById('map_canvas');
var b=a.getElementsByTagName('img');
var i, j=b.length;
for (i=0; i<j; i++) {
if(b[i].src.match('marker_sprite.png')){
if(b[i].style.width=='20px' && b[i].style.height=='34px') {
c=b[i].parentElement;
console.log(c.style.top+":"+c.style.left);
// this is a marker's enclosing div
}
}
}
A working snippet jQuery style ready to copy/paste:
step 1 - initialize your map and options
<html>
<head>
<script src="get-your-jQuery" type="text/javascript"></script>
<script src="get-your-maps.API" type="text/javascript"></script>
<script type="text/javascript">
<!--
$(document).ready(function(){
var bucharest = new google.maps.LatLng(44.43552, 26.10250);
var options = {
zoom: 14,
center: bucharest,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
As you can see, lower, the variable map is not preceded by VAR, because it should be Global as we use another function to get the fromLatLngToContainerPixel. For more details check closures.
map = new google.maps.map($("#map_canvas")[0], options);
var marker = new google.maps.Marker({
position: google.maps.LatLng(44.4407,26.0864),
map: map
});
new google.maps.event.addListener(marker, 'mouseover', function(){
placeMarker( marker.getPosition(),'#tooltip');//param1: latlng, param2: container to place result
});
new google.maps.event.addListener(map, 'bounds_changed', function(){
$("#tooltip").css({display:'none'}); //this is just so you can see that all goes well ;)
});
overlay = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.setMap(map);
}); //here ends jQuery.ready
function placeMarker(location, ID){
var containerPixel = overlay.getProjection().fromLatLngToContainerPixel(location);
var divPixel = overlay.getProjection().fromLatLngToDivPixel(location);
$(ID).css({top:containerPixel.y, left:containerPixel.x, 'dislay':'block'});
}
//-->
</script>
</head>
<body>
<div id="tooltip" style="width:100px; height:100px; position:absolute; z-index:1; background:#fff">Here I am!</div>
<div id="map_canvas" style="width:300px; height:300px"></div>
</body>
</html>
I found it's easiest to assign a custom icon and use the img src attribute to get to the element. You can still use the default google maps icon, just save it locally.
$("#map img[src='my_marker.png']").getBoundingClientRect();
For many circumstances the complex math used the calculate and change the pin position in the accepted answer may be appropriate.
For my particular use I just created a transparent PNG with a canvas significantly larger than I needed for the icon. Then I just experimented moving the pin around within the transparent background and applying the new image to the map.
Here is the spec for adding the custom pin image, with examples:
https://developers.google.com/maps/documentation/javascript/examples/icon-simple
This method will definitely scale as an offset in pixels instead of an actual different long/lat even when you zoom in.
Try this way, got div by event.
marker.addListener("click", markerClicked);
function markerClicked(event) {
// here you can get the marker div by event.currentTarget
}

Google Maps: How can I change the z-index of a Marker?

There are about 100 markers on a google map plus there is one special marker that needs to be visible. Currently, the markers around it hide it totally or partially when the map is zoomed out. I need that marker to be fully visible and I think keeping it on top of all other markers should do the trick. But I cannot find a way to modify its stacking order (z-index).
This is for Google Maps API 2.
For Google Maps API 3 use the setZIndex(zIndex:number) of the marker.
See:
http://code.google.com/apis/maps/documentation/javascript/reference.html#Marker
Use the zIndexProcess option in GMarkerOptions when you create the marker that you want on top. For example:
var pt = new GLatLng(42.2659, -83.74861);
var marker = new GMarker(pt, {zIndexProcess: function() { return 9999; }});
map.addOverlay(marker);
I believe the default is to have a z-index that is the latitude of the point of the marker, so this should be fairly safe at bringing a single marker to the front. Further, this was just a simple example; you can set the z-index of all your markers in whatever simple or complex way you want. Another example is to have two functions: one for special markers and one for the rest.
var pt1 = new GLatLng(42.2659, -83.74861);
var pt2 = new GLatLng(42.3000, -83.74000);
var marker1 = new GMarker(pt1, {zIndexProcess: specialMarker});
var marker2 = new GMarker(pt2, {zIndexProcess: normalMarker});
map.addOverlay(marker1);
map.addOverlay(marker2);
function specialMarker() {
return 9999;
}
function normalMarker() {
return Math.floor(Math.random()*1000);
}
Adding on to jhanifen's answer, if you want to get your one special marker to be on top of all the rest, set it's zIndex to google.maps.Marker.MAX_ZINDEX + 1. This will make sure that it is on top of any marker on the map.