kartena / Proj4Leaflet

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

projected bounds and nonexistent tiles #62

Open alexander-travov opened 10 years ago

alexander-travov commented 10 years ago

Hello.

I have problems with setting map bounds in projected coordinates.

My map is in oblique stereographic projection: '+proj=sterea +lat_0=50 +lon_0=100 +k_0=1.0 +x_0=0 +y_0=0'. Its extent is [minx, miny, maxx, maxy]: [-6e6, -4e6, 4e6, 6e6].

If I don't set any bounds, I get lots of requests to nonexistent tiles. See map_without_bounds.

Setting L.Proj.CRS constructor bounds option does not prevent requests. See map_with_crs_bounds.

To deal with it L.TileLayer constructor accepts bounds option, but in latlng coordinates. If I set bounds like that ...

var map = L.map('map', {
    crs: new L.Proj.CRS('EPSG:9809',
        '+proj=sterea +lat_0=50 +lon_0=100 +k_0=1.0 +x_0=0 +y_0=0',
        {
            resolutions: [39062.5, 19531.25, 9765.625, 4882.8125, 2441.40625, 1220.703125],
            origin: [-6e6, 6e6]
        }
    ),
    center: L.latLng(68, 90),
    zoom: 3,
    layers: [
        new L.TileLayer(
            'http://81.5.88.53/russia_tiles/{z}/{x}/{y}.png',
            {
                maxZoom: 5,
                minZoom: 2,
                continuousWorld: true,
                bounds: L.latLngBounds(L.latLng(70, 30), L.latLng(40, 150))
            }
        )
    ]
});

Leaflet requests map tiles from nonrectangular area. See map_with_layer_latlng_bounds.

Is there a way to set layer bounds in projected coordinates.

perliedman commented 10 years ago

I would start with trying

    new L.TileLayer(
        'http://81.5.88.53/russia_tiles/{z}/{x}/{y}.png',
        {
            maxZoom: 5,
            minZoom: 2,
            continuousWorld: true,
            noWrap: true,   /* <-- enables bounds checking in L.TileLayer */
            bounds: L.latLngBounds(L.latLng(70, 30), L.latLng(40, 150))
        }

(added noWrap option.) Combine this with the bounds option. I think it should work.

Also worth noting, is that this is one of the use cases that have been greatly improved in the development version of Leaflet (master branch) - CRS, projection, bounds and wrap have all been greatly improved and should be clearer to use.

alexander-travov commented 10 years ago

Thanks for advice.

Unfortunately, noWrap option didn't do the trick.

Problem is not that L.tileLayers's bounds does not work. It works, but only accepts bounds in latlng coordinates. Therefore in general you get a nonrectangular map in your custom projection.

I was wondering, if there is a way to set L.tileLayer's bounds in coordinates of custom projection. Like OpenLayers.Map maxExtent or restrictedExtent.

When I use Leaflet version from master branch, I get Uncaught TypeError: Cannot read property 'min' of undefined in L.CRS.getProjectedBounds

perliedman commented 10 years ago

Sorry, I meant Proj4Leaflet's bounds option, which is in projected coordinates. That will affect what getSize returns, which in turn should fix tile loading if noWrap is enabled.

alexander-travov commented 10 years ago

I got the desired behavior with leaflet-master. Works even without noWrap

  var map = L.map('map', {
      crs: new L.Proj.CRS('EPSG:9809',
          '+proj=sterea +lat_0=50 +lon_0=100 +k_0=1.0 +x_0=0 +y_0=0',
          {
              resolutions: [39062.5, 19531.25, 9765.625, 4882.8125, 2441.40625, 1220.703125],
              origin: [-6e6, 6e6],
              bounds: L.bounds(L.point(-6e6, -4e6), L.point(4e6, 6e6))
          }
      ),
      center: L.latLng(68, 90),
      zoom: 3,
      layers: [
          L.tileLayer(
              'http://81.5.88.53/russia_tiles/{z}/{x}/{y}.png',
              {
                  maxZoom: 5,
                  minZoom: 2,
                  //noWrap: true,
                  continuousWorld: true
              }
          )
      ]
  });

But I believe there is error in leaflet-master/leaflet-src.js in

    getProjectedBounds: function (zoom) {
        if (this.infinite) { return null; }

        var b = this.projection.bounds, // <-- projection does not have bounds property
                // Thus it generates Uncaught TypeError: Cannot read property 'min' of undefined
            s = this.scale(zoom),
            min = this.transformation.transform(b.min, s),
            max = this.transformation.transform(b.max, s);

        return L.bounds(min, max);
    },

so I changed this to

    getProjectedBounds: function (zoom) {
        if (this.infinite) { return null; }

        var b = this.options.bounds,
            s = this.scale(zoom),
            min = this.transformation.transform(b.min, s),
            max = this.transformation.transform(b.max, s);

        return L.bounds(min, max);
    },

and everything worked.

alexander-travov commented 10 years ago

BTW leaflet-0.7.2 does not react to L.Proj.CRS's bounds option

alexander-travov commented 10 years ago

Created pull request: https://github.com/Leaflet/Leaflet/pull/2474

perliedman commented 10 years ago

Ok, I didn't know you mixed current Proj4Leaflet (written for Leaflet 0.7.2) and Leaflet master, which has a lot of changes regarding CRS and Projection. That will be a problem.

My suggestion above relates to current Proj4Leaflet (master) and Leaflet 0.7.2.

If you want to use Leaflet master, you should look at the Proj4Leaflet branch https://github.com/kartena/Proj4Leaflet/tree/leaflet-proj-refactor

alexander-travov commented 10 years ago

Thank you. Leaflet's master works with Proj4Leaflet's leaflet-proj-refactor branch smoothly. L.Proj.CRS bounds works as expected without Leaflet monkey-patching.

With Leaflet's master and Proj4Leaflet's master I get exception in getProjectedBounds.

I could not make leaflet-0.7.2 react to L.Proj.CRS bounds option. Always get requests for tiles out of bounds. With and without noWrap.

perliedman commented 10 years ago

Ok, thanks for the information. I'll try to find some time to look into why it doesn't work with 0.7.2.

tcoopman commented 10 years ago

Any updates on this. With the current master of leaflet, I also need the refactor branch to get proj4leaflet working.

(An fiddle here: http://jsfiddle.net/6x77qua0/4/ (this.projection.bounds also fails when not on the correct branch)

perliedman commented 10 years ago

Leaflet master (that will become 1.0) is quite heavily refactored regarding CRS/projection handling. An updated version of Proj4Leaflet that should work with Leaflet master/1.0 is in this branch: https://github.com/kartena/Proj4Leaflet/tree/leaflet-proj-refactor