diff --git a/docs/Changelog.md b/docs/Changelog.md
index 58ab32e1..febdc923 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -5,6 +5,7 @@
- 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)
+- POI Search example [#394](https://github.com/mapsforge/vtm/issues/394)
- Mapsforge fix artifacts for zoom > 17 [#231](https://github.com/mapsforge/vtm/issues/231)
- vtm-theme-comparator module [#387](https://github.com/mapsforge/vtm/issues/387)
- Many other minor improvements and bug fixes
diff --git a/vtm-android-example/AndroidManifest.xml b/vtm-android-example/AndroidManifest.xml
index 4321c70f..dc87e92b 100644
--- a/vtm-android-example/AndroidManifest.xml
+++ b/vtm-android-example/AndroidManifest.xml
@@ -88,6 +88,12 @@
+
+
diff --git a/vtm-android-example/build.gradle b/vtm-android-example/build.gradle
index fbc058d0..ebac62c9 100644
--- a/vtm-android-example/build.gradle
+++ b/vtm-android-example/build.gradle
@@ -1,5 +1,9 @@
apply plugin: 'com.android.application'
+repositories {
+ maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
+}
+
dependencies {
compile project(':vtm-android')
compile project(':vtm-extras')
@@ -12,6 +16,14 @@ dependencies {
compile project(':vtm-themes')
compile 'com.noveogroup.android:android-logger:1.3.6'
compile 'com.android.support:support-v4:26.0.0'
+
+ compile 'org.mapsforge:mapsforge-core:master-SNAPSHOT'
+ compile 'org.mapsforge:mapsforge-poi:master-SNAPSHOT'
+ compile 'org.mapsforge:mapsforge-poi-android:master-SNAPSHOT'
+ compile 'org.mapsforge:spatialite-android:master-SNAPSHOT'
+ compile 'org.mapsforge:spatialite-android:master-SNAPSHOT:natives-armeabi'
+ compile 'org.mapsforge:spatialite-android:master-SNAPSHOT:natives-armeabi-v7a'
+ compile 'org.mapsforge:spatialite-android:master-SNAPSHOT:natives-x86'
}
android {
diff --git a/vtm-android-example/src/org/oscim/android/test/MapsforgeMapActivity.java b/vtm-android-example/src/org/oscim/android/test/MapsforgeMapActivity.java
index b4e44ae5..a8d5d039 100644
--- a/vtm-android-example/src/org/oscim/android/test/MapsforgeMapActivity.java
+++ b/vtm-android-example/src/org/oscim/android/test/MapsforgeMapActivity.java
@@ -46,8 +46,9 @@ import org.oscim.tiling.source.mapfile.MapFileTileSource;
import org.oscim.tiling.source.mapfile.MapInfo;
public class MapsforgeMapActivity extends MapActivity {
- private static final int SELECT_MAP_FILE = 0;
- private static final int SELECT_THEME_FILE = 1;
+
+ static final int SELECT_MAP_FILE = 0;
+ static final int SELECT_THEME_FILE = SELECT_MAP_FILE + 1;
private TileGridLayer mGridLayer;
private DefaultMapScaleBar mMapScaleBar;
diff --git a/vtm-android-example/src/org/oscim/android/test/PoiSearchActivity.java b/vtm-android-example/src/org/oscim/android/test/PoiSearchActivity.java
new file mode 100644
index 00000000..5968f998
--- /dev/null
+++ b/vtm-android-example/src/org/oscim/android/test/PoiSearchActivity.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2017 devemux86
+ *
+ * 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.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.Menu;
+import android.widget.Toast;
+
+import org.mapsforge.poi.android.storage.AndroidPoiPersistenceManagerFactory;
+import org.mapsforge.poi.storage.ExactMatchPoiCategoryFilter;
+import org.mapsforge.poi.storage.PoiCategoryFilter;
+import org.mapsforge.poi.storage.PoiCategoryManager;
+import org.mapsforge.poi.storage.PoiPersistenceManager;
+import org.mapsforge.poi.storage.PointOfInterest;
+import org.oscim.android.filepicker.FilePicker;
+import org.oscim.android.filepicker.FilterByFileExtension;
+import org.oscim.backend.canvas.Bitmap;
+import org.oscim.core.BoundingBox;
+import org.oscim.core.GeoPoint;
+import org.oscim.event.Gesture;
+import org.oscim.event.GestureListener;
+import org.oscim.event.MotionEvent;
+import org.oscim.layers.Layer;
+import org.oscim.layers.marker.ItemizedLayer;
+import org.oscim.layers.marker.MarkerItem;
+import org.oscim.layers.marker.MarkerSymbol;
+import org.oscim.map.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import static org.oscim.android.canvas.AndroidGraphics.drawableToBitmap;
+
+/**
+ * POI search.
+ * Long press on map to search inside visible bounding box.
+ * Tap on POIs to show their name (in default locale).
+ */
+public class PoiSearchActivity extends MapsforgeMapActivity implements ItemizedLayer.OnItemGestureListener {
+
+ private static final Logger log = LoggerFactory.getLogger(PoiSearchActivity.class);
+
+ private static String POI_FILE;
+ private static final String POI_CATEGORY = "Restaurants";
+ private static final int SELECT_POI_FILE = MapsforgeMapActivity.SELECT_THEME_FILE + 1;
+
+ public static class PoiFilePicker extends FilePicker {
+ public PoiFilePicker() {
+ setFileDisplayFilter(new FilterByFileExtension(".poi"));
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Map events receiver
+ mMap.layers().add(new PoiSearchActivity.MapEventsReceiver(mMap));
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ return false;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ super.onActivityResult(requestCode, resultCode, intent);
+
+ if (requestCode == SELECT_MAP_FILE) {
+ startActivityForResult(new Intent(this, PoiFilePicker.class),
+ SELECT_POI_FILE);
+ } else if (requestCode == SELECT_POI_FILE) {
+ if (resultCode != RESULT_OK || intent == null || intent.getStringExtra(FilePicker.SELECTED_FILE) == null) {
+ finish();
+ return;
+ }
+
+ POI_FILE = intent.getStringExtra(FilePicker.SELECTED_FILE);
+ }
+ }
+
+ @Override
+ public boolean onItemSingleTapUp(int index, MarkerItem item) {
+ Toast.makeText(this, item.getTitle(), Toast.LENGTH_SHORT).show();
+ return true;
+ }
+
+ @Override
+ public boolean onItemLongPress(int index, MarkerItem item) {
+ return false;
+ }
+
+ private class MapEventsReceiver extends Layer implements GestureListener {
+
+ MapEventsReceiver(Map map) {
+ super(map);
+ }
+
+ @Override
+ public boolean onGesture(Gesture g, MotionEvent e) {
+ if (g instanceof Gesture.LongPress) {
+ // POI search
+ new PoiSearchTask(PoiSearchActivity.this, POI_CATEGORY).execute(mMap.getBoundingBox(0));
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private class PoiSearchTask extends AsyncTask> {
+ private final WeakReference weakActivity;
+ private final String category;
+
+ private PoiSearchTask(PoiSearchActivity activity, String category) {
+ this.weakActivity = new WeakReference<>(activity);
+ this.category = category;
+ }
+
+ @Override
+ protected Collection doInBackground(BoundingBox... params) {
+ // Search POI
+ PoiPersistenceManager persistenceManager = null;
+ try {
+ persistenceManager = AndroidPoiPersistenceManagerFactory.getPoiPersistenceManager(POI_FILE);
+ PoiCategoryManager categoryManager = persistenceManager.getCategoryManager();
+ PoiCategoryFilter categoryFilter = new ExactMatchPoiCategoryFilter();
+ categoryFilter.addCategory(categoryManager.getPoiCategoryByTitle(this.category));
+ org.mapsforge.core.model.BoundingBox bb = new org.mapsforge.core.model.BoundingBox(
+ params[0].getMinLatitude(), params[0].getMinLongitude(),
+ params[0].getMaxLatitude(), params[0].getMaxLongitude());
+ return persistenceManager.findInRect(bb, categoryFilter, null, Integer.MAX_VALUE);
+ } catch (Throwable t) {
+ log.error(t.getMessage(), t);
+ } finally {
+ if (persistenceManager != null) {
+ persistenceManager.close();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Collection pointOfInterests) {
+ PoiSearchActivity activity = weakActivity.get();
+ if (activity == null) {
+ return;
+ }
+ Toast.makeText(activity, category + ": " + (pointOfInterests != null ? pointOfInterests.size() : 0), Toast.LENGTH_SHORT).show();
+ if (pointOfInterests == null) {
+ return;
+ }
+
+ // Overlay POI
+ Bitmap bitmap = drawableToBitmap(getResources(), R.drawable.marker_poi);
+ MarkerSymbol symbol = new MarkerSymbol(bitmap, MarkerSymbol.HotspotPlace.CENTER);
+ ItemizedLayer markerLayer = new ItemizedLayer<>(mMap, new ArrayList(), symbol, PoiSearchActivity.this);
+ mMap.layers().add(markerLayer);
+ List pts = new ArrayList<>();
+ for (PointOfInterest pointOfInterest : pointOfInterests)
+ pts.add(new MarkerItem(pointOfInterest.getName(), "", new GeoPoint(pointOfInterest.getLatitude(), pointOfInterest.getLongitude())));
+ markerLayer.addItems(pts);
+ mMap.render();
+ }
+ }
+}
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 031f68ab..5289c016 100644
--- a/vtm-android-example/src/org/oscim/android/test/Samples.java
+++ b/vtm-android-example/src/org/oscim/android/test/Samples.java
@@ -92,6 +92,7 @@ public class Samples extends Activity {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/graphhopper/graphhopper/tree/master/android")));
}
}));
+ linearLayout.addView(createButton(PoiSearchActivity.class));
linearLayout.addView(createLabel("Vector Features"));
linearLayout.addView(createButton(MapsforgeStyleActivity.class));