Reputation: 21
I've setup a leaflet map with a custom CRS (EPSG:32633). The map generally works well, except that it freezes whenever I try to zoom the map using pinch to zoom on mobile devices.
The error occurring under pinch to zoom is "Uncaught Error: Invalid LatLng object: (NaN, NaN)".
Here's a jsfiddle that can be used to reproduce the problem:
http://jsfiddle.net/thL5bbnv/2/
Here's the code (which is also used in the jsfiddle):
// Defining the projection system
L.Projection.UTM33 = {
ZONE: 33,
R_MINOR: 6356752.3142,
R_MAJOR: 6378137,
DEG_TO_RAD: Math.PI / 180,
RAD_TO_DEG: 180 / Math.PI,
bounds: L.bounds([-2500000, 3500000], [3045984, 9045984]),
project: function(latlng) {
var latRad = latlng.lat * this.DEG_TO_RAD,
longRad = latlng.lng * this.DEG_TO_RAD,
longOriginRad = (-183 + (6 * this.ZONE)) * this.DEG_TO_RAD,
eccs = 1 - ((this.R_MINOR / this.R_MAJOR) * (this.R_MINOR / this.R_MAJOR)),
k0 = 0.9996,
eccps = eccs / (1 - eccs),
n = this.R_MAJOR / Math.sqrt(1 - eccs * Math.sin(latRad) * Math.sin(latRad)),
t = Math.tan(latRad) * Math.tan(latRad),
c = eccps * Math.cos(latRad) * Math.cos(latRad),
a = Math.cos(latRad) * (longRad - longOriginRad),
m = this.R_MAJOR * ((1 - eccs / 4 - 3 * eccs * eccs / 64 - 5 * eccs * eccs * eccs / 256) * latRad - (3 * eccs / 8 + 3 * eccs * eccs / 32 + 45 * eccs * eccs * eccs / 1024) * Math.sin(2 * latRad) + (15 * eccs * eccs / 256 + 45 * eccs * eccs * eccs / 1024) * Math.sin(4 * latRad) - (35 * eccs * eccs * eccs / 3072) * Math.sin(6 * latRad)),
x = k0 * n * (a + (1 - t + c) * a * a * a / 6 + (5 - 18 * t + t * t + 72 * c - 58 * eccps) * a * a * a * a * a / 120) + 500000.0,
y = k0 * (m + n * Math.tan(latRad) * (a * a / 2 + (5 - t + 9 * c + 4 * c * c) * a * a * a * a / 24.0 + (61.0 - 58 * t + t * t + 600.0 * c - 330.0 * eccps) * a * a * a * a * a * a / 720));
return new L.Point(x, y);
},
unproject: function(point) {
var eccs = 1 - ((this.R_MINOR / this.R_MAJOR) * (this.R_MINOR / this.R_MAJOR)),
e1 = (1 - Math.sqrt(1 - eccs)) / (1 + Math.sqrt(1 - eccs)),
k0 = 0.9996,
x = point.x - 500000,
y = point.y,
longOrigin = (this.ZONE - 1) * 6 - 180 + 3,
eccps = (eccs) / (1 - eccs),
m = y / k0,
mu = m / (this.R_MAJOR * (1 - eccs / 4 - 3 * eccs * eccs / 64 - 5 * eccs * eccs * eccs / 256)),
phi1Rad = (mu + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * Math.sin(2 * mu) + (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * Math.sin(4 * mu) + (151 * e1 * e1 * e1 / 96) * Math.sin(6 * mu)),
n1 = this.R_MAJOR / Math.sqrt(1 - eccs * Math.sin(phi1Rad) * Math.sin(phi1Rad)),
t1 = Math.tan(phi1Rad) * Math.tan(phi1Rad),
c1 = eccps * Math.cos(phi1Rad) * Math.cos(phi1Rad),
r1 = this.R_MAJOR * (1 - eccs) / Math.pow(1 - eccs * Math.sin(phi1Rad) * Math.sin(phi1Rad), 1.5),
d = x / (n1 * k0),
lng = ((longOrigin * this.DEG_TO_RAD + ((d - (1 + 2 * t1 + c1) * d * d * d / 6 + (5 - 2 * c1 + 28 * t1 - 3 * c1 * c1 + 8 * eccps + 24 * t1 * t1) * d * d * d * d * d / 120) / Math.cos(phi1Rad))) * this.RAD_TO_DEG),
lat = ((phi1Rad - (n1 * Math.tan(phi1Rad) / r1) * (d * d / 2 - (5 + 3 * t1 + 10 * c1 - 4 * c1 * c1 - 9 * eccps) * d * d * d * d / 24 + (61 + 90 * t1 + 298 * c1 + 45 * t1 * t1 - 252 * eccps - 3 * c1 * c1) * d * d * d * d * d * d / 720)) * this.RAD_TO_DEG);
return new L.LatLng(lat, lng);
}
};
// Defining the utm crs
L.CRS.EPSG32633 = L.extend({}, L.CRS.Earth, {
code: "EPSG:32633",
projection: L.Projection.UTM33,
transformation: new L.Transformation(1, 2500000, -1, 9045984),
scale: function(zoom) {
return 1 / (21664 / Math.pow(2, zoom));
}
});
// Creating map with the utm crs
var map = new L.map('map', {
crs: L.CRS.EPSG32633,
minZoom: 0,
maxZoom: 15,
zoomControl: true
}).on("load", function(e) {
// Adding utm basemap
var layer = new L.TileLayer.WMS('https://opencache.statkart.no/gatekeeper/gk/gk.open', {
layers: "topo2",
format: "image/png",
transparent: false,
attribution: "© Kartverket"
});
layer.addTo(e.target);
}).setView([65.276178, 16.683775], 3);
UPDATE: Here's a stack trace for the error I'm getting:
Uncaught Error: Invalid LatLng object: (NaN, NaN)
at o.LatLng (https://unpkg.com/[email protected]/dist/leaflet.js:5:14154)
at Object.unproject (http://fiddle.jshell.net/thL5bbnv/2/show/:105:12)
at Object.pointToLatLng (https://unpkg.com/[email protected]/dist/leaflet.js:5:18431)
at e.unproject (https://unpkg.com/[email protected]/dist/leaflet.js:5:30126)
at e.layerPointToLatLng (https://unpkg.com/[email protected]/dist/leaflet.js:5:30242)
at e._fireDOMEvent (https://unpkg.com/[email protected]/dist/leaflet.js:6:3634)
at e._handleDOMEvent (https://unpkg.com/[email protected]/dist/leaflet.js:6:3071)
at HTMLDivElement.h (https://unpkg.com/[email protected]/dist/leaflet.js:6:11270)
at e._simulateEvent (https://unpkg.com/[email protected]/dist/leaflet.js:8:31479)
at e._onMove (https://unpkg.com/[email protected]/dist/leaflet.js:8:31242)o.LatLng @ leaflet.js:5unproject @ (index):105pointToLatLng @ leaflet.js:5unproject @ leaflet.js:5layerPointToLatLng @ leaflet.js:5_fireDOMEvent @ leaflet.js:6_handleDOMEvent @ leaflet.js:6h @ leaflet.js:6_simulateEvent @ leaflet.js:8_onMove @ leaflet.js:8h @ leaflet.js:6
leaflet.js:5 Uncaught Error: Invalid LatLng object: (NaN, NaN)(…)o.LatLng @ leaflet.js:5unproject @ (index):105pointToLatLng @ leaflet.js:5unproject @ leaflet.js:5layerPointToLatLng @ leaflet.js:5_fireDOMEvent @ leaflet.js:6_handleDOMEvent @ leaflet.js:6h @ leaflet.js:6_simulateEvent @ leaflet.js:8_onMove @ leaflet.js:8h @ leaflet.js:6
I'm trying to understand why pinch to zoom is failing, and what I can do to fix the problem. Does anyone know why pinch to zoom would result in a Invalid LatLng object: (NaN, NaN) when a custom projection is used?
Upvotes: 2
Views: 497
Reputation: 19089
OK, so I have traced the problem down to a call to map.getScaleZoom()
:
That function depends directly on the scale
method of the map's CRS, which you have defined as
scale: function(zoom) {
return 1 / (21664 / Math.pow(2, zoom));
}
Now, the scale
method of the CRS must implement the inverse of the zoom
method of the CRS. It says so in the Leaflet API documentation for L.CRS
.
You're not defining a zoom
method in L.CRS.EPSG32633
, but...
L.CRS.EPSG32633 = L.extend({}, L.CRS.Earth, {
...you're using L.CRS.Earth.zoom()
instead. So if scale(zoom(x))
is not 1 for any value of x
, things will fail. First, a zoom level jumps from 1.01 to -19.4. Then, a LatLng
gets a value of 1e+233
. Then your projection code takes that and starts spewing out Infinity
and NaN
, and everything ends up in flames.
Fix the scale
and zoom
of methods of L.CRS.EPSG32633
so they are idempotent.
Regardless of the bug, I encourage you to use Proj4Leaflet instead of implementing projection code yourself. It's tested, it's maintained, and it supports all known EPSG projections and CRSs.
Upvotes: 2