kartena / Proj4Leaflet

Smooth Proj4js integration with Leaflet.
http://kartena.github.io/Proj4Leaflet/
BSD 2-Clause "Simplified" License
589 stars 173 forks source link

Invalid LatLng object (NaN, NaN) when trying to zoom out/in #82

Closed pmikla14 closed 7 years ago

pmikla14 commented 9 years ago

I tried to include a specific open-data map from the Austrian government with the coordinate system EPSG 31258. The map shows up, but only in one zoom level (17). When i try to switch the zoom level I get an Invalid LatLng object (NaN, NaN) error. Dragging around works fine.

Could anybody tell me if there is an error in my code?

    <script>
    window.addEventListener('load', init); 
    function init() { 
        var crs31258 = new L.Proj.CRS('urn:ogc:def:crs:EPSG::31258',
              "+proj=tmerc +lat_0=0 +lon_0=13.33333333333333 +k=1 +x_0=450000 +y_0=-5000000 +ellps=bessel +towgs84=577.326,90.129,463.919,5.137,1.474,5.297,2.4232 +units=m +no_defs",
              {
                origin: [-5172500.0, 5001000.0],
                scales: [1511811, 755906, 377953, 188976, 94488, 56693, 37795, 18898, 11339, 9449, 7559, 5669, 3780, 1890, 945],
                resolutions: [400.00079375158754, 200.000529167725, 100.0002645838625, 50, 25, 15.000052916772502, 9.9999470832275, 5.000105833545001, 3.0001164168995005, 2.5000529167725003, 1.9999894166455001, 1.4999259165184997, 1.0001270002540006, 0.5000635001270003, 0.25003175006350015],
              });

    var map = L.map('map', {
        crs: crs31258,
        minZoom: 8
    }).setView([46.66411, 14.63602], 17);

    var basemap = new L.TileLayer("http://gis.ktn.gv.at/arcgis/rest/services/tilecache/Ortho_2010_2012/MapServer/tile/{z}/{y}/{x}", {
        tileSize: 512,
        maxZoom: 19,
        zoomOffset: -5
    });

    map.addLayer(basemap);

}
</script>

Thanks!

Ricardo-C-Oliveira commented 8 years ago

I'm also heaving the exactly same problem.

I found that even when the WMS layer comes in the same projection set-up by Proj4Leaflet the error occurs.

semone commented 8 years ago

Sorry for not answering this issue...

One thing is you should not use both scales and resolution, using just resolution could do the trick. Are you alse using both scales and resolution @Ricardo-C-Oliveira

There is a working example here using leaflet 1.0.0-rc.3 https://github.com/kartena/Proj4Leaflet/blob/leaflet-proj-refactor/examples/local-projections/europe/script_austria.js

Ricardo-C-Oliveira commented 8 years ago

@semone I'm only using resolution which is defined by: var ORIGINS = { '3573': [-20037508, 20037508] };

`var maxResolution = (tileDetails.origin[1] - tileDetails.origin[0]) / 256;
var resolutions = [];
for (var i = tileDetails.zoom.min; i <= tileDetails.zoom.max; i++) {
    resolutions.push(maxResolution / Math.pow(2, i));
};

`

The projection I'm attempting to use is EPSG:3573

Everything works fine with the base tile layer: http://webmap.arcticconnect.org/tiles.html The problem starts when I try to add a WMS layer on top of it, even when WMS is being served in the same projection.

duckontheweb commented 8 years ago

I'm actually able to replicate this using base tiles as well. The issue comes up any time Leaflet tries to call L.CRS.pointToLatLng on a screen point that the CRS instance can't unproject (probably outside of the extent). This is coming up most often with WMS layers because Leaflet is trying to unproject the (0,0) tile point, and if you're zoomed out enough that origin is outside of the projection's extent, but it can also come up if you try to scrollwheel zoom in or out in the right place or even just pan far enough outside of a projection's extent.

I think for our purposes we may try altering the way that Leaflet makes its WMS tile requests to see if we can restrict those requests to only valid points. I'll let you know what I find on that front.

duckontheweb commented 8 years ago

Wrapping this line in a basic try/catch did the the trick, but we'd probably want to do something a bit more elegant there:

try {
    this._addTile(queue[i], fragment);
} catch () {}
perliedman commented 8 years ago

I'm a bit confused by this issue. Which version of Leaflet are you using, and which version of Proj4Leaflet?

Also, providing an actual running example, like on http://playground-leaflet.rhcloud.com, would help a lot.

duckontheweb commented 8 years ago

I'll put together an example of this, but you can also see the error in action in one of the PolarMap demos here that uses proj4leaflet. Choose the "Use ArcticConnect" option at the right, zoom all the way out and then try to scroll zoom with the mouse located near one of the viewport edges and you'll see the same error.

duckontheweb commented 8 years ago

@perliedman You can find an example of the WMS layer issue in this JSFiddle. The zoom level set in this example (zoom: 4) causes the error, but if you change this to zoom: 5 you'll see that it fetches the tiles correctly. The error traceback goes back to L.TileLayer.prototype._addTilesFromCenterOut. In this version of the JSFiddle you'll see a crude patch to that method that eliminates the original error.

perliedman commented 8 years ago

@jduckworth Digging through the stacktrace, it seems the projection in the fiddle isn't well defined for the point (0, 0) - doing inverse returns the lat/lng (0, NaN).

I note that (0,0) is well outside the projected bounds if you look at http://epsg.io/3572, so the problem is probably trying to use the projection outside its bounds. I think it should work if you try to limit the map's bounds to avoid it loading these tiles.

duckontheweb commented 8 years ago

Actually, it looks like using the maxBounds option in these polar projections doesn't have the desired effect. I'll open a separate issue on that. See #122.

duckontheweb commented 8 years ago

After looking into this some more, the issue does not seem to be that the (0,0) point is outside of the projected bounds. When I implemented my hacky try-catch above it correctly rendered all of the tiles except the one with its north-west corner on the projection origin. As it turns out, it was basically trying to unproject that origin, which corresponds to {lat: 90, lon: 180}.

The point of failure seems to be here, where Leaflet first unprojects this origin point to the a LatLng object and then reprojects to get the coordinates that are actually used in the tile request.

This fix gets the projected coordinates for the tile request directly from the pixel point rather than going through a LatLng object.

duckontheweb commented 7 years ago

@perliedman We ended up working out a fix for this issue and #122 in our application that involved reopening some core Leaflet classes and changing the way that some calculations are done. There were a number of other places in the core Leaflet code where points are first converted to LatLng before being converted into, say EPSG:3857, even when such a conversion is not strictly necessary.

It sounds like Leaflet is making an effort to include better support for non-standard projections. I'd be happy to put in a pull request with a more thorough solution to this problem in the Leaflet library, but I also don't want to duplicate effort if others are already working on solving this issue. Do you know if there is already development happening on this?

perliedman commented 7 years ago

@duckontheweb pretty sure there's no such work going on, so we'd be very happy to see your suggestions!