simonw / datasette-tiles

Mapping tile server for Datasette, serving tiles from MBTiles packages
https://datasette.io/plugins/datasette-tiles
8 stars 5 forks source link

Default to OSM z/x/y.png format, provide TMS (with y flipped) as an option #15

Closed dracos closed 3 years ago

dracos commented 3 years ago

As I understand it, mbtiles stores the y co-ordinate in the reverse of the standard OSM z/x/y notation. OSM has y=0 at the top counting down, whereas mbtiles have y=0 at the bottom counting up. For example, currently you have https://datasette-tiles-demo.datasette.io/-/tiles/basemap/4/8/9.png equal to https://tile.openstreetmap.org/4/8/6.png (so Y(osm) = 2^Z - Y(mbtile) - 1 and vice-versa). For ease of translating from one to the other (and perhaps with your proxy suggestion) it would be good if it could use the standard z/x/y co-ordinates for tile URLs, I think.

simonw commented 3 years ago

Well that's confusing! Explains why I need the "tms": true property for the tile layer (I got there through much frustration and trial-and-error): https://github.com/simonw/datasette-tiles/blob/f7aa1a49df23584445cf154ad0e3e6d750965b15/datasette_tiles/templates/tiles_explorer.html#L36-L41

Maybe I could also support the OSM format directly at a different path - have /-/tiles/... for one format and /-/tiles-tms/... for the other format.

I want to make this change ASAP because it's a breaking change for anything consuming tiles served by this plugin. Need to make sure I fully understand the options first. Anything I should read about this?

simonw commented 3 years ago

I want to match what the other MBTiles implementations are doing: https://github.com/mapbox/mbtiles-spec/wiki/Implementations

https://github.com/infostreams/mbtiles-php/blob/a7258d140788156bf3ad8eff7585ad65fac379c7/tileserver.php#L31-L39 is interesting:

$r->map("1.0.0/:layer/:z/:x/:y:is_retina.:ext",
    array("controller" => "maptile", "action" => "serveTmsTile"),
    array("layer"     => $_identifier, "x" => $_number, "y" => $_number, "z" => $_number,
          "is_retina" => $_retina, "ext" => "(png|jpg|jpeg|json)"));

$r->map(":layer/:z/:x/:y:is_retina.:ext",
    array("controller" => "maptile", "action" => "serveTile"),
    array("layer"     => $_identifier, "x" => $_number, "y" => $_number, "z" => $_number,
          "is_retina" => $_retina, "ext" => "(png|jpg|jpeg|json)"));

Note that the 1.0.0/ one callsserveTmsTile while the other one calls serveTile - which has this effect: https://github.com/infostreams/mbtiles-php/blob/a7258d140788156bf3ad8eff7585ad65fac379c7/tileserver.php#L182-L187

        if (!$this->is_tms) {
            $this->y = pow(2, $this->z) - 1 - $this->y;
        }
simonw commented 3 years ago

This one lets you pass an ?origin= option: https://github.com/perrygeo/python-mbtiles/blob/1cc63047be0254139e1079ba4bb036a6c6904337/serve_mbtiles.py#L17-L33

        origin = self.get_arguments('origin')
        try:
            origin = origin[0]
        except IndexError:
            origin = 'bottom' 

        if origin == 'top':
            # invert y axis to top origin
            ymax = 1 << int(z);
            y = ymax - int(y) - 1;
simonw commented 3 years ago

I feel like I've stumbled into some kind of weird multi-decade battle between geographers here that I don't understand the implications of!

simonw commented 3 years ago

Useful background information from @tmcw: https://gist.github.com/tmcw/4954720

There are no advantages of XYZ over TMS or vice-versa for most maps*, but XYZ is more popular.

simonw commented 3 years ago

I think I'll go with XYZ by default and offer TMS as an option - maybe at /-/tiles-tms.

simonw commented 3 years ago

I need to check that I understand how this issue affects the MBTiles files I'm creating with https://github.com/simonw/download-tiles

simonw commented 3 years ago

Made myself a diagram showing how the OpenStreetMap tiles work:

Map_tile_coordinate_systems_–_Figma
simonw commented 3 years ago

Zoom level 2 is more useful for understanding how these work:

Map_tile_coordinate_systems_–_Figma
simonw commented 3 years ago

Same diagram for OpenStreetMap:

Map_tile_coordinate_systems_–_Figma
simonw commented 3 years ago

I'll use this to calculate the y value for non-TMS tiles:

y = int(math.pow(2, z) - 1 - y)