Using SVG for paths in Google Maps with Deer Park

The Google Maps API uses VML in Internet Explorer for drawing polylines over the top of the map - for directions etc. In the other supported browsers though, it renders transparent pngs on the server, so a higher bandwidth load, and not so neat for the user. SVG shares a lot in common with VML, so I decided to see if I could hack SVG support into the Google Maps API code in the SVG Enabled Deer Park.

It was surprisingly easy, the google maps API is all in one large function and relies on a closure so modification of the functions requires you to rewrite that function. That’s simple get the source code of the string by converting the function ref to a string: GMapsNamespace+'' then you can use regular expressions to change the code.

Adding SVG support requires only 2 changes:

  • From this.createImageSegments to this.createSVGVectorSegments this was simply to make the Mozilla path call a new function, and be slightly more robust to changes than trying to replace the entire createImageSegments function.
  • Inserted a function before G.prototype.createVectorSegments that is my new createSVGVectorSegments function.
  • These changes only happen if window.SVGElement exists, which is a current way of testing for SVG support in Mozilla, it may not be future proof when Opera and Safari get scripted SVG support (Opera currently throws a strange script error stopping rendering but the nodes are added to the DOM).

The createSVGVectorSegments function:

G.prototype.createSVGVectorSegments=function(a,b,c){ var d=this.getVectors(a,b); var e=new Array(); var f=new D(); this.getBitmapVectors(d,e,f); if(!c){ c=new D(); } var g=D.intersection(c,f); var h; if(e.length>0){ var i=this.map.centerBitmap; var n=1; var v=1;

This is all the same as the VML version, it just gets the vectors from the polyline and does some initialisation work.

 h=document.createElementNS(\"http://www.w3.org/2000/svg\",\"svg\"); var path=document.createElementNS(\"http://www.w3.org/2000/svg\",\"path\"); h.appendChild(path); h.style.position=\"absolute\"; h.style.width=\"100px\"; h.style.height=\"100px\";

This creates the svg element and a path element that will be the actual polyline, note the importance of using createElementNS and the SVG namespace -http://www.w3.org/2000/svg.

   var vpath=this.getVectorPath(e).toUpperCase().replace(\"E\",\"\"); arr=vpath.split(/[ML,\\s]+/gim);arr=arr.splice(1,arr.length-2); var aminX=arr[0];var aminY=arr[1]; for (var zzz=2;zzz<arr.length;zzz+=2) { if (arr[zzz]<aminX) aminX=arr[zzz]; if (arr[zzz+1]<aminY) aminY=arr[zzz+1]; } outstr=\"M\"+(Number(arr[0])-aminX)+\",\"+(Number(arr[1])-aminY)+\"L\"; for (var zzz=2;zzz<arr.length;zzz+=2) {; outstr+=(Number(arr[zzz])-aminX)+\",\"+(Number(arr[zzz+1])-aminY)+\" \"; } var k=this.map.getDivCoordinate(aminX,aminY,sb); h.style.left=l(k.x); h.style.top=l(k.y);

This converts the VML path into an SVG one, and rescales it to be in a coordinate space near 0, I had to do this because of a bug in deer park where it clips to the initial viewport even if overflow is set to visible, so just translating the coordinates with a translate to bring it back to 0 didn’t work. It’s quite simple, simply loop through the path moving it by the offset of the smallest coordinate in the string. The getDivCoordinate is a Google function which converts the google world coordinates into screen px coordinates, so allows us to position the path in the right place.

 path.setAttributeNS(null,\"d\",outstr); path.setAttributeNS(null,\"opacity\",this.opacity); path.setAttributeNS(null,\"stroke\",this.color); path.setAttributeNS(null,\"fill\",\"none\"); path.setAttributeNS(null,\"stroke-weight\",l(this.weight)); } return h;}

This is just to set the last few properties on the path - this is exact same as in the VML.

And that’s it, that’s all you need to do to make your Google Maps API use SVG for polylines rather than crappy old pngs, hopefully google will soon integrate the code themselves as Deer Park moves towards release, but until then, or at least until google doesn’t break the hack somehow - which is of course extremely easy for it to do. If you want to try it out for yourself, just include the code before you call the new GMap( ... to initialise the google map.

You can also see it in action in my Exmouth to Seaton Cycle ride tracklog demo., which was what got me interested in the Google Maps API, and will be writing more on shortly…

Comments

  1. Antoine Quint Says:

    Totally awesome!!! Now you can just send that patch to Google for inclusion in the real Google maps code…

  2. Jeff Schiller Says:

    What about Opera 8.02?

  3. Jeff Schiller Says:

    Woops - Opera doesn’t support SVG scripting yet, sorry. :)

  4. Craig Says:

    Jim,
    Too many beers before that ride I think! Either that or you recorded the data with a GPS.

  5. Jim Ley Says:

    Jeff, Opera can have the SVG elements added - the elevation graph in the page does it, but nothing is yet rendered, hopefully soon, and as soon as it does, then the script should work fine yeah.

    Craig, Unfortunately it was the GPS, as if I’d had the beer, I’d never have been silly enough to ride 40 miles and 1300m of climbing…

  6. » Aspettando Firefox 1.5 beta 1 . . . | Central Scrutinizer - [ il web in 175 comode rate ] Says:

    […] …che dovrebbe uscire quest’oggi, č stata rilasciata ieri sera la Release Candidate. Nel frattempo, un esperto di javascript* come Jim Ley ha trovato il modo per rendirizzare i path disegnati da Google Maps in SVG al posto di VML. […]

  7. a.css, esbudellant est ndards » Google Maps i SVG Says:

    […] En Jim Ley explica una manera de mostrar la capa d’informaciķ de Google Maps, que en Internet Explorer usa VML i en la resta PNG transparents, usant SVG. […]