package com.nutiteq.maps;
import com.nutiteq.components.MapPos;
import com.nutiteq.components.Point;
import com.nutiteq.components.TileMapBounds;
import com.nutiteq.maps.projections.EPSG4326;
import com.nutiteq.ui.Copyright;
import com.nutiteq.ui.StringCopyright;
public abstract class BaseKaMap extends EPSG4326 implements GeoMap {
private static final int WORLD_HEIGHT_DEGREES = 180;
private static final int WORLD_WIDTH_DEGREES = 360;
//dots per inch
private static final int DPI = 72;
//inches per decimal degree?
private static final double INCHES_PER_DECIMAL_DEGREE = 4374754D;
private final int[] mapWidth;
private final int[] mapHeight;
protected final int[] scales;
protected final int[] tileMapWidth;
protected final int[] tileMapHeight;
public BaseKaMap(final String copyright, final int tileSize, final int scales[],
final int minZoom, final int maxZoom) {
this(new StringCopyright(copyright), tileSize, scales, minZoom, maxZoom);
}
public BaseKaMap(final Copyright copyright, final int tileSize, final int scales[],
final int minZoom, final int maxZoom) {
super(copyright, tileSize, minZoom, maxZoom);
this.scales = scales;
mapWidth = new int[scales.length];
mapHeight = new int[scales.length];
tileMapWidth = new int[scales.length];
tileMapHeight = new int[scales.length];
for (int i = 0; i < scales.length; i++) {
mapWidth[i] = calculateMapEdge(WORLD_WIDTH_DEGREES, scales[i]);
mapHeight[i] = calculateMapEdge(WORLD_HEIGHT_DEGREES, scales[i]);
tileMapWidth[i] = calculateTileMapEdge(mapWidth[i], tileSize);
tileMapHeight[i] = calculateTileMapEdge(mapHeight[i], tileSize);
}
}
private int calculateMapEdge(final int edgeDegrees, final int scale) {
// based on http://lists.maptools.org/pipermail/ka-map-users/2006-June/001644.html
return (int) Math.floor((edgeDegrees * DPI * INCHES_PER_DECIMAL_DEGREE) / scale + 0.5);
}
private int calculateTileMapEdge(final int edgeLength, final int tileSize) {
final int tileEdge = edgeLength + (tileSize - (edgeLength / 2) % tileSize) * 2;
return tileEdge - tileEdge % tileSize;
}
public Point mapPosToWgs(final MapPos pos) {
final int zoom = pos.getZoom() - getMinZoom();
final MapPos converted = pos.copy();
// because map might not fill the whole tile area we need to remove 'buffer'
// area around map, to have calculations with real map size
converted.setX(pos.getX() - (tileMapWidth[zoom] - mapWidth[zoom]) / 2);
converted.setY(pos.getY() - (tileMapHeight[zoom] - mapHeight[zoom]) / 2);
return super.mapPosToWgs(converted);
}
public MapPos wgsToMapPos(final Point wgs, final int zoom) {
final MapPos result = super.wgsToMapPos(wgs, zoom);
final int mapZoom = zoom - getMinZoom();
//add buffer size to have real position on map tile
result.setX(result.getX() + (tileMapWidth[mapZoom] - mapWidth[mapZoom]) / 2);
result.setY(result.getY() + (tileMapHeight[mapZoom] - mapHeight[mapZoom]) / 2);
return result;
}
public int getMapHeight(final int zoom) {
return mapHeight[zoom - getMinZoom()];
}
public int getMapWidth(final int zoom) {
return mapWidth[zoom - getMinZoom()];
}
protected int[] getMapWidth() {
return mapWidth;
}
protected int[] getMapHeight() {
return mapHeight;
}
protected int[] getTileMapWidth() {
return tileMapWidth;
}
protected int[] getTileMapHeight() {
return tileMapHeight;
}
public MapPos zoom(final MapPos middlePoint, final int zoomSteps) {
final Point currentMiddle = mapPosToWgs(middlePoint);
return wgsToMapPos(currentMiddle, middlePoint.getZoom() + zoomSteps);
}
public TileMapBounds getTileMapBounds(final int zoom) {
final int zoomIndex = zoom - getMinZoom();
final MapPos min = new MapPos(0, 0, zoom);
final MapPos max = new MapPos(tileMapWidth[zoomIndex] - 1, tileMapHeight[zoomIndex] - 1, zoom);
return new TileMapBounds(min, max);
}
}