Last active
June 14, 2018 14:01
-
-
Save 599316527/7ac588fb34e628c4e2dcef32b7bc854d to your computer and use it in GitHub Desktop.
Leaflet coordinates helper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const L = require('./leaflet-0.5.0-coord-part') | |
const simpleCrs = L.Util.extend({}, L.CRS, { | |
code : 'simple', | |
projection : L.Projection.LonLat, | |
transformation : new L.Transformation(1,0,-1,0) | |
}) | |
console.log( | |
L.CRS.EPSG3857.pointToLatLng( | |
simpleCrs.latLngToPoint({ | |
"lat": -0.6068115234375, | |
"lng": 0.82440185546875 | |
}, 1), 1)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com | |
(c) 2010-2013, Vladimir Agafonkin, CloudMade | |
*/ | |
/* | |
* Leaflet coordinates helper | |
* Mofified for NodeJS | |
*/ | |
var L, originalL; | |
if (typeof exports !== undefined + '') { | |
L = exports; | |
} else { | |
originalL = window.L; | |
L = {}; | |
L.noConflict = function () { | |
window.L = originalL; | |
return this; | |
}; | |
window.L = L; | |
} | |
L.version = '0.5.1'; | |
/* | |
* L.Util contains various utility functions used throughout Leaflet code. | |
*/ | |
L.Util = { | |
extend: function (dest) { // (Object[, Object, ...]) -> | |
var sources = Array.prototype.slice.call(arguments, 1), | |
i, j, len, src; | |
for (j = 0, len = sources.length; j < len; j++) { | |
src = sources[j] || {}; | |
for (i in src) { | |
if (src.hasOwnProperty(i)) { | |
dest[i] = src[i]; | |
} | |
} | |
} | |
return dest; | |
}, | |
bind: function (fn, obj) { // (Function, Object) -> Function | |
var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : null; | |
return function () { | |
return fn.apply(obj, args || arguments); | |
}; | |
}, | |
stamp: (function () { | |
var lastId = 0, key = '_leaflet_id'; | |
return function (/*Object*/ obj) { | |
obj[key] = obj[key] || ++lastId; | |
return obj[key]; | |
}; | |
}()), | |
limitExecByInterval: function (fn, time, context) { | |
var lock, execOnUnlock; | |
return function wrapperFn() { | |
var args = arguments; | |
if (lock) { | |
execOnUnlock = true; | |
return; | |
} | |
lock = true; | |
setTimeout(function () { | |
lock = false; | |
if (execOnUnlock) { | |
wrapperFn.apply(context, args); | |
execOnUnlock = false; | |
} | |
}, time); | |
fn.apply(context, args); | |
}; | |
}, | |
falseFn: function () { | |
return false; | |
}, | |
formatNum: function (num, digits) { | |
var pow = Math.pow(10, digits || 5); | |
return Math.round(num * pow) / pow; | |
}, | |
splitWords: function (str) { | |
return str.replace(/^\s+|\s+$/g, '').split(/\s+/); | |
}, | |
setOptions: function (obj, options) { | |
obj.options = L.extend({}, obj.options, options); | |
return obj.options; | |
}, | |
getParamString: function (obj, existingUrl) { | |
var params = []; | |
for (var i in obj) { | |
if (obj.hasOwnProperty(i)) { | |
params.push(i + '=' + obj[i]); | |
} | |
} | |
return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); | |
}, | |
template: function (str, data) { | |
return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) { | |
var value = data[key]; | |
if (!data.hasOwnProperty(key)) { | |
throw new Error('No value provided for variable ' + str); | |
} | |
return value; | |
}); | |
}, | |
isArray: function (obj) { | |
return (Object.prototype.toString.call(obj) === '[object Array]'); | |
}, | |
emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' | |
}; | |
// shortcuts for most used utility functions | |
L.extend = L.Util.extend; | |
L.bind = L.Util.bind; | |
L.stamp = L.Util.stamp; | |
L.setOptions = L.Util.setOptions; | |
/* | |
* L.Point represents a point with x and y coordinates. | |
*/ | |
L.Point = function (/*Number*/ x, /*Number*/ y, /*Boolean*/ round) { | |
this.x = (round ? Math.round(x) : x); | |
this.y = (round ? Math.round(y) : y); | |
}; | |
L.Point.prototype = { | |
clone: function () { | |
return new L.Point(this.x, this.y); | |
}, | |
// non-destructive, returns a new point | |
add: function (point) { | |
return this.clone()._add(L.point(point)); | |
}, | |
// destructive, used directly for performance in situations where it's safe to modify existing point | |
_add: function (point) { | |
this.x += point.x; | |
this.y += point.y; | |
return this; | |
}, | |
subtract: function (point) { | |
return this.clone()._subtract(L.point(point)); | |
}, | |
_subtract: function (point) { | |
this.x -= point.x; | |
this.y -= point.y; | |
return this; | |
}, | |
divideBy: function (num) { | |
return this.clone()._divideBy(num); | |
}, | |
_divideBy: function (num) { | |
this.x /= num; | |
this.y /= num; | |
return this; | |
}, | |
multiplyBy: function (num) { | |
return this.clone()._multiplyBy(num); | |
}, | |
_multiplyBy: function (num) { | |
this.x *= num; | |
this.y *= num; | |
return this; | |
}, | |
round: function () { | |
return this.clone()._round(); | |
}, | |
_round: function () { | |
this.x = Math.round(this.x); | |
this.y = Math.round(this.y); | |
return this; | |
}, | |
floor: function () { | |
return this.clone()._floor(); | |
}, | |
_floor: function () { | |
this.x = Math.floor(this.x); | |
this.y = Math.floor(this.y); | |
return this; | |
}, | |
distanceTo: function (point) { | |
point = L.point(point); | |
var x = point.x - this.x, | |
y = point.y - this.y; | |
return Math.sqrt(x * x + y * y); | |
}, | |
equals: function (point) { | |
return point.x === this.x && | |
point.y === this.y; | |
}, | |
toString: function () { | |
return 'Point(' + | |
L.Util.formatNum(this.x) + ', ' + | |
L.Util.formatNum(this.y) + ')'; | |
} | |
}; | |
L.point = function (x, y, round) { | |
if (x instanceof L.Point) { | |
return x; | |
} | |
if (L.Util.isArray(x)) { | |
return new L.Point(x[0], x[1]); | |
} | |
if (isNaN(x)) { | |
return x; | |
} | |
return new L.Point(x, y, round); | |
}; | |
/* | |
* L.Bounds represents a rectangular area on the screen in pixel coordinates. | |
*/ | |
L.Bounds = function (a, b) { //(Point, Point) or Point[] | |
if (!a) { return; } | |
var points = b ? [a, b] : a; | |
for (var i = 0, len = points.length; i < len; i++) { | |
this.extend(points[i]); | |
} | |
}; | |
L.Bounds.prototype = { | |
// extend the bounds to contain the given point | |
extend: function (point) { // (Point) | |
point = L.point(point); | |
if (!this.min && !this.max) { | |
this.min = point.clone(); | |
this.max = point.clone(); | |
} else { | |
this.min.x = Math.min(point.x, this.min.x); | |
this.max.x = Math.max(point.x, this.max.x); | |
this.min.y = Math.min(point.y, this.min.y); | |
this.max.y = Math.max(point.y, this.max.y); | |
} | |
return this; | |
}, | |
getCenter: function (round) { // (Boolean) -> Point | |
return new L.Point( | |
(this.min.x + this.max.x) / 2, | |
(this.min.y + this.max.y) / 2, round); | |
}, | |
getBottomLeft: function () { // -> Point | |
return new L.Point(this.min.x, this.max.y); | |
}, | |
getTopRight: function () { // -> Point | |
return new L.Point(this.max.x, this.min.y); | |
}, | |
getSize: function () { | |
return this.max.subtract(this.min); | |
}, | |
contains: function (obj) { // (Bounds) or (Point) -> Boolean | |
var min, max; | |
if (typeof obj[0] === 'number' || obj instanceof L.Point) { | |
obj = L.point(obj); | |
} else { | |
obj = L.bounds(obj); | |
} | |
if (obj instanceof L.Bounds) { | |
min = obj.min; | |
max = obj.max; | |
} else { | |
min = max = obj; | |
} | |
return (min.x >= this.min.x) && | |
(max.x <= this.max.x) && | |
(min.y >= this.min.y) && | |
(max.y <= this.max.y); | |
}, | |
intersects: function (bounds) { // (Bounds) -> Boolean | |
bounds = L.bounds(bounds); | |
var min = this.min, | |
max = this.max, | |
min2 = bounds.min, | |
max2 = bounds.max, | |
xIntersects = (max2.x >= min.x) && (min2.x <= max.x), | |
yIntersects = (max2.y >= min.y) && (min2.y <= max.y); | |
return xIntersects && yIntersects; | |
}, | |
isValid: function () { | |
return !!(this.min && this.max); | |
} | |
}; | |
L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[]) | |
if (!a || a instanceof L.Bounds) { | |
return a; | |
} | |
return new L.Bounds(a, b); | |
}; | |
/* | |
* L.Transformation is an utility class to perform simple point transformations through a 2d-matrix. | |
*/ | |
L.Transformation = function (a, b, c, d) { | |
this._a = a; | |
this._b = b; | |
this._c = c; | |
this._d = d; | |
}; | |
L.Transformation.prototype = { | |
transform: function (point, scale) { // (Point, Number) -> Point | |
return this._transform(point.clone(), scale); | |
}, | |
// destructive transform (faster) | |
_transform: function (point, scale) { | |
scale = scale || 1; | |
point.x = scale * (this._a * point.x + this._b); | |
point.y = scale * (this._c * point.y + this._d); | |
return point; | |
}, | |
untransform: function (point, scale) { | |
scale = scale || 1; | |
return new L.Point( | |
(point.x / scale - this._b) / this._a, | |
(point.y / scale - this._d) / this._c); | |
} | |
}; | |
/* | |
* L.LatLng represents a geographical point with latitude and longitude coordinates. | |
*/ | |
L.LatLng = function (rawLat, rawLng) { // (Number, Number) | |
var lat = parseFloat(rawLat), | |
lng = parseFloat(rawLng); | |
if (isNaN(lat) || isNaN(lng)) { | |
throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')'); | |
} | |
this.lat = lat; | |
this.lng = lng; | |
}; | |
L.extend(L.LatLng, { | |
DEG_TO_RAD: Math.PI / 180, | |
RAD_TO_DEG: 180 / Math.PI, | |
MAX_MARGIN: 1.0E-9 // max margin of error for the "equals" check | |
}); | |
L.LatLng.prototype = { | |
equals: function (obj) { // (LatLng) -> Boolean | |
if (!obj) { return false; } | |
obj = L.latLng(obj); | |
var margin = Math.max( | |
Math.abs(this.lat - obj.lat), | |
Math.abs(this.lng - obj.lng)); | |
return margin <= L.LatLng.MAX_MARGIN; | |
}, | |
toString: function (precision) { // (Number) -> String | |
return 'LatLng(' + | |
L.Util.formatNum(this.lat, precision) + ', ' + | |
L.Util.formatNum(this.lng, precision) + ')'; | |
}, | |
// Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula | |
// TODO move to projection code, LatLng shouldn't know about Earth | |
distanceTo: function (other) { // (LatLng) -> Number | |
other = L.latLng(other); | |
var R = 6378137, // earth radius in meters | |
d2r = L.LatLng.DEG_TO_RAD, | |
dLat = (other.lat - this.lat) * d2r, | |
dLon = (other.lng - this.lng) * d2r, | |
lat1 = this.lat * d2r, | |
lat2 = other.lat * d2r, | |
sin1 = Math.sin(dLat / 2), | |
sin2 = Math.sin(dLon / 2); | |
var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2); | |
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); | |
}, | |
wrap: function (a, b) { // (Number, Number) -> LatLng | |
var lng = this.lng; | |
a = a || -180; | |
b = b || 180; | |
lng = (lng + b) % (b - a) + (lng < a || lng === b ? b : a); | |
return new L.LatLng(this.lat, lng); | |
} | |
}; | |
L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Number) | |
if (a instanceof L.LatLng) { | |
return a; | |
} | |
if (L.Util.isArray(a)) { | |
return new L.LatLng(a[0], a[1]); | |
} | |
if (isNaN(a)) { | |
return a; | |
} | |
return new L.LatLng(a, b); | |
}; | |
/* | |
* L.LatLngBounds represents a rectangular area on the map in geographical coordinates. | |
*/ | |
L.LatLngBounds = function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[]) | |
if (!southWest) { return; } | |
var latlngs = northEast ? [southWest, northEast] : southWest; | |
for (var i = 0, len = latlngs.length; i < len; i++) { | |
this.extend(latlngs[i]); | |
} | |
}; | |
L.LatLngBounds.prototype = { | |
// extend the bounds to contain the given point or bounds | |
extend: function (obj) { // (LatLng) or (LatLngBounds) | |
if (typeof obj[0] === 'number' || typeof obj[0] === 'string' || obj instanceof L.LatLng) { | |
obj = L.latLng(obj); | |
} else { | |
obj = L.latLngBounds(obj); | |
} | |
if (obj instanceof L.LatLng) { | |
if (!this._southWest && !this._northEast) { | |
this._southWest = new L.LatLng(obj.lat, obj.lng); | |
this._northEast = new L.LatLng(obj.lat, obj.lng); | |
} else { | |
this._southWest.lat = Math.min(obj.lat, this._southWest.lat); | |
this._southWest.lng = Math.min(obj.lng, this._southWest.lng); | |
this._northEast.lat = Math.max(obj.lat, this._northEast.lat); | |
this._northEast.lng = Math.max(obj.lng, this._northEast.lng); | |
} | |
} else if (obj instanceof L.LatLngBounds) { | |
this.extend(obj._southWest); | |
this.extend(obj._northEast); | |
} | |
return this; | |
}, | |
// extend the bounds by a percentage | |
pad: function (bufferRatio) { // (Number) -> LatLngBounds | |
var sw = this._southWest, | |
ne = this._northEast, | |
heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, | |
widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; | |
return new L.LatLngBounds( | |
new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), | |
new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); | |
}, | |
getCenter: function () { // -> LatLng | |
return new L.LatLng( | |
(this._southWest.lat + this._northEast.lat) / 2, | |
(this._southWest.lng + this._northEast.lng) / 2); | |
}, | |
getSouthWest: function () { | |
return this._southWest; | |
}, | |
getNorthEast: function () { | |
return this._northEast; | |
}, | |
getNorthWest: function () { | |
return new L.LatLng(this._northEast.lat, this._southWest.lng); | |
}, | |
getSouthEast: function () { | |
return new L.LatLng(this._southWest.lat, this._northEast.lng); | |
}, | |
contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean | |
if (typeof obj[0] === 'number' || obj instanceof L.LatLng) { | |
obj = L.latLng(obj); | |
} else { | |
obj = L.latLngBounds(obj); | |
} | |
var sw = this._southWest, | |
ne = this._northEast, | |
sw2, ne2; | |
if (obj instanceof L.LatLngBounds) { | |
sw2 = obj.getSouthWest(); | |
ne2 = obj.getNorthEast(); | |
} else { | |
sw2 = ne2 = obj; | |
} | |
return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && | |
(sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); | |
}, | |
intersects: function (bounds) { // (LatLngBounds) | |
bounds = L.latLngBounds(bounds); | |
var sw = this._southWest, | |
ne = this._northEast, | |
sw2 = bounds.getSouthWest(), | |
ne2 = bounds.getNorthEast(), | |
latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), | |
lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); | |
return latIntersects && lngIntersects; | |
}, | |
toBBoxString: function () { | |
var sw = this._southWest, | |
ne = this._northEast; | |
return [sw.lng, sw.lat, ne.lng, ne.lat].join(','); | |
}, | |
equals: function (bounds) { // (LatLngBounds) | |
if (!bounds) { return false; } | |
bounds = L.latLngBounds(bounds); | |
return this._southWest.equals(bounds.getSouthWest()) && | |
this._northEast.equals(bounds.getNorthEast()); | |
}, | |
isValid: function () { | |
return !!(this._southWest && this._northEast); | |
} | |
}; | |
//TODO International date line? | |
L.latLngBounds = function (a, b) { // (LatLngBounds) or (LatLng, LatLng) | |
if (!a || a instanceof L.LatLngBounds) { | |
return a; | |
} | |
return new L.LatLngBounds(a, b); | |
}; | |
/* | |
* L.Projection contains various geographical projections used by CRS classes. | |
*/ | |
L.Projection = {}; | |
/* | |
* Spherical Mercator is the most popular map projection, used by EPSG:3857 CRS used by default. | |
*/ | |
L.Projection.SphericalMercator = { | |
MAX_LATITUDE: 85.0511287798, | |
project: function (latlng) { // (LatLng) -> Point | |
var d = L.LatLng.DEG_TO_RAD, | |
max = this.MAX_LATITUDE, | |
lat = Math.max(Math.min(max, latlng.lat), -max), | |
x = latlng.lng * d, | |
y = lat * d; | |
y = Math.log(Math.tan((Math.PI / 4) + (y / 2))); | |
return new L.Point(x, y); | |
}, | |
unproject: function (point) { // (Point, Boolean) -> LatLng | |
var d = L.LatLng.RAD_TO_DEG, | |
lng = point.x * d, | |
lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d; | |
return new L.LatLng(lat, lng); | |
} | |
}; | |
/* | |
* Simple equirectangular (Plate Carree) projection, used by CRS like EPSG:4326 and Simple. | |
*/ | |
L.Projection.LonLat = { | |
project: function (latlng) { | |
return new L.Point(latlng.lng, latlng.lat); | |
}, | |
unproject: function (point) { | |
return new L.LatLng(point.y, point.x); | |
} | |
}; | |
/* | |
* L.CRS is a base object for all defined CRS (Coordinate Reference Systems) in Leaflet. | |
*/ | |
L.CRS = { | |
latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point | |
var projectedPoint = this.projection.project(latlng), | |
scale = this.scale(zoom); | |
return this.transformation._transform(projectedPoint, scale); | |
}, | |
pointToLatLng: function (point, zoom) { // (Point, Number[, Boolean]) -> LatLng | |
var scale = this.scale(zoom), | |
untransformedPoint = this.transformation.untransform(point, scale); | |
return this.projection.unproject(untransformedPoint); | |
}, | |
project: function (latlng) { | |
return this.projection.project(latlng); | |
}, | |
scale: function (zoom) { | |
return 256 * Math.pow(2, zoom); | |
} | |
}; | |
/* | |
* A simple CRS that can be used for flat non-Earth maps like panoramas or game maps. | |
*/ | |
L.CRS.Simple = L.extend({}, L.CRS, { | |
projection: L.Projection.LonLat, | |
transformation: new L.Transformation(1, 0, -1, 0), | |
scale: function (zoom) { | |
return Math.pow(2, zoom); | |
} | |
}); | |
/* | |
* L.CRS.EPSG3857 (Spherical Mercator) is the most common CRS for web mapping | |
* and is used by Leaflet by default. | |
*/ | |
L.CRS.EPSG3857 = L.extend({}, L.CRS, { | |
code: 'EPSG:3857', | |
projection: L.Projection.SphericalMercator, | |
transformation: new L.Transformation(0.5 / Math.PI, 0.5, -0.5 / Math.PI, 0.5), | |
project: function (latlng) { // (LatLng) -> Point | |
var projectedPoint = this.projection.project(latlng), | |
earthRadius = 6378137; | |
return projectedPoint.multiplyBy(earthRadius); | |
} | |
}); | |
L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, { | |
code: 'EPSG:900913' | |
}); | |
/* | |
* L.CRS.EPSG4326 is a CRS popular among advanced GIS specialists. | |
*/ | |
L.CRS.EPSG4326 = L.extend({}, L.CRS, { | |
code: 'EPSG:4326', | |
projection: L.Projection.LonLat, | |
transformation: new L.Transformation(1 / 360, 0.5, -1 / 360, 0.5) | |
}); | |
/* | |
* Mercator projection that takes into account that the Earth is not a perfect sphere. | |
* Less popular than spherical mercator; used by projections like EPSG:3395. | |
*/ | |
L.Projection.Mercator = { | |
MAX_LATITUDE: 85.0840591556, | |
R_MINOR: 6356752.3142, | |
R_MAJOR: 6378137, | |
project: function (latlng) { // (LatLng) -> Point | |
var d = L.LatLng.DEG_TO_RAD, | |
max = this.MAX_LATITUDE, | |
lat = Math.max(Math.min(max, latlng.lat), -max), | |
r = this.R_MAJOR, | |
r2 = this.R_MINOR, | |
x = latlng.lng * d * r, | |
y = lat * d, | |
tmp = r2 / r, | |
eccent = Math.sqrt(1.0 - tmp * tmp), | |
con = eccent * Math.sin(y); | |
con = Math.pow((1 - con) / (1 + con), eccent * 0.5); | |
var ts = Math.tan(0.5 * ((Math.PI * 0.5) - y)) / con; | |
y = -r2 * Math.log(ts); | |
return new L.Point(x, y); | |
}, | |
unproject: function (point) { // (Point, Boolean) -> LatLng | |
var d = L.LatLng.RAD_TO_DEG, | |
r = this.R_MAJOR, | |
r2 = this.R_MINOR, | |
lng = point.x * d / r, | |
tmp = r2 / r, | |
eccent = Math.sqrt(1 - (tmp * tmp)), | |
ts = Math.exp(- point.y / r2), | |
phi = (Math.PI / 2) - 2 * Math.atan(ts), | |
numIter = 15, | |
tol = 1e-7, | |
i = numIter, | |
dphi = 0.1, | |
con; | |
while ((Math.abs(dphi) > tol) && (--i > 0)) { | |
con = eccent * Math.sin(phi); | |
dphi = (Math.PI / 2) - 2 * Math.atan(ts * | |
Math.pow((1.0 - con) / (1.0 + con), 0.5 * eccent)) - phi; | |
phi += dphi; | |
} | |
return new L.LatLng(phi * d, lng); | |
} | |
}; | |
L.CRS.EPSG3395 = L.extend({}, L.CRS, { | |
code: 'EPSG:3395', | |
projection: L.Projection.Mercator, | |
transformation: (function () { | |
var m = L.Projection.Mercator, | |
r = m.R_MAJOR, | |
r2 = m.R_MINOR; | |
return new L.Transformation(0.5 / (Math.PI * r), 0.5, -0.5 / (Math.PI * r2), 0.5); | |
}()) | |
}); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment