All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.oscim.map.Viewport Maven / Gradle / Ivy

/*
 * Copyright 2012 Hannes Janetzek
 * Copyright 2016-2018 devemux86
 * Copyright 2016 Erik Duisters
 * Copyright 2017 Luca Osten
 * Copyright 2018 Izumi Kawashima
 * Copyright 2019 Gustl22
 *
 * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see .
 */
package org.oscim.map;

import org.oscim.core.*;
import org.oscim.renderer.GLMatrix;
import org.oscim.utils.FastMath;

/**
 * The Viewport class contains a MapPosition and the projection matrices.
 * It provides functions to modify the MapPosition and translate between
 * map and screen coordinates.
 * 

* Public methods are thread safe. */ public class Viewport { public static final int MAX_ZOOM_LEVEL = 20; public static final int MIN_ZOOM_LEVEL = 2; public static final float MIN_TILT = 0; /** * Limited by: *

* - numTiles in {@link org.oscim.layers.tile.TileManager#init() TileManager} *

* - tilt of map when cutting map on near and far plane. */ public static final float MAX_TILT = 65; protected double mMaxScale = (1 << MAX_ZOOM_LEVEL); protected double mMinScale = (1 << MIN_ZOOM_LEVEL); protected float mMinTilt = MIN_TILT; protected float mMaxTilt = MAX_TILT; protected float mMinBearing = -180; protected float mMaxBearing = 180; protected float mMinRoll = -180; protected float mMaxRoll = 180; protected double mMinX = 0; protected double mMaxX = 1; protected double mMinY = 0; protected double mMaxY = 1; protected final MapPosition mPos = new MapPosition(); protected final GLMatrix mProjMatrix = new GLMatrix(); protected final GLMatrix mProjMatrixUnscaled = new GLMatrix(); protected final GLMatrix mProjMatrixInverse = new GLMatrix(); protected final GLMatrix mRotationMatrix = new GLMatrix(); protected final GLMatrix mViewMatrix = new GLMatrix(); protected final GLMatrix mViewProjMatrix = new GLMatrix(); protected final GLMatrix mUnprojMatrix = new GLMatrix(); protected final GLMatrix mTmpMatrix = new GLMatrix(); /* temporary vars: only use in synchronized functions! */ protected final Point mMovePoint = new Point(); protected final float[] mv = new float[4]; protected final float[] mu = new float[4]; protected final float[] mViewCoords = new float[8]; /** * Height and width in pixels */ protected float mHeight, mWidth; public static final float VIEW_DISTANCE = 3.0f; public static final float VIEW_NEAR = 1; public static final float VIEW_FAR = 8; /** * scale map plane at VIEW_DISTANCE to near plane */ public static final float VIEW_SCALE = (VIEW_NEAR / VIEW_DISTANCE) * 0.5f; public Viewport() { mPos.scale = mMinScale; mPos.x = 0.5; mPos.y = 0.5; mPos.zoomLevel = MIN_ZOOM_LEVEL; mPos.bearing = 0; mPos.tilt = 0; mPos.roll = 0; } public double limitScale(double scale) { if (scale > mMaxScale) return mMaxScale; else if (scale < mMinScale) return mMinScale; return scale; } public float limitTilt(float tilt) { if (tilt > mMaxTilt) return mMaxTilt; else if (tilt < mMinTilt) return mMinTilt; return tilt; } public boolean limitPosition(MapPosition pos) { boolean changed = false; if (pos.scale > mMaxScale) { pos.scale = mMaxScale; changed = true; } else if (pos.scale < mMinScale) { pos.scale = mMinScale; changed = true; } if (pos.tilt > mMaxTilt) { pos.tilt = mMaxTilt; changed = true; } else if (pos.tilt < mMinTilt) { pos.tilt = mMinTilt; changed = true; } if (pos.bearing > mMaxBearing) { pos.bearing = mMaxBearing; changed = true; } else if (pos.bearing < mMinBearing) { pos.bearing = mMinBearing; changed = true; } if (pos.roll > mMaxRoll) { pos.roll = mMaxRoll; changed = true; } else if (pos.roll < mMinRoll) { pos.roll = mMinRoll; changed = true; } if (pos.x > mMaxX) { pos.x = mMaxX; changed = true; } else if (pos.x < mMinX) { pos.x = mMinX; changed = true; } if (pos.y > mMaxY) { pos.y = mMaxY; changed = true; } else if (pos.y < mMinY) { pos.y = mMinY; changed = true; } return changed; } /** * Get the current MapPosition. * * @param pos MapPosition to be updated. * @return true iff current position is different from * passed position. */ public boolean getMapPosition(MapPosition pos) { boolean changed = (pos.scale != mPos.scale || pos.x != mPos.x || pos.y != mPos.y || pos.bearing != mPos.bearing || pos.tilt != mPos.tilt || pos.roll != mPos.roll); pos.bearing = mPos.bearing; pos.tilt = mPos.tilt; pos.roll = mPos.roll; pos.x = mPos.x; pos.y = mPos.y; pos.scale = mPos.scale; pos.zoomLevel = FastMath.log2((int) mPos.scale); return changed; } /** * Get the inverse projection of the viewport, i.e. the * coordinates with z==0 that will be projected exactly * to screen corners by current view-projection-matrix. *

* Except when screen corners don't hit the map (e.g. on large tilt), * then it will return the intersection with near and far plane. * * @param box float[8] will be set to * 0,1 -> x,y bottom-right, * 2,3 -> x,y bottom-left, * 4,5 -> x,y top-left, * 6,7 -> x,y top-right. * @param add increase extents of box */ public void getMapExtents(float[] box, float add) { /* bottom-right */ unproject(1, -1, box, 0); /* bottom-left */ unproject(-1, -1, box, 2); /* top-left */ unproject(-1, 1, box, 4); /* top-right */ unproject(1, 1, box, 6); if (add == 0) return; for (int i = 0; i < 8; i += 2) { float x = box[i]; float y = box[i + 1]; float len = (float) Math.sqrt(x * x + y * y); box[i + 0] += x / len * add; box[i + 1] += y / len * add; } } protected synchronized void unproject(float x, float y, float[] coords, int position) { // Get point for near / opposite plane mv[0] = x; mv[1] = y; mv[2] = -1; mUnprojMatrix.prj(mv); double nx = mv[0]; double ny = mv[1]; double nz = mv[2]; // Get point for far plane mv[0] = x; mv[1] = y; mv[2] = 1; mUnprojMatrix.prj(mv); double fx = mv[0]; double fy = mv[1]; double fz = mv[2]; // Calc diffs double dx = fx - nx; double dy = fy - ny; double dz = fz - nz; double dist; if (y > 0 && nz < dz && fz > dz) { /* Keep far distance (y > 0), while world flips between the screen coordinates. * Screen coordinates can't be correctly converted to map coordinates * as map plane doesn't intersect with top screen corners. */ dist = 1; // Far plane } else if (y < 0 && fz < dz && nz > dz) { /* Keep near distance (y < 0), while world flips between the screen coordinates. * Screen coordinates can't be correctly converted to map coordinates * as map plane doesn't intersect with bottom screen corners. */ dist = 0; // Near plane } else { // Calc factor to get map coordinates on current distance dist = Math.abs(-nz / dz); if (Double.isNaN(dist) || dist > 1) { // Limit distance as it may exceeds to infinity dist = 1; // Far plane } } // near + dist * (far - near) coords[position + 0] = (float) (nx + dist * dx); coords[position + 1] = (float) (ny + dist * dy); } /** * Get the minimal axis-aligned BoundingBox that encloses * the visible part of the map. Sets box to map coordinates: * xmin,ymin,xmax,ymax */ public synchronized Box getBBox(Box box, int expand) { if (box == null) box = new Box(); float[] coords = mViewCoords; getMapExtents(coords, expand); box.xmin = coords[0]; box.xmax = coords[0]; box.ymin = coords[1]; box.ymax = coords[1]; for (int i = 2; i < 8; i += 2) { box.xmin = Math.min(box.xmin, coords[i]); box.xmax = Math.max(box.xmax, coords[i]); box.ymin = Math.min(box.ymin, coords[i + 1]); box.ymax = Math.max(box.ymax, coords[i + 1]); } double cs = mPos.scale * Tile.SIZE; double cx = mPos.x * cs; double cy = mPos.y * cs; box.xmin = (cx + box.xmin) / cs; box.xmax = (cx + box.xmax) / cs; box.ymin = (cy + box.ymin) / cs; box.ymax = (cy + box.ymax) / cs; return box; } /** * Get the GeoPoint for x,y from screen coordinates. * * @param x screen coordinate * @param y screen coordinate * @return the corresponding GeoPoint */ public synchronized GeoPoint fromScreenPoint(float x, float y) { fromScreenPoint(x, y, mMovePoint); return new GeoPoint( MercatorProjection.toLatitude(mMovePoint.y), MercatorProjection.toLongitude(mMovePoint.x)); } protected void unprojectScreen(double x, double y, float[] out) { /* scale to -1..1 */ float mx = (float) (1 - (x / mWidth * 2)); float my = (float) (1 - (y / mHeight * 2)); unproject(-mx, my, out, 0); } /** * Get the map position x,y from screen coordinates. * * @param x screen coordinate * @param y screen coordinate * @param out map position as Point {@link MapPosition#getX() x} and {@link MapPosition#getY() y} */ public synchronized void fromScreenPoint(double x, double y, Point out) { unprojectScreen(x, y, mu); double cs = mPos.scale * Tile.SIZE; double cx = mPos.x * cs; double cy = mPos.y * cs; double dx = cx + mu[0]; double dy = cy + mu[1]; dx /= cs; dy /= cs; while (dx > 1) dx -= 1; while (dx < 0) dx += 1; if (dy > 1) dy = 1; else if (dy < 0) dy = 0; out.x = dx; out.y = dy; } /** * Get the screen pixel for a GeoPoint (relative to center) * * @param geoPoint the GeoPoint * @param out Point projected to screen pixel relative to center */ public void toScreenPoint(GeoPoint geoPoint, Point out) { toScreenPoint(geoPoint, true, out); } /** * Get the screen pixel for a GeoPoint * * @param geoPoint the GeoPoint * @param out Point projected to screen pixel */ public void toScreenPoint(GeoPoint geoPoint, boolean relativeToCenter, Point out) { MercatorProjection.project(geoPoint, out); toScreenPoint(out.x, out.y, relativeToCenter, out); } /** * Get the screen pixel for map coordinates (relative to center) * * @param out Point projected to screen coordinate relative to center */ public void toScreenPoint(double x, double y, Point out) { toScreenPoint(x, y, true, out); } /** * Get the screen pixel for map coordinates * * @param out Point projected to screen coordinate */ public synchronized void toScreenPoint(double x, double y, boolean relativeToCenter, Point out) { double cs = mPos.scale * Tile.SIZE; double cx = mPos.x * cs; double cy = mPos.y * cs; mv[0] = (float) (x * cs - cx); mv[1] = (float) (y * cs - cy); mv[2] = 0; mv[3] = 1; mViewProjMatrix.prj(mv); out.x = (mv[0] * (mWidth / 2)); out.y = -(mv[1] * (mHeight / 2)); if (!relativeToCenter) { out.x += mWidth / 2; out.y += mHeight / 2; } } boolean sizeChanged(Viewport viewport) { return mHeight != viewport.mHeight || mWidth != viewport.mWidth; } protected boolean copy(Viewport viewport) { boolean sizeChanged = sizeChanged(viewport); mHeight = viewport.mHeight; mWidth = viewport.mWidth; mProjMatrix.copy(viewport.mProjMatrix); mProjMatrixUnscaled.copy(viewport.mProjMatrixUnscaled); mProjMatrixInverse.copy(viewport.mProjMatrixInverse); mUnprojMatrix.copy(viewport.mUnprojMatrix); mRotationMatrix.copy(viewport.mRotationMatrix); mViewMatrix.copy(viewport.mViewMatrix); mViewProjMatrix.copy(viewport.mViewProjMatrix); return viewport.getMapPosition(mPos) || sizeChanged; } public double getMaxScale() { return mMaxScale; } public void setMaxScale(double maxScale) { this.mMaxScale = maxScale; } public double getMinScale() { return mMinScale; } public void setMinScale(double minScale) { this.mMinScale = minScale; } public int getMaxZoomLevel() { return FastMath.log2((int) mMaxScale); } public void setMaxZoomLevel(int maxZoomLevel) { this.mMaxScale = (1 << maxZoomLevel); } public int getMinZoomLevel() { return FastMath.log2((int) mMinScale); } public void setMinZoomLevel(int minZoomLevel) { this.mMinScale = (1 << minZoomLevel); } public float getMaxTilt() { return mMaxTilt; } public void setMaxTilt(float maxTilt) { this.mMaxTilt = maxTilt; } public float getMinTilt() { return mMinTilt; } public void setMinTilt(float minTilt) { this.mMinTilt = minTilt; } public float getMaxBearing() { return mMaxBearing; } public void setMaxBearing(float maxBearing) { this.mMaxBearing = maxBearing; } public float getMinBearing() { return mMinBearing; } public void setMinBearing(float minBearing) { this.mMinBearing = minBearing; } public float getMaxRoll() { return mMaxRoll; } public void setMaxRoll(float maxRoll) { this.mMaxRoll = maxRoll; } public float getMinRoll() { return mMinRoll; } public void setMinRoll(float minRoll) { this.mMinRoll = minRoll; } public double getMaxX() { return mMaxX; } public void setMaxX(double maxX) { this.mMaxX = maxX; } public double getMinX() { return mMinX; } public void setMinX(double minX) { this.mMinX = minX; } public double getMaxY() { return mMaxY; } public void setMaxY(double maxY) { this.mMaxY = maxY; } public double getMinY() { return mMinY; } public void setMinY(double minY) { this.mMinY = minY; } public void setMapLimit(double minX, double minY, double maxX, double maxY) { this.mMinX = minX; this.mMinY = minY; this.mMaxX = maxX; this.mMaxY = maxY; } public BoundingBox getMapLimit() { return new BoundingBox( MercatorProjection.toLatitude(mMaxY), MercatorProjection.toLongitude(mMinX), MercatorProjection.toLatitude(mMinY), MercatorProjection.toLongitude(mMaxX) ); } public void setMapLimit(BoundingBox mapLimit) { this.mMinX = MercatorProjection.longitudeToX(mapLimit.getMinLongitude()); this.mMinY = MercatorProjection.latitudeToY(mapLimit.getMaxLatitude()); this.mMaxX = MercatorProjection.longitudeToX(mapLimit.getMaxLongitude()); this.mMaxY = MercatorProjection.latitudeToY(mapLimit.getMinLatitude()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy