OsmAnd/OsmAnd-java/src/net/osmand/data/RotatedTileBox.java

577 lines
15 KiB
Java
Raw Normal View History

2013-09-27 11:04:49 +02:00
package net.osmand.data;
import net.osmand.util.MapUtils;
public class RotatedTileBox {
/// primary fields
private double lat;
private double lon;
private float rotate;
2013-09-27 14:07:07 +02:00
private float density;
2013-09-27 11:04:49 +02:00
private int zoom;
2014-12-06 14:55:36 +01:00
private double mapDensity = 1;
private double zoomAnimation;
private double zoomFloatPart;
2013-09-27 11:04:49 +02:00
private int cx;
private int cy;
private int pixWidth;
private int pixHeight;
// derived
// all geometry math is done in tileX, tileY of phisycal given zoom
// zoomFactor is conversion factor, from dtileX * zoomFactor = dPixelX
2013-10-01 00:19:54 +02:00
private double zoomFactor;
private double rotateCos;
private double rotateSin;
private double oxTile;
private double oyTile;
2013-09-27 11:04:49 +02:00
private QuadRect tileBounds;
private QuadRect latLonBounds;
2013-11-03 23:23:37 +01:00
private QuadPointDouble tileLT;
private QuadPointDouble tileRT;
private QuadPointDouble tileRB;
private QuadPointDouble tileLB;
2013-09-27 11:04:49 +02:00
2013-09-27 14:07:07 +02:00
private RotatedTileBox(){
2013-09-27 11:04:49 +02:00
}
public RotatedTileBox(RotatedTileBox r){
2013-09-27 14:07:07 +02:00
this.pixWidth = r.pixWidth;
this.pixHeight = r.pixHeight;
this.lat = r.lat;
this.lon = r.lon;
this.zoom = r.zoom;
2014-12-06 14:55:36 +01:00
this.mapDensity = r.mapDensity;
this.zoomFloatPart = r.zoomFloatPart;
2013-09-27 17:58:04 +02:00
this.zoomAnimation = r.zoomAnimation;
2013-09-27 14:07:07 +02:00
this.rotate = r.rotate;
this.density = r.density;
this.cx = r.cx;
this.cy = r.cy;
copyDerivedFields(r);
}
private void copyDerivedFields(RotatedTileBox r) {
zoomFactor = r.zoomFactor;
rotateCos = r.rotateCos;
rotateSin = r.rotateSin;
oxTile = r.oxTile;
2013-10-16 17:39:58 +02:00
oyTile = r.oyTile;
if (r.tileBounds != null && r.latLonBounds != null) {
2013-10-06 11:59:01 +02:00
tileBounds = new QuadRect(r.tileBounds);
latLonBounds = new QuadRect(r.latLonBounds);
2013-11-03 23:23:37 +01:00
tileLT = new QuadPointDouble(r.tileLT);
tileRT = new QuadPointDouble(r.tileRT);
tileRB = new QuadPointDouble(r.tileRB);
tileLB = new QuadPointDouble(r.tileLB);
2013-10-06 11:59:01 +02:00
}
2013-09-27 11:04:49 +02:00
}
2013-09-27 14:07:07 +02:00
public void calculateDerivedFields() {
zoomFactor = Math.pow(2, zoomAnimation + zoomFloatPart) * 256 * mapDensity;
2013-10-01 00:19:54 +02:00
double rad = Math.toRadians(this.rotate);
rotateCos = Math.cos(rad);
rotateSin = Math.sin(rad);
oxTile = MapUtils.getTileNumberX(zoom, lon);
oyTile = MapUtils.getTileNumberY(zoom, lat);
2013-09-27 11:04:49 +02:00
while(rotate < 0){
rotate += 360;
}
while(rotate > 360){
rotate -= 360;
}
2013-10-06 11:59:01 +02:00
tileBounds = null;
// lazy
// calculateTileRectangle();
2013-09-27 11:04:49 +02:00
}
2013-09-27 14:07:07 +02:00
public double getLatFromPixel(float x, float y) {
return MapUtils.getLatitudeFromTile(zoom, getTileYFromPixel(x, y));
}
public double getLonFromPixel(float x, float y) {
return MapUtils.getLongitudeFromTile(zoom, getTileXFromPixel(x, y));
}
public LatLon getLatLonFromPixel(float x, float y) {
return new LatLon(getLatFromPixel(x, y), getLonFromPixel(x, y));
}
public LatLon getCenterLatLon() {
return new LatLon(lat, lon);
}
public QuadPoint getCenterPixelPoint() {
return new QuadPoint(cx, cy);
}
public int getCenterPixelX(){
return cx;
}
2013-09-27 11:04:49 +02:00
2013-09-27 14:07:07 +02:00
public int getCenterPixelY(){
return cy;
}
public void setDensity(float density) {
this.density = density;
}
public double getCenterTileX(){
return oxTile;
}
2014-09-13 17:18:13 +02:00
public int getCenter31X(){
return MapUtils.get31TileNumberX(lon);
}
public int getCenter31Y(){
return MapUtils.get31TileNumberY(lat);
}
2013-09-27 14:07:07 +02:00
public double getCenterTileY(){
return oyTile;
}
2013-10-01 00:19:54 +02:00
protected double getTileXFromPixel(float x, float y) {
2013-09-27 14:07:07 +02:00
float dx = x - cx;
float dy = y - cy;
2013-10-01 00:19:54 +02:00
double dtilex;
2013-09-27 11:04:49 +02:00
if(isMapRotateEnabled()){
dtilex = (rotateCos * (float) dx + rotateSin * (float) dy);
} else {
dtilex = (float) dx;
}
return dtilex / zoomFactor + oxTile;
}
2013-10-01 00:19:54 +02:00
protected double getTileYFromPixel(float x, float y) {
2013-09-27 14:07:07 +02:00
float dx = x - cx;
float dy = y - cy;
2013-10-01 00:19:54 +02:00
double dtiley;
2013-09-27 11:04:49 +02:00
if(isMapRotateEnabled()){
dtiley = (-rotateSin * (float) dx + rotateCos * (float) dy);
} else {
dtiley = (float) dy;
}
return dtiley / zoomFactor + oyTile;
}
2013-09-27 14:07:07 +02:00
public QuadRect getTileBounds() {
2013-10-06 11:59:01 +02:00
checkTileRectangleCalculated();
2013-09-27 14:07:07 +02:00
return tileBounds;
}
2013-09-27 11:04:49 +02:00
public void calculateTileRectangle() {
2013-10-01 00:19:54 +02:00
double x1 = getTileXFromPixel(0, 0);
double x2 = getTileXFromPixel(pixWidth, 0);
double x3 = getTileXFromPixel(pixWidth, pixHeight);
double x4 = getTileXFromPixel(0, pixHeight);
double y1 = getTileYFromPixel(0, 0);
double y2 = getTileYFromPixel(pixWidth, 0);
double y3 = getTileYFromPixel(pixWidth, pixHeight);
double y4 = getTileYFromPixel(0, pixHeight);
2013-11-03 23:23:37 +01:00
tileLT = new QuadPointDouble(x1, y1);
tileRT = new QuadPointDouble(x2, y2);
tileRB = new QuadPointDouble(x3, y3);
tileLB = new QuadPointDouble(x4, y4);
2013-10-01 00:19:54 +02:00
double l = Math.min(Math.min(x1, x2), Math.min(x3, x4)) ;
double r = Math.max(Math.max(x1, x2), Math.max(x3, x4)) ;
double t = Math.min(Math.min(y1, y2), Math.min(y3, y4)) ;
double b = Math.max(Math.max(y1, y2), Math.max(y3, y4)) ;
tileBounds = new QuadRect((float)l, (float)t,(float) r, (float)b);
2013-10-20 00:19:44 +02:00
float top = (float) MapUtils.getLatitudeFromTile(zoom, alignTile(tileBounds.top));
float left = (float) MapUtils.getLongitudeFromTile(zoom, alignTile(tileBounds.left));
float bottom = (float) MapUtils.getLatitudeFromTile(zoom, alignTile(tileBounds.bottom));
float right = (float) MapUtils.getLongitudeFromTile(zoom, alignTile(tileBounds.right));
2013-09-27 11:04:49 +02:00
latLonBounds = new QuadRect(left, top, right, bottom);
}
2013-10-20 00:19:44 +02:00
private double alignTile(double tile) {
if(tile < 0) {
return 0;
}
if(tile >= MapUtils.getPowZoom(zoom)) {
return MapUtils.getPowZoom(zoom) - .000001;
}
return tile;
}
2013-09-27 11:04:49 +02:00
public int getPixWidth() {
return pixWidth;
}
public int getPixHeight() {
return pixHeight;
}
2013-10-23 23:26:05 +02:00
public float getPixXFromLatLon(double latitude, double longitude) {
2013-11-03 23:23:37 +01:00
double xTile = MapUtils.getTileNumberX(zoom, longitude);
double yTile = MapUtils.getTileNumberY(zoom, latitude);
2013-09-27 11:04:49 +02:00
return getPixXFromTile(xTile, yTile);
}
2013-10-20 00:19:44 +02:00
2013-10-23 23:26:05 +02:00
public float getPixXFromTile(double tileX, double tileY, float zoom) {
2013-10-20 00:19:44 +02:00
double pw = MapUtils.getPowZoom(zoom - this.zoom);
2013-11-03 23:23:37 +01:00
double xTile = tileX / pw;
double yTile = tileY / pw;
2013-10-20 00:19:44 +02:00
return getPixXFromTile(xTile, yTile);
}
2013-09-27 11:04:49 +02:00
2013-10-23 23:26:05 +02:00
protected float getPixXFromTile(double xTile, double yTile) {
2013-10-01 00:19:54 +02:00
double rotX;
final double dTileX = xTile - oxTile;
final double dTileY = yTile - oyTile;
2013-09-27 11:04:49 +02:00
if(isMapRotateEnabled()){
rotX = (rotateCos * dTileX - rotateSin * dTileY);
} else {
rotX = dTileX;
}
2013-10-01 00:19:54 +02:00
double dx = rotX * zoomFactor;
2013-10-23 23:26:05 +02:00
return (float) (dx + cx);
2013-09-27 11:04:49 +02:00
}
2013-10-23 23:26:05 +02:00
public float getPixYFromLatLon(double latitude, double longitude) {
2013-11-03 23:23:37 +01:00
double xTile = MapUtils.getTileNumberX(zoom, longitude);
double yTile = MapUtils.getTileNumberY(zoom, latitude);
2013-09-27 11:04:49 +02:00
return getPixYFromTile(xTile, yTile);
}
2013-10-20 00:19:44 +02:00
2013-10-23 23:26:05 +02:00
public float getPixYFromTile(double tileX, double tileY, float zoom) {
2013-10-20 00:19:44 +02:00
double pw = MapUtils.getPowZoom(zoom - this.zoom);
2013-11-03 23:23:37 +01:00
double xTile = (tileX / pw);
double yTile = (tileY / pw);
2013-10-20 00:19:44 +02:00
return getPixYFromTile(xTile, yTile);
}
2013-09-27 11:04:49 +02:00
2013-11-03 23:23:37 +01:00
protected float getPixYFromTile(double xTile, double yTile) {
2013-10-01 00:19:54 +02:00
final double dTileX = xTile - oxTile;
final double dTileY = yTile - oyTile;
double rotY;
2013-09-27 11:04:49 +02:00
if(isMapRotateEnabled()){
rotY = (rotateSin * dTileX + rotateCos * dTileY);
} else {
rotY = dTileY;
}
2013-10-01 00:19:54 +02:00
double dy = rotY * zoomFactor;
2013-10-23 23:26:05 +02:00
return (float) (dy + cy);
2013-09-27 11:04:49 +02:00
}
2013-09-27 14:07:07 +02:00
public int getPixXFromLonNoRot(double longitude) {
2013-10-01 00:19:54 +02:00
double dTilex = (float) MapUtils.getTileNumberX(zoom, longitude) - oxTile;
2013-09-27 14:07:07 +02:00
return (int) (dTilex * zoomFactor + cx);
}
2013-09-27 17:58:04 +02:00
public int getPixXFromTileXNoRot(double tileX) {
2013-10-01 00:19:54 +02:00
double dTilex = tileX - oxTile;
2013-09-27 17:58:04 +02:00
return (int) (dTilex * zoomFactor + cx);
}
2013-09-27 14:07:07 +02:00
public int getPixYFromLatNoRot(double latitude) {
2013-10-01 00:19:54 +02:00
double dTileY = MapUtils.getTileNumberY(zoom, latitude) - oyTile;
2013-09-27 14:07:07 +02:00
return (int) ((dTileY * zoomFactor) + cy);
}
2013-09-27 17:58:04 +02:00
public int getPixYFromTileYNoRot(double tileY) {
2013-10-01 00:19:54 +02:00
double dTileY = tileY - oyTile;
2013-09-27 17:58:04 +02:00
return (int) ((dTileY * zoomFactor) + cy);
}
2013-09-27 11:04:49 +02:00
private boolean isMapRotateEnabled() {
return rotate != 0;
}
public QuadRect getLatLonBounds() {
2013-10-06 11:59:01 +02:00
checkTileRectangleCalculated();
2013-09-27 11:04:49 +02:00
return latLonBounds;
}
2013-10-01 00:19:54 +02:00
public double getRotateCos() {
2013-09-27 11:04:49 +02:00
return rotateCos;
}
2013-10-01 00:19:54 +02:00
public double getRotateSin() {
2013-09-27 11:04:49 +02:00
return rotateSin;
}
2013-09-27 14:07:07 +02:00
public int getZoom() {
2013-09-27 11:04:49 +02:00
return zoom;
}
2013-09-27 14:07:07 +02:00
// Change lat/lon center
public void setLatLonCenter(double lat, double lon) {
this.lat = lat;
this.lon = lon;
calculateDerivedFields();
}
public void setRotate(float rotate) {
this.rotate = rotate;
calculateDerivedFields();
}
2013-09-27 17:58:04 +02:00
public void increasePixelDimensions(int dwidth, int dheight) {
this.pixWidth += 2 * dwidth;
this.pixHeight += 2 * dheight;
this.cx += dwidth;
this.cy += dheight;
calculateDerivedFields();
}
2013-09-27 14:07:07 +02:00
public void setPixelDimensions(int width, int height) {
setPixelDimensions(width, height, 0.5f, 0.5f);
}
public void setPixelDimensions(int width, int height, float ratiocx, float ratiocy) {
this.pixHeight = height;
this.pixWidth = width;
this.cx = (int) (pixWidth * ratiocx);
this.cy = (int) (pixHeight * ratiocy);
calculateDerivedFields();
}
2013-09-27 17:58:04 +02:00
public boolean isZoomAnimated() {
return zoomAnimation != 0;
}
public double getZoomAnimation() {
2013-09-27 17:58:04 +02:00
return zoomAnimation;
}
public double getZoomFloatPart() {
return zoomFloatPart;
}
2013-09-27 17:58:04 +02:00
public void setZoomAndAnimation(int zoom, double zoomAnimation, double zoomFloatPart) {
this.zoomAnimation = zoomAnimation;
this.zoomFloatPart = zoomFloatPart;
this.zoom = zoom;
2013-09-27 17:58:04 +02:00
calculateDerivedFields();
}
public void setZoomAndAnimation(int zoom, double zoomAnimation) {
2013-10-06 11:59:01 +02:00
this.zoomAnimation = zoomAnimation;
this.zoom = zoom;
calculateDerivedFields();
}
2013-09-27 14:07:07 +02:00
public void setCenterLocation(float ratiocx, float ratiocy) {
this.cx = (int) (pixWidth * ratiocx);
this.cy = (int) (pixHeight * ratiocy);
calculateDerivedFields();
}
public LatLon getLeftTopLatLon() {
2013-10-06 11:59:01 +02:00
checkTileRectangleCalculated();
2013-10-20 00:19:44 +02:00
return new LatLon(MapUtils.getLatitudeFromTile(zoom, alignTile(tileLT.y)),
MapUtils.getLongitudeFromTile(zoom, alignTile(tileLT.x)));
2013-09-27 14:07:07 +02:00
2013-09-27 11:04:49 +02:00
}
2013-10-20 00:19:44 +02:00
public QuadPointDouble getLeftTopTile(double zoom) {
2013-10-20 00:19:44 +02:00
checkTileRectangleCalculated();
2013-11-03 23:23:37 +01:00
return new QuadPointDouble((tileLT.x * MapUtils.getPowZoom(zoom - this.zoom)),
(tileLT.y * MapUtils.getPowZoom(zoom - this.zoom)));
2013-10-20 00:19:44 +02:00
}
2013-10-20 00:19:44 +02:00
2013-11-03 23:23:37 +01:00
public QuadPointDouble getRightBottomTile(float zoom) {
2013-10-20 00:19:44 +02:00
checkTileRectangleCalculated();
2013-11-03 23:23:37 +01:00
return new QuadPointDouble((tileRB.x * MapUtils.getPowZoom(zoom - this.zoom)),
(tileRB.y * MapUtils.getPowZoom(zoom - this.zoom)));
2013-10-20 00:19:44 +02:00
}
2013-09-27 11:04:49 +02:00
2013-10-06 11:59:01 +02:00
private void checkTileRectangleCalculated() {
if(tileBounds == null){
2013-10-20 00:19:44 +02:00
calculateTileRectangle();
2013-10-06 11:59:01 +02:00
}
}
2013-09-27 14:07:07 +02:00
public LatLon getRightBottomLatLon() {
2013-10-06 11:59:01 +02:00
checkTileRectangleCalculated();
2013-10-20 00:19:44 +02:00
return new LatLon(MapUtils.getLatitudeFromTile(zoom, alignTile(tileRB.y)),
MapUtils.getLongitudeFromTile(zoom, alignTile(tileRB.x)));
2013-09-27 14:07:07 +02:00
}
2014-12-06 14:55:36 +01:00
public void setMapDensity(double mapDensity) {
this.mapDensity = mapDensity;
2013-09-27 14:07:07 +02:00
calculateDerivedFields();
}
2014-12-06 14:55:36 +01:00
public double getMapDensity() {
return mapDensity;
}
2013-09-27 14:07:07 +02:00
2013-10-06 11:59:01 +02:00
public void setZoom(int zoom) {
this.zoom = zoom;
calculateDerivedFields();
}
2013-09-27 11:04:49 +02:00
public float getRotate() {
return rotate;
}
2013-09-27 14:07:07 +02:00
public float getDensity() {
return density;
}
public RotatedTileBox copy() {
return new RotatedTileBox(this);
}
2013-09-27 11:04:49 +02:00
public boolean containsTileBox(RotatedTileBox box) {
2013-10-06 11:59:01 +02:00
checkTileRectangleCalculated();
// thread safe
box = box.copy();
2013-10-06 11:59:01 +02:00
box.checkTileRectangleCalculated();
2013-09-27 11:04:49 +02:00
if(!containsTilePoint(box.tileLB)){
return false;
}
if(!containsTilePoint(box.tileLT)){
return false;
}
if(!containsTilePoint(box.tileRB)){
return false;
}
if(!containsTilePoint(box.tileRT)){
return false;
}
return true;
}
2013-09-27 14:07:07 +02:00
2013-09-27 11:04:49 +02:00
public boolean containsTilePoint(QuadPoint qp) {
double tx = getPixXFromTile(qp.x, qp.y);
double ty = getPixYFromTile(qp.x, qp.y);
return tx >= 0 && tx <= pixWidth && ty >= 0 && ty <= pixHeight;
}
2013-11-03 23:23:37 +01:00
public boolean containsTilePoint(QuadPointDouble qp) {
double tx = getPixXFromTile(qp.x, qp.y);
double ty = getPixYFromTile(qp.x, qp.y);
return tx >= 0 && tx <= pixWidth && ty >= 0 && ty <= pixHeight;
}
2013-09-27 14:07:07 +02:00
public boolean containsLatLon(double lat, double lon) {
double tx = getPixXFromLatLon(lat, lon);
double ty = getPixYFromLatLon(lat, lon);
return tx >= 0 && tx <= pixWidth && ty >= 0 && ty <= pixHeight;
}
2014-07-06 16:17:46 +02:00
public boolean containsLatLon(LatLon latLon) {
double tx = getPixXFromLatLon(latLon.getLatitude(), latLon.getLongitude());
double ty = getPixYFromLatLon(latLon.getLatitude(), latLon.getLongitude());
return tx >= 0 && tx <= pixWidth && ty >= 0 && ty <= pixHeight;
}
2013-09-27 14:07:07 +02:00
public double getDistance(int pixX, int pixY, int pixX2, int pixY2) {
final double lat1 = getLatFromPixel(pixX, pixY);
final double lon1 = getLonFromPixel(pixX, pixY);
final double lat2 = getLatFromPixel(pixX2, pixY2);
2013-09-28 01:00:10 +02:00
final double lon2 = getLonFromPixel(pixX2, pixY2);
2013-09-27 14:07:07 +02:00
return MapUtils.getDistance(lat1,lon1, lat2, lon2);
}
public static class RotatedTileBoxBuilder {
private RotatedTileBox tb;
private boolean pixelDimensionsSet = false;
private boolean locationSet = false;
private boolean zoomSet = false;
public RotatedTileBoxBuilder() {
tb = new RotatedTileBox();
tb.density = 1;
tb.rotate = 0;
}
public RotatedTileBoxBuilder density(float d) {
tb.density = d;
return this;
}
2014-12-06 14:55:36 +01:00
public RotatedTileBoxBuilder setMapDensity(double mapDensity) {
tb.mapDensity = mapDensity;
return this;
}
public RotatedTileBoxBuilder setZoom(int zoom) {
2013-09-27 14:07:07 +02:00
tb.zoom = zoom;
zoomSet = true;
return this;
}
2014-12-06 14:55:36 +01:00
2013-09-27 14:07:07 +02:00
public RotatedTileBoxBuilder setLocation(double lat, double lon) {
tb.lat = lat;
tb.lon = lon;
locationSet = true;
return this;
}
public RotatedTileBoxBuilder setRotate(float degrees) {
tb.rotate = degrees;
return this;
}
public RotatedTileBoxBuilder setPixelDimensions(int pixWidth, int pixHeight, float centerX, float centerY) {
tb.pixWidth = pixWidth;
tb.pixHeight = pixHeight;
tb.cx = (int) (pixWidth * centerX);
tb.cy = (int) (pixHeight * centerY);
pixelDimensionsSet = true;
return this;
}
public RotatedTileBoxBuilder setPixelDimensions(int pixWidth, int pixHeight) {
return setPixelDimensions(pixWidth, pixHeight, 0.5f, 0.5f);
}
public RotatedTileBox build() {
if(!pixelDimensionsSet) {
throw new IllegalArgumentException("Please specify pixel dimensions");
}
if(!zoomSet) {
throw new IllegalArgumentException("Please specify zoom");
}
if(!locationSet) {
throw new IllegalArgumentException("Please specify location");
}
final RotatedTileBox local = tb;
local.calculateDerivedFields();
tb = null;
return local;
}
}
public double getLongitude() {
return lon;
}
public double getLatitude() {
return lat;
}
2015-01-25 22:56:48 +01:00
@Override
public String toString() {
return "RotatedTileBox [lat=" + lat + ", lon=" + lon + ", rotate="
+ rotate + ", density=" + density + ", zoom=" + zoom
+ ", mapDensity=" + mapDensity + ", zoomAnimation="
+ zoomAnimation + ", zoomFloatPart=" + zoomFloatPart + ", cx="
+ cx + ", cy=" + cy + ", pixWidth=" + pixWidth + ", pixHeight="
+ pixHeight + "]";
}
2013-09-27 11:04:49 +02:00
}