package com.nutiteq.maps.projections; import com.nutiteq.components.MapPos; import com.nutiteq.components.Point; import com.nutiteq.maps.BaseMap; import com.nutiteq.maps.GeoMap; import com.nutiteq.ui.Copyright; /** * Abstract class for doing WGS84 coordinates calculations to map pixels in * EPSG4326 (Plate-carree (flat) projection, to be used with WMS) and back. * * Supports custom per-zoom scale specification and standard parallel (phi0) */ public abstract class EPSG4326v2 extends BaseMap implements Projection, GeoMap { private final double equatorLength; public EPSG4326v2(final Ellipsoid ellipsoid, final Copyright copyright, final int tileSize, final int minZoom, final int maxZoom) { super(copyright, tileSize, minZoom, maxZoom); equatorLength = 2 * Math.PI * ellipsoid.getEquatorialRadius(); } public EPSG4326v2(final Ellipsoid ellipsoid, final String copyright, final int tileSize, final int minZoom, final int maxZoom) { super(copyright, tileSize, minZoom, maxZoom); equatorLength = 2 * Math.PI * ellipsoid.getEquatorialRadius(); } // only ratios <= 1 are accepted (?) private double yRatio = 1.0; // whether to add offset for ratio > 1 private boolean addYOffset = true; // cos(standard parallel) private double xRatio = 1.0; // scales private double[] scales = null; public int getMapWidth(int zoom) { if (scales == null) { return super.getMapWidth(zoom); } else { return (int) Math.floor(equatorLength/scales[zoom-getMinZoom()]+0.5); } } public int getMapHeight(int zoom) { if (scales == null) { return super.getMapHeight(zoom); } else { return getMapWidth(zoom); } } public Point mapPosToWgs(final MapPos pos) { final int mapWidth = getMapWidth(pos.getZoom()); final int mapHeight = getMapHeight(pos.getZoom()); final int tmpX = pos.getX() % mapWidth; // compute map y offset final int yOffset = addYOffset ? 0 : ((int) Math.floor(mapHeight - mapHeight / yRatio + 0.5) / 2); // x final double tx = tmpX / xRatio / (mapWidth / 2) - 1D; final int lon = (int) (tx * 180000000D); // y final double ty = (pos.getY() - yOffset) / yRatio / (mapHeight / 2) - 1D; final int lat = (int) (ty * 90000000D); return new Point(lon, -lat); } public MapPos wgsToMapPos(final Point wgs, final int zoom) { final int mapWidth = getMapWidth(zoom); final int mapHeight = getMapHeight(zoom); // compute map y offset final int yOffset = addYOffset ? 0 : ((int) Math.floor(mapHeight - mapHeight / yRatio + 0.5) / 2); final int pX = wgs.getX(); final int pY = wgs.getY(); // x final double x = pX / 180000000D + 1D; final int cx = (int) (x * (mapWidth / 2) * xRatio); // y final double y = -pY / 90000000D + 1D; final int cy = (int) (y * (mapHeight / 2) * yRatio) + yOffset; final int tmpX = cx % mapWidth; return new MapPos(tmpX, cy, zoom); } /** * Set ratio between width and height. Use 1 for a "square world map" or 2 for * a stretched map. Only values >= 1 are accepted. * * @param yRatio * width/height ratio */ public void setWidthHeightRatio(final double yRatio) { if (yRatio < 1.0) { throw new IllegalArgumentException("Ratio must be >= 1"); } this.yRatio = yRatio; } /** * Set standard parallel (used for determining x ratio). * @param phi0 standard parallel */ public void setStandardParallel(final double phi0) { if (phi0 <= -90.0 || phi0 >= 90.0) { throw new IllegalArgumentException("Standard parallel must be within [-90,90]"); } this.xRatio = Math.cos(phi0*Math.PI/180); } /** * Set an array of scales used for various zoom levels. * @param scales null for default scales */ public void setScales(double[] scales) { this.scales = scales; } /** * Whether to add Y offset when ratio > 1. * @param addYOffset */ public void setAddYOffset(final boolean addYOffset) { this.addYOffset = addYOffset; } public boolean getAddYOffset() { return addYOffset; } public double getYRatio() { return yRatio; } public double getXRatio() { return xRatio; } public double[] getScales() { return scales; } public MapPos zoom(final MapPos middlePoint, final int zoomSteps) { if (scales == null) { return super.zoom(middlePoint, zoomSteps); } else { final Point currentMiddle = mapPosToWgs(middlePoint); return wgsToMapPos(currentMiddle, middlePoint.getZoom() + zoomSteps); } } }