Merge pull request #10967 from osmandapp/GpxOnlineRouting

Implement GPX online routing
This commit is contained in:
Vitaliy 2021-03-01 13:54:05 +02:00 committed by GitHub
commit b6bcf9033b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 447 additions and 179 deletions

View file

@ -53,6 +53,8 @@ public class Algorithms {
public static final int XML_FILE_SIGNATURE = 0x3c3f786d; public static final int XML_FILE_SIGNATURE = 0x3c3f786d;
public static final int OBF_FILE_SIGNATURE = 0x08029001; public static final int OBF_FILE_SIGNATURE = 0x08029001;
public static final int SQLITE_FILE_SIGNATURE = 0x53514C69; public static final int SQLITE_FILE_SIGNATURE = 0x53514C69;
public static final int BZIP_FILE_SIGNATURE = 0x425a;
public static final int GZIP_FILE_SIGNATURE = 0x1f8b;
public static String normalizeSearchText(String s) { public static String normalizeSearchText(String s) {
boolean norm = false; boolean norm = false;
@ -322,6 +324,24 @@ public class Algorithms {
return test == ZIP_FILE_SIGNATURE; return test == ZIP_FILE_SIGNATURE;
} }
public static boolean checkFileSignature(InputStream inputStream, int fileSignature) throws IOException {
if (inputStream == null) return false;
int firstBytes;
if (isSmallFileSignature(fileSignature)) {
firstBytes = readSmallInt(inputStream);
} else {
firstBytes = readInt(inputStream);
}
if (inputStream.markSupported()) {
inputStream.reset();
}
return firstBytes == fileSignature;
}
public static boolean isSmallFileSignature(int fileSignature) {
return fileSignature == BZIP_FILE_SIGNATURE || fileSignature == GZIP_FILE_SIGNATURE;
}
/** /**
* Checks, whether the child directory is a subdirectory of the parent * Checks, whether the child directory is a subdirectory of the parent
* directory. * directory.
@ -358,6 +378,14 @@ public class Algorithms {
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4); return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4);
} }
public static int readSmallInt(InputStream in) throws IOException {
int ch1 = in.read();
int ch2 = in.read();
if ((ch1 | ch2) < 0)
throw new EOFException();
return ((ch1 << 8) + ch2);
}
public static String capitalizeFirstLetterAndLowercase(String s) { public static String capitalizeFirstLetterAndLowercase(String s) {
if (s != null && s.length() > 1) { if (s != null && s.length() > 1) {
// not very efficient algorithm // not very efficient algorithm
@ -537,6 +565,13 @@ public class Algorithms {
} }
} }
public static ByteArrayInputStream createByteArrayIS(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
streamCopy(in, out);
in.close();
return new ByteArrayInputStream(out.toByteArray());
}
@SuppressWarnings("ResultOfMethodCallIgnored") @SuppressWarnings("ResultOfMethodCallIgnored")
public static void updateAllExistingImgTilesToOsmandFormat(File f) { public static void updateAllExistingImgTilesToOsmandFormat(File f) {
if (f.isDirectory()) { if (f.isDirectory()) {

View file

@ -1,36 +0,0 @@
package net.osmand.plus.onlinerouting;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.plus.onlinerouting.engine.EngineType;
import net.osmand.plus.onlinerouting.engine.GraphhopperEngine;
import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine;
import net.osmand.plus.onlinerouting.engine.OrsEngine;
import net.osmand.plus.onlinerouting.engine.OsrmEngine;
import java.util.Map;
public class OnlineRoutingFactory {
public static OnlineRoutingEngine createEngine(@NonNull EngineType type) {
return createEngine(type, null);
}
@NonNull
public static OnlineRoutingEngine createEngine(@NonNull EngineType type,
@Nullable Map<String, String> params) {
switch (type) {
case GRAPHHOPPER:
return new GraphhopperEngine(params);
case OSRM:
return new OsrmEngine(params);
case ORS:
return new OrsEngine(params);
default:
throw new IllegalArgumentException(
"Online routing type {" + type.name() + "} not supported");
}
}
}

View file

@ -18,14 +18,20 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipInputStream;
import static net.osmand.util.Algorithms.GZIP_FILE_SIGNATURE;
import static net.osmand.util.Algorithms.ZIP_FILE_SIGNATURE;
import static net.osmand.util.Algorithms.isEmpty; import static net.osmand.util.Algorithms.isEmpty;
public class OnlineRoutingHelper { public class OnlineRoutingHelper {
@ -88,7 +94,7 @@ public class OnlineRoutingHelper {
boolean leftSideNavigation) throws IOException, JSONException { boolean leftSideNavigation) throws IOException, JSONException {
String url = engine.getFullUrl(path); String url = engine.getFullUrl(path);
String content = makeRequest(url); String content = makeRequest(url);
return engine.parseServerResponse(content, app, leftSideNavigation); return engine.parseResponse(content, app, leftSideNavigation);
} }
@NonNull @NonNull
@ -98,7 +104,7 @@ public class OnlineRoutingHelper {
StringBuilder content = new StringBuilder(); StringBuilder content = new StringBuilder();
BufferedReader reader; BufferedReader reader;
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); reader = new BufferedReader(new InputStreamReader(getInputStream(connection)));
} else { } else {
reader = new BufferedReader(new InputStreamReader(connection.getErrorStream())); reader = new BufferedReader(new InputStreamReader(connection.getErrorStream()));
} }
@ -113,6 +119,18 @@ public class OnlineRoutingHelper {
return content.toString(); return content.toString();
} }
private InputStream getInputStream(@NonNull HttpURLConnection connection) throws IOException {
ByteArrayInputStream localIS = Algorithms.createByteArrayIS(connection.getInputStream());
if (Algorithms.checkFileSignature(localIS, ZIP_FILE_SIGNATURE)) {
ZipInputStream zipIS = new ZipInputStream(localIS);
zipIS.getNextEntry(); // set position to reading for the first item
return zipIS;
} else if (Algorithms.checkFileSignature(localIS, GZIP_FILE_SIGNATURE)) {
return new GZIPInputStream(localIS);
}
return localIS;
}
public void saveEngine(@NonNull OnlineRoutingEngine engine) { public void saveEngine(@NonNull OnlineRoutingEngine engine) {
deleteInaccessibleParameters(engine); deleteInaccessibleParameters(engine);
String key = createEngineKeyIfNeeded(engine); String key = createEngineKeyIfNeeded(engine);

View file

@ -7,8 +7,8 @@ import androidx.annotation.NonNull;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import net.osmand.plus.onlinerouting.engine.EngineType;
import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine; import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine;
import net.osmand.plus.onlinerouting.engine.EngineType;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import org.json.JSONArray; import org.json.JSONArray;
@ -60,10 +60,10 @@ public class OnlineRoutingUtils {
for (int i = 0; i < itemsJson.length(); i++) { for (int i = 0; i < itemsJson.length(); i++) {
JSONObject object = itemsJson.getJSONObject(i); JSONObject object = itemsJson.getJSONObject(i);
if (object.has(TYPE) && object.has(PARAMS)) { if (object.has(TYPE) && object.has(PARAMS)) {
EngineType type = EngineType.getTypeByName(object.getString(TYPE)); OnlineRoutingEngine type = EngineType.getTypeByName(object.getString(TYPE));
String paramsString = object.getString(PARAMS); String paramsString = object.getString(PARAMS);
HashMap<String, String> params = gson.fromJson(paramsString, typeToken); HashMap<String, String> params = gson.fromJson(paramsString, typeToken);
OnlineRoutingEngine engine = OnlineRoutingFactory.createEngine(type, params); OnlineRoutingEngine engine = type.newInstance(params);
if (!Algorithms.isEmpty(engine.getStringKey())) { if (!Algorithms.isEmpty(engine.getStringKey())) {
engines.add(engine); engines.add(engine);
} }
@ -82,7 +82,7 @@ public class OnlineRoutingUtils {
continue; continue;
} }
JSONObject jsonObject = new JSONObject(); JSONObject jsonObject = new JSONObject();
jsonObject.put(TYPE, engine.getType().name()); jsonObject.put(TYPE, engine.getTypeName());
jsonObject.put(PARAMS, gson.toJson(engine.getParams(), type)); jsonObject.put(PARAMS, gson.toJson(engine.getParams(), type));
jsonArray.put(jsonObject); jsonArray.put(jsonObject);
} }

View file

@ -6,6 +6,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
public class VehicleType { public class VehicleType {
private final String key; private final String key;
@StringRes @StringRes
private final int titleId; private final int titleId;

View file

@ -1,34 +1,38 @@
package net.osmand.plus.onlinerouting.engine; package net.osmand.plus.onlinerouting.engine;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
public enum EngineType { public class EngineType {
GRAPHHOPPER("Graphhopper"),
OSRM("OSRM"),
ORS("Openroute Service");
private final String title; public final static OnlineRoutingEngine GRAPHHOPPER_TYPE = new GraphhopperEngine(null);
public final static OnlineRoutingEngine OSRM_TYPE = new OsrmEngine(null);
public final static OnlineRoutingEngine ORS_TYPE = new OrsEngine(null);
public final static OnlineRoutingEngine GPX_TYPE = new GpxEngine(null);
EngineType(String title) { private static OnlineRoutingEngine[] enginesTypes;
this.title = title;
}
public String getTitle() { public static OnlineRoutingEngine[] values() {
return title; if (enginesTypes == null) {
enginesTypes = new OnlineRoutingEngine[]{
GRAPHHOPPER_TYPE,
OSRM_TYPE,
ORS_TYPE,
GPX_TYPE
};
}
return enginesTypes;
} }
@NonNull @NonNull
public static EngineType getTypeByName(@Nullable String name) { public static OnlineRoutingEngine getTypeByName(@NonNull String typeName) {
if (!Algorithms.isEmpty(name)) { for (OnlineRoutingEngine type : values()) {
for (EngineType type : values()) { if (Algorithms.objectEquals(type.getTypeName(), typeName)) {
if (type.name().equals(name)) { return type;
return type;
}
} }
} }
return values()[0]; return values()[0];
} }
} }

View file

@ -0,0 +1,107 @@
package net.osmand.plus.onlinerouting.engine;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.data.LatLon;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.onlinerouting.EngineParameter;
import net.osmand.plus.onlinerouting.VehicleType;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static net.osmand.plus.onlinerouting.engine.EngineType.GPX_TYPE;
public class GpxEngine extends OnlineRoutingEngine {
public GpxEngine(@Nullable Map<String, String> params) {
super(params);
}
@NonNull
@Override
public OnlineRoutingEngine getType() {
return GPX_TYPE;
}
@Override
@NonNull
public String getTitle() {
return "GPX";
}
@NonNull
@Override
public String getTypeName() {
return "GPX";
}
@Override
protected void makeFullUrl(@NonNull StringBuilder sb,
@NonNull List<LatLon> path) {
for (int i = 0; i < path.size(); i++) {
LatLon point = path.get(i);
sb.append(point.getLongitude()).append(',').append(point.getLatitude());
if (i < path.size() - 1) {
sb.append(';');
}
}
}
@NonNull
@Override
public String getStandardUrl() {
return "";
}
@Override
protected void collectAllowedVehicles(@NonNull List<VehicleType> vehicles) {
}
@Override
protected void collectAllowedParameters(@NonNull Set<EngineParameter> params) {
params.add(EngineParameter.KEY);
params.add(EngineParameter.CUSTOM_NAME);
params.add(EngineParameter.NAME_INDEX);
params.add(EngineParameter.CUSTOM_URL);
}
@Override
public OnlineRoutingEngine newInstance(Map<String, String> params) {
return new GpxEngine(params);
}
@Override
@Nullable
public OnlineRoutingResponse parseResponse(@NonNull String content,
@NonNull OsmandApplication app,
boolean leftSideNavigation) {
GPXFile gpxFile = parseGpx(content);
return gpxFile != null ? new OnlineRoutingResponse(parseGpx(content)) : null;
}
@Override
public boolean isResultOk(@NonNull StringBuilder errorMessage,
@NonNull String content) {
return parseGpx(content) != null;
}
private GPXFile parseGpx(@NonNull String content) {
InputStream gpxStream;
try {
gpxStream = new ByteArrayInputStream(content.getBytes("UTF-8"));
return GPXUtilities.loadGPXFile(gpxStream);
} catch (UnsupportedEncodingException e) {
LOG.debug("Error when parsing GPX from server response: " + e.getMessage());
}
return null;
}
}

View file

@ -20,10 +20,12 @@ import org.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import static net.osmand.plus.onlinerouting.engine.EngineType.GRAPHHOPPER_TYPE;
import static net.osmand.util.Algorithms.isEmpty; import static net.osmand.util.Algorithms.isEmpty;
public class GraphhopperEngine extends OnlineRoutingEngine { public class GraphhopperEngine extends JsonOnlineRoutingEngine {
public GraphhopperEngine(@Nullable Map<String, String> params) { public GraphhopperEngine(@Nullable Map<String, String> params) {
super(params); super(params);
@ -31,8 +33,20 @@ public class GraphhopperEngine extends OnlineRoutingEngine {
@NonNull @NonNull
@Override @Override
public EngineType getType() { public OnlineRoutingEngine getType() {
return EngineType.GRAPHHOPPER; return GRAPHHOPPER_TYPE;
}
@Override
@NonNull
public String getTitle() {
return "Graphhopper";
}
@NonNull
@Override
public String getTypeName() {
return "GRAPHHOPPER";
} }
@NonNull @NonNull
@ -42,8 +56,18 @@ public class GraphhopperEngine extends OnlineRoutingEngine {
} }
@Override @Override
protected void collectAllowedParameters() { protected void collectAllowedParameters(@NonNull Set<EngineParameter> params) {
allowParameters(EngineParameter.API_KEY); params.add(EngineParameter.KEY);
params.add(EngineParameter.VEHICLE_KEY);
params.add(EngineParameter.CUSTOM_NAME);
params.add(EngineParameter.NAME_INDEX);
params.add(EngineParameter.CUSTOM_URL);
params.add(EngineParameter.API_KEY);
}
@Override
public OnlineRoutingEngine newInstance(Map<String, String> params) {
return new GraphhopperEngine(params);
} }
@Override @Override
@ -82,10 +106,9 @@ public class GraphhopperEngine extends OnlineRoutingEngine {
} }
@Nullable @Nullable
@Override protected OnlineRoutingResponse parseServerResponse(@NonNull JSONObject root,
public OnlineRoutingResponse parseServerResponse(@NonNull JSONObject root, @NonNull OsmandApplication app,
@NonNull OsmandApplication app, boolean leftSideNavigation) throws JSONException {
boolean leftSideNavigation) throws JSONException {
String encoded = root.getString("points"); String encoded = root.getString("points");
List<LatLon> points = GeoPolylineParserUtil.parse(encoded, GeoPolylineParserUtil.PRECISION_5); List<LatLon> points = GeoPolylineParserUtil.parse(encoded, GeoPolylineParserUtil.PRECISION_5);
if (isEmpty(points)) return null; if (isEmpty(points)) return null;
@ -223,4 +246,5 @@ public class GraphhopperEngine extends OnlineRoutingEngine {
protected String getRootArrayKey() { protected String getRootArrayKey() {
return "paths"; return "paths";
} }
} }

View file

@ -0,0 +1,82 @@
package net.osmand.plus.onlinerouting.engine;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.Location;
import net.osmand.data.LatLon;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.routing.RouteProvider;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static net.osmand.util.Algorithms.isEmpty;
public abstract class JsonOnlineRoutingEngine extends OnlineRoutingEngine {
public JsonOnlineRoutingEngine(@Nullable Map<String, String> params) {
super(params);
}
@Nullable
public OnlineRoutingResponse parseResponse(@NonNull String content,
@NonNull OsmandApplication app,
boolean leftSideNavigation) throws JSONException {
JSONObject root = parseRootResponseObject(content);
return root != null ? parseServerResponse(root, app, leftSideNavigation) : null;
}
@Nullable
protected JSONObject parseRootResponseObject(@NonNull String content) throws JSONException {
JSONObject fullJSON = new JSONObject(content);
String key = getRootArrayKey();
JSONArray array = null;
if (fullJSON.has(key)) {
array = fullJSON.getJSONArray(key);
}
return array != null && array.length() > 0 ? array.getJSONObject(0) : null;
}
public boolean isResultOk(@NonNull StringBuilder errorMessage,
@NonNull String content) throws JSONException {
JSONObject obj = new JSONObject(content);
String messageKey = getErrorMessageKey();
if (obj.has(messageKey)) {
String message = obj.getString(messageKey);
errorMessage.append(message);
}
return obj.has(getRootArrayKey());
}
@Nullable
protected abstract OnlineRoutingResponse parseServerResponse(@NonNull JSONObject root,
@NonNull OsmandApplication app,
boolean leftSideNavigation) throws JSONException;
@NonNull
protected abstract String getRootArrayKey();
@NonNull
protected abstract String getErrorMessageKey();
@NonNull
protected static List<Location> convertRouteToLocationsList(@NonNull List<LatLon> route) {
List<Location> result = new ArrayList<>();
if (!isEmpty(route)) {
for (LatLon pt : route) {
WptPt wpt = new WptPt();
wpt.lat = pt.getLatitude();
wpt.lon = pt.getLongitude();
result.add(RouteProvider.createLocation(wpt));
}
}
return result;
}
}

View file

@ -5,24 +5,21 @@ import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.Location; import net.osmand.Location;
import net.osmand.PlatformUtil;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.onlinerouting.EngineParameter; import net.osmand.plus.onlinerouting.EngineParameter;
import net.osmand.plus.onlinerouting.OnlineRoutingFactory;
import net.osmand.plus.onlinerouting.VehicleType; import net.osmand.plus.onlinerouting.VehicleType;
import net.osmand.plus.routing.RouteDirectionInfo; import net.osmand.plus.routing.RouteDirectionInfo;
import net.osmand.plus.routing.RouteProvider;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import org.json.JSONArray; import org.apache.commons.logging.Log;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -34,6 +31,8 @@ import static net.osmand.util.Algorithms.isEmpty;
public abstract class OnlineRoutingEngine implements Cloneable { public abstract class OnlineRoutingEngine implements Cloneable {
protected static final Log LOG = PlatformUtil.getLog(OnlineRoutingEngine.class);
public final static String ONLINE_ROUTING_ENGINE_PREFIX = "online_routing_engine_"; public final static String ONLINE_ROUTING_ENGINE_PREFIX = "online_routing_engine_";
public final static VehicleType CUSTOM_VEHICLE = new VehicleType("", R.string.shared_string_custom); public final static VehicleType CUSTOM_VEHICLE = new VehicleType("", R.string.shared_string_custom);
@ -46,11 +45,17 @@ public abstract class OnlineRoutingEngine implements Cloneable {
this.params.putAll(params); this.params.putAll(params);
} }
collectAllowedVehiclesInternal(); collectAllowedVehiclesInternal();
collectAllowedParametersInternal(); collectAllowedParameters(allowedParameters);
} }
@NonNull @NonNull
public abstract EngineType getType(); public abstract OnlineRoutingEngine getType();
@NonNull
public abstract String getTitle();
@NonNull
public abstract String getTypeName();
@Nullable @Nullable
public String getStringKey() { public String getStringKey() {
@ -100,45 +105,13 @@ public abstract class OnlineRoutingEngine implements Cloneable {
@NonNull @NonNull
public abstract String getStandardUrl(); public abstract String getStandardUrl();
public OnlineRoutingResponse parseServerResponse(@NonNull String content,
@NonNull OsmandApplication app,
boolean leftSideNavigation) throws JSONException {
JSONObject root = parseRootResponseObject(content);
return root != null ? parseServerResponse(root, app, leftSideNavigation) : null;
}
@Nullable @Nullable
protected abstract OnlineRoutingResponse parseServerResponse(@NonNull JSONObject root, public abstract OnlineRoutingResponse parseResponse(@NonNull String content,
@NonNull OsmandApplication app, @NonNull OsmandApplication app,
boolean leftSideNavigation) throws JSONException; boolean leftSideNavigation) throws JSONException;
@Nullable public abstract boolean isResultOk(@NonNull StringBuilder errorMessage,
protected JSONObject parseRootResponseObject(@NonNull String content) throws JSONException { @NonNull String content) throws JSONException;
JSONObject fullJSON = new JSONObject(content);
String responseArrayKey = getRootArrayKey();
JSONArray array = null;
if (fullJSON.has(responseArrayKey)) {
array = fullJSON.getJSONArray(responseArrayKey);
}
return array != null && array.length() > 0 ? array.getJSONObject(0) : null;
}
@NonNull
protected abstract String getRootArrayKey();
@NonNull
protected List<Location> convertRouteToLocationsList(@NonNull List<LatLon> route) {
List<Location> result = new ArrayList<>();
if (!isEmpty(route)) {
for (LatLon pt : route) {
WptPt wpt = new WptPt();
wpt.lat = pt.getLatitude();
wpt.lon = pt.getLongitude();
result.add(RouteProvider.createLocation(wpt));
}
}
return result;
}
@NonNull @NonNull
public Map<String, String> getParams() { public Map<String, String> getParams() {
@ -171,23 +144,12 @@ public abstract class OnlineRoutingEngine implements Cloneable {
return Collections.unmodifiableList(allowedVehicles); return Collections.unmodifiableList(allowedVehicles);
} }
private void collectAllowedParametersInternal() { protected abstract void collectAllowedParameters(@NonNull Set<EngineParameter> params);
allowedParameters.clear();
allowParameters(EngineParameter.KEY, EngineParameter.VEHICLE_KEY,
EngineParameter.CUSTOM_NAME, EngineParameter.NAME_INDEX, EngineParameter.CUSTOM_URL);
collectAllowedParameters();
}
protected abstract void collectAllowedParameters();
public boolean isParameterAllowed(EngineParameter key) { public boolean isParameterAllowed(EngineParameter key) {
return allowedParameters.contains(key); return allowedParameters.contains(key);
} }
protected void allowParameters(@NonNull EngineParameter... allowedParams) {
allowedParameters.addAll(Arrays.asList(allowedParams));
}
@Nullable @Nullable
private String getSelectedVehicleName(@NonNull Context ctx) { private String getSelectedVehicleName(@NonNull Context ctx) {
String key = get(EngineParameter.VEHICLE_KEY); String key = get(EngineParameter.VEHICLE_KEY);
@ -216,24 +178,10 @@ public abstract class OnlineRoutingEngine implements Cloneable {
return CUSTOM_VEHICLE; return CUSTOM_VEHICLE;
} }
public boolean checkServerResponse(@NonNull StringBuilder errorMessage,
@NonNull String content) throws JSONException {
JSONObject obj = new JSONObject(content);
String messageKey = getErrorMessageKey();
if (obj.has(messageKey)) {
String message = obj.getString(messageKey);
errorMessage.append(message);
}
return obj.has(getRootArrayKey());
}
@NonNull
protected abstract String getErrorMessageKey();
@NonNull @NonNull
@Override @Override
public Object clone() { public Object clone() {
return OnlineRoutingFactory.createEngine(getType(), getParams()); return newInstance(getParams());
} }
@Override @Override
@ -246,20 +194,30 @@ public abstract class OnlineRoutingEngine implements Cloneable {
return Algorithms.objectEquals(getParams(), engine.getParams()); return Algorithms.objectEquals(getParams(), engine.getParams());
} }
public abstract OnlineRoutingEngine newInstance(Map<String, String> params);
@NonNull @NonNull
public static String generateKey() { public static String generateKey() {
return ONLINE_ROUTING_ENGINE_PREFIX + System.currentTimeMillis(); return ONLINE_ROUTING_ENGINE_PREFIX + System.currentTimeMillis();
} }
public static class OnlineRoutingResponse { public static class OnlineRoutingResponse {
private List<Location> route; private List<Location> route;
private List<RouteDirectionInfo> directions; private List<RouteDirectionInfo> directions;
private GPXFile gpxFile;
// constructor for JSON responses
public OnlineRoutingResponse(List<Location> route, List<RouteDirectionInfo> directions) { public OnlineRoutingResponse(List<Location> route, List<RouteDirectionInfo> directions) {
this.route = route; this.route = route;
this.directions = directions; this.directions = directions;
} }
// constructor for GPX responses
public OnlineRoutingResponse(GPXFile gpxFile) {
this.gpxFile = gpxFile;
}
public List<Location> getRoute() { public List<Location> getRoute() {
return route; return route;
} }
@ -267,5 +225,10 @@ public abstract class OnlineRoutingEngine implements Cloneable {
public List<RouteDirectionInfo> getDirections() { public List<RouteDirectionInfo> getDirections() {
return directions; return directions;
} }
public GPXFile getGpxFile() {
return gpxFile;
}
} }
} }

View file

@ -17,10 +17,12 @@ import org.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import static net.osmand.plus.onlinerouting.engine.EngineType.ORS_TYPE;
import static net.osmand.util.Algorithms.isEmpty; import static net.osmand.util.Algorithms.isEmpty;
public class OrsEngine extends OnlineRoutingEngine { public class OrsEngine extends JsonOnlineRoutingEngine {
public OrsEngine(@Nullable Map<String, String> params) { public OrsEngine(@Nullable Map<String, String> params) {
super(params); super(params);
@ -28,8 +30,20 @@ public class OrsEngine extends OnlineRoutingEngine {
@NonNull @NonNull
@Override @Override
public EngineType getType() { public OnlineRoutingEngine getType() {
return EngineType.ORS; return ORS_TYPE;
}
@Override
@NonNull
public String getTitle() {
return "Openroute Service";
}
@NonNull
@Override
public String getTypeName() {
return "ORS";
} }
@NonNull @NonNull
@ -39,8 +53,18 @@ public class OrsEngine extends OnlineRoutingEngine {
} }
@Override @Override
protected void collectAllowedParameters() { protected void collectAllowedParameters(@NonNull Set<EngineParameter> params) {
allowParameters(EngineParameter.API_KEY); params.add(EngineParameter.KEY);
params.add(EngineParameter.VEHICLE_KEY);
params.add(EngineParameter.CUSTOM_NAME);
params.add(EngineParameter.NAME_INDEX);
params.add(EngineParameter.CUSTOM_URL);
params.add(EngineParameter.API_KEY);
}
@Override
public OnlineRoutingEngine newInstance(Map<String, String> params) {
return new OrsEngine(params);
} }
@Override @Override
@ -109,4 +133,5 @@ public class OrsEngine extends OnlineRoutingEngine {
protected String getRootArrayKey() { protected String getRootArrayKey() {
return "features"; return "features";
} }
} }

View file

@ -22,20 +22,34 @@ import org.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import static net.osmand.plus.onlinerouting.engine.EngineType.OSRM_TYPE;
import static net.osmand.util.Algorithms.isEmpty; import static net.osmand.util.Algorithms.isEmpty;
import static net.osmand.util.Algorithms.objectEquals; import static net.osmand.util.Algorithms.objectEquals;
public class OsrmEngine extends OnlineRoutingEngine { public class OsrmEngine extends JsonOnlineRoutingEngine {
public OsrmEngine(@Nullable Map<String, String> params) { public OsrmEngine(@Nullable Map<String, String> params) {
super(params); super(params);
} }
@Override @Override
public @NonNull @NonNull
EngineType getType() { public OnlineRoutingEngine getType() {
return EngineType.OSRM; return OSRM_TYPE;
}
@Override
@NonNull
public String getTitle() {
return "OSRM";
}
@NonNull
@Override
public String getTypeName() {
return "OSRM";
} }
@NonNull @NonNull
@ -45,7 +59,17 @@ public class OsrmEngine extends OnlineRoutingEngine {
} }
@Override @Override
protected void collectAllowedParameters() { protected void collectAllowedParameters(@NonNull Set<EngineParameter> params) {
params.add(EngineParameter.KEY);
params.add(EngineParameter.VEHICLE_KEY);
params.add(EngineParameter.CUSTOM_NAME);
params.add(EngineParameter.NAME_INDEX);
params.add(EngineParameter.CUSTOM_URL);
}
@Override
public OnlineRoutingEngine newInstance(Map<String, String> params) {
return new OsrmEngine(params);
} }
@Override @Override
@ -76,9 +100,9 @@ public class OsrmEngine extends OnlineRoutingEngine {
@Nullable @Nullable
@Override @Override
public OnlineRoutingResponse parseServerResponse(@NonNull JSONObject root, protected OnlineRoutingResponse parseServerResponse(@NonNull JSONObject root,
@NonNull OsmandApplication app, @NonNull OsmandApplication app,
boolean leftSideNavigation) throws JSONException { boolean leftSideNavigation) throws JSONException {
String encodedPoints = root.getString("geometry"); String encodedPoints = root.getString("geometry");
List<LatLon> points = GeoPolylineParserUtil.parse(encodedPoints, GeoPolylineParserUtil.PRECISION_5); List<LatLon> points = GeoPolylineParserUtil.parse(encodedPoints, GeoPolylineParserUtil.PRECISION_5);
if (isEmpty(points)) return null; if (isEmpty(points)) return null;
@ -225,13 +249,14 @@ public class OsrmEngine extends OnlineRoutingEngine {
@NonNull @NonNull
@Override @Override
protected String getErrorMessageKey() { protected String getRootArrayKey() {
return "message"; return "routes";
} }
@NonNull @NonNull
@Override @Override
protected String getRootArrayKey() { protected String getErrorMessageKey() {
return "routes"; return "message";
} }
} }

View file

@ -39,12 +39,11 @@ import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.BaseOsmAndFragment; import net.osmand.plus.base.BaseOsmAndFragment;
import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter.HorizontalSelectionItem; import net.osmand.plus.mapcontextmenu.other.HorizontalSelectionAdapter.HorizontalSelectionItem;
import net.osmand.plus.onlinerouting.EngineParameter; import net.osmand.plus.onlinerouting.EngineParameter;
import net.osmand.plus.onlinerouting.OnlineRoutingFactory;
import net.osmand.plus.onlinerouting.OnlineRoutingHelper; import net.osmand.plus.onlinerouting.OnlineRoutingHelper;
import net.osmand.plus.onlinerouting.OnlineRoutingUtils; import net.osmand.plus.onlinerouting.OnlineRoutingUtils;
import net.osmand.plus.onlinerouting.VehicleType; import net.osmand.plus.onlinerouting.VehicleType;
import net.osmand.plus.onlinerouting.engine.EngineType;
import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine; import net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine;
import net.osmand.plus.onlinerouting.engine.EngineType;
import net.osmand.plus.onlinerouting.ui.OnlineRoutingCard.OnTextChangedListener; import net.osmand.plus.onlinerouting.ui.OnlineRoutingCard.OnTextChangedListener;
import net.osmand.plus.routepreparationmenu.cards.BaseCard; import net.osmand.plus.routepreparationmenu.cards.BaseCard;
import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.ApplicationMode;
@ -54,7 +53,9 @@ import org.json.JSONException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import static net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine.CUSTOM_VEHICLE; import static net.osmand.plus.onlinerouting.engine.OnlineRoutingEngine.CUSTOM_VEHICLE;
@ -201,15 +202,15 @@ public class OnlineRoutingEngineFragment extends BaseOsmAndFragment {
typeCard = new OnlineRoutingCard(mapActivity, isNightMode(), appMode); typeCard = new OnlineRoutingCard(mapActivity, isNightMode(), appMode);
typeCard.build(mapActivity); typeCard.build(mapActivity);
typeCard.setHeaderTitle(getString(R.string.shared_string_type)); typeCard.setHeaderTitle(getString(R.string.shared_string_type));
List<HorizontalSelectionItem> serverItems = new ArrayList<>(); List<HorizontalSelectionItem> typeItems = new ArrayList<>();
for (EngineType server : EngineType.values()) { for (OnlineRoutingEngine type : EngineType.values()) {
serverItems.add(new HorizontalSelectionItem(server.getTitle(), server)); typeItems.add(new HorizontalSelectionItem(type.getTitle(), type));
} }
typeCard.setSelectionMenu(serverItems, engine.getType().getTitle(), typeCard.setSelectionMenu(typeItems, engine.getType().getTitle(),
new CallbackWithObject<HorizontalSelectionItem>() { new CallbackWithObject<HorizontalSelectionItem>() {
@Override @Override
public boolean processResult(HorizontalSelectionItem result) { public boolean processResult(HorizontalSelectionItem result) {
EngineType type = (EngineType) result.getObject(); OnlineRoutingEngine type = (OnlineRoutingEngine) result.getObject();
if (engine.getType() != type) { if (engine.getType() != type) {
changeEngineType(type); changeEngineType(type);
return true; return true;
@ -366,9 +367,9 @@ public class OnlineRoutingEngineFragment extends BaseOsmAndFragment {
}); });
} }
private void changeEngineType(EngineType type) { private void changeEngineType(OnlineRoutingEngine type) {
OnlineRoutingEngine tmp = (OnlineRoutingEngine) engine.clone(); OnlineRoutingEngine tmp = (OnlineRoutingEngine) engine.clone();
engine = OnlineRoutingFactory.createEngine(type, tmp.getParams()); engine = type.newInstance(tmp.getParams());
// after changing the type, select the vehicle // after changing the type, select the vehicle
// with the same name that was selected before // with the same name that was selected before
@ -462,7 +463,7 @@ public class OnlineRoutingEngineFragment extends BaseOsmAndFragment {
boolean resultOk = false; boolean resultOk = false;
try { try {
String response = helper.makeRequest(exampleCard.getEditedText()); String response = helper.makeRequest(exampleCard.getEditedText());
resultOk = requestedEngine.checkServerResponse(errorMessage, response); resultOk = requestedEngine.isResultOk(errorMessage, response);
} catch (IOException | JSONException e) { } catch (IOException | JSONException e) {
errorMessage.append(e.toString()); errorMessage.append(e.toString());
} }
@ -514,6 +515,12 @@ public class OnlineRoutingEngineFragment extends BaseOsmAndFragment {
} else { } else {
apiKeyCard.hide(); apiKeyCard.hide();
} }
if (engine.isParameterAllowed(EngineParameter.VEHICLE_KEY)) {
vehicleCard.show();
} else {
vehicleCard.hide();
}
} else if (vehicleCard.equals(card)) { } else if (vehicleCard.equals(card)) {
VehicleType vt = engine.getSelectedVehicleType(); VehicleType vt = engine.getSelectedVehicleType();
@ -609,7 +616,7 @@ public class OnlineRoutingEngineFragment extends BaseOsmAndFragment {
} }
private void saveState(@NonNull Bundle outState) { private void saveState(@NonNull Bundle outState) {
outState.putString(ENGINE_TYPE_KEY, engine.getType().name()); outState.putString(ENGINE_TYPE_KEY, engine.getTypeName());
for (EngineParameter key : EngineParameter.values()) { for (EngineParameter key : EngineParameter.values()) {
String value = engine.get(key); String value = engine.get(key);
if (value != null) { if (value != null) {
@ -626,14 +633,15 @@ public class OnlineRoutingEngineFragment extends BaseOsmAndFragment {
editedEngineKey = savedState.getString(EngineParameter.KEY.name()); editedEngineKey = savedState.getString(EngineParameter.KEY.name());
initEngine = createInitStateEngine(); initEngine = createInitStateEngine();
String typeKey = savedState.getString(ENGINE_TYPE_KEY); String typeKey = savedState.getString(ENGINE_TYPE_KEY);
EngineType type = EngineType.getTypeByName(typeKey); OnlineRoutingEngine type = EngineType.getTypeByName(typeKey);
engine = OnlineRoutingFactory.createEngine(type); Map<String, String> params = new HashMap<>();
for (EngineParameter key : EngineParameter.values()) { for (EngineParameter key : EngineParameter.values()) {
String value = savedState.getString(key.name()); String value = savedState.getString(key.name());
if (value != null) { if (value != null) {
engine.put(key, value); params.put(key.name(), value);
} }
} }
engine = type.newInstance(params);
customVehicleKey = savedState.getString(ENGINE_CUSTOM_VEHICLE_KEY); customVehicleKey = savedState.getString(ENGINE_CUSTOM_VEHICLE_KEY);
selectedLocation = ExampleLocation.valueOf(savedState.getString(EXAMPLE_LOCATION_KEY)); selectedLocation = ExampleLocation.valueOf(savedState.getString(EXAMPLE_LOCATION_KEY));
appMode = ApplicationMode.valueOfStringKey(savedState.getString(APP_MODE_KEY), null); appMode = ApplicationMode.valueOfStringKey(savedState.getString(APP_MODE_KEY), null);
@ -656,7 +664,7 @@ public class OnlineRoutingEngineFragment extends BaseOsmAndFragment {
if (editedEngine != null) { if (editedEngine != null) {
engine = (OnlineRoutingEngine) editedEngine.clone(); engine = (OnlineRoutingEngine) editedEngine.clone();
} else { } else {
engine = OnlineRoutingFactory.createEngine(EngineType.values()[0]); engine = EngineType.values()[0].newInstance(null);
String vehicle = engine.getAllowedVehicles().get(0).getKey(); String vehicle = engine.getAllowedVehicles().get(0).getKey();
engine.put(EngineParameter.VEHICLE_KEY, vehicle); engine.put(EngineParameter.VEHICLE_KEY, vehicle);
if (editedEngineKey != null) { if (editedEngineKey != null) {

View file

@ -1228,16 +1228,28 @@ public class RouteProvider {
} }
private RouteCalculationResult findOnlineRoute(RouteCalculationParams params) throws IOException, JSONException { private RouteCalculationResult findOnlineRoute(RouteCalculationParams params) throws IOException, JSONException {
OnlineRoutingHelper helper = params.ctx.getOnlineRoutingHelper(); OsmandApplication app = params.ctx;
String stringKey = params.mode.getRoutingProfile(); OnlineRoutingHelper helper = app.getOnlineRoutingHelper();
OsmandSettings settings = app.getSettings();
String engineKey = params.mode.getRoutingProfile();
OnlineRoutingResponse response = OnlineRoutingResponse response =
helper.calculateRouteOnline(stringKey, getPathFromParams(params), params.leftSide); helper.calculateRouteOnline(engineKey, getPathFromParams(params), params.leftSide);
if (response != null) { if (response != null) {
params.intermediates = null; if (response.getGpxFile() != null) {
return new RouteCalculationResult(response.getRoute(), response.getDirections(), params, null, false); GPXRouteParamsBuilder builder = new GPXRouteParamsBuilder(response.getGpxFile(), settings);
} else { params.gpxRoute = builder.build(app);
return new RouteCalculationResult("Route is empty"); return calculateGpxRoute(params);
}
List<Location> route = response.getRoute();
List<RouteDirectionInfo> directions = response.getDirections();
if (!Algorithms.isEmpty(route) && !Algorithms.isEmpty(directions)) {
params.intermediates = null;
return new RouteCalculationResult(route, directions, params, null, false);
}
} }
return new RouteCalculationResult("Route is empty");
} }
private static List<LatLon> getPathFromParams(RouteCalculationParams params) { private static List<LatLon> getPathFromParams(RouteCalculationParams params) {