Skip to content

Commit

Permalink
Implement new Projection.
Browse files Browse the repository at this point in the history
Move most Overlays to new Projection.
  • Loading branch information
kurtzmarc committed Mar 24, 2014
1 parent e5ded90 commit 423680b
Show file tree
Hide file tree
Showing 16 changed files with 247 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import org.osmdroid.util.BoundingBoxE6;
import org.osmdroid.views.MapView;
import org.osmdroid.views.MapView.Projection;
import org.osmdroid.views.Projection;
import org.osmdroid.views.overlay.Overlay;

import android.content.Context;
Expand Down Expand Up @@ -33,7 +33,7 @@ public class SampleLimitedScrollArea extends BaseSampleFragment {
private static final int MENU_LIMIT_SCROLLING_ID = Menu.FIRST;

private static final BoundingBoxE6 sCentralParkBoundingBox;
private static final SafePaint sPaint;
private static final Paint sPaint;

// ===========================================================
// Fields
Expand Down Expand Up @@ -140,7 +140,13 @@ public ShadeAreaOverlay(Context ctx) {
@Override
protected void draw(Canvas c, MapView osmv, boolean shadow) {
final Projection proj = osmv.getProjection();
Rect area = proj.toPixels(sCentralParkBoundingBox);

Point topLeft = proj.toPixelsProjected(sCentralParkBoundingBox.getLatNorthE6(),
sCentralParkBoundingBox.getLonWestE6(), null);
Point bottomRight = proj.toPixelsProjected(sCentralParkBoundingBox.getLatSouthE6(),
sCentralParkBoundingBox.getLonEastE6(), null);

Rect area = new Rect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
c.drawRect(area, sPaint);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ public void onCreate(final Bundle savedInstanceState) {
this.mOsmv.getOverlays().add(mScaleBarOverlay);
// Scale bar tries to draw as 1-inch, so to put it in the top center, set x offset to
// half screen width, minus half an inch.
this.mScaleBarOverlay.setScaleBarOffset(getResources().getDisplayMetrics().widthPixels
/ 2 - getResources().getDisplayMetrics().xdpi / 2, 10);
this.mScaleBarOverlay.setScaleBarOffset(
(int) (getResources().getDisplayMetrics().widthPixels / 2 - getResources()
.getDisplayMetrics().xdpi / 2), 10);
}

/* SingleLocation-Overlay */
Expand Down
11 changes: 6 additions & 5 deletions osmdroid-android/src/main/java/org/osmdroid/api/IProjection.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.osmdroid.api;

import org.osmdroid.views.MapView.Projection;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.Projection;

import android.graphics.Point;

Expand All @@ -14,9 +15,9 @@
public interface IProjection {

/**
* Converts the given GeoPoint to onscreen pixel coordinates, relative to the top-left of the
* MapView that provided this Projection.
*
* Converts the given {@link IGeoPoint} to onscreen pixel coordinates, relative to the top-left
* of the {@link #MapView} that provided this Projection.
*
* @param in
* The latitude/longitude pair to convert.
* @param out
Expand All @@ -29,7 +30,7 @@ public interface IProjection {
* Create a new GeoPoint from pixel coordinates relative to the top-left of the MapView that
* provided this PixelConverter.
*/
IGeoPoint fromPixels(int x, int y);
IGeoPoint fromPixels(int x, int y, GeoPoint out);

/**
* Converts a distance in meters (along the equator) to one in (horizontal) pixels at the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ public void zoomToSpan(int latSpanE6, int lonSpanE6) {
return;
}

final BoundingBoxE6 bb = this.mMapView.getBoundingBox();
final int curZoomLevel = this.mMapView.getZoomLevel();
final BoundingBoxE6 bb = this.mMapView.getProjection().getBoundingBox();
final int curZoomLevel = this.mMapView.getProjection().getZoomLevel();

final int curLatSpan = bb.getLatitudeSpanE6();
final int curLonSpan = bb.getLongitudeSpanE6();
Expand All @@ -108,7 +108,7 @@ public void zoomToSpan(int latSpanE6, int lonSpanE6) {
*/
@Override
public void animateTo(final IGeoPoint point) {
Point p = mMapView.getProjection().toMapPixels(point, null);
Point p = mMapView.getProjection().toPixels(point, null);
animateTo(p.x, p.y);
}

Expand All @@ -118,10 +118,13 @@ public void animateTo(final IGeoPoint point) {
public void animateTo(int x, int y) {
if (!mMapView.isAnimating()) {
mMapView.mIsFlinging = false;
Point mercatorPoint = mMapView.getProjection().toMercatorPixels(x, y, null);
// The points provided are "center", we want relative to upper-left for scrolling
mercatorPoint.offset(-mMapView.getWidth() / 2, -mMapView.getHeight() / 2);
final int xStart = mMapView.getScrollX();
final int yStart = mMapView.getScrollY();
mMapView.getScroller().startScroll(xStart, yStart, x - xStart, y - yStart,
ANIMATION_DURATION_DEFAULT);
mMapView.getScroller().startScroll(xStart, yStart, mercatorPoint.x - xStart,
mercatorPoint.y - yStart, ANIMATION_DURATION_DEFAULT);
mMapView.postInvalidate();
}
}
Expand All @@ -136,8 +139,11 @@ public void scrollBy(int x, int y) {
*/
@Override
public void setCenter(final IGeoPoint point) {
Point p = mMapView.getProjection().toMapPixels(point, null);
this.mMapView.scrollTo(p.x, p.y);
Point p = mMapView.getProjection().toPixels(point, null);
p = mMapView.getProjection().toMercatorPixels(p.x, p.y, p);
// The points provided are "center", we want relative to upper-left for scrolling
p.offset(-mMapView.getWidth() / 2, -mMapView.getHeight() / 2);
mMapView.scrollTo(p.x, p.y);
}

@Override
Expand Down Expand Up @@ -187,8 +193,7 @@ public int setZoom(final int zoomlevel) {
*/
@Override
public boolean zoomIn() {
Point coords = mMapView.getProjection().toMapPixels(mMapView.getMapCenter(), null);
return zoomInFixing(coords.x, coords.y);
return zoomInFixing(mMapView.getWidth() / 2, mMapView.getHeight() / 2);
}

@Override
Expand Down Expand Up @@ -218,8 +223,7 @@ public boolean zoomInFixing(final int xPixel, final int yPixel) {
*/
@Override
public boolean zoomOut() {
Point coords = mMapView.getProjection().toMapPixels(mMapView.getMapCenter(), null);
return zoomOutFixing(coords.x, coords.y);
return zoomOutFixing(mMapView.getWidth() / 2, mMapView.getHeight() / 2);
}

@Override
Expand Down Expand Up @@ -256,10 +260,13 @@ protected void onAnimationEnd() {
m.postRotate(-mMapView.getMapOrientation(), screenRect.exactCenterX(),
screenRect.exactCenterY());
float[] pts = new float[2];
pts[0] = mMapView.getScrollX();
pts[1] = mMapView.getScrollY();
pts[0] = screenRect.exactCenterX();
pts[1] = screenRect.exactCenterY();
m.mapPoints(pts);
mMapView.scrollTo((int) pts[0], (int) pts[1]);
Point p = mMapView.getProjection().toMercatorPixels((int) pts[0], (int) pts[1], null);
// The points provided are "center", we want relative to upper-left for scrolling
p.offset(-mMapView.getWidth() / 2, -mMapView.getHeight() / 2);
mMapView.scrollTo(p.x, p.y);
setZoom(mMapView.mTargetZoomLevel.get());
mMapView.mMultiTouchScale = 1f;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ public void zoomToSpan(final int reqLatSpan, final int reqLonSpan) {
return;
}

final BoundingBoxE6 bb = this.mOsmv.getBoundingBox();
final int curZoomLevel = this.mOsmv.getZoomLevel();
final BoundingBoxE6 bb = this.mOsmv.getProjection().getBoundingBox();
final int curZoomLevel = this.mOsmv.getProjection().getZoomLevel();

final int curLatSpan = bb.getLatitudeSpanE6();
final int curLonSpan = bb.getLongitudeSpanE6();
Expand Down
158 changes: 158 additions & 0 deletions osmdroid-android/src/main/java/org/osmdroid/views/Projection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package org.osmdroid.views;


import org.osmdroid.api.IGeoPoint;
import org.osmdroid.api.IProjection;
import org.osmdroid.util.BoundingBoxE6;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.util.TileSystem;
import org.osmdroid.views.util.constants.MapViewConstants;

import android.graphics.Point;
import android.graphics.Rect;

/**
* A Projection serves to translate between the coordinate system of x/y on-screen pixel coordinates
* and that of latitude/longitude points on the surface of the earth. You obtain a Projection from
* MapView.getProjection(). You should not hold on to this object for more than one draw, since the
* projection of the map could change. <br />
* <br />
* <b>Note:</b> This class will "wrap" all pixel and lat/long values that overflow their bounds
* (rather than clamping to their bounds).
*
* @author Marc Kurtz
* @author Nicolas Gramlich
* @author Manuel Stahl
*/
public class Projection implements IProjection, MapViewConstants {

private final int mMapViewWidth;
private final int mMapViewHeight;
// The offsets will take us from the MapView's current coordinate system
// to a 0,0 coordinate system
protected final int mOffsetX;
protected final int mOffsetY;

private final BoundingBoxE6 mBoundingBoxProjection;
private final int mZoomLevelProjection;
private final Rect mScreenRectProjection;
private final Rect mIntrinsicScreenRectProjection;
private final float mMapOrientation;

Projection(MapView mapView) {

mZoomLevelProjection = mapView.getZoomLevel(false);
mScreenRectProjection = mapView.getScreenRect(null);
mIntrinsicScreenRectProjection = mapView.getIntrinsicScreenRect(null);
mMapOrientation = mapView.getMapOrientation();

mMapViewWidth = mapView.getWidth();
mMapViewHeight = mapView.getHeight();
mOffsetX = -mapView.getScrollX();
mOffsetY = -mapView.getScrollY();

final IGeoPoint neGeoPoint = fromPixels(mMapViewWidth, 0, null);
final IGeoPoint swGeoPoint = fromPixels(0, mMapViewHeight, null);

mBoundingBoxProjection = new BoundingBoxE6(neGeoPoint.getLatitudeE6(),
neGeoPoint.getLongitudeE6(), swGeoPoint.getLatitudeE6(),
swGeoPoint.getLongitudeE6());
}

public int getZoomLevel() {
return mZoomLevelProjection;
}

public BoundingBoxE6 getBoundingBox() {
return mBoundingBoxProjection;
}

public Rect getScreenRect() {
return mScreenRectProjection;
}

public Rect getIntrinsicScreenRect() {
return mIntrinsicScreenRectProjection;
}

public float getMapOrientation() {
return mMapOrientation;
}

@Override
public IGeoPoint fromPixels(int x, int y, GeoPoint reuse) {
return TileSystem.PixelXYToLatLong(x - mOffsetX, y - mOffsetY, mZoomLevelProjection, reuse);
}

@Override
public Point toPixels(final IGeoPoint in, final Point reuse) {
Point out = TileSystem.LatLongToPixelXY(in.getLatitude(), in.getLongitude(),
getZoomLevel(), reuse);
out = fromMercatorPixels(out.x, out.y, out);
return out;
}

/**
* Performs only the first computationally heavy part of the projection. Call
* {@link #toPixelsTranslated(Point, Point)} to get the final position.
*
* @param latituteE6
* the latitute of the point
* @param longitudeE6
* the longitude of the point
* @param reuse
* just pass null if you do not have a Point to be 'recycled'.
* @return intermediate value to be stored and passed to toMapPixelsTranslated.
*/
public Point toPixelsProjected(final int latituteE6, final int longitudeE6, final Point reuse) {
return TileSystem.LatLongToPixelXY(latituteE6 * 1E-6, longitudeE6 * 1E-6,
MAXIMUM_ZOOMLEVEL, reuse);
}

/**
* Performs the second computationally light part of the projection.
*
* @param in
* the Point calculated by the {@link #toPixelsProjected(int, int, Point)}
* @param reuse
* just pass null if you do not have a Point to be 'recycled'.
* @return the Point containing the coordinates of the initial GeoPoint passed to the
* {@link #toPixelsProjected(int, int, Point)}.
*/
public Point toPixelsTranslated(final Point in, final Point reuse) {
final Point out = reuse != null ? reuse : new Point();

final int zoomDifference = MAXIMUM_ZOOMLEVEL - getZoomLevel();
out.set(in.x >> zoomDifference, in.y >> zoomDifference);
return fromMercatorPixels(out.x, out.y, out);
}

public Point fromMercatorPixels(int x, int y, Point reuse) {
final Point out = reuse != null ? reuse : new Point();
out.set(x, y);
out.offset(mOffsetX, mOffsetY);
return out;
}

public Point toMercatorPixels(int x, int y, Point reuse) {
final Point out = reuse != null ? reuse : new Point();
out.set(x, y);
out.offset(-mOffsetX, -mOffsetY);
return out;
}

@Override
public float metersToEquatorPixels(final float meters) {
return meters / (float) TileSystem.GroundResolution(0, mZoomLevelProjection);
}

@Override
public IGeoPoint getNorthEast() {
return fromPixels(mMapViewWidth, 0, null);
}

@Override
public IGeoPoint getSouthWest() {
return fromPixels(0, mMapViewHeight, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import org.osmdroid.ResourceProxy;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapView;
import org.osmdroid.views.MapView.Projection;
import org.osmdroid.views.Projection;

import android.content.Context;
import android.graphics.Bitmap;
Expand Down Expand Up @@ -114,7 +114,7 @@ public void draw(final Canvas c, final MapView osmv, final boolean shadow) {

if (this.mLocation != null) {
final Projection pj = osmv.getProjection();
pj.toMapPixels(this.mLocation, screenCoords);
pj.toPixels(this.mLocation, screenCoords);

if (this.mShowAccuracy && this.mAccuracy > 10) {
final float accuracyRadius = pj.metersToEquatorPixels(this.mAccuracy);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.osmdroid.ResourceProxy.bitmap;
import org.osmdroid.api.IMapView;
import org.osmdroid.views.MapView;
import org.osmdroid.views.MapView.Projection;
import org.osmdroid.views.Projection;

import android.content.Context;
import android.graphics.Point;
Expand All @@ -19,7 +19,6 @@ public class ItemizedIconOverlay<Item extends OverlayItem> extends ItemizedOverl
protected final List<Item> mItemList;
protected OnItemGestureListener<Item> mOnItemGestureListener;
private int mDrawnItemsLimit = Integer.MAX_VALUE;
private final Point mTouchScreenPoint = new Point();
private final Point mItemPoint = new Point();

public ItemizedIconOverlay(
Expand Down Expand Up @@ -164,18 +163,14 @@ private boolean activateSelectedItems(final MotionEvent event, final MapView map
final int eventX = (int) event.getX();
final int eventY = (int) event.getY();

/* These objects are created to avoid construct new ones every cycle. */
pj.fromMapPixels(eventX, eventY, mTouchScreenPoint);

for (int i = 0; i < this.mItemList.size(); ++i) {
final Item item = getItem(i);
final Drawable marker = (item.getMarker(0) == null) ? this.mDefaultMarker : item
.getMarker(0);

pj.toPixels(item.getPoint(), mItemPoint);

if (hitTest(item, marker, mTouchScreenPoint.x - mItemPoint.x, mTouchScreenPoint.y
- mItemPoint.y)) {
if (hitTest(item, marker, eventX - mItemPoint.x, eventY - mItemPoint.y)) {
if (task.run(i)) {
return true;
}
Expand Down
Loading

0 comments on commit 423680b

Please sign in to comment.