TileProvider method getTile - need to translate x and y to lat/long

前端 未结 3 781
北荒
北荒 2020-12-08 11:12

I’m porting an iOS app to Android, and using Google Maps Android API v2. The application needs to draw a heatmap overlay onto the map.

So far, it looks

3条回答
  •  生来不讨喜
    2020-12-08 11:56

    On zoom level 0, there is only one tile (x=0,y=0). On next zoom level number of tiles are quadrupled (doubled on x and y).

    This means on zoom level W, x may be a value in range <0, 1 << W).

    From documentation:

    The coordinates of the tiles are measured from the top left (northwest) corner of the map. At zoom level N, the x values of the tile coordinates range from 0 to 2N - 1 and increase from west to east and the y values range from 0 to 2N - 1 and increase from north to south.

    You can achieve this using simple calculations.

    For longitude this is straightforward :

    double longitudeMin = (((double) x) / (1 << zoom)) * 360 - 180;
    double longitudeMax = (((double) x + 1) / (1 << zoom)) * 360 - 180;
    longitudeMax = Double.longBitsToDouble(Double.doubleToLongBits(longitudeMax) - 1); // adjust
    

    Here x is first scaled into <0,1), then into <-180,180).

    The max value is adjusted, so it doesn't overlap with the next area. You may skip this.

    For latitude this will be a bit harder, because Google Maps use Mercator projection.

    First you scale y just like it was in range <-180,180). Note that the values need to be reversed.

    double mercatorMax = 180 - (((double) y) / (1 << zoom)) * 360;
    double mercatorMin = 180 - (((double) y + 1) / (1 << zoom)) * 360;
    

    Now you use a magical function that does Mercator projection (from SphericalMercator.java):

    public static double toLatitude(double mercator) {
        double radians = Math.atan(Math.exp(Math.toRadians(mercator)));
        return Math.toDegrees(2 * radians) - 90;
    }
    
    latitudeMax = SphericalMercator.toLatitude(mercatorMax);
    latitudeMin = SphericalMercator.toLatitude(mercatorMin);
    latitudeMin = Double.longBitsToDouble(Double.doubleToLongBits(latitudeMin) + 1);
    

    This was was typed from memory and was not tested in any way, so if there is an error there, please put a comment and I will fix it.

提交回复
热议问题