Permalink
Browse files

Feature/#725 (#729)

* feature/#329: store and restore the actual map center (instead of scroll) and the map orientation

This is the first (slightly unrelated) step.
Why should we use the map center instead of the scroll? Because when tilting the smartphone portrait-landscape-wise, the map center is kept only modulo the difference between `MapView`'s width and height.

Impactes classes:
* `OpenStreetMapConstants`: deprecated scroll preference tags, added latitude, longitude and orientation preference tags
* `MapView`: added the concept of `IGeoPoint initCenter`, took it into account in method `myOnLayout`, added a no-force-redraw version of method `setMapOrientation`
* `StarterMapFragment`: deprecated the use of scroll preferences tags, introduced the use of new preferences' tags (latitude, longitude, orientation), gently refactored in order to avoid Android Studio's nagging

* bug/#683: mMapView.zoomToBoundingBox is not calculating the required zoom level correctly

This commit:
* fixes the calculation of the zoom that matches a bounding box
* adds Unit Tests for `TileSystem`
* makes a more explicit demo

Impact on existing classes:
* `BoundingBox`: created method `getCenterWithDateLine` that takes into consideration the date line; deprecated `getCenter`
* `MapView`: created method `zoomToBoundingBox` with an additional `borderSizeInPixels` parameter; modified previous method `zoomToBoundingBox` in order to use the new `zoomToBoundingBox` with 0 as border size; fixed the bounding box zoom calculation that is now testable and moved to `TileSystem.getBoundingBoxZoom`
* `TileSystem`: created methods `getBoundingBoxZoom`, `getLongitudeZoom`, `getLatitudeZoom`, `getX01FromLongitude`and `getY01FromLatitude`; modified `LatLongToPixelXYMapSize` in order to take into account the new "XY01" methods; gently refactored javadoc version `6.0.0` to `5.6.6`
* `SampleZoomToBounding`: made a more explicit test (which is accessed in "More Samples / Events / Zoom to Bounding Box" demo)

New class:
* `TileSystemTest`: created in order to test methods `getY01FromLatitude`, `getX01FromLongitude`, `LatLongToPixelXYMapSize` and `getBoundingBoxZoom`

* Fixed and enhanced a unit test

Impact on existing classes:
* OpenStreetMapViewTest: added random iterations; set zoom before center for decent results; added a +-1 tolerance because of my benevolence towards rounding; wrote a relevant test tag; removed commented and deprecated code

* Fixed and enhanced unit tests

Impact on existing classes:
* Bug445Caching: made the test less resource dependent by limiting max zoom level to 16 (cf. https://operations.osmfoundation.org/policies/tiles/: "[tiles at zoom levels 17 and higher] are generally not available (cached) on the server in advance, and have to be rendered specifically for those requests, putting an unjustified burden on the available resources"); gently refactoring javadoc from `6.0` to `5.6.6`
* TileSystemTest: made the XY01 test stronger by checking [0,1]

* #329 Zoom level is now up to 29

New files:
* 30 `.png` files corresponding to the tiles of the same place on the 30 zoom levels from 0 to 29. I had no way to compute real tiles for high zoom levels, therefore I created the "Abstract" source of tiles with simple colored .png where the zoom level is displayed.

New classes:
* `PointL`: like a `Point`, but with long instead of int
* `ProjectionTest`: a unit test class for `Projection`
* `RectL`: like a `Rect`, but with long instead of int
* `SampleVeryHighZoomLevel`: a demo dedicated to high zoom levels, available in "More Samples / Tileproviders / Offline abstract tiles for zoom levels 0 to 29"

Biggest impacts on existing classes:
* `MapView`: used `PointL` and `RectL` types for Projected data; removed `initCenter`; added `GeoPoint mCenter`, `long mMapScrollX` and `mMapScrollY` and their setters/getters; overridden `scrollBy`; changed `scrollTo`; added `getMapScale`
* `Projection`: created a new constructor without any reference to `MapView`; created `getOffspring` in order to compute a `Projection` from another (e.g. for `MinimapOverlay`); renamed methods for clarity; created _many_ methods
* org.osmdroid.util.TileSystem: renamed methods for clarity; used `PointL` and `RectL` types for Mercator data; created _many_ methods

Other impacted classes:
* `CacheManager`: removed useless code that would have required refactoring
* `GeometryMath`: refactored `DEG2RAD` / `RAD2DEG`; used `MyMath.floorToInt` instead of `(int)` for better handling of negative values; used simpler syntax on `Rect` for easier unit testing purposes
* `GeoPoint`: created method `distanceToAsDouble` to go beyond the meter; used a distance calculation algorithm more precise for small distances; increased precision from float to double
* `GeoPointTest`: create methods `test_distanceTo_itself`, `test_distanceTo_Equator`, `test_distanceTo_Equator_Smaller`, `test_distanceTo_Parallels`, `getCleanLongitudeDiff`, `getRandomLongitude`, `getRandomLatitude`; removed less relevant methods `test_distanceTo_zero`, `test_distanceTo_one`, test_distanceTo_one_degree`
* `MapController`: changed animation methods according to the new scroll behavior
* `MapTileProviderBase`: used `PointL` and `RectL` types for Mercator data
* `Marker`: refactored the use of methods from `Projection` to `MapView`
* `MathConstants`: increased precision from float to double
* `MinimapOverlay`: relied more on inherited methods than on specific code
* `MyLocationNewOverlay`: used `PointL` type for Projected data; using `double` instead of `int` zoom level; using new method `Projection.getPixelsFromProjected`
* `MyMath`: created new methods `flootToLong` and `flootToInt` in order to fix a counter intuitive Java behavior
* `OpenStreetMapViewTest`: unrelated light refactoring
* `OsmBitmapShader`: used `PointL` type for Mercator data
* `PathOverlay`: used `PointL` and `RectL` types for Projected data
* `PathProjection`: used `PointL` type for Mercator data
* `Polygon`: used `PointL` type for Projected data
* `Polyline`: used `PointL` type for Projected data
* `SampleAnimateTo`: changed slightly for testing reasons
* `SampleFactory`: added new class `SampleVeryHighZoomLevel`
* `SampleZoomToBounding`: unrelated light refactoring
* `ScaleBarOverlay`: used more precise new method `GeoPoint.distanceToAsDouble`; handled double distances in method `scaleBarLengthText`; added helper method `getScaleString`; refactored the use of methods from `Projection` to `MapView`
* `StarterMapFragment`: unrelated bug fix *** setInitCenter
* `TileLooper`: used `RectL` type for Mercator data; slightly refactored
* `TilesOverlay`: used `PointL` and `RectL` types for Mercator data; refactored `onTileReadyToDraw`; created `protected` methods `setCanvasRect`, `getCanvasRect`, `setProjection` and `getProjection`
* `microsoft.mappoint.TileSystem`: used 64 instead of 32 as a limit parameter in `setTileSize`; introduced the notions of `primaryKeyMaxZoomLevel` and `projectionZoomLevel`
* `TileSystemTest`: added `testGetLatitudeFromY01`, `testLatitude`, `testGetLongitudeFromX01`, `testLongitude`, `checkLatitude` and `checkLongitude`; removed `testLatLongToPixelXYMapSize`

* Javadoc gentle fix

* Javadoc gentle fix

* Javadoc gentle fix

* Javadoc gentle fix

* Javadoc gentle fix

* Javadoc gentle fix

* Javadoc gentle fix

* Gentle conflict fix

* Gentle conflict fix

* Gentle conflict fix

* Impacts:
* `SampleFactory`: added a reference to new demo `SampleVeryHighZoomLevel`
* `TileSystem`: switched to `double` zoom level versions for methods `MapScale` and `GroundResolution`
* `TileSystemMathTest`: moved to `TileSystemTest` methods `test_MapSize`, `test_groundResolution` and `test_groundMapScale`
* `TileSystemTest`: moved from `TileSystemTest` methods `test_MapSize`, `test_groundResolution` and `test_groundMapScale`; improved their handling of high zoom levels

* Fixed `Polygon` issue as in https://www.youtube.com/watch?v=S4Pf2u9WSyQ

Impacts in `Polygon` class:
* `mOriginalPoints` is now an array of `double`, no more `intE6` (but it's not related to the bug fixing)
* new method `setCloserPoint`: the assumption is that 2 consecutive points should be as close as possible, therefore we add or subtract "the size of the world" if necessary

* First part of #725 - Polyline and Polygon issues at higher zoom levels with/wo hardware acceleration

With this fix, the `Polygon`s looks OK up to zoom 29.
What remains to be done:
* check with holes
* same work on `Polyline`
* low-zoom "best top-left version" of `Polygon` / `Polyline`

Regarding this delivery...

New classes:
* `SegmentIntersection`: tools in order to compute the intersection between two 2D segments through `public` method `intersection`
* `SegmentIntersectionTest`: unit tests on `SegmentIntersection`

Impacted classes:
* `PointL`: added overriden methods `toString` and `equals`; added methods `set` and `squareDistanceTo`
* `Polygon`: modified method `buildPathPortion` in order to use less overflow prone `PointL` (instead of `Point`), to consider `Path` as a list of 2D segments, and to clip those segments into a min/max values square; added methods `clip`, `isInClipArea`, `intersection`, `lineTo`
* `Projection`: added methods `getLongPixelsFromProjected`, `getLongPixelXFromMercator`, `getLongPixelYFromMercator` and `getLongPixelFromMercator` for overflow reasons; removed 2 unused imports

* Last part of #725 - Polyline and Polygon issues at higher zoom levels with/wo hardware acceleration :
* check with holes
* same work on `Polyline`
* low-zoom "best top-left version" of `Polygon` / `Polyline`

Regarding this delivery...

New classes:
* `Distance`: a tool class dedicated to the computation of 2D distances
* `DistanceTest`: a Unit Test class dedicated to `Distance`
* `LinearRing`: used to be an inner class in `Polygon` but needed to go out in order to be used by `Polyline` too; was enhanced too in order to match the new requirements and the new zoom level limit

Impacted classes:
* `PointL`: removed methods `squareDistanceTo` that are now more or less available in new dedicated class `Distance`
* `Polygon`: moved `LinearRing` code to the new eponymous class; handled a common offset for the main polygon and the holes
* `Polyline`: removed all the code that could now be handled by `LinearRing`
* `Projection`: added parameter `pCloser` to method `getLongPixelsFromProjected`

* Lower clip area threshold values:
* mClipMax is now 17000 (instead of Integer.MAX_VALUE / 8)
* mClipMin is now -mClipMax (instead of Integer.MIN_VALUE / 8)

* Modified `Bug445Caching` in order to avoid random Travis crash on "queue size":
* added `ensureCapacity` in order to actually cache all tiles
* added methods `getMaxTileExpected`, `getMinNumberExpected`, `getMaxNumberExpected`

* Lowered the clip area size in `LinearRing` in order to fix hardware acceleration related path not displaying bugs:
* `mClipMax` = 1400
* `mClipMin` = -600

* Final (?) fix for #725

Impacted classes:
* `LinearRing`: used `List<RectL>` for segments instead of `List<PointL>` for both segments start and end points; added method `applyOffset`; changed `getBestOffset` so that it focuses on the smallest distance to the screen center rather than on the biggest common area with the screen rect; removed `getCommonArea`
* `RectL`: added constructor `RectL(RectL other)` and method `set(RectL other)`

* (forgotten file of previous commit/push)

* Fix as we cannot use only `path.close()`. More robust segment clipping.

New class:
* `SegmentClipper`: a tool to clip segments
* `SegmentClipperTest`: a Unit Test class dedicated to `SegmentClipper`

Impacted classes:
* `LinearRing`: now has a constructor with a `Path` as a parameter; now implements `SegmentClipper.SegmentClippable`; moved clipping code to new class `SegmentClipper`; added parameter `boolean pClosePath` to method `getSegmentsFromProjected` as we cannot just use `path.close()` with the clipping process; remove parameter `boolean pClosePath` from method `getPathFromSegments` for the same reason ;)
* `PointL`: added constructor `PointL(PointL other)`
* `Polygon`: impacted the fact that `LinearRing`'s constructor now demands a `Path`
* `Polyline`: impacted the fact that `LinearRing`'s constructor now demands a `Path`
* `RectL`: added overridden methods `equals` and `hashCode`
  • Loading branch information...
monsieurtanuki authored and spyhunter99 committed Oct 10, 2017
1 parent 0990b70 commit 0e63c3dd07fc9e43ba3c8b1ffb6c873d48515d2b
@@ -54,6 +54,12 @@ public void run() {
if (count != 0)
throw new Exception("purge should remove all tiles, but " + count + " were found");
int maxTilesNeeded = 0;
for (int zoom = minZoom ; zoom <= maxZoom ; zoom ++) {
maxTilesNeeded += getMaxTileExpected(zoom);
}
mMapView.getTileProvider().ensureCapacity(maxTilesNeeded);
for (int zoom = minZoom ; zoom <= maxZoom ; zoom ++) {
checkDownload(zoom);
}
@@ -71,6 +77,13 @@ public void run() {
for (int zoom = minZoom ; zoom <= maxZoom ; zoom ++) {
checkCache(zoom);
}
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getActivity(), "done", Toast.LENGTH_SHORT).show();
}
});
}
/**
@@ -138,15 +151,42 @@ private int getMinTileExpected(final int pZoomLevel) {
Log.i(TAG, "height " + height);
final int tileSize = TileSystem.getTileSize();
Log.i(TAG, "tile size " + tileSize);
final int minCols = Math.min(maxPerZoom, width / tileSize + (width % tileSize == 0 ? 0 : 1));
final int minCols = getMinNumberExpected(tileSize, width, maxPerZoom);
Log.i(TAG, "min cols " + minCols);
final int minRows = Math.min(maxPerZoom, height / tileSize + (height % tileSize == 0 ? 0 : 1));
final int minRows = getMinNumberExpected(tileSize, height, maxPerZoom);
Log.i(TAG, "min rows " + minRows);
final int minExpected = minCols * minRows;
Log.i(TAG, "min expected " + minExpected);
return minExpected;
}
/**
* @since 6.0.0
*/
private int getMaxTileExpected(final int pZoomLevel) {
final int maxPerZoom = 1 << pZoomLevel;
final int width = mMapView.getWidth();
final int height = mMapView.getHeight();
final int tileSize = TileSystem.getTileSize();
final int minCols = getMaxNumberExpected(tileSize, width, maxPerZoom);
final int minRows = getMaxNumberExpected(tileSize, height, maxPerZoom);
return minCols * minRows;
}
/**
* @since 6.0.0
*/
private int getMinNumberExpected(final int pTileSize, final int pScreenSize, final int pMaxPerZoom) {
return Math.min(pMaxPerZoom, pScreenSize / pTileSize + (pScreenSize % pTileSize == 0 ? 0 : 1));
}
/**
* @since 6.0.0
*/
private int getMaxNumberExpected(final int pTileSize, final int pScreenSize, final int pMaxPerZoom) {
return Math.min(pMaxPerZoom, 1 + getMinNumberExpected(pTileSize, pScreenSize, pMaxPerZoom));
}
/**
* @since 6.0.0
*/
@@ -162,5 +202,6 @@ private long getDbCount() {
private void setZoomAndCenter(final int pZoomLevel) {
mMapView.getController().setZoom(pZoomLevel);
mMapView.getController().setCenter(center);
mMapView.invalidate();
}
}
@@ -0,0 +1,76 @@
package org.osmdroid.util;
/**
* Tools about 2D distance computation
* Optimized code: we compute the square of the distance.
* If you really want to know the distance, apply Math.sqrt
* @since 6.0.0
* @author Fabrice Fontaine
*/
public class Distance {
/**
* Compute the distance between two points
*/
public static double getSquaredDistanceToPoint(
final double pFromX, final double pFromY, final double pToX, final double pToY) {
final double dX = pFromX - pToX;
final double dY = pFromY - pToY;
return dX * dX + dY * dY;
}
/**
* Square of the distance between a point and line AB
*/
public static double getSquaredDistanceToLine(
final double pFromX, final double pFromY,
final double pAX, final double pAY, final double pBX, final double pBY
) {
if (pAX == pBX && pAY == pBY) {
return getSquaredDistanceToPoint(pAX, pAY, pFromX, pFromY);
}
final double cross = crossProduct(pAX, pAY, pBX, pBY, pFromX, pFromY);
return cross * cross / getSquaredDistanceToPoint(pAX, pAY, pBX, pBY);
}
/**
* Square of the distance between a point and segment AB
*/
public static double getSquaredDistanceToSegment(
final double pFromX, final double pFromY,
final double pAX, final double pAY, final double pBX, final double pBY
) {
if (pAX == pBX && pAY == pBY) {
return getSquaredDistanceToPoint(pAX, pAY, pFromX, pFromY);
}
double dot = dotProduct(pAX, pAY, pBX, pBY, pFromX, pFromY);
if (dot > 0) {
return getSquaredDistanceToPoint(pFromX, pFromY, pBX, pBY);
}
dot = dotProduct(pBX, pBY, pAX, pAY, pFromX, pFromY);
if (dot > 0) {
return getSquaredDistanceToPoint(pFromX, pFromY, pAX, pAY);
}
final double cross = crossProduct(pAX, pAY, pBX, pBY, pFromX, pFromY);
return cross * cross / getSquaredDistanceToPoint(pAX, pAY, pBX, pBY);
}
/**
* Compute the cross product AB x AC
*/
private static double crossProduct(
final double pAX, final double pAY, final double pBX, final double pBY,
final double pCX, final double pCY) {
return (pBX - pAX) * (pCY - pAY) - (pBY - pAY) * (pCX - pAX);
}
/**
* Compute the dot product AB x AC
*/
private static double dotProduct(
final double pAX, final double pAY, final double pBX, final double pBY,
final double pCX, final double pCY) {
return (pBX - pAX) * (pCX - pBX) + (pBY - pAY) * (pCY - pBY);
}
}
@@ -17,4 +17,47 @@ public PointL(final long pX, final long pY) {
x = pX;
y = pY;
}
public PointL(final PointL pOther) {
set(pOther);
}
/**
* @since 6.0.0
*/
public void set(final PointL pOther) {
x = pOther.x;
y = pOther.y;
}
/**
* @since 6.0.0
*/
public void set(final long pX, final long pY) {
x = pX;
y = pY;
}
/**
* @since 6.0.0
*/
@Override
public String toString() {
return "PointL(" + x + ", " + y + ")";
}
/**
* @since 6.0.0
*/
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof PointL)) {
return false;
}
final PointL other = (PointL) object;
return x == other.x && y == other.y;
}
}
@@ -19,13 +19,24 @@ public RectL(final long pLeft, final long pTop, final long pRight, final long pB
set(pLeft, pTop, pRight, pBottom);
}
public RectL(final RectL pOther) {
set(pOther);
}
public void set(final long pLeft, final long pTop, final long pRight, final long pBottom) {
left = pLeft;
top = pTop;
right = pRight;
bottom = pBottom;
}
public void set(final RectL pOther) {
left = pOther.left;
top = pOther.top;
right = pOther.right;
bottom = pOther.bottom;
}
public void union(long x, long y) {
if (x < left) {
left = x;
@@ -55,4 +66,22 @@ public final long height() {
public String toString() {
return "RectL(" +left+", "+top+" - "+right+", "+bottom+")";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final RectL r = (RectL) o;
return left == r.left && top == r.top && right == r.right && bottom == r.bottom;
}
@Override
public int hashCode() {
long result = left;
result = 31 * result + top;
result = 31 * result + right;
result = 31 * result + bottom;
return (int) (result % Integer.MAX_VALUE);
}
}
Oops, something went wrong.

0 comments on commit 0e63c3d

Please sign in to comment.