0

I am using the CanvasTileProvider in Google Maps Android v2.

I can convert lat long points to screen pixels.

However I would like to create a method to convert a distance to screen pixels. This will allow me to draw a circle of x radius. Can anyone help with this?

The code below I have butchered and modified from somewhere else so credit to the original author.

/**
 * Converts between LatLng coordinates and the pixels inside a tile.
 */
public class TileProjection {

    public int x;
    public int y;
    private int zoom;
    private int TILE_SIZE;

    private DoublePoint pixelOrigin_;
    private double pixelsPerLonDegree_;
    private double pixelsPerLonRadian_;

    TileProjection(int tileSize, int x, int y, int zoom) {
        this.TILE_SIZE = tileSize;
        this.x = x;
        this.y = y;
        this.zoom = zoom;
        pixelOrigin_ = new DoublePoint(TILE_SIZE / 2, TILE_SIZE / 2);
        pixelsPerLonDegree_ = TILE_SIZE / 360d;
        pixelsPerLonRadian_ = TILE_SIZE / (2 * Math.PI);
    }

    /**
     * Get the dimensions of the Tile in LatLng coordinates
     */
    public LatLngBounds getTileBounds() {
        DoublePoint tileSW = new DoublePoint(x * TILE_SIZE, (y + 1) * TILE_SIZE);
        DoublePoint worldSW = pixelToWorldCoordinates(tileSW);
        LatLng SW = worldCoordToLatLng(worldSW);
        DoublePoint tileNE = new DoublePoint((x + 1) * TILE_SIZE, y * TILE_SIZE);
        DoublePoint worldNE = pixelToWorldCoordinates(tileNE);
        LatLng NE = worldCoordToLatLng(worldNE);
        return new LatLngBounds(SW, NE);
    }

    /**
     * Calculate the pixel coordinates inside a tile, relative to the left upper
     * corner (origin) of the tile.
     */
    public PointF latLngToPoint(LatLng latLng) {
        DoublePoint result = new DoublePoint(1, 1);
        //  Log.d("Aero","x " + String.valueOf(x));
        // Log.d("Aero","y " + String.valueOf(y));

        latLngToWorldCoordinates(latLng, result);
        worldToPixelCoordinates(result, result);
        result.x -= x * TILE_SIZE;

        int numTiles = 1 << zoom;
        if (latLng.longitude < 0) {
            result.x = result.x + (numTiles * TILE_SIZE);
        }

        result.y -= y * TILE_SIZE;
        return new PointF((float) result.x, (float) result.y);
    }    

    private DoublePoint pixelToWorldCoordinates(DoublePoint pixelCoord) {
        int numTiles = 1 << zoom;
        DoublePoint worldCoordinate = new DoublePoint(pixelCoord.x / numTiles,
                pixelCoord.y / numTiles);
        return worldCoordinate;
    }

    /**
     * Transform the world coordinates into pixel-coordinates relative to the
     * whole tile-area. (i.e. the coordinate system that spans all tiles.)
     * <p/>
     * <p/>
     * Takes the resulting point as parameter, to avoid creation of new objects.
     */
    private void worldToPixelCoordinates(DoublePoint worldCoord, DoublePoint result) {
        int numTiles = 1 << zoom;
        result.x = worldCoord.x * numTiles;
        result.y = worldCoord.y * numTiles;
    }

    private LatLng worldCoordToLatLng(DoublePoint worldCoordinate) {
        DoublePoint origin = pixelOrigin_;
        double lng = (worldCoordinate.x - origin.x) / pixelsPerLonDegree_;
        double latRadians = (worldCoordinate.y - origin.y)
                / -pixelsPerLonRadian_;
        double lat = Math.toDegrees(2 * Math.atan(Math.exp(latRadians))
                - Math.PI / 2);
        return new LatLng(lat, lng);
    }

    /**
     * Get the coordinates in a system describing the whole globe in a
     * coordinate range from 0 to TILE_SIZE (type double).
     * <p/>
     * Takes the resulting point as parameter, to avoid creation of new objects.
     */
    private void latLngToWorldCoordinates(LatLng latLng, DoublePoint result) {
        DoublePoint origin = pixelOrigin_;

        result.x = origin.x + latLng.longitude * pixelsPerLonDegree_;


        // Truncating to 0.9999 effectively limits latitude to 89.189. This is
        // about a third of a tile past the edge of the world tile.
        double siny = bound(Math.sin(Math.toRadians(latLng.latitude)), -0.9999,
                0.9999);
        result.y = origin.y + 0.5 * Math.log((1 + siny) / (1 - siny))
                * -pixelsPerLonRadian_;
    }

    ;

    /**
     * Return value reduced to min and max if outside one of these bounds.
     */
    private double bound(double value, double min, double max) {
        value = Math.max(value, min);
        value = Math.min(value, max);
        return value;
    }

    /**
     * A Point in an x/y coordinate system with coordinates of type double
     */
    public static class DoublePoint {
        double x;
        double y;

        public DoublePoint(double x, double y) {
            this.x = x;
            this.y = y;
        }
    }

}

This is what I am proposing to use:

public Double MetersToPixels(LatLng latLng, Double distance){
    double tileScale = TILE_SIZE / 256;
    double pixelsPerMeter =1 / (156543.03392 * Math.cos(latLng.latitude * Math.PI / 180) / Math.pow(2, zoom)) * tileScale;
    return  pixelsPerMeter * distance;
}
Reafidy
  • 8,240
  • 5
  • 51
  • 83

1 Answers1

1

At first you should be aware of the fact, that a circle on the surface of the earth is not exactly a circle on the map. But if you ignore this inaccuracy, you just need to create a LatLng point in 25nm distance, and then use latLngToPoint method to get the pixels. Comparing them with the pixels of the center, gives you the radius. For creating a LatLng in a given distance see the answer to this SO question (method move)

Community
  • 1
  • 1
user2808624
  • 2,502
  • 14
  • 28
  • Thanks for your help, I checked out your answer/link and it makes sense. However, before you posted I had come up with some code, would you mind checking out the code at the bottom of my edited question and tell me if you think it is correct? It seems to work to me. – Reafidy Jun 06 '16 at 03:38
  • Hi Reafidy, its quite a while ago I was deep into that. So I can not judge out of the box, whether your formula is o.k. or not. But I think if you calculate the y-distance (North-South) you should be independent on the latLng itself, as the north south perimeter has always the same length, whereas the east-west perimeter depends on the latitude. As you have the latitude in your formula, it seems that you are calculating the east west distance. (As said in may answer. an exact circle on the earth will not be an exact circle on the map and vice versa) – user2808624 Jun 06 '16 at 11:17