implement navigation using gpx file

git-svn-id: https://osmand.googlecode.com/svn/trunk@508 e29c36b1-1cfa-d876-8d93-3434fc2bb7b8
This commit is contained in:
Victor Shcherb 2010-09-18 22:52:06 +00:00
parent 5618985c9e
commit 0afdfa96dc
13 changed files with 475 additions and 315 deletions

View file

@ -10,7 +10,7 @@ public class ToDoConstants {
// TODO swing
// !!! 12. Reinvent UI of swing app (remove Region object and clear other MapObject) use indexes to show results
// TODO max 87
// TODO max 94
// ! 81. Add some objects to POI category (1) to add them into OSM 2) to help navigation)
// highway (?), traffic_calming (?), barrier(?), military(?-), landuse (?), office(?), man_made(?), power(?),
// railway( station, subway?) - issue 17
@ -19,7 +19,7 @@ public class ToDoConstants {
// 89. Transport redesign UI (enable run from context menu, switch go to goal/not) !
// 90. Use Junidecode library on the client for fast english translation
// 94. Implement navigate using selected gpx track
// 91. Invent binary format (minimize disk space, maximize speed)
// 92. Replace poi index with standard map index and unify POI categories
@ -49,16 +49,9 @@ public class ToDoConstants {
// 19. colors for road trunk and motorway
// 12. Fix : find proper location for streets ! centralize them (when create index)?
// TODO Check
// 1. check postal_code if the building was registered by relation!
// 2. TEST after refactoring : poi custom filters
// 8. Download with wget
// 9. progress while map is loading
// Unscheduled (complex)
// 65. Intermediate points - for better control routing, to avoid traffic jams ...(?)
// 40. Support simple vector road rendering (require new index file) (?)
// 63. Support simple offline routing(require new index file) (?)
// Not clear if it is really needed
@ -68,18 +61,8 @@ public class ToDoConstants {
// 83. Add monitoring service to send locations to internet (?)
// DONE ANDROID :
// 70. Show building numbers over map (require changing address index - index 2 more columns lat/lon for fast search).
// (Not needed, because of vector rendering)
// 82. Rotate map according compass
// 85. Remove context menu on long press map ! Accumulate actions and show label (+)
// 88. Implement show gpx track from folder
// DONE SWING
// 10. Improve address indexing (use relations). (+)
// use relation "a6" (to accumulate streets!), "a3" to read all cities & define boundaries for city (& define that street in city).
// ! 9. Fix issues with big files (such as netherlands) - save memory (!)
// Current result : for big file (1 - task 60-80% time, 90% memory) (?) (+)
// 11. Index buildings using interpolations (from nodes) (+)
}

View file

@ -7,7 +7,7 @@
<item android:id="@+id/map_layers" android:title="@string/menu_layers" android:icon="@android:drawable/ic_menu_mapmode"></item>
<item android:id="@+id/map_show_settings" android:title="@string/settings_Button" android:icon="@android:drawable/ic_menu_preferences"></item>
<!-- not visible currently -->
<!-- not visible by default -->
<item android:id="@+id/map_navigate_to_point" android:title="@string/stop_navigation" android:visible="false" android:icon="@android:drawable/ic_menu_close_clear_cancel"></item>
<item android:id="@+id/map_mute" android:title="@string/menu_mute_off" android:visible="false"></item>
@ -16,6 +16,7 @@
<item android:id="@+id/map_transport" android:title="@string/transport"></item>
<item android:title="@string/show_gps_status" android:id="@+id/map_show_gps_status" android:icon="@android:drawable/ic_menu_compass"></item>
<item android:title="@string/map_route_by_gpx" android:id="@+id/map_gpx_routing"></item>
</group>

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="gpx_reverse_route">Обратный путь</string>
<string name="gpx_direct_route">Прямой путь</string>
<string name="map_route_by_gpx">Навигация по GPX</string>
<string name="gpx_files_not_found">GPX файлы были не найдены в /osmand/tracks директории</string>
<string name="layer_gpx_layer">GPX треки</string>
<string name="error_reading_gpx">Ошибка при чтении gpx файла</string>

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="gpx_reverse_route">Reverse route</string>
<string name="gpx_direct_route">Direct route</string>
<string name="map_route_by_gpx">Route using GPX</string>
<string name="gpx_files_not_found">GPX files were not found in /osmand/tracks directory</string>
<string name="layer_gpx_layer">GPX tracks</string>
<string name="error_reading_gpx">Error reading gpx data</string>

View file

@ -0,0 +1,222 @@
package net.osmand;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import android.content.Context;
import android.location.Location;
import android.util.Xml;
import android.widget.Toast;
public class GPXUtilities {
public final static Log log = LogUtil.getLog(GPXUtilities.class);
public final static String GPX_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; //$NON-NLS-1$
public static class TrkPt {
public double lat;
public double lon;
public double ele;
public double speed;
public long time;
}
public static class WptPt {
public double lat;
public double lon;
public String name;
}
public static boolean saveToXMLFiles(File fout, List<WptPt> data, Context ctx ){
try {
FileOutputStream output = new FileOutputStream(fout);
XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(output, "UTF-8"); //$NON-NLS-1$
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); //$NON-NLS-1$
serializer.startDocument("UTF-8", true); //$NON-NLS-1$
serializer.startTag(null, "gpx"); //$NON-NLS-1$
serializer.attribute(null, "version", "1.1"); //$NON-NLS-1$ //$NON-NLS-2$
serializer.attribute(null, "creator", Version.APP_NAME_VERSION); //$NON-NLS-1$
serializer.attribute("xmlns", "xsi", "http://www.w3.org/2001/XMLSchema-instance"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
serializer.attribute("xsi", "schemaLocation", "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
serializer.attribute(null, "xmlns", "http://www.topografix.com/GPX/1/1"); //$NON-NLS-1$ //$NON-NLS-2$
for (WptPt l : data) {
serializer.startTag(null, "wpt"); //$NON-NLS-1$
serializer.attribute(null, "lat", l.lat + ""); //$NON-NLS-1$ //$NON-NLS-2$
serializer.attribute(null, "lon", l.lon + ""); //$NON-NLS-1$ //$NON-NLS-2$
serializer.startTag(null, "name"); //$NON-NLS-1$
serializer.text(l.name);
serializer.endTag(null, "name"); //$NON-NLS-1$
serializer.endTag(null, "wpt"); //$NON-NLS-1$
}
serializer.endTag(null, "gpx"); //$NON-NLS-1$
serializer.flush();
serializer.endDocument();
return true;
} catch (RuntimeException e) {
log.error("Error saving gpx", e); //$NON-NLS-1$
Toast.makeText(ctx, ctx.getString(R.string.error_occurred_saving_gpx), Toast.LENGTH_LONG).show();
return false;
} catch (IOException e) {
log.error("Error saving gpx", e); //$NON-NLS-1$
Toast.makeText(ctx, ctx.getString(R.string.error_occurred_saving_gpx), Toast.LENGTH_LONG).show();
return false;
}
}
public static String saveToXMLFiles(File dir, Map<String, List<List<TrkPt>>> data, Context ctx){
SimpleDateFormat format = new SimpleDateFormat(GPX_TIME_FORMAT);
try {
for (String f : data.keySet()) {
File fout = new File(dir, f + ".gpx"); //$NON-NLS-1$
int ind = 1;
while(fout.exists()){
fout = new File(dir, f + "_"+(++ind)+".gpx"); //$NON-NLS-1$ //$NON-NLS-2$
}
FileOutputStream output = new FileOutputStream(fout);
XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(output, "UTF-8"); //$NON-NLS-1$
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); //$NON-NLS-1$
serializer.startDocument("UTF-8", true); //$NON-NLS-1$
serializer.startTag(null, "gpx"); //$NON-NLS-1$
serializer.attribute(null, "version", "1.1"); //$NON-NLS-1$ //$NON-NLS-2$
serializer.attribute(null, "creator", Version.APP_NAME_VERSION); //$NON-NLS-1$
serializer.attribute("xmlns", "xsi", "http://www.w3.org/2001/XMLSchema-instance"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
serializer.attribute("xsi", "schemaLocation", "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
serializer.attribute(null, "xmlns", "http://www.topografix.com/GPX/1/1"); //$NON-NLS-1$ //$NON-NLS-2$
serializer.startTag(null, "trk"); //$NON-NLS-1$
for(List<TrkPt> l : data.get(f)){
serializer.startTag(null, "trkseg"); //$NON-NLS-1$
for(TrkPt p : l){
serializer.startTag(null, "trkpt"); //$NON-NLS-1$
serializer.attribute(null, "lat", p.lat+""); //$NON-NLS-1$ //$NON-NLS-2$
serializer.attribute(null, "lon", p.lon+""); //$NON-NLS-1$ //$NON-NLS-2$
serializer.startTag(null, "time"); //$NON-NLS-1$
serializer.text(format.format(new Date(p.time)));
serializer.endTag(null, "time"); //$NON-NLS-1$
serializer.startTag(null, "ele"); //$NON-NLS-1$
serializer.text(p.ele+""); //$NON-NLS-1$
serializer.endTag(null, "ele"); //$NON-NLS-1$
if (p.speed > 0) {
serializer.startTag(null, "speed"); //$NON-NLS-1$
serializer.text(p.speed + ""); //$NON-NLS-1$
serializer.endTag(null, "speed"); //$NON-NLS-1$
}
serializer.endTag(null, "trkpt"); //$NON-NLS-1$
}
serializer.endTag(null, "trkseg"); //$NON-NLS-1$
}
serializer.endTag(null, "trk"); //$NON-NLS-1$
serializer.endTag(null, "gpx"); //$NON-NLS-1$
serializer.flush();
serializer.endDocument();
}
return null;
} catch (RuntimeException e) {
log.error("Error saving gpx", e); //$NON-NLS-1$
return ctx.getString(R.string.error_occurred_saving_gpx);
} catch (IOException e) {
log.error("Error saving gpx", e); //$NON-NLS-1$
return ctx.getString(R.string.error_occurred_saving_gpx);
}
}
public static class GPXFileResult {
public ArrayList<List<Location>> locations = new ArrayList<List<Location>>();
public ArrayList<WptPt> wayPoints = new ArrayList<WptPt>();
// special case for cloudmate gpx : they discourage common schema
// by using waypoint as track points and rtept are not very close to real way
// such as wpt. However they provide additional information into gpx.
public boolean cloudMadeFile;
public String error;
}
public static GPXFileResult loadGPXFile(Context ctx, File f){
GPXFileResult res = new GPXFileResult();
try {
boolean cloudMade = false;
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$
int tok;
Location current = null;
String currentName = ""; //$NON-NLS-1$
while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (tok == XmlPullParser.START_TAG) {
if (parser.getName().equals("copyright")) { //$NON-NLS-1$
cloudMade |= "cloudmade".equalsIgnoreCase(parser.getAttributeValue("", "author")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} else if (parser.getName().equals("trkseg")) { //$NON-NLS-1$
res.locations.add(new ArrayList<Location>());
} else if (parser.getName().equals("wpt") || parser.getName().equals("trkpt") || //$NON-NLS-1$//$NON-NLS-2$
(!cloudMade && parser.getName().equals("rtept"))) { //$NON-NLS-1$
// currently not distinguish different point represents all as a line
try {
currentName = ""; //$NON-NLS-1$
current = new Location("gpx_file"); //$NON-NLS-1$
current.setLatitude(Double.parseDouble(parser.getAttributeValue("", "lat"))); //$NON-NLS-1$ //$NON-NLS-2$
current.setLongitude(Double.parseDouble(parser.getAttributeValue("", "lon"))); //$NON-NLS-1$ //$NON-NLS-2$
} catch (NumberFormatException e) {
current = null;
}
} else if (current != null && parser.getName().equals("name")) { //$NON-NLS-1$
if (parser.next() == XmlPullParser.TEXT) {
currentName = parser.getText();
}
}
} else if (tok == XmlPullParser.END_TAG) {
if (parser.getName().equals("wpt") || //$NON-NLS-1$
parser.getName().equals("trkpt") || (!cloudMade && parser.getName().equals("rtept"))) { //$NON-NLS-1$ //$NON-NLS-2$
if (current != null) {
if (parser.getName().equals("wpt") && !cloudMade) { //$NON-NLS-1$
WptPt pt = new WptPt();
pt.lat = current.getLatitude();
pt.lon = current.getLongitude();
pt.name = currentName;
res.wayPoints.add(pt);
} else {
if (res.locations.isEmpty()) {
res.locations.add(new ArrayList<Location>());
}
res.locations.get(res.locations.size() - 1).add(current);
}
}
}
}
}
} catch (XmlPullParserException e) {
log.error("Error reading gpx", e); //$NON-NLS-1$
res.error = ctx.getString(R.string.error_reading_gpx);
} catch (IOException e) {
log.error("Error reading gpx", e); //$NON-NLS-1$
res.error = ctx.getString(R.string.error_reading_gpx);
}
return res;
}
}

View file

@ -10,10 +10,12 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import net.osmand.GPXUtilities;
import net.osmand.OsmandSettings;
import net.osmand.R;
import net.osmand.ResourceManager;
import net.osmand.activities.SavingTrackHelper.WptPt;
import net.osmand.GPXUtilities.GPXFileResult;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.osm.LatLon;
import net.osmand.osm.MapUtils;
import android.app.AlertDialog;
@ -179,7 +181,7 @@ public class FavouritesActivity extends ListActivity {
pt.name = p.name;
wpt.add(pt);
}
if(SavingTrackHelper.saveToXMLFiles(f, wpt, this)){
if(GPXUtilities.saveToXMLFiles(f, wpt, this)){
Toast.makeText(this, MessageFormat.format(getString(R.string.fav_saved_sucessfully), f.getAbsolutePath()),
Toast.LENGTH_LONG).show();
}
@ -196,9 +198,9 @@ public class FavouritesActivity extends ListActivity {
existedPoints.add(fp.name);
}
}
List<WptPt> points = new ArrayList<WptPt>();
if(SavingTrackHelper.readWptPtFromFile(f, points, this)){
for(WptPt p : points){
GPXFileResult res = GPXUtilities.loadGPXFile(this, f);
if (res.error == null) {
for(WptPt p : res.wayPoints){
if(!existedPoints.contains(p.name)){
FavouritePoint fp = new FavouritePoint();
fp.name = p.name;

View file

@ -4,12 +4,14 @@ import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import net.osmand.Algoritms;
import net.osmand.AmenityIndexRepository;
import net.osmand.GPXUtilities;
import net.osmand.LogUtil;
import net.osmand.OsmandSettings;
import net.osmand.PoiFilter;
@ -18,6 +20,8 @@ import net.osmand.R;
import net.osmand.ResourceManager;
import net.osmand.SQLiteTileSource;
import net.osmand.Version;
import net.osmand.GPXUtilities.GPXFileResult;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.OsmandSettings.ApplicationMode;
import net.osmand.activities.FavouritesActivity.FavouritePoint;
import net.osmand.activities.FavouritesActivity.FavouritesDbHelper;
@ -80,6 +84,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
@ -258,7 +263,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
LatLon pointToNavigate = OsmandSettings.getPointToNavigate(this);
// TODO how this situation could be ?
if(!Algoritms.objectEquals(routingHelper.getFinalLocation(), pointToNavigate)){
// there is no way how to clear mode. Only user can do : clear point to navigate, exit from app & set up new point.
// that case help to not calculate route at all.
@ -527,7 +532,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
} else {
OsmandSettings.clearPointToNavigate(this);
}
routingHelper.setFinalAndCurrentLocation(point, null);
routingHelper.setFinalAndCurrentLocation(point, null, routingHelper.getCurrentGPXRoute());
if(point == null){
routingHelper.setFollowingMode(false);
OsmandSettings.setFollowingByRoute(MapActivity.this, false);
@ -966,6 +971,9 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
navigateToPoint(new LatLon(mapView.getLatitude(), mapView.getLongitude()));
}
mapView.refreshMap();
} else if (item.getItemId() == R.id.map_gpx_routing) {
useGPXFileLayer(true, navigationLayer.getPointToNavigate());
return true;
}
return super.onOptionsItemSelected(item);
}
@ -979,6 +987,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
return OsmandSettings.getApplicationMode(this);
}
protected void getDirections(final double lat, final double lon, boolean followEnabled){
if(navigationLayer.getPointToNavigate() == null){
Toast.makeText(this, R.string.mark_final_location_first, Toast.LENGTH_LONG).show();
@ -1037,6 +1046,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
map.setLatitude(lat);
map.setLongitude(lon);
routingHelper.setAppMode(mode);
OsmandSettings.setFollowingByRoute(MapActivity.this, false);
routingHelper.setFollowingMode(false);
routingHelper.setFinalAndCurrentLocation(navigationLayer.getPointToNavigate(), map);
}
@ -1063,9 +1073,10 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
location.setLongitude(lon);
}
routingHelper.setAppMode(mode);
OsmandSettings.setFollowingByRoute(MapActivity.this, true);
routingHelper.setFollowingMode(true);
routingHelper.setFinalAndCurrentLocation(navigationLayer.getPointToNavigate(), location);
OsmandSettings.setFollowingByRoute(MapActivity.this, true);
}
};
DialogInterface.OnClickListener showRoute = new DialogInterface.OnClickListener(){
@ -1248,7 +1259,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
gpxLayer.clearCurrentGPX();
} else {
dialog.dismiss();
selectGPXFileLayer();
useGPXFileLayer(false, null);
}
} else if(item == routeInfoInd){
routeInfoLayer.setVisible(isChecked);
@ -1264,7 +1275,7 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
builder.show();
}
private void selectGPXFileLayer() {
private void useGPXFileLayer(final boolean useRouting, final LatLon endForRouting) {
final List<String> list = new ArrayList<String>();
final File dir = new File(Environment.getExternalStorageDirectory(), ResourceManager.APP_DIR + SavingTrackHelper.TRACKS_PATH);
if (dir != null && dir.canRead()) {
@ -1306,21 +1317,43 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
new Thread(new Runnable() {
@Override
public void run() {
final String error = gpxLayer.showGPXFile(f);
if (error != null) {
Looper.prepare();
final GPXFileResult res = GPXUtilities.loadGPXFile(MapActivity.this, f);
if (res.error != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MapActivity.this, error, Toast.LENGTH_LONG).show();
Toast.makeText(MapActivity.this, res.error, Toast.LENGTH_LONG).show();
}
});
}
dlg.dismiss();
if(useRouting){
runOnUiThread(new Runnable() {
@Override
public void run() {
useGPXRouting(endForRouting, res);
}
});
} else {
OsmandSettings.setShowingFavorites(MapActivity.this, true);
List<FavouritePoint> pts = new ArrayList<FavouritePoint>();
for(WptPt p : res.wayPoints){
FavouritePoint pt = new FavouritePoint();
pt.setLatitude(p.lat);
pt.setLongitude(p.lon);
pt.setName(p.name);
pts.add(pt);
}
favoritesLayer.setAdditionalPoints(pts);
gpxLayer.setTracks(res.locations);
}
mapView.refreshMap();
}
}, "Loading gpx").start(); //$NON-NLS-1$
}
@ -1329,6 +1362,45 @@ public class MapActivity extends Activity implements IMapLocationListener, Senso
}
}
private void useGPXRouting(final LatLon endForRouting, final GPXFileResult res) {
Builder builder = new AlertDialog.Builder(this);
builder.setItems(new String[]{getString(R.string.gpx_direct_route), getString(R.string.gpx_reverse_route)},
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
boolean reverse = which == 1;
ArrayList<List<Location>> locations = res.locations;
List<Location> l = new ArrayList<Location>();
for(List<Location> s : locations){
l.addAll(s);
}
if(reverse){
Collections.reverse(l);
}
Location startForRouting = locationLayer.getLastKnownLocation();
if(startForRouting == null && !l.isEmpty()){
startForRouting = l.get(0);
}
LatLon endPoint = endForRouting;
if(/*endForRouting == null && */!l.isEmpty()){
LatLon point = new LatLon(l.get(l.size() - 1).getLatitude(), l.get(l.size() - 1).getLongitude());
OsmandSettings.setPointToNavigate(MapActivity.this, point.getLatitude(), point.getLongitude());
endPoint = point;
navigationLayer.setPointToNavigate(point);
}
if(endForRouting != null){
OsmandSettings.setFollowingByRoute(MapActivity.this, true);
routingHelper.setFollowingMode(true);
routingHelper.setFinalAndCurrentLocation(endPoint, startForRouting, l);
}
}
});
builder.show();
}
private void selectPOIFilterLayer(){
final List<PoiFilter> userDefined = new ArrayList<PoiFilter>();

View file

@ -160,7 +160,8 @@ public class RouteProvider {
}
public RouteCalculationResult calculateRouteImpl(Location start, LatLon end, ApplicationMode mode, RouteService type, Context ctx){
public RouteCalculationResult calculateRouteImpl(Location start, LatLon end, ApplicationMode mode, RouteService type, Context ctx,
List<Location> gpxRoute){
long time = System.currentTimeMillis();
if (start != null && end != null) {
if(log.isInfoEnabled()){
@ -168,7 +169,36 @@ public class RouteProvider {
}
try {
RouteCalculationResult res;
if (type == RouteService.YOURS) {
if(gpxRoute != null && !gpxRoute.isEmpty()){
// get the closest point to start and to end
float minDist = Integer.MAX_VALUE;
int startI = 0;
int endI = gpxRoute.size();
if (start != null) {
for (int i = 0; i < gpxRoute.size(); i++) {
float d = gpxRoute.get(i).distanceTo(start);
if (d < minDist) {
startI = i;
minDist = d;
}
}
} else {
start = gpxRoute.get(0);
}
Location l = new Location("temp"); //$NON-NLS-1$
l.setLatitude(end.getLatitude());
l.setLongitude(end.getLongitude());
minDist = Integer.MAX_VALUE;
for (int i = 0; i < gpxRoute.size(); i++) {
float d = gpxRoute.get(i).distanceTo(l);
if (d < minDist) {
endI = i + 1;
minDist = d;
}
}
res = new RouteCalculationResult(gpxRoute.subList(startI, endI), null, start, end, null);
addMissingTurnsToRoute(res, start, end, mode, ctx);
} else if (type == RouteService.YOURS) {
res = findYOURSRoute(start, end, mode);
addMissingTurnsToRoute(res, start, end, mode, ctx);
} else {

View file

@ -40,6 +40,7 @@ public class RoutingHelper {
private boolean isFollowingMode = false;
private List<Location> currentGPXRoute = null;
// instead of this properties RouteCalculationResult could be used
private List<Location> routeNodes = new ArrayList<Location>();
private List<RouteDirectionInfo> directionInfo = null;
@ -93,12 +94,18 @@ public class RoutingHelper {
this.isFollowingMode = isFollowingMode;
}
public synchronized void setFinalAndCurrentLocation(LatLon finalLocation, Location currentLocation){
public void setFinalAndCurrentLocation(LatLon finalLocation, Location currentLocation){
setFinalAndCurrentLocation(finalLocation, currentLocation, null);
}
public synchronized void setFinalAndCurrentLocation(LatLon finalLocation, Location currentLocation, List<Location> gpxRoute){
this.finalLocation = finalLocation;
this.routeNodes.clear();
listDistance = null;
directionInfo = null;
evalWaitInterval = 3000;
currentGPXRoute = gpxRoute;
for(IRouteInformationListener l : listeners){
l.routeWasCancelled();
}
@ -107,6 +114,10 @@ public class RoutingHelper {
}
public List<Location> getCurrentGPXRoute() {
return currentGPXRoute;
}
public void setFinalLocation(LatLon finalLocation){
setFinalAndCurrentLocation(finalLocation, getCurrentLocation());
}
@ -326,7 +337,7 @@ public class RoutingHelper {
lastFixedLocation = currentLocation;
if(calculateRoute){
calculateRoute(lastFixedLocation, finalLocation);
calculateRoute(lastFixedLocation, finalLocation, currentGPXRoute);
}
}
@ -422,7 +433,7 @@ public class RoutingHelper {
return 0;
}
public void calculateRoute(final Location start, final LatLon end){
public void calculateRoute(final Location start, final LatLon end, final List<Location> currentGPXRoute){
final RouteService service = OsmandSettings.getRouterService(context);
if(currentRunningJob == null){
// do not evaluate very often
@ -431,7 +442,7 @@ public class RoutingHelper {
currentRunningJob = new Thread(new Runnable() {
@Override
public void run() {
RouteCalculationResult res = provider.calculateRouteImpl(start, end, mode, service, context);
RouteCalculationResult res = provider.calculateRouteImpl(start, end, mode, service, context, currentGPXRoute);
synchronized (RoutingHelper.this) {
if (res.isCalculated()) {
setNewRoute(res);
@ -439,30 +450,31 @@ public class RoutingHelper {
evalWaitInterval = 3000;
} else {
evalWaitInterval = evalWaitInterval * 4 / 3;
if(evalWaitInterval > 120000){
evalWaitInterval = 120000;
if (evalWaitInterval > 120000) {
evalWaitInterval = 120000;
}
}
currentRunningJob = null;
}
if (res.isCalculated()) {
int[] dist = res.getListDistance();
int l = dist != null && dist.length > 0 ? dist[0] : 0;
showMessage(context.getString(R.string.new_route_calculated_dist) +" : "+ MapUtils.getFormattedDistance(l)); //$NON-NLS-1$
if (uiActivity instanceof MapActivity) {
// be aware that is non ui thread
((MapActivity) uiActivity).getMapView().refreshMap();
}
} else {
if (res.getErrorMessage() != null) {
showMessage(context.getString(R.string.error_calculating_route)+" : " + res.getErrorMessage()); //$NON-NLS-1$
} else if (res.getLocations() == null) {
showMessage(context.getString(R.string.error_calculating_route_occured));
} else {
showMessage(context.getString(R.string.empty_route_calculated));
}
if (res.isCalculated()) {
int[] dist = res.getListDistance();
int l = dist != null && dist.length > 0 ? dist[0] : 0;
showMessage(context.getString(R.string.new_route_calculated_dist)
+ " : " + MapUtils.getFormattedDistance(l)); //$NON-NLS-1$
if (uiActivity instanceof MapActivity) {
// be aware that is non ui thread
((MapActivity) uiActivity).getMapView().refreshMap();
}
} else {
if (res.getErrorMessage() != null) {
showMessage(context.getString(R.string.error_calculating_route) + " : " + res.getErrorMessage()); //$NON-NLS-1$
} else if (res.getLocations() == null) {
showMessage(context.getString(R.string.error_calculating_route_occured));
} else {
showMessage(context.getString(R.string.empty_route_calculated));
}
}
lastTimeEvaluatedRoute = System.currentTimeMillis();
}
}, "Calculating route"); //$NON-NLS-1$

View file

@ -1,25 +1,17 @@
package net.osmand.activities;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import net.osmand.GPXUtilities;
import net.osmand.LogUtil;
import net.osmand.OsmandSettings;
import net.osmand.R;
import net.osmand.Version;
import net.osmand.GPXUtilities.TrkPt;
import org.apache.commons.logging.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import android.content.Context;
import android.database.Cursor;
@ -27,8 +19,6 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Environment;
import android.text.format.DateFormat;
import android.util.Xml;
import android.widget.Toast;
public class SavingTrackHelper extends SQLiteOpenHelper {
public final static String TRACKS_PATH = "tracks"; //$NON-NLS-1$
@ -45,7 +35,7 @@ public class SavingTrackHelper extends SQLiteOpenHelper {
public final static Log log = LogUtil.getLog(SavingTrackHelper.class);
public final static String GPX_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; //$NON-NLS-1$
private String updateScript;
@ -69,166 +59,9 @@ public class SavingTrackHelper extends SQLiteOpenHelper {
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public static class TrkPt {
public double lat;
public double lon;
public double ele;
public double speed;
public long time;
}
public static class WptPt {
public double lat;
public double lon;
public String name;
}
public static boolean readWptPtFromFile(File fout, List<WptPt> readTo, Context ctx){
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new FileInputStream(fout), "UTF-8"); //$NON-NLS-1$
int tok;
WptPt current = null;
while((tok=parser.next()) != XmlPullParser.END_DOCUMENT){
if(tok == XmlPullParser.START_TAG){
if(parser.getName().equals("wpt")){ //$NON-NLS-1$
try {
current = new WptPt();
current.lat = Double.parseDouble(parser.getAttributeValue("", "lat")); //$NON-NLS-1$ //$NON-NLS-2$
current.lon = Double.parseDouble(parser.getAttributeValue("", "lon")); //$NON-NLS-1$ //$NON-NLS-2$
} catch (NumberFormatException e) {
current= null;
}
} else if(current != null && parser.getName().equals("name")){ //$NON-NLS-1$
if(parser.next() == XmlPullParser.TEXT){
current.name = parser.getText();
}
}
} else if(tok == XmlPullParser.END_TAG){
if(parser.getName().equals("wpt")){ //$NON-NLS-1$
if(current != null && current.name != null){
readTo.add(current);
}
current = null;
}
}
}
return true;
} catch (IOException e) {
log.error("Error loading gpx", e); //$NON-NLS-1$
Toast.makeText(ctx, ctx.getString(R.string.error_occurred_loading_gpx), Toast.LENGTH_LONG).show();
return false;
} catch (XmlPullParserException e) {
log.error("Error loading gpx", e); //$NON-NLS-1$
Toast.makeText(ctx, ctx.getString(R.string.error_occurred_loading_gpx), Toast.LENGTH_LONG).show();
return false;
}
}
public static boolean saveToXMLFiles(File fout, List<WptPt> data, Context ctx ){
try {
FileOutputStream output = new FileOutputStream(fout);
XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(output, "UTF-8"); //$NON-NLS-1$
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); //$NON-NLS-1$
serializer.startDocument("UTF-8", true); //$NON-NLS-1$
serializer.startTag(null, "gpx"); //$NON-NLS-1$
serializer.attribute(null, "version", "1.1"); //$NON-NLS-1$ //$NON-NLS-2$
serializer.attribute(null, "creator", Version.APP_NAME_VERSION); //$NON-NLS-1$
serializer.attribute("xmlns", "xsi", "http://www.w3.org/2001/XMLSchema-instance"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
serializer.attribute("xsi", "schemaLocation", "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
serializer.attribute(null, "xmlns", "http://www.topografix.com/GPX/1/1"); //$NON-NLS-1$ //$NON-NLS-2$
for (WptPt l : data) {
serializer.startTag(null, "wpt"); //$NON-NLS-1$
serializer.attribute(null, "lat", l.lat + ""); //$NON-NLS-1$ //$NON-NLS-2$
serializer.attribute(null, "lon", l.lon + ""); //$NON-NLS-1$ //$NON-NLS-2$
serializer.startTag(null, "name"); //$NON-NLS-1$
serializer.text(l.name);
serializer.endTag(null, "name"); //$NON-NLS-1$
serializer.endTag(null, "wpt"); //$NON-NLS-1$
}
serializer.endTag(null, "gpx"); //$NON-NLS-1$
serializer.flush();
serializer.endDocument();
return true;
} catch (RuntimeException e) {
log.error("Error saving gpx", e); //$NON-NLS-1$
Toast.makeText(ctx, ctx.getString(R.string.error_occurred_saving_gpx), Toast.LENGTH_LONG).show();
return false;
} catch (IOException e) {
log.error("Error saving gpx", e); //$NON-NLS-1$
Toast.makeText(ctx, ctx.getString(R.string.error_occurred_saving_gpx), Toast.LENGTH_LONG).show();
return false;
}
}
public static String saveToXMLFiles(File dir, Map<String, List<List<TrkPt>>> data, Context ctx){
SimpleDateFormat format = new SimpleDateFormat(GPX_TIME_FORMAT);
try {
for (String f : data.keySet()) {
File fout = new File(dir, f + ".gpx"); //$NON-NLS-1$
int ind = 1;
while(fout.exists()){
fout = new File(dir, f + "_"+(++ind)+".gpx"); //$NON-NLS-1$ //$NON-NLS-2$
}
FileOutputStream output = new FileOutputStream(fout);
XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(output, "UTF-8"); //$NON-NLS-1$
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); //$NON-NLS-1$
serializer.startDocument("UTF-8", true); //$NON-NLS-1$
serializer.startTag(null, "gpx"); //$NON-NLS-1$
serializer.attribute(null, "version", "1.1"); //$NON-NLS-1$ //$NON-NLS-2$
serializer.attribute(null, "creator", Version.APP_NAME_VERSION); //$NON-NLS-1$
serializer.attribute("xmlns", "xsi", "http://www.w3.org/2001/XMLSchema-instance"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
serializer.attribute("xsi", "schemaLocation", "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
serializer.attribute(null, "xmlns", "http://www.topografix.com/GPX/1/1"); //$NON-NLS-1$ //$NON-NLS-2$
serializer.startTag(null, "trk"); //$NON-NLS-1$
for(List<TrkPt> l : data.get(f)){
serializer.startTag(null, "trkseg"); //$NON-NLS-1$
for(TrkPt p : l){
serializer.startTag(null, "trkpt"); //$NON-NLS-1$
serializer.attribute(null, "lat", p.lat+""); //$NON-NLS-1$ //$NON-NLS-2$
serializer.attribute(null, "lon", p.lon+""); //$NON-NLS-1$ //$NON-NLS-2$
serializer.startTag(null, "time"); //$NON-NLS-1$
serializer.text(format.format(new Date(p.time)));
serializer.endTag(null, "time"); //$NON-NLS-1$
serializer.startTag(null, "ele"); //$NON-NLS-1$
serializer.text(p.ele+""); //$NON-NLS-1$
serializer.endTag(null, "ele"); //$NON-NLS-1$
if (p.speed > 0) {
serializer.startTag(null, "speed"); //$NON-NLS-1$
serializer.text(p.speed + ""); //$NON-NLS-1$
serializer.endTag(null, "speed"); //$NON-NLS-1$
}
serializer.endTag(null, "trkpt"); //$NON-NLS-1$
}
serializer.endTag(null, "trkseg"); //$NON-NLS-1$
}
serializer.endTag(null, "trk"); //$NON-NLS-1$
serializer.endTag(null, "gpx"); //$NON-NLS-1$
serializer.flush();
serializer.endDocument();
}
return null;
} catch (RuntimeException e) {
log.error("Error saving gpx", e); //$NON-NLS-1$
return ctx.getString(R.string.error_occurred_saving_gpx);
} catch (IOException e) {
log.error("Error saving gpx", e); //$NON-NLS-1$
return ctx.getString(R.string.error_occurred_saving_gpx);
}
}
public boolean hasDataToSave(){
SQLiteDatabase db = getReadableDatabase();
if(db != null){
@ -295,7 +128,7 @@ public class SavingTrackHelper extends SQLiteOpenHelper {
} while (query.moveToNext());
}
query.close();
String w = saveToXMLFiles(file, data, ctx);
String w = GPXUtilities.saveToXMLFiles(file, data, ctx);
if(w != null){
warnings.add(w);
}

View file

@ -30,6 +30,7 @@ public class FavoritesLayer implements OsmandMapLayer, ContextMenuLayer.IContext
private OsmandMapTileView view;
private List<FavouritePoint> favouritePoints;
private List<FavouritePoint> additionalPoints;
private Rect pixRect = new Rect();
private RectF tileRect = new RectF();
private Path path;
@ -84,6 +85,10 @@ public class FavoritesLayer implements OsmandMapLayer, ContextMenuLayer.IContext
}
public void setAdditionalPoints(List<FavouritePoint> additionalPoints) {
this.additionalPoints = additionalPoints;
}
public void reloadFavorites(Context ctx){
FavouritesDbHelper helper = new FavouritesActivity.FavouritesDbHelper(ctx);
favouritePoints = helper.getFavouritePoints();
@ -118,6 +123,19 @@ public class FavoritesLayer implements OsmandMapLayer, ContextMenuLayer.IContext
canvas.drawPath(pathDst, paintBlack);
}
}
if(additionalPoints != null){
for (FavouritePoint o : additionalPoints) {
if (o.getLatitude() <= topLatitude && o.getLatitude() >= bottomLatitude && o.getLongitude() >= leftLongitude
&& o.getLongitude() <= rightLongitude) {
int x = view.getMapXForPoint(o.getLongitude());
int y = view.getMapYForPoint(o.getLatitude());
matrix.setTranslate(x, y);
path.transform(matrix, pathDst);
canvas.drawPath(pathDst, paint);
canvas.drawPath(pathDst, paintBlack);
}
}
}
}
}
@ -143,6 +161,19 @@ public class FavoritesLayer implements OsmandMapLayer, ContextMenuLayer.IContext
}
}
}
if (additionalPoints != null) {
int ex = (int) point.x;
int ey = (int) point.y;
for (int i = 0; i < additionalPoints.size(); i++) {
FavouritePoint n = additionalPoints.get(i);
int x = view.getRotatedMapXForPoint(n.getLatitude(), n.getLongitude());
int y = view.getRotatedMapYForPoint(n.getLatitude(), n.getLongitude());
if (Math.abs(x - ex) <= r && Math.abs(y - ey) <= r) {
r = Math.max(Math.abs(x - ex), Math.abs(y - ey));
result = n;
}
}
}
return result;
}

View file

@ -1,19 +1,9 @@
package net.osmand.views;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import net.osmand.LogUtil;
import net.osmand.R;
import net.osmand.osm.MapUtils;
import org.apache.commons.logging.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@ -25,18 +15,17 @@ import android.graphics.Paint.Cap;
import android.graphics.Paint.Join;
import android.graphics.Paint.Style;
import android.location.Location;
import android.util.Xml;
public class GPXLayer implements OsmandMapLayer {
private final static Log log = LogUtil.getLog(GPXLayer.class);
private OsmandMapTileView view;
private Rect boundsRect;
private RectF tileRect;
private List<Location> points = new ArrayList<Location>();
private List<List<Location>> points = new ArrayList<List<Location>>();
private Paint paint;
private Paint paintPoint;
private Path path;
@ -55,6 +44,11 @@ public class GPXLayer implements OsmandMapLayer {
paint.setAntiAlias(true);
paint.setStrokeCap(Cap.ROUND);
paint.setStrokeJoin(Join.ROUND);
paintPoint = new Paint();
paintPoint.setColor(Color.argb(190, 160, 10, 215));
paintPoint.setStyle(Style.FILL_AND_STROKE);
path = new Path();
}
@ -67,51 +61,59 @@ public class GPXLayer implements OsmandMapLayer {
@Override
public void onDraw(Canvas canvas) {
path.reset();
if(points.isEmpty()){
return;
}
int w = view.getWidth();
int h = view.getHeight();
boundsRect = new Rect(-w / 2, -h / 2, 3 * w / 2, 3 * h / 2);
boundsRect = new Rect(0, 0, w, h);
view.calculateTileRectangle(boundsRect, view.getCenterPointX(), view.getCenterPointY(), view.getXTile(), view.getYTile(),
tileRect);
double topLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.top);
double leftLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.left);
double bottomLatitude = MapUtils.getLatitudeFromTile(view.getZoom(), tileRect.bottom);
double rightLongitude = MapUtils.getLongitudeFromTile(view.getZoom(), tileRect.right);
int startIndex = -1;
int endIndex = -1;
for (int i = 0; i < points.size(); i++) {
Location ls = points.get(i);
if(leftLongitude <= ls.getLongitude() && ls.getLongitude() <= rightLongitude &&
bottomLatitude <= ls.getLatitude() && ls.getLatitude() <= topLatitude){
if(startIndex == -1){
startIndex = i > 0 ? i - 1 : i;
for (List<Location> l : points) {
path.rewind();
int startIndex = -1;
int endIndex = -1;
for (int i = 0; i < l.size(); i++) {
Location ls = l.get(i);
if (startIndex == -1) {
if (leftLongitude <= ls.getLongitude() && ls.getLongitude() <= rightLongitude && bottomLatitude <= ls.getLatitude()
&& ls.getLatitude() <= topLatitude) {
startIndex = i > 0 ? i - 1 : i;
}
} else if (!(leftLongitude <= ls.getLongitude() + 0.01 && ls.getLongitude() - 0.01 <= rightLongitude
&& bottomLatitude <= ls.getLatitude() + 0.01 && ls.getLatitude() - 0.01 <= topLatitude)) {
endIndex = i;
// do not continue make method more efficient (because it calls in UI thread)
// this break also has logical sense !
break;
}
} else if(startIndex > 0){
endIndex = i;
// do not continue make method more efficient (because it calls in UI thread)
// this break also has logical sense !
break;
}
}
if(startIndex == -1){
return;
} else if(endIndex == -1){
endIndex = points.size() - 1;
if (startIndex == -1) {
return;
}
if (endIndex == -1) {
endIndex = l.size() - 1;
}
int px = view.getMapXForPoint(l.get(startIndex).getLongitude());
int py = view.getMapYForPoint(l.get(startIndex).getLatitude());
path.moveTo(px, py);
for (int i = startIndex + 1; i <= endIndex; i++) {
Location p = l.get(i);
int x = view.getMapXForPoint(p.getLongitude());
int y = view.getMapYForPoint(p.getLatitude());
path.lineTo(x, y);
}
canvas.drawPath(path, paint);
}
int px = view.getMapXForPoint(points.get(startIndex).getLongitude());
int py = view.getMapYForPoint(points.get(startIndex).getLatitude());
path.moveTo(px, py);
for (int i = startIndex + 1; i <= endIndex; i++) {
Location o = points.get(i);
int x = view.getMapXForPoint(o.getLongitude());
int y = view.getMapYForPoint(o.getLatitude());
path.lineTo(x, y);
}
canvas.drawPath(path, paint);
}
@ -119,51 +121,18 @@ public class GPXLayer implements OsmandMapLayer {
return !points.isEmpty();
}
public String showGPXFile(File f){
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$
ArrayList<Location> locations = new ArrayList<Location>();
int tok;
Location current = null;
while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) {
if(tok == XmlPullParser.START_TAG){
// currently not distinguish different point represents all as a line
if(parser.getName().equals("wpt") || parser.getName().equals("trkpt") /*|| parser.getName().equals("rtept")*/){ //$NON-NLS-1$ //$NON-NLS-2$
try {
current = new Location("gpx_file"); //$NON-NLS-1$
current.setLatitude(Double.parseDouble(parser.getAttributeValue("", "lat"))); //$NON-NLS-1$ //$NON-NLS-2$
current.setLongitude(Double.parseDouble(parser.getAttributeValue("", "lon"))); //$NON-NLS-1$ //$NON-NLS-2$
} catch (NumberFormatException e) {
current= null;
}
}
} else if(tok == XmlPullParser.END_TAG){
if(parser.getName().equals("wpt") || //$NON-NLS-1$
parser.getName().equals("trkpt") /*|| parser.getName().equals("rtept")*/){ //$NON-NLS-1$
if(current != null){
locations.add(current);
}
}
}
}
this.points = locations;
} catch (XmlPullParserException e) {
log.error("Error reading gpx", e); //$NON-NLS-1$
return view.getContext().getString(R.string.error_reading_gpx);
} catch (IOException e) {
log.error("Error reading gpx", e); //$NON-NLS-1$
return view.getContext().getString(R.string.error_reading_gpx);
}
return null;
}
public void clearCurrentGPX(){
points.clear();
}
public void setTracks(List<List<Location>> tracks){
if(tracks == null){
clearCurrentGPX();
} else {
points = tracks;
}
}
@Override

View file

@ -31,7 +31,6 @@ import alice.tuprolog.Var;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Environment;
import android.view.WindowManager;
public class CommandPlayer {