Files
vtm/vtm-app/src/org/oscim/app/RouteSearch.java

449 lines
15 KiB
Java

/*
* 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 android.app.Activity;
import android.location.Address;
import android.os.AsyncTask;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.oscim.android.canvas.AndroidGraphics;
import org.oscim.core.GeoPoint;
import org.oscim.layers.PathLayer;
import org.oscim.layers.marker.MarkerInterface;
import org.oscim.layers.marker.MarkerSymbol;
import org.oscim.layers.marker.MarkerSymbol.HotspotPlace;
import org.osmdroid.location.GeocoderNominatim;
import org.osmdroid.overlays.DefaultInfoWindow;
import org.osmdroid.overlays.ExtendedMarkerItem;
import org.osmdroid.overlays.ItemizedOverlayWithBubble;
import org.osmdroid.routing.Route;
import org.osmdroid.routing.RouteProvider;
import org.osmdroid.routing.provider.OSRMRouteProvider;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
public class RouteSearch {
private static int START_INDEX = -2, DEST_INDEX = -1;
private final PathLayer mRouteOverlay;
//private final ItemizedOverlayWithBubble<ExtendedOverlayItem> mRouteMarkers;
private final ItemizedOverlayWithBubble mItineraryMarkers;
private final RouteBar mRouteBar;
private GeoPoint mStartPoint, mDestinationPoint;
private final ArrayList<GeoPoint> mViaPoints;
private ExtendedMarkerItem markerStart, markerDestination;
private UpdateRouteTask mRouteTask;
RouteSearch() {
mViaPoints = new ArrayList<GeoPoint>();
// Itinerary markers:
ArrayList<MarkerInterface> waypointsItems = new ArrayList<>();
mItineraryMarkers = new ItemizedOverlayWithBubble(App.map,
App.activity,
null,
waypointsItems,
new ViaPointInfoWindow(R.layout.itinerary_bubble));
//updateIternaryMarkers();
//Route and Directions
//ArrayList<ExtendedOverlayItem> routeItems = new ArrayList<ExtendedOverlayItem>();
//mRouteMarkers = new ItemizedOverlayWithBubble<ExtendedOverlayItem>(App.map, App.activity,
// null, routeItems);
mRouteOverlay = new PathLayer(App.map, 0xAA0000FF, 3);
// TODO use LayerGroup
App.map.layers().add(mRouteOverlay);
//App.map.getOverlays().add(mRouteMarkers);
App.map.layers().add(mItineraryMarkers);
mRouteBar = new RouteBar(App.activity);
}
/**
* Retrieve route between p1 and p2 and update overlays.
*/
public void showRoute(GeoPoint p1, GeoPoint p2) {
clearOverlays();
mStartPoint = p1;
markerStart = putMarkerItem(markerStart, mStartPoint, START_INDEX,
R.string.departure, R.drawable.marker_departure, -1);
mDestinationPoint = p2;
markerDestination = putMarkerItem(markerDestination, mDestinationPoint, DEST_INDEX,
R.string.destination,
R.drawable.marker_destination, -1);
getRouteAsync();
}
/**
* Reverse Geocoding
*/
public String getAddress(GeoPoint p) {
GeocoderNominatim geocoder = new GeocoderNominatim(App.activity);
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> {
ExtendedMarkerItem marker;
@Override
protected String doInBackground(Object... params) {
marker = (ExtendedMarkerItem) params[0];
return getAddress(marker.getPoint());
}
@Override
protected void onPostExecute(String result) {
marker.setDescription(result);
}
}
/* add (or replace) an item in markerOverlays. p position. */
public ExtendedMarkerItem putMarkerItem(ExtendedMarkerItem item, GeoPoint p, int index,
int titleResId, int markerResId, int iconResId) {
if (item != null)
mItineraryMarkers.removeItem(item);
MarkerSymbol marker = AndroidGraphics.makeMarker(App.res.getDrawable(markerResId),
HotspotPlace.BOTTOM_CENTER);
ExtendedMarkerItem overlayItem =
new ExtendedMarkerItem(App.res.getString(titleResId), "", p);
overlayItem.setMarker(marker);
if (iconResId != -1)
overlayItem.setImage(App.res.getDrawable(iconResId));
overlayItem.setRelatedObject(Integer.valueOf(index));
mItineraryMarkers.addItem(overlayItem);
App.map.updateMap(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) {
mViaPoints.add(p);
putMarkerItem(null, p, mViaPoints.size() - 1,
R.string.viapoint, R.drawable.marker_via, -1);
}
public void removePoint(int index) {
if (index == START_INDEX) {
mStartPoint = null;
} else if (index == DEST_INDEX) {
mDestinationPoint = null;
} else
mViaPoints.remove(index);
getRouteAsync();
updateIternaryMarkers();
}
public void updateIternaryMarkers() {
mItineraryMarkers.removeAllItems();
//Start marker:
if (mStartPoint != null) {
markerStart = putMarkerItem(null, mStartPoint, START_INDEX,
R.string.departure, R.drawable.marker_departure, -1);
}
//Via-points markers if any:
for (int index = 0; index < mViaPoints.size(); index++) {
putMarkerItem(null, mViaPoints.get(index), index,
R.string.viapoint, R.drawable.marker_via, -1);
}
//Destination marker if any:
if (mDestinationPoint != null) {
markerDestination = putMarkerItem(null, mDestinationPoint, DEST_INDEX,
R.string.destination,
R.drawable.marker_destination, -1);
}
}
//------------ Route and Directions
private void updateOverlays(Route route) {
//mRouteMarkers.removeAllItems();
mRouteOverlay.clearPath();
if (route == null || route.status == Route.STATUS_DEFAULT) {
App.activity.showToastOnUiThread(App.res.getString(R.string.route_lookup_error));
return;
}
mRouteOverlay.setPoints(route.routeHigh);
//OverlayMarker marker = AndroidGraphics.makeMarker(App.res, R.drawable.marker_node, null);
//int n = route.nodes.size();
//for (int i = 0; i < n; i++) {
// RouteNode node = route.nodes.get(i);
// String instructions = (node.instructions == null ? "" : node.instructions);
// ExtendedOverlayItem nodeMarker = new ExtendedOverlayItem(
// "Step " + (i + 1), instructions, node.location);
//
// nodeMarker.setSubDescription(route.getLengthDurationText(node.length, node.duration));
// nodeMarker.setMarkerHotspot(OverlayItem.HotspotPlace.CENTER);
// nodeMarker.setMarker(marker);
//
// mRouteMarkers.addItem(nodeMarker);
//}
App.map.updateMap(true);
}
void clearOverlays() {
//mRouteMarkers.removeAllItems(true);
mItineraryMarkers.removeAllItems(true);
mRouteOverlay.clearPath();
mStartPoint = null;
mDestinationPoint = null;
mViaPoints.clear();
App.map.updateMap(true);
}
/**
* Async task to get the route in a separate thread.
*/
class UpdateRouteTask extends AsyncTask<List<GeoPoint>, Void, Route> {
@Override
protected Route doInBackground(List<GeoPoint>... wp) {
List<GeoPoint> waypoints = wp[0];
//RouteProvider routeProvider = new MapQuestRouteProvider();
//Locale locale = Locale.getDefault();
//routeProvider.addRequestOption("locale=" + locale.getLanguage() + "_"
// + locale.getCountry());
//routeProvider.addRequestOption("routeType=pedestrian");
//RouteProvider routeProvider = new GoogleRouteProvider();
RouteProvider routeProvider = new OSRMRouteProvider();
return routeProvider.getRoute(waypoints);
}
@Override
protected void onPostExecute(Route result) {
updateOverlays(result);
mRouteBar.set(result);
mRouteTask = null;
}
}
@SuppressWarnings("unchecked")
public void getRouteAsync() {
if (mRouteTask != null) {
mRouteTask.cancel(true);
mRouteTask = null;
}
if (mStartPoint == null || mDestinationPoint == null) {
mRouteOverlay.clearPath();
return;
}
List<GeoPoint> waypoints = new ArrayList<GeoPoint>();
waypoints.add(mStartPoint);
//add intermediate via points:
for (GeoPoint p : mViaPoints) {
waypoints.add(p);
}
waypoints.add(mDestinationPoint);
mRouteTask = new UpdateRouteTask();
mRouteTask.execute(waypoints);
}
boolean onContextItemSelected(MenuItem item, GeoPoint geoPoint) {
int itemId = item.getItemId();
if (itemId == R.id.menu_route_departure) {
mStartPoint = geoPoint;
markerStart = putMarkerItem(markerStart, mStartPoint, START_INDEX,
R.string.departure, R.drawable.marker_departure, -1);
getRouteAsync();
return true;
} else if (itemId == R.id.menu_route_destination) {
mDestinationPoint = geoPoint;
markerDestination = putMarkerItem(markerDestination, mDestinationPoint, DEST_INDEX,
R.string.destination,
R.drawable.marker_destination, -1);
getRouteAsync();
return true;
} else if (itemId == R.id.menu_route_viapoint) {
GeoPoint viaPoint = geoPoint;
addViaPoint(viaPoint);
getRouteAsync();
return true;
} else if (itemId == R.id.menu_route_clear) {
clearOverlays();
return true;
}
return false;
}
public boolean isEmpty() {
return (mItineraryMarkers.size() == 0);
}
class ViaPointInfoWindow extends DefaultInfoWindow {
int mSelectedPoint;
public ViaPointInfoWindow(int layoutResId) {
super(layoutResId, App.view);
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(ExtendedMarkerItem item) {
mSelectedPoint = ((Integer) item.getRelatedObject()).intValue();
super.onOpen(item);
}
}
class RouteBar {
TextView mDistance = null;
TextView mRouteLength = null;
TextView mTravelTime = null;
ImageView mClearButton = null;
RelativeLayout mRouteBarView = null;
RouteBar(Activity activity) {
mRouteBarView = (RelativeLayout) activity.findViewById(R.id.route_bar);
mDistance = (TextView) activity.findViewById(R.id.route_bar_distance);
mRouteLength = (TextView) activity.findViewById(R.id.route_bar_route_length);
mTravelTime = (TextView) activity.findViewById(R.id.route_bar_travel_time);
mClearButton = (ImageView) activity.findViewById(R.id.route_bar_clear);
mRouteBarView.setVisibility(View.INVISIBLE);
mClearButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRouteBarView.setVisibility(View.INVISIBLE);
clearOverlays();
}
});
}
public void set(Route result) {
DecimalFormat twoDForm = new DecimalFormat("#.#");
DecimalFormat oneDForm = new DecimalFormat("#");
int hour = ((int) result.duration / 3600);
int minute = ((int) result.duration % 3600) / 60;
String time = "";
if (hour == 0 && minute == 0) {
time = "?";
} else if (hour == 0 && minute != 0) {
time = minute + "m";
} else {
time = hour + "h " + minute + "m";
}
double dis = ((double) (mStartPoint.sphericalDistance(mDestinationPoint))) / 1000;
String distance;
String shortpath;
if (dis < 100) {
distance = twoDForm.format(dis);
} else {
distance = oneDForm.format(dis);
}
if (result.length == 0) {
shortpath = "?";
} else if (result.length < 100) {
shortpath = twoDForm.format(result.length);
} else {
shortpath = oneDForm.format(result.length);
}
mRouteBarView.setVisibility(View.VISIBLE);
mDistance.setText(distance + " km");
mTravelTime.setText(time);
mRouteLength.setText(shortpath + " km");
}
}
}