What is the actual mathematical expression for Google Mercator? Confirm it through Safari's Web Inspector on OpenLayers and Proj4js
Thanks to http://www.spatialreference.org/ , it is very easy to check the WKT definitions for WGS84 and "Google Mercator" projection. But the actual mathematical expression to convert Google Mercator to WGS84 is not so easy to check, although the expression is published at http://en.wikipedia.org/wiki/Mercator_projection#Mathematics_of_the_projection . This entry shows how we can check the expression on existing JavaScript code using Web Inspector of Safari or Chrome.
This is the English version of http://d.hatena.ne.jp/hfu/20100906/1283778909 .
The method
"Console" is a nice tool in Web Inspector accompanied with Safari or Chrome. We can access any JavaScript object loaded on the page by typing JavaScript code snippet. We can easily understand the object and the code like we can in Ruby using 'irb.'
The case for OpenLayers
http://trac.openlayers.org/wiki/SphericalMercator indicates that OpenLayers.Layer.SphericalMercator.projectInverse is the actual function for inverse-transform from Google Mercator to WGS84. Using Console, another function is revealed to handle it:
> OpenLayers.Layer.SphericalMercator.projectInverse function (point) {var lonlat=OpenLayers.Layer.SphericalMercator.inverseMercator(point.x,point.y);point.x=lonlat.lon;point.y=lonlat.lat;return point;} > OpenLayers.Layer.SphericalMercator.inverseMercator function (x, y) {var lon=(x/20037508.34)*180;var lat=(y/20037508.34)*180;lat=180/Math.PI*(2*Math.atan(Math.exp(lat*Math.PI/180))-Math.PI/2);return new OpenLayers.LonLat(lon,lat);}
This expression seems to be a plain implementation of http://en.wikipedia.org/wiki/Mercator_projection#Mathematics_of_the_projection .
On the other hand, the forward-transform is;
> OpenLayers.Layer.SphericalMercator.projectForward function (point) {var lonlat=OpenLayers.Layer.SphericalMercator.forwardMercator(point.x,point.y);point.x=lonlat.lon;point.y=lonlat.lat;return point;} > OpenLayers.Layer.SphericalMercator.forwardMercator function (lon, lat) {var x=lon*20037508.34/180;var y=Math.log(Math.tan((90+lat)*Math.PI/360))/(Math.PI/180);y=y*20037508.34/180;return new OpenLayers.LonLat(x,y);}
This seems also to be a plain implementation of http://en.wikipedia.org/wiki/Mercator_projection#Mathematics_of_the_projection .
Numerical experiment
> var m = OpenLayers.Layer.SphericalMercator undefined > var mp = m.forwardMercator(135, 35) undefined > mp.toString() "lon=15028131.255,lat=4163881.1434847" > m.inverseMercator(mp.lon, mp.lat).toString() "lon=135,lat=35"
OK, inverse transform after forward transform is confirmed to be identity.
Proj4js
I also checked Proj4js using Web Inspector of Safari, too.
Some information taken from; http://trac.osgeo.org/proj4js/wiki/UserGuide#Basics
The page at which I run Web Inspector: http://proj4js.org/
> Proj4js.transform function (source, dest, point) { if (!source.readyToUse || !dest.readyToUse) { this.reportError("Proj4js initialization for "+source.srsCode+" not yet complete"); return point; } // Workaround for Spherical Mercator if ((source.srsProjNumber =="900913" && dest.datumCode != "WGS84") || (dest.srsProjNumber == "900913" && source.datumCode != "WGS84")) { var wgs84 = Proj4js.WGS84; this.transform(source, wgs84, point); source = wgs84; } // Transform source points to long/lat, if they aren't already. if ( source.projName=="longlat") { point.x *= Proj4js.common.D2R; // convert degrees to radians point.y *= Proj4js.common.D2R; } else { if (source.to_meter) { point.x *= source.to_meter; point.y *= source.to_meter; } source.inverse(point); // Convert Cartesian to longlat } // Adjust for the prime meridian if necessary if (source.from_greenwich) { point.x += source.from_greenwich; } // Convert datums if needed, and if possible. point = this.datum_transform( source.datum, dest.datum, point ); // Adjust for the prime meridian if necessary if (dest.from_greenwich) { point.x -= dest.from_greenwich; } if( dest.projName=="longlat" ) { // convert radians to decimal degrees point.x *= Proj4js.common.R2D; point.y *= Proj4js.common.R2D; } else { // else project dest.forward(point); if (dest.to_meter) { point.x /= dest.to_meter; point.y /= dest.to_meter; } } return point; } > new Proj4js.Proj('EPSG:900913').inverse(new Proj4js.Point(15028131.255, 4163881.1434847)).toString() "x=2.35619448986436,y=0.6108652381235775" > new Proj4js.Proj('EPSG:900913').inverse(new Proj4js.Point(15028131.255, 4163881.1434847)).x 2.35619448986436 > new Proj4js.Proj('EPSG:900913').inverse(new Proj4js.Point(15028131.255, 4163881.1434847)).x * Proj4js.common.R2D 134.99999998120785
The coordinates forward transformed using OpenLayers came back to almost the same coordinates via inverse transform using Proj4js. (The slight difference might be from the string conversion of floating point numbers.) The actual inverse transform expression was like the following;
> new Proj4js.Proj('EPSG:900913').inverse function (p) { var x = p.x - this.x0; var y = p.y - this.y0; var lon,lat; if (this.sphere) { lat = Proj4js.common.HALF_PI - 2.0 * Math.atan(Math.exp(-y / this.a * this.k0)); } else { var ts = Math.exp(-y / (this.a * this.k0)); lat = Proj4js.common.phi2z(this.e,ts); if(lat == -9999) { Proj4js.reportError("merc:inverse: lat = -9999"); return null; } } lon = Proj4js.common.adjust_lon(this.long0+ x / (this.a * this.k0)); p.x = lon; p.y = lat; return p; }