Click the box titles below to expand:

How to's

XSLT

GOOGLE MAPS API

PHP

Perl

Document Object Model (DOM)

UNIX

Generic Mapping Tools (GMT)

Miscellaneous

Projects

Courses Taught

Latest Favorites

Mac OS X

Web Development

Beta

How To :: Extending the Google maps API for plotting multiple nodes at a single location

I was interested in replicating one of the behaviors of Google Earth with Google Maps, where a location had more than one node (such as the photos in Panoramio - see screendump below).

Google Earth screendump

Here you can see that there are four photos taken of Cinderella's Castle at DisneyWorld. Clicking the camera icon creates four new 'nodes' with lines to the origin location and icons for each photo.

So, how to do this with Google Maps? The result I came up with is shown in the screendump below.

Google Maps nodes screendump

What I did can be viewed at google_multiple_nodes.php. Click on any of the markers to display an HTML info window. If the station has more than one node at a location, the click event creates child nodes on a circle based on how many child nodes there are.

The application makes extensive use of the GPolyline and GMarker objects, and fromLatLngToDivPixel() and fromDivPixelToLatLng() functions from the Google Maps API.

I have to give a lot of credit to Mike Purvis and his excellent tutorials on extending the Google Maps API, in particular with regard to his Labeled Markers class, which I am using in this application. His approach is very tidy, and I have tried to emulate his style here. Also Pamela Fox posts in the Google Maps API Blog were very useful, especially in describing functions that can determine if GMarkers are visible or not.

So how does the application work? Look at init.js for the source of the Google Maps Javascript code. I make extensive use of JavaScript Object Notation which nicely allows Javascipt objects and arrays to be created and parsed with simple functions.

The only really interesting part that I wrote was the simple trigonometry to plot the nodes. This is not rocket science....

  1. GEvent.addListener( marker, "click", function() {
  2. if( display == false ) {
  3.  
  4. var pt = map.fromLatLngToDivPixel( point ) ;
  5. var angle = 360/nodes["stas"].length ;
  6. var radius = 50 ; // radius in px
  7.  
  8. markerGroups[title] = [] ;
  9.  
  10. for( var x=0; x<nodes["stas"].length; x++ ) {
  11.  
  12. var pointRatio = x/nodes["stas"].length ;
  13. var xSteps = Math.cos( pointRatio*2*Math.PI ) ;
  14. var ySteps = Math.sin( pointRatio*2*Math.PI ) ;
  15. var newPt = new Object() ;
  16.  
  17. newPt.x = pt.x + xSteps * radius ;
  18. newPt.y = pt.y + ySteps * radius ;
  19.  
  20. var poi = map.fromDivPixelToLatLng( newPt ) ;
  21.  
  22. var polyline = new GPolyline([point, poi], "#FFFFFF", 2, 1 ) ;
  23.  
  24. var subMarker = createMarker( poi, nodes["stas"][x], true ) ; // self-refer
  25.  
  26. map.addOverlay( subMarker ) ;
  27. map.addOverlay( polyline ) ;
  28.  
  29. var mynode = {} ;
  30.  
  31. mynode = {
  32. "staname": nodes["stas"][x],
  33. "nodePoint": subMarker,
  34. "nodeLine": polyline
  35. }
  36. markerGroups[title].push( mynode ) ; // add to visible group
  37. }
  38.  
  39. display = true ;
  40.  
  41. } else {
  42. toggleGroup( title ) ;
  43. display = false ;
  44. }
  45. }) ;

What I essentially do is get the pixel location of the stations center point on the div using the fromLatLngToDivPixel function (line 4) and then define a variable called angle based on the number of duplicate station names. (Aside: the method to determine if the station name is duplicated is a simple regular expression and is beyond the scope of the actual Google maps code). I then define a variable called radius which is the length of the node 'arms' in pixels that I want from the center point.

A simple for loop iterates through the duplicated names, determining the new pixel location of each new node based on the inbuilt Javascript Math object methods cos and sin (lines 13 and 14). We then simply add the resulting lengths to the original point object and then convert from the pixel length to a latitude and longitude, using the function fromDivPixelToLatLng (line 20). We create a new polyline using GPolyline that stretches from the original station point to the new point. Then we self-refer to the current function (line 24) and add to the map with the addOverlay method (lines 26 and 27).

To keep track of which nodes are visible or not, I created a new object at the beginning of the script, called markerGroups. I then use JSON notation to add a new object as a array value for each of the newly plotted points. This object (mynode) keeps track of the node name, the latitude and longitude object of the marker, and the polyline object. These can then be toggled off or on using the toggleGroup function.

And that's it!

I would appreciate any feedback on how to make this code more efficient, and also how to add labels to the nodes. I know how to make them show up when the parent station icon is clicked, but not how to toggle the labels on and off with the visibility of the node icon. Thanks and I hope this helps someone!!

Disclaimer

This information is freely provided as–is. Messing around with the command line and creating files is a serious business, and I accept no liability for errors created, systems corrupted, or hard–disk damage by you following these instructions. They worked for me but may not work for you. Remember to back–up EVERYTHING before you try any of this stuff — it is not simple OR easy!!!

If you have any questions about this please email me at rlnewman@ucsd.edu and I will try my best to help you out.

made with CSS     Valid XHTML 1.0!      Valid CSS!