package com.mutu.mapapi.tilesystem; import com.mutu.mapapi.util.GeoPoint; import android.graphics.Point; public abstract class TileSystem { protected int mTileSize = 256; protected double EarthRadius = 6378137; protected double MinLatitude = -90.0; protected double MaxLatitude = 90.0; protected double MinLongitude = -180; protected double MaxLongitude = 180; public void setTileSize(final int tileSize) { mTileSize = tileSize; } public int getTileSize() { return mTileSize; } /** * Clips a number to the specified minimum and maximum values. * * @param n * The number to clip * @param minValue * Minimum allowable value * @param maxValue * Maximum allowable value * @return The clipped value. */ protected static double Clip(final double n, final double minValue, final double maxValue) { return Math.min(Math.max(n, minValue), maxValue); } /** * Determines the map width and height (in pixels) at a specified level of detail. * * @param levelOfDetail * Level of detail, from 1 (lowest detail) to 23 (highest detail) * @return The map width and height in pixels */ public abstract int MapWidthPixelSize(final int levelOfDetail); public abstract int MapHeigthPixelSize(final int levelOfDetail); public abstract int MapWidthTileSize(final int levelOfDetail); public abstract int MapHeigthTileSize(final int levelOfDetail); /** * Determines the ground resolution (in meters per pixel) at a specified latitude and level of * detail. * * @param latitude * Latitude (in degrees) at which to measure the ground resolution * @param levelOfDetail * Level of detail, from 1 (lowest detail) to 23 (highest detail) * @return The ground resolution, in meters per pixel */ public double GroundResolution(double latitude, final int levelOfDetail) { latitude = Clip(latitude, MinLatitude, MaxLatitude); return Math.cos(latitude * Math.PI / 180) * 2 * Math.PI * EarthRadius / MapWidthPixelSize(levelOfDetail); } /** * Determines the map scale at a specified latitude, level of detail, and screen resolution. * * @param latitude * Latitude (in degrees) at which to measure the map scale * @param levelOfDetail * Level of detail, from 1 (lowest detail) to 23 (highest detail) * @param screenDpi * Resolution of the screen, in dots per inch * @return The map scale, expressed as the denominator N of the ratio 1 : N */ public double MapScale(final double latitude, final int levelOfDetail, final int screenDpi) { return GroundResolution(latitude, levelOfDetail) * screenDpi / 0.0254; } public abstract String name(); public abstract Point LatLongToPixelXY(double latitude, double longitude, final int levelOfDetail, final Point reuse); public abstract GeoPoint PixelXYToLatLong(final int pixelX, final int pixelY, final int levelOfDetail, final GeoPoint reuse); /** * Converts pixel XY coordinates into tile XY coordinates of the tile containing the specified * pixel. * * @param pixelX * Pixel X coordinate * @param pixelY * Pixel Y coordinate * @param reuse * An optional Point to be recycled, or null to create a new one automatically * @return Output parameter receiving the tile X and Y coordinates */ public Point PixelXYToTileXY(final int pixelX, final int pixelY, final Point reuse) { final Point out = (reuse == null ? new Point() : reuse); out.x = pixelX / mTileSize; out.y = pixelY / mTileSize; return out; } /** * Converts tile XY coordinates into pixel XY coordinates of the upper-left pixel of the * specified tile. * * @param tileX * Tile X coordinate * @param tileY * Tile X coordinate * @param reuse * An optional Point to be recycled, or null to create a new one automatically * @return Output parameter receiving the pixel X and Y coordinates */ public Point TileXYToPixelXY(final int tileX, final int tileY, final Point reuse) { final Point out = (reuse == null ? new Point() : reuse); out.x = tileX * mTileSize; out.y = tileY * mTileSize; return out; } /** * Converts tile XY coordinates into a QuadKey at a specified level of detail. * * @param tileX * Tile X coordinate * @param tileY * Tile Y coordinate * @param levelOfDetail * Level of detail, from 1 (lowest detail) to 23 (highest detail) * @return A string containing the QuadKey */ public String TileXYToQuadKey(final int tileX, final int tileY, final int levelOfDetail) { final StringBuilder quadKey = new StringBuilder(); for (int i = levelOfDetail; i > 0; i--) { char digit = '0'; final int mask = 1 << (i - 1); if ((tileX & mask) != 0) { digit++; } if ((tileY & mask) != 0) { digit++; digit++; } quadKey.append(digit); } return quadKey.toString(); } /** * Converts a QuadKey into tile XY coordinates. * * @param quadKey * QuadKey of the tile * @param reuse * An optional Point to be recycled, or null to create a new one automatically * @return Output parameter receiving the tile X and y coordinates */ public Point QuadKeyToTileXY(final String quadKey, final Point reuse) { final Point out = (reuse == null ? new Point() : reuse); int tileX = 0; int tileY = 0; final int levelOfDetail = quadKey.length(); for (int i = levelOfDetail; i > 0; i--) { final int mask = 1 << (i - 1); switch (quadKey.charAt(levelOfDetail - i)) { case '0': break; case '1': tileX |= mask; break; case '2': tileY |= mask; break; case '3': tileX |= mask; tileY |= mask; break; default: throw new IllegalArgumentException("Invalid QuadKey digit sequence."); } } out.set(tileX, tileY); return out; } }