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
tothis.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 newcreateSVGVectorSegments
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…
Wed Sep 7 09:55:00 UTC+0100 2005
Totally awesome!!! Now you can just send that patch to Google for inclusion in the real Google maps code…
Wed Sep 7 20:15:00 UTC+0100 2005
What about Opera 8.02?
Wed Sep 7 21:35:00 UTC+0100 2005
Woops - Opera doesn’t support SVG scripting yet, sorry. :)
Thu Sep 8 00:01:00 UTC+0100 2005
Jim,
Too many beers before that ride I think! Either that or you recorded the data with a GPS.
Thu Sep 8 08:26:00 UTC+0100 2005
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…
Thu Sep 8 08:47:00 UTC+0100 2005
[…] …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. […]
Thu Sep 8 18:20:00 UTC+0100 2005
[…] 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. […]