mapbox / tippecanoe

Build vector tilesets from large collections of GeoJSON features.
BSD 2-Clause "Simplified" License
2.73k stars 432 forks source link

Looking for a function to transform input coordinates based on tileset maxzoom #888

Closed alexgonch closed 3 years ago

alexgonch commented 4 years ago

We have a tileset with maxzoom set to 10. When a view is overzoomed, the point features lose their precision as expected. Below is an example of input coordinates (left side) being mapped to the lower accuracy ones (right side).

[-114.349681, 51.284968] -> [-114.3497371673584, 51.28500414559028]
[-108.57517472, 49.21917417] -> [-108.57522010803223, 49.21922292232901]

We need to be able to generate other GeoJSON layers on-the-fly with a small subset of aforementioned point features. Question: how can we transform the input coordinates into the less precise ones so that these features are rendered directly on top of the ones from tippecanoe's tileset?

e-n-f commented 3 years ago

The coordinate conversion that Tippecanoe uses from longitude and latitude to tile coordinates is here: https://github.com/mapbox/tippecanoe/blob/master/projection.cpp#L55

You should be able to call this function with a zoom of 22 (zoom level 10 plus detail of 12) to turn your coordinates into 22-bit tile coordinates, and then convert them back to latitude and longitude with the reverse conversion, also with a zoom of 22: https://github.com/mapbox/tippecanoe/blob/master/projection.cpp#L66

#include <stdio.h>
#include <math.h>

void lonlat2tile(double lon, double lat, int zoom, long long *x, long long *y) {
    double lat_rad = lat * M_PI / 180;
    unsigned long long n = 1LL << zoom;

    long long llx = n * ((lon + 180) / 360);
    long long lly = n * (1 - (log(tan(lat_rad) + 1 / cos(lat_rad)) / M_PI)) / 2;

    *x = llx;
    *y = lly;
}

void tile2lonlat(long long x, long long y, int zoom, double *lon, double *lat) {
    unsigned long long n = 1LL << zoom;
    *lon = 360.0 * x / n - 180.0;
    *lat = atan(sinh(M_PI * (1 - 2.0 * y / n))) * 180.0 / M_PI;
}

int main() {
    double lon = -114.349681;
    double lat = 51.284968;
    long long x, y;
    lonlat2tile(lon, lat, 22, &x, &y);
    tile2lonlat(x, y, 22, &lon, &lat);
    printf("%.15f,%.15f\n", lon, lat);
}
alexgonch commented 3 years ago

@ericfischer Thank you so much for your response, I've converted these functions into JavaScript and they worked perfectly. This was a great success for our project.