25
votes

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.

  1. How can I access marker's DIV?
  2. 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 + ")");
});
11
Shouldn't this question be marked accepted? It looks like your question was addressed.Sean Mickey
@jeff -- you're editing questions that are over 2 years old, continually bumping them to the top of the Active list. The code is already properly formatted, making the colors pretty is a minor change and isn't necessary.LittleBobbyTables - Au Revoir
@LittleBobbyTables - This question has been viewed almost 19,000 times. I think someone will eventually find the syntax highlighting useful.jeff

11 Answers

52
votes

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.

11
votes
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
}
3
votes

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)
);
2
votes

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.

1
votes

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

1
votes

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.

1
votes

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

    }
  }
}
0
votes

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>
0
votes

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();
0
votes

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.

adjusting the pin offset in photoshop

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.

0
votes

Try this way, got div by event.

marker.addListener("click", markerClicked);

function markerClicked(event) {
    // here you can get the marker div by event.currentTarget
}