├── Makefile ├── .gitignore ├── package.json ├── LICENSE ├── README.md ├── test └── index.js └── index.js /Makefile: -------------------------------------------------------------------------------- 1 | REPORTER = dot 2 | 3 | test: 4 | @NODE_ENV=test ./node_modules/.bin/mocha --reporter $(REPORTER) -u tdd --require should 5 | 6 | .PHONY: test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #* 2 | *$ 3 | *.BAK 4 | *.Z 5 | *.bak 6 | *.class 7 | *.elc 8 | *.ln 9 | *.log 10 | *.o 11 | *.obj 12 | *.olb 13 | *.old 14 | *.orig 15 | *.project 16 | *.pyc 17 | *.pyo 18 | *.rej 19 | */.git/* 20 | *~ 21 | ,* 22 | .#* 23 | .DS_Store 24 | .del-* 25 | .make.state 26 | .nse_depinfo 27 | .project 28 | .svn 29 | CVS.adm 30 | RCS 31 | RCSLOG 32 | SCCS 33 | _$* 34 | _svn 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mercator-projection", 3 | "description": "Translate latitude and longitude values into 'world' coordinates as used by the Google Map API.", 4 | "version": "0.0.2", 5 | "author": { 6 | "name": "Google" 7 | }, 8 | "scripts": { 9 | "pretest": "npm install mocha should", 10 | "test": "make test" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/zacbarton/node-mercator-projection" 15 | }, 16 | "keywords": [ 17 | "mercator" 18 | , "projection" 19 | , "map" 20 | , "google" 21 | , "googlemaps" 22 | , "transformations" 23 | ] 24 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Zac Barton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mercator-projection 2 | ======== 3 | 4 | Whenever the Google Maps API needs to translate a location in the world to a location on a map (the screen), it needs to first translate latitude and longitude values into a "world" coordinate. This translation is accomplished using the Mercator projection. 5 | 6 | More details can be found [here](https://developers.google.com/maps/documentation/javascript/maptypes#WorldCoordinates). 7 | 8 | Ported from [here](https://developers.google.com/maps/documentation/javascript/examples/map-coordinates). 9 | 10 | Installation 11 | -------- 12 | 13 | $ npm install -g mercator-projection 14 | 15 | Examples 16 | -------- 17 | 18 | The following examples show you how to use mercator-projection. 19 | 20 | ```javascript 21 | var merc = require('mercator-projection'); 22 | 23 | // translate a latlng to a xy 24 | var xy = merc.fromLatLngToPoint({lat: -27.470127, lng: 153.0147027}); 25 | // {x: 236.81045525333332, y: 148.32879785796487} 26 | 27 | // translate a xy to a latlng 28 | var ll = merc.fromPointToLatLng({x: 236.81045525333332, y: 148.32879785796487}) 29 | // ~ {lat: -27.470127, lng: 153.0147027} 30 | ``` 31 | 32 | Running tests 33 | ---- 34 | 35 | $ npm test -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var merc = require('../'); 2 | 3 | describe('MercatorProjection', function() { 4 | 5 | // ll 6 | it('ll returns an object', function(done) { 7 | merc.fromLatLngToPoint({lat: -27.470127, lng: 153.0147027}).should.be.an.Object; 8 | done(); 9 | }); 10 | 11 | it('ll returns both xy keys', function(done) { 12 | merc.fromLatLngToPoint({lat: -27.470127, lng: 153.0147027}).should.have 13 | .keys('x', 'y'); 14 | done(); 15 | }); 16 | 17 | it('ll returns expected xy', function(done) { 18 | merc.fromLatLngToPoint({lat: -27.470127, lng: 153.0147027}).should.have 19 | .properties({x: 236.81045525333332, y: 148.32879785796487}); 20 | done(); 21 | }); 22 | 23 | // xy 24 | it('xy returns an object', function(done) { 25 | merc.fromPointToLatLng({x: 236.81045525333332, y: 148.32879785796487}).should.be.an.Object; 26 | done(); 27 | }); 28 | 29 | it('xy returns both ll keys', function(done) { 30 | merc.fromPointToLatLng({x: 236.81045525333332, y: 148.32879785796487}).should.have 31 | .keys('lat', 'lng'); 32 | done(); 33 | }); 34 | 35 | it('xy returns expected ll', function(done) { 36 | var ll = merc.fromPointToLatLng({x: 236.81045525333332, y: 148.32879785796487}); 37 | ll.lat.should.approximately(-27.470127, 0.01); 38 | ll.lng.should.approximately(153.0147027, 0.01); 39 | done(); 40 | }); 41 | 42 | }); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | exports.fromLatLngToPoint = fromLatLngToPoint; 2 | exports.fromPointToLatLng = fromPointToLatLng; 3 | 4 | var TILE_SIZE = 256; 5 | var pixelOrigin_ = {x: TILE_SIZE / 2, y: TILE_SIZE / 2}; 6 | var pixelsPerLonDegree_ = TILE_SIZE / 360; 7 | var pixelsPerLonRadian_ = TILE_SIZE / (2 * Math.PI); 8 | 9 | function _bound(value, opt_min, opt_max) { 10 | if (opt_min != null) value = Math.max(value, opt_min); 11 | if (opt_max != null) value = Math.min(value, opt_max); 12 | return value; 13 | } 14 | 15 | function _degreesToRadians(deg) { 16 | return deg * (Math.PI / 180); 17 | } 18 | 19 | function _radiansToDegrees(rad) { 20 | return rad / (Math.PI / 180); 21 | } 22 | 23 | function fromLatLngToPoint(latLng, opt_point) { 24 | var point = {x: null, y: null}; 25 | var origin = pixelOrigin_; 26 | 27 | point.x = origin.x + latLng.lng * pixelsPerLonDegree_; 28 | 29 | // Truncating to 0.9999 effectively limits latitude to 89.189. This is 30 | // about a third of a tile past the edge of the world tile. 31 | var siny = _bound(Math.sin(_degreesToRadians(latLng.lat)), -0.9999, 0.9999); 32 | point.y = origin.y + 0.5 * Math.log((1 + siny) / (1 - siny)) * -pixelsPerLonRadian_; 33 | 34 | return point; 35 | }; 36 | 37 | function fromPointToLatLng(point) { 38 | var origin = pixelOrigin_; 39 | var lng = (point.x - origin.x) / pixelsPerLonDegree_; 40 | var latRadians = (point.y - origin.y) / -pixelsPerLonRadian_; 41 | var lat = _radiansToDegrees(2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2); 42 | 43 | return {lat: lat, lng: lng}; 44 | }; --------------------------------------------------------------------------------