/* * Copyright 2012 osmdroid: M.Kergall * Copyright 2012 Hannes Janetzek * * 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 <http://www.gnu.org/licenses/>. */ package org.oscim.app; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Locale; import org.oscim.core.GeoPoint; import org.oscim.overlay.Overlay; import org.oscim.overlay.OverlayItem; import org.oscim.overlay.PathOverlay; import org.oscim.view.MapView; import org.osmdroid.location.GeocoderNominatim; import org.osmdroid.overlays.DefaultInfoWindow; import org.osmdroid.overlays.ExtendedOverlayItem; import org.osmdroid.overlays.ItemizedOverlayWithBubble; import org.osmdroid.routing.Road; import org.osmdroid.routing.RoadManager; import org.osmdroid.routing.RoadNode; import org.osmdroid.routing.provider.MapQuestRoadManager; import android.app.AlertDialog; import android.content.DialogInterface; import android.graphics.drawable.Drawable; import android.location.Address; import android.os.AsyncTask; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.Toast; public class RouteSearch { protected Road mRoad; protected PathOverlay mRoadOverlay; protected ItemizedOverlayWithBubble<ExtendedOverlayItem> mRoadNodeMarkers; protected ItemizedOverlayWithBubble<ExtendedOverlayItem> itineraryMarkers; protected GeoPoint startPoint, destinationPoint; protected ArrayList<GeoPoint> viaPoints; protected static int START_INDEX = -2, DEST_INDEX = -1; protected ExtendedOverlayItem markerStart, markerDestination; private final TileMap tileMap; RouteSearch(TileMap tileMap) { this.tileMap = tileMap; startPoint = null; destinationPoint = null; viaPoints = new ArrayList<GeoPoint>(); // Itinerary markers: final ArrayList<ExtendedOverlayItem> waypointsItems = new ArrayList<ExtendedOverlayItem>(); itineraryMarkers = new ItemizedOverlayWithBubble<ExtendedOverlayItem>(tileMap.map, tileMap, waypointsItems, new ViaPointInfoWindow(R.layout.itinerary_bubble, tileMap.map)); tileMap.map.getOverlays().add(itineraryMarkers); updateUIWithItineraryMarkers(); //Route and Directions final ArrayList<ExtendedOverlayItem> roadItems = new ArrayList<ExtendedOverlayItem>(); mRoadNodeMarkers = new ItemizedOverlayWithBubble<ExtendedOverlayItem>(tileMap, roadItems, tileMap.map); tileMap.map.getOverlays().add(mRoadNodeMarkers); } //------------- Geocoding and Reverse Geocoding /** * Reverse Geocoding * @param p * ... * @return ... */ public String getAddress(GeoPoint p) { GeocoderNominatim geocoder = new GeocoderNominatim(tileMap); String theAddress; try { double dLatitude = p.getLatitude(); double dLongitude = p.getLongitude(); List<Address> addresses = geocoder.getFromLocation(dLatitude, dLongitude, 1); StringBuilder sb = new StringBuilder(); if (addresses.size() > 0) { Address address = addresses.get(0); int n = address.getMaxAddressLineIndex(); for (int i = 0; i <= n; i++) { if (i != 0) sb.append(", "); sb.append(address.getAddressLine(i)); } theAddress = new String(sb.toString()); } else { theAddress = null; } } catch (IOException e) { theAddress = null; } if (theAddress != null) { return theAddress; } return ""; } // Async task to reverse-geocode the marker position in a separate thread: class GeocodingTask extends AsyncTask<Object, Void, String> { ExtendedOverlayItem marker; @Override protected String doInBackground(Object... params) { marker = (ExtendedOverlayItem) params[0]; return getAddress(marker.getPoint()); } @Override protected void onPostExecute(String result) { marker.setDescription(result); //itineraryMarkers.showBubbleOnItem(???, map); //open bubble on the item } } //------------ Itinerary markers /* add (or replace) an item in markerOverlays. p position. */ public ExtendedOverlayItem putMarkerItem(ExtendedOverlayItem item, GeoPoint p, int index, int titleResId, int markerResId, int iconResId) { if (item != null) { itineraryMarkers.removeItem(item); } Drawable marker = App.res.getDrawable(markerResId); String title = App.res.getString(titleResId); ExtendedOverlayItem overlayItem = new ExtendedOverlayItem(title, "", p); overlayItem.setMarkerHotspot(OverlayItem.HotspotPlace.BOTTOM_CENTER); overlayItem.setMarker(marker); if (iconResId != -1) overlayItem.setImage(App.res.getDrawable(iconResId)); overlayItem.setRelatedObject(Integer.valueOf(index)); itineraryMarkers.addItem(overlayItem); tileMap.map.redrawMap(true); //Start geocoding task to update the description of the marker with its address: new GeocodingTask().execute(overlayItem); return overlayItem; } public void addViaPoint(GeoPoint p) { viaPoints.add(p); putMarkerItem(null, p, viaPoints.size() - 1, R.string.viapoint, R.drawable.marker_via, -1); } public void removePoint(int index) { if (index == START_INDEX) startPoint = null; else if (index == DEST_INDEX) destinationPoint = null; else viaPoints.remove(index); getRoadAsync(); updateUIWithItineraryMarkers(); } public void updateUIWithItineraryMarkers() { itineraryMarkers.removeAllItems(); //Start marker: if (startPoint != null) { markerStart = putMarkerItem(null, startPoint, START_INDEX, R.string.departure, R.drawable.marker_departure, -1); } //Via-points markers if any: for (int index = 0; index < viaPoints.size(); index++) { putMarkerItem(null, viaPoints.get(index), index, R.string.viapoint, R.drawable.marker_via, -1); } //Destination marker if any: if (destinationPoint != null) { markerDestination = putMarkerItem(null, destinationPoint, DEST_INDEX, R.string.destination, R.drawable.marker_destination, -1); } } //------------ Route and Directions private void putRoadNodes(Road road) { mRoadNodeMarkers.removeAllItems(); Drawable marker = App.res.getDrawable(R.drawable.marker_node); int n = road.nodes.size(); // TypedArray iconIds = App.res.obtainTypedArray(R.array.direction_icons); for (int i = 0; i < n; i++) { RoadNode node = road.nodes.get(i); String instructions = (node.instructions == null ? "" : node.instructions); ExtendedOverlayItem nodeMarker = new ExtendedOverlayItem( "Step " + (i + 1), instructions, node.location); nodeMarker.setSubDescription(road.getLengthDurationText(node.length, node.duration)); nodeMarker.setMarkerHotspot(OverlayItem.HotspotPlace.CENTER); nodeMarker.setMarker(marker); // int iconId = iconIds.getResourceId(node.mManeuverType, R.drawable.ic_empty); // if (iconId != R.drawable.ic_empty) { // Drawable icon = App.res.getDrawable(iconId); // nodeMarker.setImage(icon); // } mRoadNodeMarkers.addItem(nodeMarker); } } void updateUIWithRoad(Road road) { mRoadNodeMarkers.removeAllItems(); List<Overlay> mapOverlays = tileMap.map.getOverlays(); if (mRoadOverlay != null) { mapOverlays.remove(mRoadOverlay); } if (road == null) return; if (road.status == Road.STATUS_DEFAULT) Toast.makeText(tileMap, "We have a problem to get the route", Toast.LENGTH_SHORT).show(); mRoadOverlay = RoadManager.buildRoadOverlay(tileMap.map, road); Overlay removedOverlay = mapOverlays.set(1, mRoadOverlay); //we set the road overlay at the "bottom", just above the MapEventsOverlay, //to avoid covering the other overlays. mapOverlays.add(removedOverlay); putRoadNodes(road); tileMap.map.redrawMap(true); //Set route info in the text view: // ((TextView) findViewById(R.id.routeInfo)).setText(road.getLengthDurationText(-1)); } void removeRoadPath() { List<Overlay> mapOverlays = tileMap.map.getOverlays(); if (mRoadOverlay != null) { mapOverlays.remove(mRoadOverlay); } tileMap.map.redrawMap(true); } void removeRoadNodes() { mRoadNodeMarkers.removeAllItems(); tileMap.map.redrawMap(true); } void removeAllOverlay() { List<Overlay> mapOverlays = tileMap.map.getOverlays(); if (mRoadOverlay != null) { mapOverlays.remove(mRoadOverlay); } if (mRoadNodeMarkers != null) { mapOverlays.remove(mRoadNodeMarkers); } final ArrayList<ExtendedOverlayItem> roadItems = new ArrayList<ExtendedOverlayItem>(); mRoadNodeMarkers = new ItemizedOverlayWithBubble<ExtendedOverlayItem>(tileMap, roadItems, tileMap.map); tileMap.map.getOverlays().add(mRoadNodeMarkers); //removePoint(-2); removePoint(-1); tileMap.map.redrawMap(true); } /** * Async task to get the road in a separate thread. */ class UpdateRoadTask extends AsyncTask<WayPoints, Void, Road> { @Override protected Road doInBackground(WayPoints... wp) { ArrayList<GeoPoint> waypoints = wp[0].waypoints; //RoadManager roadManager = new GoogleRoadManager(); //RoadManager roadManager = new OSRMRoadManager(); RoadManager roadManager = new MapQuestRoadManager(); Locale locale = Locale.getDefault(); roadManager.addRequestOption("locale=" + locale.getLanguage() + "_" + locale.getCountry()); return roadManager.getRoad(waypoints); } @Override protected void onPostExecute(Road result) { mRoad = result; updateUIWithRoad(result); } } // Just to make JAVA shut up! class WayPoints { public ArrayList<GeoPoint> waypoints; } public void getRoadAsync() { mRoad = null; if (startPoint == null || destinationPoint == null) { updateUIWithRoad(mRoad); return; } ArrayList<GeoPoint> waypoints = new ArrayList<GeoPoint>(2); waypoints.add(startPoint); //add intermediate via points: for (GeoPoint p : viaPoints) { waypoints.add(p); } waypoints.add(destinationPoint); WayPoints wp = new WayPoints(); wp.waypoints = waypoints; new UpdateRoadTask().execute(wp); } GeoPoint tempClickedGeoPoint; //any other way to pass the position to the menu ??? boolean longPress(GeoPoint p) { tempClickedGeoPoint = p; return true; } void singleTapUp() { mRoadNodeMarkers.hideBubble(); itineraryMarkers.hideBubble(); } // ----------- context menu boolean onContextItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_departure: startPoint = tempClickedGeoPoint; markerStart = putMarkerItem(markerStart, startPoint, START_INDEX, R.string.departure, R.drawable.marker_departure, -1); getRoadAsync(); return true; case R.id.menu_destination: destinationPoint = tempClickedGeoPoint; markerDestination = putMarkerItem(markerDestination, destinationPoint, DEST_INDEX, R.string.destination, R.drawable.marker_destination, -1); getRoadAsync(); return true; case R.id.menu_viapoint: GeoPoint viaPoint = tempClickedGeoPoint; addViaPoint(viaPoint); getRoadAsync(); return true; case R.id.menu_clear: AlertDialog.Builder builder = new AlertDialog.Builder(tileMap); // if (mRoadOverlay != null) { // mapOverlays.remove(mRoadOverlay); // } // if (mRoadNodeMarkers != null) { // mapOverlays.remove(mRoadNodeMarkers); // } List<Overlay> mapOverlays = tileMap.map.getOverlays(); ArrayList<String> list = new ArrayList<String>(); if (!mapOverlays.contains(mRoadOverlay) && mRoadNodeMarkers.size() != 0) { list.clear(); list.add("Clear Route Node Only"); list.add("Clear All"); } else if (mRoadNodeMarkers.size() == 0 && mapOverlays.contains(mRoadOverlay)) { list.clear(); list.add("Clear Route Only"); list.add("Clear All"); } else if (!mapOverlays.contains(mRoadOverlay) && mRoadNodeMarkers.size() == 0 && markerDestination == null) { list.clear(); list.add("Nothing to Clear"); } else if (!mapOverlays.contains(mRoadOverlay) && mRoadNodeMarkers.size() == 0 && markerDestination != null) { list.clear(); list.add("Clear All"); } else { list.clear(); list.add("Clear Route Node Only"); list.add("Clear Route Only"); list.add("Clear All"); } final String[] test = new String[list.size()]; for (int i = 0; i < list.size(); i++) { test[i] = list.get(i); } builder.setTitle("Clear"); builder.setItems(test, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { if (test[which].equals("Clear Route Only")) { removeRoadPath(); } else if (test[which].equals("Clear Route Node Only")) { removeRoadNodes(); } else if (test[which].equals("Clear All")) { removeAllOverlay(); } // The 'which' argument contains the index position // of the selected item } }); AlertDialog alertDialog = builder.create(); alertDialog.show(); return true; default: } return false; } class ViaPointInfoWindow extends DefaultInfoWindow { int mSelectedPoint; public ViaPointInfoWindow(int layoutResId, MapView mapView) { super(layoutResId, mapView); Button btnDelete = (Button) (mView.findViewById(R.id.bubble_delete)); btnDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { removePoint(mSelectedPoint); close(); } }); } @Override public void onOpen(ExtendedOverlayItem item) { mSelectedPoint = ((Integer) item.getRelatedObject()).intValue(); super.onOpen(item); } } }