diff --git a/vtm-android-example/AndroidManifest.xml b/vtm-android-example/AndroidManifest.xml
index 9e6be991..45fd562d 100644
--- a/vtm-android-example/AndroidManifest.xml
+++ b/vtm-android-example/AndroidManifest.xml
@@ -34,6 +34,9 @@
+
diff --git a/vtm-android-example/res/layout/activity_map_draggable.xml b/vtm-android-example/res/layout/activity_map_draggable.xml
new file mode 100644
index 00000000..b5dd1b75
--- /dev/null
+++ b/vtm-android-example/res/layout/activity_map_draggable.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/vtm-android-example/src/org/oscim/android/drag/DragAndDropListener.java b/vtm-android-example/src/org/oscim/android/drag/DragAndDropListener.java
new file mode 100644
index 00000000..04c86495
--- /dev/null
+++ b/vtm-android-example/src/org/oscim/android/drag/DragAndDropListener.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2021 Frank Knoll
+ *
+ * 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.android.drag;
+
+import org.oscim.core.GeoPoint;
+
+public interface DragAndDropListener {
+
+ void startDragItemAtGeoPoint(DraggableMarkerItem item, GeoPoint geoPoint);
+
+ void ongoingDragItemToGeoPoint(DraggableMarkerItem item, GeoPoint geoPoint);
+
+ void dropItemAtGeoPoint(DraggableMarkerItem item, GeoPoint geoPoint);
+}
diff --git a/vtm-android-example/src/org/oscim/android/drag/DragGestureHandler.java b/vtm-android-example/src/org/oscim/android/drag/DragGestureHandler.java
new file mode 100644
index 00000000..902a6b86
--- /dev/null
+++ b/vtm-android-example/src/org/oscim/android/drag/DragGestureHandler.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013 Hannes Janetzek
+ * Copyright 2016 devemux86
+ * Copyright 2021 Frank Knoll
+ *
+ * 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.android.drag;
+
+import android.view.MotionEvent;
+import org.oscim.android.input.AndroidMotionEvent;
+import org.oscim.android.input.GestureHandler;
+import org.oscim.event.Gesture;
+import org.oscim.map.Map;
+
+class DragGestureHandler extends GestureHandler {
+
+ public static final Gesture START_DRAG = new Gesture() {
+ };
+ public static final Gesture ONGOING_DRAG = new Gesture() {
+ };
+ public static final Gesture END_DRAG = new Gesture() {
+ };
+
+ private final AndroidMotionEvent mMotionEvent;
+ private final Map mMap;
+ private boolean scrolling = false;
+
+ public DragGestureHandler(Map map) {
+ super(map);
+ mMotionEvent = new AndroidMotionEvent();
+ mMap = map;
+ }
+
+ public boolean isScrolling() {
+ return scrolling;
+ }
+
+ public void setScrolling(boolean scrolling) {
+ this.scrolling = scrolling;
+ }
+
+ @Override
+ public void onShowPress(MotionEvent e) {
+ scrolling = true;
+ mMap.handleGesture(START_DRAG, mMotionEvent.wrap(e));
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ scrolling = true;
+ return mMap.handleGesture(ONGOING_DRAG, mMotionEvent.wrap(e2));
+ }
+}
diff --git a/vtm-android-example/src/org/oscim/android/drag/DragMapView.java b/vtm-android-example/src/org/oscim/android/drag/DragMapView.java
new file mode 100644
index 00000000..877ecc7f
--- /dev/null
+++ b/vtm-android-example/src/org/oscim/android/drag/DragMapView.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012 Hannes Janetzek
+ * Copyright 2016-2020 devemux86
+ * Copyright 2018-2019 Gustl22
+ * Copyright 2021 Frank Knoll
+ *
+ * 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.android.drag;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import org.oscim.android.MapView;
+import org.oscim.map.Map;
+import org.oscim.utils.Parameters;
+
+public class DragMapView extends MapView {
+
+ private DragGestureHandler gestureHandler;
+
+ public DragMapView(Context context) {
+ super(context);
+ }
+
+ public DragMapView(Context context, AttributeSet attributeSet) {
+ super(context, attributeSet);
+
+ if (!Parameters.MAP_EVENT_LAYER2) {
+ gestureHandler = new DragGestureHandler(mMap);
+ mGestureDetector = new GestureDetector(context, gestureHandler);
+ mGestureDetector.setOnDoubleTapListener(gestureHandler);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(android.view.MotionEvent motionEvent) {
+ if (!isClickable()) {
+ return false;
+ }
+
+ if (motionEvent.getAction() == android.view.MotionEvent.ACTION_UP) {
+ if (gestureHandler.isScrolling()) {
+ gestureHandler.setScrolling(false);
+ return ((Map) mMap).handleGesture(DragGestureHandler.END_DRAG, mMotionEvent.wrap(motionEvent));
+ }
+ }
+
+ return super.onTouchEvent(motionEvent);
+ }
+}
diff --git a/vtm-android-example/src/org/oscim/android/drag/DraggableItemizedLayer.java b/vtm-android-example/src/org/oscim/android/drag/DraggableItemizedLayer.java
new file mode 100644
index 00000000..266e4f9e
--- /dev/null
+++ b/vtm-android-example/src/org/oscim/android/drag/DraggableItemizedLayer.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012 osmdroid authors: Nicolas Gramlich, Theodore Hong, Fred Eisele
+ *
+ * Copyright 2013 Hannes Janetzek
+ * Copyright 2016-2021 devemux86
+ * Copyright 2016 Stephan Leuschner
+ * Copyright 2016 Pedinel
+ * Copyright 2019 Carlos Alberto MartÃnez Gadea
+ * Copyright 2021 Frank Knoll
+ *
+ * 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.android.drag;
+
+import org.oscim.core.GeoPoint;
+import org.oscim.event.Gesture;
+import org.oscim.event.GestureListener;
+import org.oscim.event.MotionEvent;
+import org.oscim.layers.marker.ItemizedLayer;
+import org.oscim.layers.marker.MarkerInterface;
+import org.oscim.layers.marker.MarkerSymbol;
+import org.oscim.map.Map;
+
+import java.util.List;
+
+import static org.oscim.android.drag.DragGestureHandler.*;
+
+public class DraggableItemizedLayer extends ItemizedLayer implements GestureListener {
+
+ private final ItemDragger itemDragger;
+
+ public DraggableItemizedLayer(Map map,
+ List markerItems,
+ MarkerSymbol defaultMarker,
+ OnItemGestureListener listener) {
+ super(map, markerItems, defaultMarker, listener);
+ itemDragger = new ItemDragger(this, map);
+ }
+
+ @Override
+ protected boolean activateSelectedItems(MotionEvent event, ActiveItem task) {
+ return super.activateSelectedItems(event, task);
+ }
+
+ protected List getMarkerItems() {
+ return mItemList;
+ }
+
+ @Override
+ public boolean onGesture(Gesture gesture, MotionEvent event) {
+ if (!isEnabled()) {
+ return false;
+ }
+
+ if (gesture == START_DRAG) {
+ return itemDragger.startDragItem(event, getGeoPoint(event));
+ } else if (gesture == ONGOING_DRAG) {
+ return itemDragger.ongoingDragItemTo(getGeoPoint(event));
+ } else if (gesture == END_DRAG) {
+ return itemDragger.dropItemAt(getGeoPoint(event));
+ } else {
+ itemDragger.noDrag();
+ }
+
+ return super.onGesture(gesture, event);
+ }
+
+ private GeoPoint getGeoPoint(MotionEvent event) {
+ return map().viewport().fromScreenPoint(event.getX(), event.getY());
+ }
+}
diff --git a/vtm-android-example/src/org/oscim/android/drag/DraggableMarkerItem.java b/vtm-android-example/src/org/oscim/android/drag/DraggableMarkerItem.java
new file mode 100644
index 00000000..a31396bf
--- /dev/null
+++ b/vtm-android-example/src/org/oscim/android/drag/DraggableMarkerItem.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2012 osmdroid authors:
+ * Copyright 2012 Nicolas Gramlich
+ * Copyright 2012 Theodore Hong
+ * Copyright 2012 Fred Eisele
+ *
+ * Copyright 2014 Hannes Janetzek
+ * Copyright 2016 devemux86
+ * Copyright 2016 Erik Duisters
+ * Copyright 2017 Longri
+ * Copyright 2021 Frank Knoll
+ *
+ * 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.android.drag;
+
+import org.oscim.core.GeoPoint;
+import org.oscim.layers.marker.MarkerItem;
+
+public class DraggableMarkerItem extends MarkerItem {
+
+ private final DragAndDropListener dragAndDropListener;
+
+ public DraggableMarkerItem(String title, String description, GeoPoint geoPoint, DragAndDropListener dragAndDropListener) {
+ super(title, description, geoPoint);
+ this.dragAndDropListener = dragAndDropListener;
+ }
+
+ public DragAndDropListener getDragAndDropListener() {
+ return dragAndDropListener;
+ }
+}
diff --git a/vtm-android-example/src/org/oscim/android/drag/ItemDragger.java b/vtm-android-example/src/org/oscim/android/drag/ItemDragger.java
new file mode 100644
index 00000000..da09fd2e
--- /dev/null
+++ b/vtm-android-example/src/org/oscim/android/drag/ItemDragger.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2021 Frank Knoll
+ *
+ * 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.android.drag;
+
+import org.oscim.core.GeoPoint;
+import org.oscim.event.MotionEvent;
+import org.oscim.layers.marker.ItemizedLayer;
+import org.oscim.layers.marker.MarkerItem;
+import org.oscim.layers.marker.MarkerLayer;
+import org.oscim.map.Map;
+
+class ItemDragger {
+
+ private final DraggableItemizedLayer draggableItemizedLayer;
+ private final DragAndDropListener dragItemAndRedrawListener;
+ private DraggableMarkerItem dragItem;
+
+ public ItemDragger(DraggableItemizedLayer draggableItemizedLayer, Map map) {
+ this.draggableItemizedLayer = draggableItemizedLayer;
+ this.dragItemAndRedrawListener = createDragItemAndRedrawListener(draggableItemizedLayer, map);
+ }
+
+ public boolean startDragItem(MotionEvent event, final GeoPoint geoPoint) {
+ dragItem = null;
+ return draggableItemizedLayer.activateSelectedItems(
+ event,
+ new ItemizedLayer.ActiveItem() {
+ @Override
+ public boolean run(int index) {
+ dragItem = (DraggableMarkerItem) draggableItemizedLayer.getMarkerItems().get(index);
+ dragItemAndRedrawListener.startDragItemAtGeoPoint(dragItem, geoPoint);
+ return true;
+ }
+ });
+ }
+
+ public boolean ongoingDragItemTo(GeoPoint geoPoint) {
+ if (dragItem == null) {
+ return false;
+ }
+ dragItemAndRedrawListener.ongoingDragItemToGeoPoint(dragItem, geoPoint);
+ return true;
+ }
+
+ public boolean dropItemAt(GeoPoint geoPoint) {
+ if (dragItem == null) {
+ return false;
+ }
+ dragItemAndRedrawListener.dropItemAtGeoPoint(dragItem, geoPoint);
+ return true;
+ }
+
+ public void noDrag() {
+ dragItem = null;
+ }
+
+ private DragAndDropListener createDragItemAndRedrawListener(final MarkerLayer markerLayer, final Map map) {
+ return new DragAndDropListener() {
+ @Override
+ public void startDragItemAtGeoPoint(DraggableMarkerItem item, GeoPoint geoPoint) {
+ item.getDragAndDropListener().startDragItemAtGeoPoint(item, geoPoint);
+ updateLocationOfMarkerItemAndRedraw(item, geoPoint);
+ }
+
+ @Override
+ public void ongoingDragItemToGeoPoint(DraggableMarkerItem item, GeoPoint geoPoint) {
+ item.getDragAndDropListener().ongoingDragItemToGeoPoint(item, geoPoint);
+ updateLocationOfMarkerItemAndRedraw(item, geoPoint);
+ }
+
+ @Override
+ public void dropItemAtGeoPoint(DraggableMarkerItem item, GeoPoint geoPoint) {
+ item.getDragAndDropListener().dropItemAtGeoPoint(item, geoPoint);
+ updateLocationOfMarkerItemAndRedraw(item, geoPoint);
+ }
+
+ private void updateLocationOfMarkerItemAndRedraw(MarkerItem markerItem, GeoPoint location) {
+ markerItem.geoPoint = location;
+ markerLayer.populate();
+ map.render();
+ }
+ };
+ }
+}
diff --git a/vtm-android-example/src/org/oscim/android/test/DraggableMarkerOverlayActivity.java b/vtm-android-example/src/org/oscim/android/test/DraggableMarkerOverlayActivity.java
new file mode 100644
index 00000000..89a8fc96
--- /dev/null
+++ b/vtm-android-example/src/org/oscim/android/test/DraggableMarkerOverlayActivity.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2014 Hannes Janetzek
+ * Copyright 2016-2020 devemux86
+ * Copyright 2021 Frank Knoll
+ *
+ * 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.android.test;
+
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.util.Log;
+import org.oscim.android.canvas.AndroidBitmap;
+import org.oscim.android.drag.DragAndDropListener;
+import org.oscim.android.drag.DraggableItemizedLayer;
+import org.oscim.android.drag.DraggableMarkerItem;
+import org.oscim.core.GeoPoint;
+import org.oscim.layers.marker.ItemizedLayer;
+import org.oscim.layers.marker.MarkerInterface;
+import org.oscim.layers.marker.MarkerSymbol;
+import org.oscim.layers.marker.MarkerSymbol.HotspotPlace;
+import org.oscim.layers.tile.bitmap.BitmapTileLayer;
+import org.oscim.tiling.source.OkHttpEngine;
+import org.oscim.tiling.source.UrlTileSource;
+import org.oscim.tiling.source.bitmap.DefaultSources;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class DraggableMarkerOverlayActivity extends MapActivity {
+
+ public DraggableMarkerOverlayActivity() {
+ super(R.layout.activity_map_draggable);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ createLayers();
+ }
+
+ void createLayers() {
+ UrlTileSource tileSource = DefaultSources.OPENSTREETMAP
+ .httpFactory(new OkHttpEngine.OkHttpFactory())
+ .build();
+ tileSource.setHttpRequestHeaders(Collections.singletonMap("User-Agent", "vtm-android-example"));
+ mMap.layers().add(new BitmapTileLayer(mMap, tileSource));
+
+ ItemizedLayer mMarkerLayer = new DraggableItemizedLayer(
+ mMap,
+ new ArrayList(),
+ new MarkerSymbol(
+ new AndroidBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.marker_poi)),
+ HotspotPlace.BOTTOM_CENTER),
+ null);
+ mMap.layers().add(mMarkerLayer);
+
+ DragAndDropListener dragAndDropListener = new DragAndDropListener() {
+ @Override
+ public void startDragItemAtGeoPoint(DraggableMarkerItem item, GeoPoint geoPoint) {
+ Log.i(DraggableMarkerOverlayActivity.this.getClass().getSimpleName(), "startDragItemAtGeoPoint: " + geoPoint);
+ }
+
+ @Override
+ public void ongoingDragItemToGeoPoint(DraggableMarkerItem item, GeoPoint geoPoint) {
+ Log.i(DraggableMarkerOverlayActivity.this.getClass().getSimpleName(), "ongoingDragItemToGeoPoint: " + geoPoint);
+ }
+
+ @Override
+ public void dropItemAtGeoPoint(DraggableMarkerItem item, GeoPoint geoPoint) {
+ Log.i(DraggableMarkerOverlayActivity.this.getClass().getSimpleName(), "dropItemAtGeoPoint: " + geoPoint);
+ }
+ };
+
+ List pts = new ArrayList<>();
+ for (double lat = -90; lat <= 90; lat += 45) {
+ for (double lon = -180; lon <= 180; lon += 45) {
+ pts.add(new DraggableMarkerItem(lat + "/" + lon, "", new GeoPoint(lat, lon), dragAndDropListener));
+ }
+ }
+
+ mMarkerLayer.addItems(pts);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ /* ignore saved position */
+ mMap.setMapPosition(0, 0, 1 << 2);
+ }
+}
diff --git a/vtm-android-example/src/org/oscim/android/test/Samples.java b/vtm-android-example/src/org/oscim/android/test/Samples.java
index 23ae7c7c..32cd113a 100644
--- a/vtm-android-example/src/org/oscim/android/test/Samples.java
+++ b/vtm-android-example/src/org/oscim/android/test/Samples.java
@@ -124,6 +124,7 @@ public class Samples extends Activity {
linearLayout.addView(createButton(JeoIndoorActivity.class));
linearLayout.addView(createButton(GdxPoi3DActivity.class));
linearLayout.addView(createButton(OverpassActivity.class));
+ linearLayout.addView(createButton(DraggableMarkerOverlayActivity.class));
linearLayout.addView(createButton(ClusterMarkerOverlayActivity.class));
linearLayout.addView(createButton(FragmentActivity.class));
}