/* * Copyright 2017-2018 devemux86 * Copyright 2018 Gustl22 * * 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.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.Menu; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.Toast; import android.widget.ToggleButton; import org.mapsforge.core.model.Tag; 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 MapsforgeActivity implements ItemizedLayer.OnItemGestureListener { private static final Logger log = LoggerFactory.getLogger(PoiSearchActivity.class); private static final String POI_CATEGORY = "Restaurants"; private static final int SELECT_POI_FILE = MapsforgeActivity.SELECT_THEME_FILE + 1; private ItemizedLayer mMarkerLayer; private PoiPersistenceManager mPersistenceManager; public static class PoiFilePicker extends FilePicker { public PoiFilePicker() { setFileDisplayFilter(new FilterByFileExtension(".poi")); } } public PoiSearchActivity() { super(false, R.layout.activity_map_poi); } private void initSearch() { final List searchPatterns = new ArrayList<>(); final PatternAdapter adapter = new PatternAdapter(this, searchPatterns); ListView searchList = (ListView) findViewById(R.id.search_list); searchList.setAdapter(adapter); Button addItem = (Button) findViewById(R.id.add_item); addItem.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { searchPatterns.add(new Pattern("", "")); adapter.notifyDataSetChanged(); } }); Button startSearch = (Button) findViewById(R.id.start_search); startSearch.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { // Clear overlays mMarkerLayer.removeAllItems(); mMap.render(); // POI search List tags = new ArrayList<>(); for (Pattern pattern : searchPatterns) tags.add(new Tag(pattern.key, pattern.val)); new PoiSearchTask(PoiSearchActivity.this, null, tags).execute(mMap.getBoundingBox(0)); } }); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initSearch(); // Map events receiver mMap.layers().add(new PoiSearchActivity.MapEventsReceiver(mMap)); } @Override protected void onDestroy() { if (mPersistenceManager != null) mPersistenceManager.close(); super.onDestroy(); } @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) { if (mTileSource != null) startActivityForResult(new Intent(this, PoiFilePicker.class), SELECT_POI_FILE); else finish(); } else if (requestCode == SELECT_POI_FILE) { if (resultCode != RESULT_OK || intent == null || intent.getStringExtra(FilePicker.SELECTED_FILE) == null) { finish(); return; } String file = intent.getStringExtra(FilePicker.SELECTED_FILE); mPersistenceManager = AndroidPoiPersistenceManagerFactory.getPoiPersistenceManager(file); Bitmap bitmap = drawableToBitmap(getResources().getDrawable(R.drawable.marker_green)); MarkerSymbol symbol = new MarkerSymbol(bitmap, MarkerSymbol.HotspotPlace.BOTTOM_CENTER); mMarkerLayer = new ItemizedLayer<>(mMap, new ArrayList(), symbol, this); mMap.layers().add(mMarkerLayer); } } @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; } public void onToggleControls(View view) { findViewById(R.id.controls).setVisibility(((ToggleButton) view).isChecked() ? View.VISIBLE : View.GONE); } 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) { // Clear overlays mMarkerLayer.removeAllItems(); mMap.render(); // POI search new PoiSearchTask(PoiSearchActivity.this, POI_CATEGORY, null).execute(mMap.getBoundingBox(0)); return true; } return false; } } private class Pattern { String key; String val; Pattern(String key, String val) { this.key = key; this.val = val; } } private class PatternAdapter extends ArrayAdapter { PatternAdapter(Context context, List patterns) { super(context, 0, patterns); } @Override public View getView(int position, View convertView, ViewGroup parent) { Pattern pattern = getItem(position); assert pattern != null; // Check if an existing view is being reused, otherwise inflate the view if (convertView == null) convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_poi_search, parent, false); // Populate the data EditText etKey = (EditText) convertView.findViewById(R.id.key); etKey.removeTextChangedListener((PatternTextWatcher) etKey.getTag()); // remove previous listeners etKey.setText(pattern.key); // set text when no listener is attached PatternTextWatcher ptwKey = new PatternTextWatcher(pattern, true); etKey.setTag(ptwKey); etKey.addTextChangedListener(ptwKey); EditText etValue = (EditText) convertView.findViewById(R.id.value); etValue.removeTextChangedListener((PatternTextWatcher) etValue.getTag()); etValue.setText(pattern.val); PatternTextWatcher ptwVal = new PatternTextWatcher(pattern, false); etValue.setTag(ptwVal); etValue.addTextChangedListener(ptwVal); Button remove = (Button) convertView.findViewById(R.id.remove); remove.setTag(pattern); remove.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Pattern pat = (Pattern) v.getTag(); remove(pat); } }); return convertView; } } private class PatternTextWatcher implements TextWatcher { private Pattern pattern; private boolean isKey; PatternTextWatcher(Pattern pattern, boolean isKey) { this.pattern = pattern; this.isKey = isKey; } @Override public void afterTextChanged(Editable s) { } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (isKey) pattern.key = s.toString(); else pattern.val = s.toString(); } } private class PoiSearchTask extends AsyncTask> { private final WeakReference weakActivity; private final String category; private final List patterns; private PoiSearchTask(PoiSearchActivity activity, String category, List patterns) { this.weakActivity = new WeakReference<>(activity); this.category = category; this.patterns = patterns; } @Override protected Collection doInBackground(BoundingBox... params) { // Search POI try { PoiCategoryManager categoryManager = mPersistenceManager.getCategoryManager(); PoiCategoryFilter categoryFilter = new ExactMatchPoiCategoryFilter(); if (category != null) categoryFilter.addCategory(categoryManager.getPoiCategoryByTitle(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 mPersistenceManager.findInRect(bb, categoryFilter, patterns, Integer.MAX_VALUE); } catch (Throwable t) { log.error(t.getMessage(), t); } return null; } @Override protected void onPostExecute(Collection pointOfInterests) { PoiSearchActivity activity = weakActivity.get(); if (activity == null) { return; } Toast.makeText(activity, (category != null ? category : "Results") + ": " + (pointOfInterests != null ? pointOfInterests.size() : 0), Toast.LENGTH_SHORT).show(); if (pointOfInterests == null) { return; } // Overlay POI List pts = new ArrayList<>(); for (PointOfInterest pointOfInterest : pointOfInterests) pts.add(new MarkerItem(pointOfInterest.getName(), "", new GeoPoint(pointOfInterest.getLatitude(), pointOfInterest.getLongitude()))); mMarkerLayer.addItems(pts); mMap.render(); } } }