From e3f2f191190ee13e5886490b75e3f6a919211163 Mon Sep 17 00:00:00 2001 From: Emux Date: Thu, 5 Oct 2017 20:39:30 +0300 Subject: [PATCH] PathLayer, VectorLayer (polygon) overlays touch events #316 #424 (#425) --- docs/Changelog.md | 2 ++ .../android/test/PathOverlayActivity.java | 17 ++++++++- .../org/oscim/layers/vector/PathLayer.java | 34 ++++++++++++++++++ .../org/oscim/layers/vector/VectorLayer.java | 22 +++++++++++- .../src/org/oscim/test/PathLayerTest.java | 15 +++++++- vtm/src/org/oscim/layers/PathLayer.java | 35 +++++++++++++++++-- vtm/src/org/oscim/utils/GeoPointUtils.java | 32 +++++++++++++++-- 7 files changed, 150 insertions(+), 7 deletions(-) diff --git a/docs/Changelog.md b/docs/Changelog.md index 62b2ed56..488ac3fc 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -5,6 +5,8 @@ - Mapsforge themes compatibility [#100](https://github.com/mapsforge/vtm/issues/100) - Render themes: line symbol [#124](https://github.com/mapsforge/vtm/issues/124) - Render themes: stroke dash array [#131](https://github.com/mapsforge/vtm/issues/131) +- PathLayer overlay touch events [#316](https://github.com/mapsforge/vtm/issues/316) +- VectorLayer (polygon) overlay touch events [#424](https://github.com/mapsforge/vtm/issues/424) - Two finger tap zoom out gesture [#423](https://github.com/mapsforge/vtm/issues/423) - POI Search example [#394](https://github.com/mapsforge/vtm/issues/394) - Mapsforge Reverse Geocoding [#383](https://github.com/mapsforge/vtm/issues/383) diff --git a/vtm-android-example/src/org/oscim/android/test/PathOverlayActivity.java b/vtm-android-example/src/org/oscim/android/test/PathOverlayActivity.java index 644547da..5f876c52 100644 --- a/vtm-android-example/src/org/oscim/android/test/PathOverlayActivity.java +++ b/vtm-android-example/src/org/oscim/android/test/PathOverlayActivity.java @@ -18,10 +18,14 @@ package org.oscim.android.test; import android.os.Bundle; +import android.widget.Toast; +import org.oscim.backend.CanvasAdapter; import org.oscim.backend.canvas.Color; import org.oscim.core.MapPosition; import org.oscim.event.Event; +import org.oscim.event.Gesture; +import org.oscim.event.MotionEvent; import org.oscim.layers.vector.PathLayer; import org.oscim.map.Map.UpdateListener; @@ -44,7 +48,18 @@ public class PathOverlayActivity extends SimpleMapActivity { for (double lat = -90; lat <= 90; lat += 5) { int c = Color.fade(Color.rainbow((float) (lat + 90) / 180), 0.5f); - PathLayer pathLayer = new PathLayer(mMap, c, 6); + PathLayer pathLayer = new PathLayer(mMap, c, 6 * CanvasAdapter.getScale()) { + @Override + public boolean onGesture(Gesture g, MotionEvent e) { + if (g instanceof Gesture.Tap) { + if (contains(e.getX(), e.getY())) { + Toast.makeText(PathOverlayActivity.this, "PathLayer tap\n" + mMap.viewport().fromScreenPoint(e.getX(), e.getY()), Toast.LENGTH_SHORT).show(); + return true; + } + } + return false; + } + }; mMap.layers().add(pathLayer); mPathLayers.add(pathLayer); } diff --git a/vtm-jts/src/org/oscim/layers/vector/PathLayer.java b/vtm-jts/src/org/oscim/layers/vector/PathLayer.java index 8d7790b7..f82c6936 100644 --- a/vtm-jts/src/org/oscim/layers/vector/PathLayer.java +++ b/vtm-jts/src/org/oscim/layers/vector/PathLayer.java @@ -20,12 +20,16 @@ */ package org.oscim.layers.vector; +import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.LineString; +import org.oscim.backend.CanvasAdapter; import org.oscim.core.GeoPoint; +import org.oscim.core.Point; import org.oscim.layers.vector.geometries.LineDrawable; import org.oscim.layers.vector.geometries.Style; import org.oscim.map.Map; +import org.oscim.utils.GeoPointUtils; import org.oscim.utils.geom.GeomBuilder; import java.util.ArrayList; @@ -42,6 +46,9 @@ public class PathLayer extends VectorLayer { protected Style mStyle; protected LineDrawable mDrawable; + private final Point mPoint1 = new Point(); + private final Point mPoint2 = new Point(); + public PathLayer(Map map, Style style) { super(map); mStyle = style; @@ -194,6 +201,12 @@ public class PathLayer extends VectorLayer { remove(mDrawable); mDrawable = new LineDrawable(path, mStyle); add(mDrawable); + + mPoints.clear(); + for (int i = 0; i < path.getNumPoints(); i++) { + Coordinate c = path.getCoordinateN(i); + mPoints.add(new GeoPoint(c.y, c.x)); + } } mWorker.submit(0); } @@ -204,8 +217,29 @@ public class PathLayer extends VectorLayer { remove(mDrawable); mDrawable = new LineDrawable(lonLat, mStyle); add(mDrawable); + + mPoints.clear(); + for (int i = 0; i < lonLat.length; i += 2) + mPoints.add(new GeoPoint(lonLat[i + 1], lonLat[i])); } mWorker.submit(0); } + @Override + public synchronized boolean contains(float x, float y) { + // Touch min 20 px at baseline mdpi (160dpi) + double distance = Math.max(20 / 2 * CanvasAdapter.getScale(), mStyle.strokeWidth); + for (int i = 0; i < mPoints.size() - 1; i++) { + if (i == 0) + mMap.viewport().toScreenPoint(mPoints.get(i), false, mPoint1); + else { + mPoint1.x = mPoint2.x; + mPoint1.y = mPoint2.y; + } + mMap.viewport().toScreenPoint(mPoints.get(i + 1), false, mPoint2); + if (GeoPointUtils.distanceSegmentPoint(mPoint1.x, mPoint1.y, mPoint2.x, mPoint2.y, x, y) <= distance) + return true; + } + return false; + } } diff --git a/vtm-jts/src/org/oscim/layers/vector/VectorLayer.java b/vtm-jts/src/org/oscim/layers/vector/VectorLayer.java index 8f99a963..26ef0386 100644 --- a/vtm-jts/src/org/oscim/layers/vector/VectorLayer.java +++ b/vtm-jts/src/org/oscim/layers/vector/VectorLayer.java @@ -26,9 +26,13 @@ import com.vividsolutions.jts.simplify.DouglasPeuckerSimplifier; import org.oscim.backend.canvas.Color; import org.oscim.core.Box; +import org.oscim.core.GeoPoint; import org.oscim.core.GeometryBuffer; import org.oscim.core.MapPosition; import org.oscim.core.Tile; +import org.oscim.event.Gesture; +import org.oscim.event.GestureListener; +import org.oscim.event.MotionEvent; import org.oscim.layers.vector.geometries.Drawable; import org.oscim.layers.vector.geometries.LineDrawable; import org.oscim.layers.vector.geometries.PointDrawable; @@ -41,6 +45,7 @@ import org.oscim.theme.styles.LineStyle; import org.oscim.utils.FastMath; import org.oscim.utils.QuadTree; import org.oscim.utils.SpatialIndex; +import org.oscim.utils.geom.GeomBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,7 +62,7 @@ import static org.oscim.core.MercatorProjection.longitudeToX; * package and * JTS geometries together with a GeometryStyle */ -public class VectorLayer extends AbstractVectorLayer { +public class VectorLayer extends AbstractVectorLayer implements GestureListener { public static final Logger log = LoggerFactory.getLogger(VectorLayer.class); @@ -351,4 +356,19 @@ public class VectorLayer extends AbstractVectorLayer { (float) (y + radius * Math.sin(i * step))); } } + + public synchronized boolean contains(float x, float y) { + GeoPoint geoPoint = mMap.viewport().fromScreenPoint(x, y); + Point point = new GeomBuilder().point(geoPoint.getLongitude(), geoPoint.getLatitude()).toPoint(); + for (Drawable drawable : tmpDrawables) { + if (drawable.getGeometry().contains(point)) + return true; + } + return false; + } + + @Override + public boolean onGesture(Gesture g, MotionEvent e) { + return false; + } } diff --git a/vtm-playground/src/org/oscim/test/PathLayerTest.java b/vtm-playground/src/org/oscim/test/PathLayerTest.java index 18460148..513cd765 100644 --- a/vtm-playground/src/org/oscim/test/PathLayerTest.java +++ b/vtm-playground/src/org/oscim/test/PathLayerTest.java @@ -20,6 +20,8 @@ import org.oscim.backend.canvas.Color; import org.oscim.core.GeoPoint; import org.oscim.core.MapPosition; import org.oscim.event.Event; +import org.oscim.event.Gesture; +import org.oscim.event.MotionEvent; import org.oscim.gdx.GdxMapApp; import org.oscim.layers.tile.buildings.BuildingLayer; import org.oscim.layers.tile.vector.VectorTileLayer; @@ -92,7 +94,18 @@ public class PathLayerTest extends GdxMapApp { PathLayer pathLayer; if (init) { int c = Color.fade(Color.rainbow((float) (lat + 90) / 180), 0.5f); - pathLayer = new PathLayer(mMap, c, 6); + pathLayer = new PathLayer(mMap, c, 6) { + @Override + public boolean onGesture(Gesture g, MotionEvent e) { + if (g instanceof Gesture.Tap) { + if (contains(e.getX(), e.getY())) { + System.out.println("PathLayer tap " + mMap.viewport().fromScreenPoint(e.getX(), e.getY())); + return true; + } + } + return false; + } + }; mMap.layers().add(pathLayer); mPathLayers.add(pathLayer); } else { diff --git a/vtm/src/org/oscim/layers/PathLayer.java b/vtm/src/org/oscim/layers/PathLayer.java index 6304eba3..c30923b9 100644 --- a/vtm/src/org/oscim/layers/PathLayer.java +++ b/vtm/src/org/oscim/layers/PathLayer.java @@ -1,7 +1,7 @@ /* * Copyright 2012 osmdroid authors: Viesturs Zarins, Martin Pearman * Copyright 2012 Hannes Janetzek - * Copyright 2016 devemux86 + * Copyright 2016-2017 devemux86 * Copyright 2016 Bezzu * Copyright 2016 Pedinel * Copyright 2017 Andrey Novikov @@ -21,12 +21,17 @@ */ package org.oscim.layers; +import org.oscim.backend.CanvasAdapter; import org.oscim.backend.canvas.Paint.Cap; import org.oscim.core.GeoPoint; import org.oscim.core.GeometryBuffer; import org.oscim.core.MapPosition; import org.oscim.core.MercatorProjection; +import org.oscim.core.Point; import org.oscim.core.Tile; +import org.oscim.event.Gesture; +import org.oscim.event.GestureListener; +import org.oscim.event.MotionEvent; import org.oscim.map.Map; import org.oscim.renderer.BucketRenderer; import org.oscim.renderer.GLViewport; @@ -34,6 +39,7 @@ import org.oscim.renderer.bucket.LineBucket; import org.oscim.renderer.bucket.RenderBuckets; import org.oscim.theme.styles.LineStyle; import org.oscim.utils.FastMath; +import org.oscim.utils.GeoPointUtils; import org.oscim.utils.async.SimpleWorker; import org.oscim.utils.geom.LineClipper; @@ -44,7 +50,7 @@ import java.util.List; /** * This class draws a path line in given color or texture. */ -public class PathLayer extends Layer { +public class PathLayer extends Layer implements GestureListener { /** * Stores points, converted to the map projection. @@ -52,6 +58,9 @@ public class PathLayer extends Layer { protected final ArrayList mPoints; protected boolean mUpdatePoints; + private final Point mPoint1 = new Point(); + private final Point mPoint2 = new Point(); + /** * Line style */ @@ -437,4 +446,26 @@ public class PathLayer extends Layer { return i; } } + + public synchronized boolean contains(float x, float y) { + // Touch min 20 px at baseline mdpi (160dpi) + double distance = Math.max(20 / 2 * CanvasAdapter.getScale(), mLineStyle.width); + for (int i = 0; i < mPoints.size() - 1; i++) { + if (i == 0) + mMap.viewport().toScreenPoint(mPoints.get(i), false, mPoint1); + else { + mPoint1.x = mPoint2.x; + mPoint1.y = mPoint2.y; + } + mMap.viewport().toScreenPoint(mPoints.get(i + 1), false, mPoint2); + if (GeoPointUtils.distanceSegmentPoint(mPoint1.x, mPoint1.y, mPoint2.x, mPoint2.y, x, y) <= distance) + return true; + } + return false; + } + + @Override + public boolean onGesture(Gesture g, MotionEvent e) { + return false; + } } diff --git a/vtm/src/org/oscim/utils/GeoPointUtils.java b/vtm/src/org/oscim/utils/GeoPointUtils.java index 853288e1..9f6db6b6 100644 --- a/vtm/src/org/oscim/utils/GeoPointUtils.java +++ b/vtm/src/org/oscim/utils/GeoPointUtils.java @@ -15,12 +15,14 @@ package org.oscim.utils; import org.oscim.core.GeoPoint; +import org.oscim.core.Point; public final class GeoPointUtils { /** - * Find if the given point lies within this polygon.
- * See http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html + * Find if the given point lies within this polygon. + *

+ * http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html * * @return true if this polygon contains the given point, false otherwise. */ @@ -36,6 +38,16 @@ public final class GeoPointUtils { return result; } + /** + * Returns the distance between the given segment and point. + *

+ * libGDX (Apache 2.0) + */ + public static double distanceSegmentPoint(double startX, double startY, double endX, double endY, double pointX, double pointY) { + Point nearest = nearestSegmentPoint(startX, startY, endX, endY, pointX, pointY); + return Math.hypot(nearest.x - pointX, nearest.y - pointY); + } + /** * Find if this way is closed. * @@ -45,6 +57,22 @@ public final class GeoPointUtils { return geoPoints[0].distance(geoPoints[geoPoints.length - 1]) < 0.000000001; } + /** + * Returns a point on the segment nearest to the specified point. + *

+ * libGDX (Apache 2.0) + */ + public static Point nearestSegmentPoint(double startX, double startY, double endX, double endY, double pointX, double pointY) { + double xDiff = endX - startX; + double yDiff = endY - startY; + double length2 = xDiff * xDiff + yDiff * yDiff; + if (length2 == 0) return new Point(startX, startY); + double t = ((pointX - startX) * (endX - startX) + (pointY - startY) * (endY - startY)) / length2; + if (t < 0) return new Point(startX, startY); + if (t > 1) return new Point(endX, endY); + return new Point(startX + t * (endX - startX), startY + t * (endY - startY)); + } + private GeoPointUtils() { throw new IllegalStateException(); }