Merge branch 'master' into WikipediaSearchBanner
|
@ -33,7 +33,16 @@ public class CollatorStringMatcher implements StringMatcher {
|
|||
|
||||
public CollatorStringMatcher(String part, StringMatcherMode mode) {
|
||||
this.collator = OsmAndCollator.primaryCollator();
|
||||
this.part = simplifyStringAndAlignChars(part);
|
||||
part = simplifyStringAndAlignChars(part);
|
||||
if (part.length() > 0 && part.charAt(part.length() - 1) == '.') {
|
||||
part = part.substring(0, part.length() - 1);
|
||||
if (mode == StringMatcherMode.CHECK_EQUALS_FROM_SPACE) {
|
||||
mode = StringMatcherMode.CHECK_STARTS_FROM_SPACE;
|
||||
} else if (mode == StringMatcherMode.CHECK_EQUALS) {
|
||||
mode = StringMatcherMode.CHECK_ONLY_STARTS_WITH;
|
||||
}
|
||||
}
|
||||
this.part = part;
|
||||
this.mode = mode;
|
||||
|
||||
}
|
||||
|
|
|
@ -2660,8 +2660,10 @@ public class BinaryMapIndexReader {
|
|||
incompleteTransportRoutes = new TLongObjectHashMap<>();
|
||||
for (TransportIndex ti : transportIndexes) {
|
||||
if (ti.incompleteRoutesLength > 0) {
|
||||
transportAdapter.readIncompleteRoutesList(incompleteTransportRoutes, ti.incompleteRoutesLength,
|
||||
ti.incompleteRoutesOffset);
|
||||
codedIS.seek(ti.incompleteRoutesOffset);
|
||||
int oldLimit = codedIS.pushLimit(ti.incompleteRoutesLength);
|
||||
transportAdapter.readIncompleteRoutesList(incompleteTransportRoutes);
|
||||
codedIS.popLimit(oldLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -772,7 +772,8 @@ public class BinaryMapPoiReaderAdapter {
|
|||
}
|
||||
}
|
||||
subtype = poiTypes.replaceDeprecatedSubtype(type, subtype);
|
||||
if (req.poiTypeFilter == null || req.poiTypeFilter.accept(type, subtype)) {
|
||||
boolean isForbidden = poiTypes.isTypeForbidden(subtype);
|
||||
if (!isForbidden && (req.poiTypeFilter == null || req.poiTypeFilter.accept(type, subtype))) {
|
||||
if (amenityType == null) {
|
||||
amenityType = type;
|
||||
am.setSubType(subtype);
|
||||
|
|
|
@ -1,9 +1,25 @@
|
|||
package net.osmand.binary;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import com.google.protobuf.CodedInputStream;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.WireFormat;
|
||||
|
||||
import gnu.trove.iterator.TLongObjectIterator;
|
||||
import gnu.trove.list.array.TIntArrayList;
|
||||
import gnu.trove.list.array.TLongArrayList;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
import gnu.trove.map.hash.TLongObjectHashMap;
|
||||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.ResultMatcher;
|
||||
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
|
||||
|
@ -18,23 +34,6 @@ import net.osmand.util.Algorithms;
|
|||
import net.osmand.util.MapUtils;
|
||||
import net.osmand.util.OpeningHoursParser;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import gnu.trove.iterator.TLongObjectIterator;
|
||||
import gnu.trove.list.array.TIntArrayList;
|
||||
import gnu.trove.list.array.TLongArrayList;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
import gnu.trove.map.hash.TLongObjectHashMap;
|
||||
|
||||
public class BinaryMapRouteReaderAdapter {
|
||||
protected static final Log LOG = PlatformUtil.getLog(BinaryMapRouteReaderAdapter.class);
|
||||
private static final int SHIFT_COORDINATES = 4;
|
||||
|
@ -172,7 +171,7 @@ public class BinaryMapRouteReaderAdapter {
|
|||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
|
||||
public int onewayDirection(){
|
||||
if(type == ONEWAY){
|
||||
return intValue;
|
||||
|
@ -297,6 +296,14 @@ public class BinaryMapRouteReaderAdapter {
|
|||
List<RouteSubregion> subregions = new ArrayList<RouteSubregion>();
|
||||
List<RouteSubregion> basesubregions = new ArrayList<RouteSubregion>();
|
||||
|
||||
public int directionForward = -1;
|
||||
public int directionBackward = -1;
|
||||
public int directionTrafficSignalsForward = -1;
|
||||
public int directionTrafficSignalsBackward = -1;
|
||||
public int trafficSignals = -1;
|
||||
public int stopSign = -1;
|
||||
public int giveWaySign = -1;
|
||||
|
||||
int nameTypeRule = -1;
|
||||
int refTypeRule = -1;
|
||||
int destinationTypeRule = -1;
|
||||
|
@ -353,9 +360,28 @@ public class BinaryMapRouteReaderAdapter {
|
|||
destinationTypeRule = id;
|
||||
} else if (tags.equals("destination:ref") || tags.equals("destination:ref:forward") || tags.equals("destination:ref:backward")) {
|
||||
destinationRefTypeRule = id;
|
||||
} else if (tags.equals("highway") && val.equals("traffic_signals")){
|
||||
trafficSignals = id;
|
||||
} else if (tags.equals("highway") && val.equals("stop")){
|
||||
stopSign = id;
|
||||
} else if (tags.equals("highway") && val.equals("give_way")){
|
||||
giveWaySign = id;
|
||||
} else if (tags.equals("traffic_signals:direction") && val != null){
|
||||
if (val.equals("forward")) {
|
||||
directionTrafficSignalsForward = id;
|
||||
} else if (val.equals("backward")) {
|
||||
directionTrafficSignalsBackward = id;
|
||||
}
|
||||
} else if (tags.equals("direction") && val != null) {
|
||||
if (val.equals("forward")) {
|
||||
directionForward = id;
|
||||
} else if (val.equals("backward")) {
|
||||
directionBackward = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void completeRouteEncodingRules() {
|
||||
for(int i = 0; i < routeEncodingRules.size(); i++) {
|
||||
RouteTypeRule rtr = routeEncodingRules.get(i);
|
||||
|
|
|
@ -251,9 +251,7 @@ public class BinaryMapTransportReaderAdapter {
|
|||
return ((char) i)+"";
|
||||
}
|
||||
|
||||
public void readIncompleteRoutesList(TLongObjectHashMap<net.osmand.data.IncompleteTransportRoute> incompleteRoutes,
|
||||
int length, int offset) throws IOException {
|
||||
codedIS.seek(offset);
|
||||
public void readIncompleteRoutesList(TLongObjectHashMap<net.osmand.data.IncompleteTransportRoute> incompleteRoutes) throws IOException {
|
||||
boolean end = false;
|
||||
while (!end) {
|
||||
int t = codedIS.readTag();
|
||||
|
|
|
@ -1054,6 +1054,4 @@ public class RouteDataObject {
|
|||
}
|
||||
restrictionsVia[k] = viaWay;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -67,7 +67,6 @@ public abstract class AbstractPoiType {
|
|||
return this instanceof PoiType && this.isAdditional();
|
||||
}
|
||||
|
||||
|
||||
public String getTranslation() {
|
||||
if(translation == null) {
|
||||
translation = registry.getTranslation(this);
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
|
@ -34,12 +35,14 @@ public class MapPoiTypes {
|
|||
private static final Log log = PlatformUtil.getLog(MapRenderingTypes.class);
|
||||
private String resourceName;
|
||||
private List<PoiCategory> categories = new ArrayList<PoiCategory>();
|
||||
private Set<String> forbiddenTypes = new HashSet<>();
|
||||
private PoiCategory otherCategory;
|
||||
private PoiCategory otherMapCategory;
|
||||
|
||||
public static final String WIKI_LANG = "wiki_lang";
|
||||
public static final String WIKI_PLACE = "wiki_place";
|
||||
public static final String OSM_WIKI_CATEGORY = "osmwiki";
|
||||
public static final String SPEED_CAMERA = "speed_camera";
|
||||
|
||||
private PoiTranslator poiTranslator = null;
|
||||
private boolean init;
|
||||
|
@ -945,4 +948,12 @@ public class MapPoiTypes {
|
|||
return pat.isText();
|
||||
}
|
||||
}
|
||||
|
||||
public void setForbiddenTypes(Set<String> forbiddenTypes) {
|
||||
this.forbiddenTypes = forbiddenTypes;
|
||||
}
|
||||
|
||||
public boolean isTypeForbidden(String typeName) {
|
||||
return forbiddenTypes.contains(typeName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,13 +30,14 @@ public class PoiFilter extends AbstractPoiType {
|
|||
List<PoiType> npoiTypes = null;
|
||||
Map<String, PoiType> nmap = null;
|
||||
for (PoiType poiType : poiTypesToAdd.values()) {
|
||||
if (!map.containsKey(poiType.getKeyName())) {
|
||||
String keyName = poiType.getKeyName();
|
||||
if (!map.containsKey(keyName) && !registry.isTypeForbidden(keyName)) {
|
||||
if (npoiTypes == null) {
|
||||
npoiTypes = new ArrayList<PoiType>(this.poiTypes);
|
||||
nmap = new LinkedHashMap<>(map);
|
||||
}
|
||||
npoiTypes.add(poiType);
|
||||
nmap.put(poiType.getKeyName(), poiType);
|
||||
nmap.put(keyName, poiType);
|
||||
}
|
||||
}
|
||||
if (npoiTypes != null) {
|
||||
|
@ -46,6 +47,9 @@ public class PoiFilter extends AbstractPoiType {
|
|||
}
|
||||
|
||||
public void addPoiType(PoiType type) {
|
||||
if (registry.isTypeForbidden(type.keyName)) {
|
||||
return;
|
||||
}
|
||||
if (!map.containsKey(type.getKeyName())) {
|
||||
poiTypes.add(type);
|
||||
map.put(type.getKeyName(), type);
|
||||
|
|
|
@ -87,6 +87,11 @@ public class OsmMapUtils {
|
|||
}
|
||||
|
||||
public static LatLon getComplexPolyCenter(Collection<Node> outer, List<List<Node>> inner) {
|
||||
if (outer.size() > 3 && outer.size() <= 5 && inner == null) {
|
||||
List<Node> sub = new ArrayList<>(outer);
|
||||
return getWeightCenterForNodes(sub.subList(0, sub.size()-1));
|
||||
}
|
||||
|
||||
final List<List<LatLon>> rings = new ArrayList<>();
|
||||
List<LatLon> outerRing = new ArrayList<>();
|
||||
|
||||
|
|
|
@ -442,7 +442,8 @@ public class BinaryRoutePlanner {
|
|||
segmentDist += squareRootDist(x, y, prevx, prevy);
|
||||
|
||||
// 2.1 calculate possible obstacle plus time
|
||||
double obstacle = ctx.getRouter().defineRoutingObstacle(road, segmentPoint);
|
||||
|
||||
double obstacle = ctx.getRouter().defineRoutingObstacle(road, segmentPoint, (dir && !reverseWaySearch));
|
||||
if (obstacle < 0) {
|
||||
directionAllowed = false;
|
||||
continue;
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.Map;
|
|||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import gnu.trove.list.array.TIntArrayList;
|
||||
import gnu.trove.set.hash.TLongHashSet;
|
||||
|
||||
public class GeneralRouter implements VehicleRouter {
|
||||
|
@ -353,33 +354,71 @@ public class GeneralRouter implements VehicleRouter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public float defineObstacle(RouteDataObject road, int point) {
|
||||
public float defineObstacle(RouteDataObject road, int point, boolean dir) {
|
||||
int[] pointTypes = road.getPointTypes(point);
|
||||
if(pointTypes != null) {
|
||||
Float obst = getCache(RouteDataObjectAttribute.OBSTACLES, road.region, pointTypes);
|
||||
Float obst = getCache(RouteDataObjectAttribute.OBSTACLES, road.region, pointTypes, dir);
|
||||
if(obst == null) {
|
||||
obst = getObjContext(RouteDataObjectAttribute.OBSTACLES).evaluateFloat(road.region, pointTypes, 0);
|
||||
putCache(RouteDataObjectAttribute.OBSTACLES, road.region, pointTypes, obst);
|
||||
int[] filteredPointTypes = filterDirectionTags(road, pointTypes, dir);
|
||||
obst = getObjContext(RouteDataObjectAttribute.OBSTACLES).evaluateFloat(road.region, filteredPointTypes, 0);
|
||||
putCache(RouteDataObjectAttribute.OBSTACLES, road.region, pointTypes, obst, dir);
|
||||
}
|
||||
return obst;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TIntArrayList filteredRules = new TIntArrayList();
|
||||
|
||||
@Override
|
||||
public float defineRoutingObstacle(RouteDataObject road, int point) {
|
||||
public float defineRoutingObstacle(RouteDataObject road, int point, boolean dir) {
|
||||
int[] pointTypes = road.getPointTypes(point);
|
||||
if(pointTypes != null){
|
||||
Float obst = getCache(RouteDataObjectAttribute.ROUTING_OBSTACLES, road.region, pointTypes);
|
||||
if(obst == null) {
|
||||
obst = getObjContext(RouteDataObjectAttribute.ROUTING_OBSTACLES).evaluateFloat(road.region, pointTypes, 0);
|
||||
putCache(RouteDataObjectAttribute.ROUTING_OBSTACLES, road.region, pointTypes, obst);
|
||||
if(pointTypes != null) {
|
||||
Float obst = getCache(RouteDataObjectAttribute.ROUTING_OBSTACLES, road.region, pointTypes, dir);
|
||||
if(obst == null) {
|
||||
int[] filteredPointTypes = filterDirectionTags(road, pointTypes, dir);
|
||||
obst = getObjContext(RouteDataObjectAttribute.ROUTING_OBSTACLES).evaluateFloat(road.region, filteredPointTypes, 0);
|
||||
putCache(RouteDataObjectAttribute.ROUTING_OBSTACLES, road.region, pointTypes, obst, dir);
|
||||
}
|
||||
return obst;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int[] filterDirectionTags(RouteDataObject road, int[] pointTypes, boolean dir) {
|
||||
int wayOppositeDirection = dir ? -1 : 1;
|
||||
int direction = 0;
|
||||
int tdirection = 0;
|
||||
for (int i = 0; i < pointTypes.length; i++) {
|
||||
if (pointTypes[i] == road.region.directionBackward) {
|
||||
direction = -1;
|
||||
} else if(pointTypes[i] == road.region.directionForward) {
|
||||
direction = 1;
|
||||
} else if (pointTypes[i] == road.region.directionTrafficSignalsBackward) {
|
||||
tdirection = -1;
|
||||
} else if(pointTypes[i] == road.region.directionTrafficSignalsForward) {
|
||||
tdirection = 1;
|
||||
}
|
||||
}
|
||||
if (direction != 0 || tdirection != 0) {
|
||||
TIntArrayList filteredRules = new TIntArrayList();
|
||||
for (int i = 0; i < pointTypes.length; i++) {
|
||||
boolean skip = false;
|
||||
if ((pointTypes[i] == road.region.stopSign || pointTypes[i] == road.region.giveWaySign)
|
||||
&& direction == wayOppositeDirection) {
|
||||
skip = true;
|
||||
} else if (pointTypes[i] == road.region.trafficSignals && tdirection == wayOppositeDirection) {
|
||||
skip = true;
|
||||
}
|
||||
if (!skip) {
|
||||
filteredRules.add(pointTypes[i]);
|
||||
}
|
||||
}
|
||||
return filteredRules.toArray();
|
||||
}
|
||||
return pointTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double defineHeightObstacle(RouteDataObject road, short startIndex, short endIndex) {
|
||||
if(!heightObstacles) {
|
||||
|
@ -463,16 +502,20 @@ public class GeneralRouter implements VehicleRouter {
|
|||
Float sp = getCache(RouteDataObjectAttribute.ROAD_PRIORITIES, road);
|
||||
if(sp == null) {
|
||||
sp = getObjContext(RouteDataObjectAttribute.ROAD_PRIORITIES).evaluateFloat(road, 1f);
|
||||
putCache(RouteDataObjectAttribute.ROAD_PRIORITIES, road, sp);
|
||||
putCache(RouteDataObjectAttribute.ROAD_PRIORITIES, road, sp, false);
|
||||
}
|
||||
return sp;
|
||||
}
|
||||
|
||||
private void putCache(RouteDataObjectAttribute attr, RouteDataObject road, Float val) {
|
||||
putCache(attr, road.region, road.types, val);
|
||||
putCache(attr, road.region, road.types, val, false);
|
||||
}
|
||||
|
||||
private void putCache(RouteDataObjectAttribute attr, RouteRegion reg, int[] types, Float val) {
|
||||
private void putCache(RouteDataObjectAttribute attr, RouteDataObject road, Float val, boolean extra) {
|
||||
putCache(attr, road.region, road.types, val, extra);
|
||||
}
|
||||
|
||||
private void putCache(RouteDataObjectAttribute attr, RouteRegion reg, int[] types, Float val, boolean extra) {
|
||||
Map<RouteRegion, Map<IntHolder, Float>> ch = evalCache[attr.ordinal()];
|
||||
if (USE_CACHE) {
|
||||
Map<IntHolder, Float> rM = ch.get(reg);
|
||||
|
@ -480,40 +523,56 @@ public class GeneralRouter implements VehicleRouter {
|
|||
rM = new HashMap<IntHolder, Float>();
|
||||
ch.put(reg, rM);
|
||||
}
|
||||
rM.put(new IntHolder(types), val);
|
||||
rM.put(new IntHolder(types, extra), val);
|
||||
}
|
||||
TIMER += System.nanoTime();
|
||||
}
|
||||
|
||||
class IntHolder {
|
||||
private final int[] array;
|
||||
IntHolder(int[] ts) { array = ts; }
|
||||
@Override public int hashCode() { return Arrays.hashCode(array); }
|
||||
@Override public boolean equals(Object other) {
|
||||
if (array == other) { return true; }
|
||||
if (! (other instanceof IntHolder) ) {
|
||||
return false;
|
||||
}
|
||||
//noinspection unchecked
|
||||
return Arrays.equals(array, ((IntHolder) other).array);
|
||||
}
|
||||
private final int[] array;
|
||||
private final boolean extra;
|
||||
|
||||
IntHolder(int[] ts, boolean extra) {
|
||||
array = ts;
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(array) + (extra ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (array == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof IntHolder)) {
|
||||
return false;
|
||||
}
|
||||
if (((IntHolder) other).extra != this.extra) {
|
||||
return false;
|
||||
}
|
||||
// noinspection unchecked
|
||||
return Arrays.equals(array, ((IntHolder) other).array);
|
||||
}
|
||||
}
|
||||
|
||||
private Float getCache(RouteDataObjectAttribute attr, RouteDataObject road) {
|
||||
return getCache(attr, road.region, road.types);
|
||||
return getCache(attr, road.region, road.types, false);
|
||||
}
|
||||
|
||||
private Float getCache(RouteDataObjectAttribute attr, RouteRegion reg, int[] types) {
|
||||
private Float getCache(RouteDataObjectAttribute attr, RouteRegion reg, int[] types, boolean extra) {
|
||||
Map<RouteRegion, Map<IntHolder, Float>> ch = evalCache[attr.ordinal()];
|
||||
TIMER -= System.nanoTime();
|
||||
// TIMER -= System.nanoTime();
|
||||
if (USE_CACHE) {
|
||||
Map<IntHolder, Float> rM = ch.get(reg);
|
||||
if (rM == null) {
|
||||
return null;
|
||||
}
|
||||
Float vl = rM.get(new IntHolder(types));
|
||||
Float vl = rM.get(new IntHolder(types, extra));
|
||||
if(vl != null) {
|
||||
TIMER += System.nanoTime();
|
||||
// TIMER += System.nanoTime();
|
||||
return vl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,12 +23,18 @@ import net.osmand.util.MapUtils;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import gnu.trove.list.array.TIntArrayList;
|
||||
|
||||
public class RoutePlannerFrontEnd {
|
||||
|
||||
protected static final Log log = PlatformUtil.getLog(RoutePlannerFrontEnd.class);
|
||||
// Check issue #8649
|
||||
protected static final double GPS_POSSIBLE_ERROR = 7;
|
||||
public boolean useSmartRouteRecalculation = true;
|
||||
|
||||
public static double AVERAGE_SPLIT_DISTANCE_GPX = 1500;
|
||||
public static double MINIMUM_POINT_APPROXIMATION = 50;
|
||||
public static double MINIMUM_STRAIGHT_STEP_APPROXIMATION = 50;
|
||||
|
||||
|
||||
public RoutePlannerFrontEnd() {
|
||||
|
@ -39,6 +45,44 @@ public class RoutePlannerFrontEnd {
|
|||
NORMAL,
|
||||
COMPLEX
|
||||
}
|
||||
|
||||
public static class GpxApproximationResult {
|
||||
public int routeCalculations = 0;
|
||||
public int routePointsSearched = 0;
|
||||
public int routeDistCalculations = 0;
|
||||
public List<RouteSegmentResult> res = new ArrayList<RouteSegmentResult>();
|
||||
public int routeDistance;
|
||||
public int routeDistanceUnmatched;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(">> GPX approximation (%d of %d m route calcs, %d route points searched) for %d m: %d m umatched",
|
||||
routeCalculations, routeDistCalculations, routePointsSearched, routeDistance, routeDistanceUnmatched);
|
||||
}
|
||||
|
||||
public double distFromLastPoint(LatLon startPoint) {
|
||||
if(res.size() > 0) {
|
||||
return MapUtils.getDistance(getLastPoint(), startPoint);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public LatLon getLastPoint() {
|
||||
if(res.size() > 0) {
|
||||
return res.get(res.size() - 1).getEndPoint();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class GpxPoint {
|
||||
public int ind;
|
||||
public LatLon loc;
|
||||
public double cumDist;
|
||||
public RouteSegmentPoint pnt;
|
||||
public List<RouteSegmentResult> routeToTarget;
|
||||
public int targetInd = -1;
|
||||
}
|
||||
|
||||
public RoutingContext buildRoutingContext(RoutingConfiguration config, NativeLibrary nativeLibrary, BinaryMapIndexReader[] map, RouteCalculationMode rm) {
|
||||
return new RoutingContext(config, nativeLibrary, map, rm);
|
||||
|
@ -148,6 +192,235 @@ public class RoutePlannerFrontEnd {
|
|||
}
|
||||
|
||||
|
||||
// TODO add missing turns for straight lines
|
||||
// TODO smoothness is not correct for car routing
|
||||
// TODO native crash
|
||||
// TODO big gaps when there is no match
|
||||
// TODO not correct bicycle-> pedestrian
|
||||
// TODO slow - too many findRouteSegment
|
||||
// TODO fix progress / timings routing /
|
||||
// TODO smoothen straight line Douglas-Peucker (remove noise)
|
||||
public GpxApproximationResult searchGpxRoute(final RoutingContext ctx, List<LatLon> points) throws IOException, InterruptedException {
|
||||
GpxApproximationResult gctx = new GpxApproximationResult();
|
||||
List<GpxPoint> gpxPoints = generageGpxPoints(points, gctx);
|
||||
GpxPoint start = gpxPoints.size() > 0 ? gpxPoints.get(0) : null;
|
||||
|
||||
while (start != null) {
|
||||
double routeDist = AVERAGE_SPLIT_DISTANCE_GPX;
|
||||
GpxPoint next = findNextGpxPointWithin(gctx, gpxPoints, start, routeDist);
|
||||
boolean routeFound = false;
|
||||
if (next != null && initRoutingPoint(start, gctx, ctx, MINIMUM_POINT_APPROXIMATION)) {
|
||||
boolean firstAttempt = true;
|
||||
while ((firstAttempt || next.cumDist - start.cumDist > MINIMUM_POINT_APPROXIMATION) && !routeFound) {
|
||||
firstAttempt = false;
|
||||
routeFound = initRoutingPoint(next, gctx, ctx, MINIMUM_POINT_APPROXIMATION);
|
||||
if (routeFound) {
|
||||
routeFound = findGpxRouteSegment(ctx, gctx, gpxPoints, start, next);
|
||||
}
|
||||
if (!routeFound) {
|
||||
// route is not found move next point closer to start point (distance / 2)
|
||||
routeDist = routeDist / 2;
|
||||
next = findNextGpxPointWithin(gctx, gpxPoints, start, routeDist);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (routeFound) {
|
||||
// route is found, cut the end of the route and move to next iteration
|
||||
start = next;
|
||||
} else {
|
||||
// route is not found, move start point by
|
||||
start = findNextGpxPointWithin(gctx, gpxPoints, start, MINIMUM_STRAIGHT_STEP_APPROXIMATION);
|
||||
}
|
||||
|
||||
}
|
||||
calculateGpxRoute(ctx, gctx, gpxPoints);
|
||||
|
||||
if (!gctx.res.isEmpty()) {
|
||||
new RouteResultPreparation().printResults(ctx, points.get(0), points.get(points.size() - 1), gctx.res);
|
||||
System.out.println(gctx);
|
||||
}
|
||||
return gctx;
|
||||
}
|
||||
|
||||
private void calculateGpxRoute(final RoutingContext ctx, GpxApproximationResult gctx, List<GpxPoint> gpxPoints) {
|
||||
RouteRegion reg = new RouteRegion();
|
||||
reg.initRouteEncodingRule(0, "highway", "unmatched");
|
||||
TIntArrayList lastStraightLine = null;
|
||||
for (int i = 0; i < gpxPoints.size(); ) {
|
||||
GpxPoint pnt = gpxPoints.get(i);
|
||||
if (pnt.routeToTarget != null && !pnt.routeToTarget.isEmpty()) {
|
||||
LatLon startPoint = pnt.routeToTarget.get(0).getStartPoint();
|
||||
if (lastStraightLine != null) {
|
||||
lastStraightLine.add(MapUtils.get31TileNumberX(startPoint.getLongitude()));
|
||||
lastStraightLine.add(MapUtils.get31TileNumberY(startPoint.getLatitude()));
|
||||
addStraightLine(gctx.res, lastStraightLine, reg, gctx);
|
||||
lastStraightLine = null;
|
||||
}
|
||||
// TODO
|
||||
double distLastPnt = gctx.distFromLastPoint(pnt.routeToTarget.get(0).getStartPoint());
|
||||
double gpxDistPnt = gctx.distFromLastPoint(pnt.loc);
|
||||
if (distLastPnt > MINIMUM_POINT_APPROXIMATION / 5 || gpxDistPnt > MINIMUM_POINT_APPROXIMATION / 5) {
|
||||
System.out.println(String.format("????? routePnt - prevPnt = %f, gpxPoint - prevPnt = %f ",
|
||||
distLastPnt, gpxDistPnt));
|
||||
}
|
||||
gctx.res.addAll(pnt.routeToTarget);
|
||||
i = pnt.targetInd;
|
||||
} else {
|
||||
// add straight line from i -> i+1
|
||||
if (lastStraightLine == null) {
|
||||
lastStraightLine = new TIntArrayList();
|
||||
// make smooth connection
|
||||
if(gctx.distFromLastPoint(pnt.loc) > 1) {
|
||||
lastStraightLine.add(MapUtils.get31TileNumberX(gctx.getLastPoint().getLongitude()));
|
||||
lastStraightLine.add(MapUtils.get31TileNumberY(gctx.getLastPoint().getLatitude()));
|
||||
}
|
||||
}
|
||||
lastStraightLine.add(MapUtils.get31TileNumberX(pnt.loc.getLongitude()));
|
||||
lastStraightLine.add(MapUtils.get31TileNumberY(pnt.loc.getLatitude()));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (lastStraightLine != null) {
|
||||
addStraightLine(gctx.res, lastStraightLine, reg, gctx);
|
||||
lastStraightLine = null;
|
||||
}
|
||||
// clean turns to recaculate them
|
||||
cleanupResultAndAddTurns(ctx, gctx);
|
||||
}
|
||||
|
||||
private List<GpxPoint> generageGpxPoints(List<LatLon> points, GpxApproximationResult gctx) {
|
||||
List<GpxPoint> gpxPoints = new ArrayList<>(points.size());
|
||||
GpxPoint prev = null;
|
||||
for(int i = 0; i < points.size(); i++) {
|
||||
GpxPoint p = new GpxPoint();
|
||||
p.ind = i;
|
||||
p.loc = points.get(i);
|
||||
if (prev != null) {
|
||||
p.cumDist = MapUtils.getDistance(p.loc, prev.loc) + prev.cumDist;
|
||||
}
|
||||
gpxPoints.add(p);
|
||||
gctx.routeDistance = (int) p.cumDist;
|
||||
prev = p;
|
||||
}
|
||||
return gpxPoints;
|
||||
}
|
||||
|
||||
private void cleanupResultAndAddTurns(final RoutingContext ctx, GpxApproximationResult gctx) {
|
||||
// cleanup double joints
|
||||
int LOOK_AHEAD = 4;
|
||||
for(int i = 0; i < gctx.res.size(); i++) {
|
||||
RouteSegmentResult s = gctx.res.get(i);
|
||||
for(int j = i + 2; j <= i + LOOK_AHEAD && j < gctx.res.size(); j++) {
|
||||
RouteSegmentResult e = gctx.res.get(j);
|
||||
if(e.getStartPoint().equals(s.getEndPoint())) {
|
||||
while((--j) != i) {
|
||||
gctx.res.remove(j);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
RouteResultPreparation preparation = new RouteResultPreparation();
|
||||
for (RouteSegmentResult r : gctx.res) {
|
||||
r.setTurnType(null);
|
||||
r.setDescription("");
|
||||
}
|
||||
preparation.prepareTurnResults(ctx, gctx.res);
|
||||
}
|
||||
|
||||
private void addStraightLine(List<RouteSegmentResult> res, TIntArrayList lastStraightLine, RouteRegion reg, GpxApproximationResult gctx) {
|
||||
RouteDataObject rdo = new RouteDataObject(reg);
|
||||
int l = lastStraightLine.size() / 2;
|
||||
rdo.pointsX = new int[l];
|
||||
rdo.pointsY = new int[l];
|
||||
rdo.types = new int[] { 0 } ;
|
||||
rdo.id = -1;
|
||||
for (int i = 0; i < l; i++) {
|
||||
rdo.pointsX[i] = lastStraightLine.get(i * 2);
|
||||
rdo.pointsY[i] = lastStraightLine.get(i * 2 + 1);
|
||||
if(i > 0) {
|
||||
double dist = MapUtils.squareRootDist31(rdo.pointsX[i], rdo.pointsY[i], rdo.pointsX[i-1], rdo.pointsY[i-1]);
|
||||
gctx.routeDistanceUnmatched += dist;
|
||||
}
|
||||
}
|
||||
res.add(new RouteSegmentResult(rdo, 0, rdo.getPointsLength() - 1));
|
||||
}
|
||||
|
||||
private boolean initRoutingPoint(GpxPoint start, GpxApproximationResult gctx, RoutingContext ctx, double distThreshold) throws IOException {
|
||||
if (start != null && start.pnt == null) {
|
||||
gctx.routePointsSearched++;
|
||||
RouteSegmentPoint rsp = findRouteSegment(start.loc.getLatitude(), start.loc.getLongitude(), ctx, null, false);
|
||||
if (MapUtils.getDistance(rsp.getPreciseLatLon(), start.loc) < distThreshold) {
|
||||
start.pnt = rsp;
|
||||
}
|
||||
}
|
||||
return start != null && start.pnt != null;
|
||||
}
|
||||
|
||||
private GpxPoint findNextGpxPointWithin(GpxApproximationResult gctx, List<GpxPoint> gpxPoints,
|
||||
GpxPoint start, double dist) {
|
||||
int targetInd = start.ind + 1;
|
||||
GpxPoint target = null;
|
||||
while (targetInd < gpxPoints.size()) {
|
||||
target = gpxPoints.get(targetInd);
|
||||
if (target.cumDist - start.cumDist > dist) {
|
||||
break;
|
||||
}
|
||||
targetInd++;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
private boolean findGpxRouteSegment(final RoutingContext ctx, GpxApproximationResult gctx, List<GpxPoint> gpxPoints,
|
||||
GpxPoint start, GpxPoint target) throws IOException, InterruptedException {
|
||||
List<RouteSegmentResult> res = null;
|
||||
boolean routeIsCorrect = false;
|
||||
if (start.pnt != null && target.pnt != null) {
|
||||
gctx.routeDistCalculations += (target.cumDist - start.cumDist);
|
||||
gctx.routeCalculations++;
|
||||
res = searchRouteInternalPrepare(ctx, start.pnt, target.pnt, null);
|
||||
routeIsCorrect = res != null && !res.isEmpty();
|
||||
if (routeIsCorrect) {
|
||||
makeStartEndPointsPrecise(res, start.pnt.getPreciseLatLon(), target.pnt.getPreciseLatLon(), null);
|
||||
}
|
||||
for (int k = start.ind + 1; routeIsCorrect && k < target.ind; k++) {
|
||||
GpxPoint ipoint = gpxPoints.get(k);
|
||||
if (!pointCloseEnough(ipoint, res)) {
|
||||
routeIsCorrect = false;
|
||||
}
|
||||
}
|
||||
if (routeIsCorrect) {
|
||||
start.routeToTarget = res;
|
||||
start.targetInd = target.ind;
|
||||
}
|
||||
}
|
||||
return routeIsCorrect;
|
||||
}
|
||||
|
||||
private boolean pointCloseEnough(GpxPoint ipoint, List<RouteSegmentResult> res) {
|
||||
int px = MapUtils.get31TileNumberX(ipoint.loc.getLongitude());
|
||||
int py = MapUtils.get31TileNumberY(ipoint.loc.getLatitude());
|
||||
double SQR = MINIMUM_POINT_APPROXIMATION * MINIMUM_POINT_APPROXIMATION * 4;
|
||||
for (RouteSegmentResult sr : res) {
|
||||
int start = sr.getStartPointIndex();
|
||||
int end = sr.getEndPointIndex();
|
||||
if (sr.getStartPointIndex() > sr.getEndPointIndex()) {
|
||||
start = sr.getEndPointIndex();
|
||||
end = sr.getStartPointIndex();
|
||||
}
|
||||
for (int i = start; i < end; i++) {
|
||||
RouteDataObject r = sr.getObject();
|
||||
QuadPoint pp = MapUtils.getProjectionPoint31(px, py, r.getPoint31XTile(i), r.getPoint31YTile(i),
|
||||
r.getPoint31XTile(i + 1), r.getPoint31YTile(i + 1));
|
||||
double currentsDist = squareDist((int) pp.x, (int) pp.y, px, py);
|
||||
if (currentsDist <= SQR) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean needRequestPrivateAccessRouting(RoutingContext ctx, List<LatLon> points) throws IOException {
|
||||
boolean res = false;
|
||||
GeneralRouter router = (GeneralRouter) ctx.getRouter();
|
||||
|
@ -510,7 +783,7 @@ public class RoutePlannerFrontEnd {
|
|||
ctx.relaxedSegments += local.relaxedSegments;
|
||||
ctx.routingTime += local.routingTime;
|
||||
|
||||
local.unloadAllData(ctx);
|
||||
// local.unloadAllData(ctx);
|
||||
if (restPartRecalculatedRoute != null) {
|
||||
results.addAll(restPartRecalculatedRoute);
|
||||
break;
|
||||
|
@ -539,5 +812,6 @@ public class RoutePlannerFrontEnd {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -175,6 +175,11 @@ public class RouteResultPreparation {
|
|||
splitRoadsAndAttachRoadSegments(ctx, result, recalculation);
|
||||
calculateTimeSpeed(ctx, result);
|
||||
|
||||
prepareTurnResults(ctx, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void prepareTurnResults(RoutingContext ctx, List<RouteSegmentResult> result) {
|
||||
for (int i = 0; i < result.size(); i ++) {
|
||||
TurnType turnType = getTurnInfo(result, i, ctx.leftSideNavigation);
|
||||
result.get(i).setTurnType(turnType);
|
||||
|
@ -184,7 +189,6 @@ public class RouteResultPreparation {
|
|||
ignorePrecedingStraightsOnSameIntersection(ctx.leftSideNavigation, result);
|
||||
justifyUTurns(ctx.leftSideNavigation, result);
|
||||
addTurnInfoDescriptions(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void ignorePrecedingStraightsOnSameIntersection(boolean leftside, List<RouteSegmentResult> result) {
|
||||
|
@ -270,7 +274,7 @@ public class RouteResultPreparation {
|
|||
double d = measuredDist(road.getPoint31XTile(j), road.getPoint31YTile(j), road.getPoint31XTile(next),
|
||||
road.getPoint31YTile(next));
|
||||
distance += d;
|
||||
double obstacle = ctx.getRouter().defineObstacle(road, j);
|
||||
double obstacle = ctx.getRouter().defineObstacle(road, j, plus);
|
||||
if (obstacle < 0) {
|
||||
obstacle = 0;
|
||||
}
|
||||
|
@ -283,11 +287,12 @@ public class RouteResultPreparation {
|
|||
float height = heightDistanceArray[heightIndex];
|
||||
if (prevHeight != -99999.0f) {
|
||||
float heightDiff = height - prevHeight;
|
||||
if (heightDiff > 0) { //ascent only
|
||||
distOnRoadToPass += heightDiff * 6.0f; //Naismith's rule: add 1 hour per every 600m of ascent
|
||||
if (heightDiff > 0) { // ascent only
|
||||
distOnRoadToPass += heightDiff * 6.0f; // Naismith's rule: add 1 hour per every 600m of
|
||||
// ascent
|
||||
}
|
||||
}
|
||||
prevHeight = height;
|
||||
prevHeight = height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -359,7 +364,7 @@ public class RouteResultPreparation {
|
|||
|
||||
private void checkAndInitRouteRegion(RoutingContext ctx, RouteDataObject road) throws IOException {
|
||||
BinaryMapIndexReader reader = ctx.reverseMap.get(road.region);
|
||||
if(reader != null) {
|
||||
if (reader != null) {
|
||||
reader.initRouteRegion(road.region);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ public interface VehicleRouter {
|
|||
/**
|
||||
* return delay in seconds (0 no obstacles)
|
||||
*/
|
||||
public float defineObstacle(RouteDataObject road, int point);
|
||||
public float defineObstacle(RouteDataObject road, int point, boolean dir);
|
||||
|
||||
/**
|
||||
* return delay in seconds for height obstacles
|
||||
|
@ -40,7 +40,7 @@ public interface VehicleRouter {
|
|||
/**
|
||||
* return delay in seconds (0 no obstacles)
|
||||
*/
|
||||
public float defineRoutingObstacle(RouteDataObject road, int point);
|
||||
public float defineRoutingObstacle(RouteDataObject road, int point, boolean dir);
|
||||
|
||||
/**
|
||||
* return routing speed in m/s for vehicle for specified road
|
||||
|
|
|
@ -35,6 +35,7 @@ import net.osmand.util.MapUtils;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
@ -669,6 +670,11 @@ public class SearchCoreFactory {
|
|||
return phrase.getNextRadiusSearch(BBOX_RADIUS);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class PoiTypeResult {
|
||||
public AbstractPoiType pt;
|
||||
public Set<String> foundWords = new LinkedHashSet<String>();
|
||||
}
|
||||
|
||||
public static class SearchAmenityTypesAPI extends SearchBaseAPI {
|
||||
|
||||
|
@ -704,68 +710,102 @@ public class SearchCoreFactory {
|
|||
}
|
||||
}
|
||||
|
||||
public Map<AbstractPoiType, List<String>> getPoiTypeResults(NameStringMatcher nm, boolean includeAdditionals) {
|
||||
Map<AbstractPoiType, List<String>> results = new LinkedHashMap<>();
|
||||
public Map<String, PoiTypeResult> getPoiTypeResults(NameStringMatcher nm, NameStringMatcher nmAdditional) {
|
||||
Map<String, PoiTypeResult> results = new LinkedHashMap<>();
|
||||
for (AbstractPoiType pf : topVisibleFilters) {
|
||||
checkPoiType(nm, pf, results);
|
||||
PoiTypeResult res = checkPoiType(nm, pf);
|
||||
if(res != null) {
|
||||
results.put(res.pt.getKeyName(), res);
|
||||
}
|
||||
}
|
||||
if (nmAdditional != null) {
|
||||
addAditonals(nmAdditional, results, types.getOtherMapCategory());
|
||||
}
|
||||
for (PoiCategory c : categories) {
|
||||
checkPoiType(nm, c, results);
|
||||
PoiTypeResult res = checkPoiType(nm, c);
|
||||
if(res != null) {
|
||||
results.put(res.pt.getKeyName(), res);
|
||||
}
|
||||
if (nmAdditional != null) {
|
||||
addAditonals(nmAdditional, results, c);
|
||||
}
|
||||
}
|
||||
Iterator<Entry<String, PoiType>> it = translatedNames.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry<String, PoiType> e = it.next();
|
||||
PoiType pt = e.getValue();
|
||||
if (pt.getCategory() != types.getOtherMapCategory()) {
|
||||
checkPoiType(nm, pt, results);
|
||||
List<PoiType> additionals = pt.getPoiAdditionals();
|
||||
if (additionals != null && includeAdditionals) {
|
||||
for (PoiType a : additionals) {
|
||||
if (!results.containsKey(a)) {
|
||||
String enTranslation = a.getEnTranslation().toLowerCase();
|
||||
if (!"yes".equals(enTranslation) && !"no".equals(enTranslation)) {
|
||||
checkPoiType(nm, a, results);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pt.getCategory() != types.getOtherMapCategory() && !pt.isReference()) {
|
||||
PoiTypeResult res = checkPoiType(nm, pt);
|
||||
if(res != null) {
|
||||
results.put(res.pt.getKeyName(), res);
|
||||
}
|
||||
if (nmAdditional != null) {
|
||||
addAditonals(nmAdditional, results, pt);
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private void checkPoiType(NameStringMatcher nm, AbstractPoiType pf, Map<AbstractPoiType, List<String>> results) {
|
||||
List<String> lst = results.get(pf);
|
||||
boolean nl = lst == null;
|
||||
private void addAditonals(NameStringMatcher nm, Map<String, PoiTypeResult> results, AbstractPoiType pt) {
|
||||
List<PoiType> additionals = pt.getPoiAdditionals();
|
||||
if (additionals != null) {
|
||||
for (PoiType a : additionals) {
|
||||
PoiTypeResult existingResult = results.get(a.getKeyName());
|
||||
if (existingResult != null) {
|
||||
PoiAdditionalCustomFilter f ;
|
||||
if (existingResult.pt instanceof PoiAdditionalCustomFilter) {
|
||||
f = (PoiAdditionalCustomFilter) existingResult.pt;
|
||||
} else {
|
||||
f = new PoiAdditionalCustomFilter(types, (PoiType) existingResult.pt);
|
||||
}
|
||||
f.additionalPoiTypes.add(a);
|
||||
existingResult.pt = f;
|
||||
} else {
|
||||
String enTranslation = a.getEnTranslation().toLowerCase();
|
||||
if (!"no".equals(enTranslation) // && !"yes".equals(enTranslation)
|
||||
) {
|
||||
PoiTypeResult ptr = checkPoiType(nm, a);
|
||||
if (ptr != null) {
|
||||
results.put(a.getKeyName(), ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PoiTypeResult checkPoiType(NameStringMatcher nm, AbstractPoiType pf) {
|
||||
PoiTypeResult res = null;
|
||||
if (nm.matches(pf.getTranslation())) {
|
||||
lst = addToList(pf.getTranslation(), lst);
|
||||
res = addIfMatch(nm, pf.getTranslation(), pf, res);
|
||||
}
|
||||
if (nm.matches(pf.getEnTranslation())) {
|
||||
lst = addToList(pf.getEnTranslation(), lst);
|
||||
res = addIfMatch(nm, pf.getEnTranslation(), pf, res);
|
||||
}
|
||||
if (nm.matches(pf.getKeyName())) {
|
||||
lst = addToList(pf.getKeyName().replace('_', ' '), lst);
|
||||
res = addIfMatch(nm, pf.getKeyName().replace('_', ' '), pf, res);
|
||||
}
|
||||
|
||||
if (nm.matches(pf.getSynonyms())) {
|
||||
String[] synonyms = pf.getSynonyms().split(";");
|
||||
for (String synonym : synonyms) {
|
||||
if (nm.matches(synonym)) {
|
||||
lst = addToList(synonym, lst);
|
||||
}
|
||||
res = addIfMatch(nm, synonym, pf, res);
|
||||
}
|
||||
}
|
||||
if(lst != null && nl) {
|
||||
results.put(pf, lst);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private List<String> addToList(String s, List<String> lst) {
|
||||
if(lst == null) {
|
||||
lst = new ArrayList<>();
|
||||
private PoiTypeResult addIfMatch(NameStringMatcher nm, String s, AbstractPoiType pf, PoiTypeResult res) {
|
||||
if (nm.matches(s)) {
|
||||
if (res == null) {
|
||||
res = new PoiTypeResult();
|
||||
res.pt = pf;
|
||||
}
|
||||
res.foundWords.add(s);
|
||||
|
||||
}
|
||||
lst.add(s);
|
||||
return lst;
|
||||
return res;
|
||||
}
|
||||
|
||||
private void initPoiTypes() {
|
||||
|
@ -781,6 +821,7 @@ public class SearchCoreFactory {
|
|||
public boolean search(SearchPhrase phrase, SearchResultMatcher resultMatcher) throws IOException {
|
||||
boolean showTopFiltersOnly = !phrase.isUnknownSearchWordPresent();
|
||||
NameStringMatcher nm = phrase.getFirstUnknownNameStringMatcher();
|
||||
|
||||
initPoiTypes();
|
||||
if (showTopFiltersOnly) {
|
||||
for (AbstractPoiType pt : topVisibleFilters) {
|
||||
|
@ -792,11 +833,13 @@ public class SearchCoreFactory {
|
|||
|
||||
} else {
|
||||
boolean includeAdditional = !phrase.hasMoreThanOneUnknownSearchWord();
|
||||
Map<AbstractPoiType, List<String>> poiTypes = getPoiTypeResults(nm, includeAdditional);
|
||||
for (Entry<AbstractPoiType, List<String>> pt : poiTypes.entrySet()) {
|
||||
NameStringMatcher nmAdditional = includeAdditional ?
|
||||
new NameStringMatcher(phrase.getFirstUnknownSearchWord(), StringMatcherMode.CHECK_EQUALS_FROM_SPACE) : null;
|
||||
Map<String, PoiTypeResult> poiTypes = getPoiTypeResults(nm, nmAdditional);
|
||||
for (PoiTypeResult ptr : poiTypes.values()) {
|
||||
boolean match = !phrase.isFirstUnknownSearchWordComplete();
|
||||
if (!match) {
|
||||
for (String foundName : pt.getValue()) {
|
||||
for (String foundName : ptr.foundWords) {
|
||||
CollatorStringMatcher csm = new CollatorStringMatcher(foundName, StringMatcherMode.CHECK_ONLY_STARTS_WITH);
|
||||
match = csm.matches(phrase.getUnknownSearchPhrase());
|
||||
if (match) {
|
||||
|
@ -806,9 +849,9 @@ public class SearchCoreFactory {
|
|||
}
|
||||
if (match) {
|
||||
SearchResult res = new SearchResult(phrase);
|
||||
res.localeName = pt.getKey().getTranslation();
|
||||
res.object = pt.getKey();
|
||||
addPoiTypeResult(phrase, resultMatcher, showTopFiltersOnly, getStandardFilterId(pt.getKey()),
|
||||
res.localeName = ptr.pt.getTranslation();
|
||||
res.object = ptr.pt;
|
||||
addPoiTypeResult(phrase, resultMatcher, showTopFiltersOnly, getStandardFilterId(ptr.pt),
|
||||
res);
|
||||
}
|
||||
}
|
||||
|
@ -826,7 +869,7 @@ public class SearchCoreFactory {
|
|||
}
|
||||
|
||||
private void addPoiTypeResult(SearchPhrase phrase, SearchResultMatcher resultMatcher, boolean showTopFiltersOnly,
|
||||
String stdFilterId , SearchResult res) {
|
||||
String stdFilterId, SearchResult res) {
|
||||
res.priorityDistance = 0;
|
||||
res.objectType = ObjectType.POI_TYPE;
|
||||
res.firstUnknownWordMatches = true;
|
||||
|
@ -919,7 +962,7 @@ public class SearchCoreFactory {
|
|||
SearchPoiTypeFilter poiTypeFilter = null;
|
||||
String nameFilter = null;
|
||||
int countExtraWords = 0;
|
||||
Map<String, PoiType> poiAdditionals = new LinkedHashMap<String, PoiType>();
|
||||
Set<String> poiAdditionals = new LinkedHashSet<>();
|
||||
if (phrase.isLastWord(ObjectType.POI_TYPE)) {
|
||||
Object obj = phrase.getLastSelectedWord().getResult().object;
|
||||
if (obj instanceof AbstractPoiType) {
|
||||
|
@ -932,11 +975,13 @@ public class SearchCoreFactory {
|
|||
nameFilter = phrase.getUnknownSearchPhrase();
|
||||
} else if (searchAmenityTypesAPI != null && phrase.isFirstUnknownSearchWordComplete()) {
|
||||
NameStringMatcher nm = phrase.getFirstUnknownNameStringMatcher();
|
||||
NameStringMatcher nmAdditional = new NameStringMatcher(phrase.getFirstUnknownSearchWord(),
|
||||
StringMatcherMode.CHECK_EQUALS_FROM_SPACE) ;
|
||||
searchAmenityTypesAPI.initPoiTypes();
|
||||
Map<AbstractPoiType, List<String>> poiTypeResults = searchAmenityTypesAPI.getPoiTypeResults(nm, true);
|
||||
Map<String, PoiTypeResult> poiTypeResults = searchAmenityTypesAPI.getPoiTypeResults(nm, nmAdditional);
|
||||
// find first full match only
|
||||
for (Entry<AbstractPoiType, List<String>> poiType : poiTypeResults.entrySet()) {
|
||||
for (String foundName : poiType.getValue()) {
|
||||
for (PoiTypeResult poiTypeResult : poiTypeResults.values()) {
|
||||
for (String foundName : poiTypeResult.foundWords) {
|
||||
CollatorStringMatcher csm = new CollatorStringMatcher(foundName, StringMatcherMode.CHECK_ONLY_STARTS_WITH);
|
||||
// matches only completely
|
||||
int mwords = phrase.countWords(foundName) ;
|
||||
|
@ -953,8 +998,8 @@ public class SearchCoreFactory {
|
|||
nameFilter += otherSearchWords.get(k);
|
||||
}
|
||||
}
|
||||
poiTypeFilter = getPoiTypeFilter(poiType.getKey(), poiAdditionals);
|
||||
unselectedPoiType = poiType.getKey();
|
||||
poiTypeFilter = getPoiTypeFilter(poiTypeResult.pt, poiAdditionals);
|
||||
unselectedPoiType = poiTypeResult.pt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -983,7 +1028,7 @@ public class SearchCoreFactory {
|
|||
private ResultMatcher<Amenity> getResultMatcher(final SearchPhrase phrase, final SearchPoiTypeFilter poiTypeFilter,
|
||||
final SearchResultMatcher resultMatcher, final String nameFilter,
|
||||
final BinaryMapIndexReader selected, final Set<String> searchedPois,
|
||||
final Map<String, PoiType> poiAdditionals, final int countExtraWords) {
|
||||
final Collection<String> poiAdditionals, final int countExtraWords) {
|
||||
|
||||
|
||||
final NameStringMatcher ns = nameFilter == null ? null : new NameStringMatcher(nameFilter, StringMatcherMode.CHECK_STARTS_FROM_SPACE);
|
||||
|
@ -1004,7 +1049,7 @@ public class SearchCoreFactory {
|
|||
}
|
||||
if (!poiAdditionals.isEmpty()) {
|
||||
boolean found = false;
|
||||
for (String add : poiAdditionals.keySet()) {
|
||||
for (String add : poiAdditionals) {
|
||||
if(object.getAdditionalInfo().containsKey(add)) {
|
||||
found = true;
|
||||
break;
|
||||
|
@ -1058,13 +1103,13 @@ public class SearchCoreFactory {
|
|||
};
|
||||
}
|
||||
|
||||
private SearchPoiTypeFilter getPoiTypeFilter(AbstractPoiType pt, Map<String, PoiType> poiAdditionals ) {
|
||||
private SearchPoiTypeFilter getPoiTypeFilter(AbstractPoiType pt, Set<String> poiAdditionals ) {
|
||||
final Map<PoiCategory, LinkedHashSet<String>> acceptedTypes = new LinkedHashMap<PoiCategory,
|
||||
LinkedHashSet<String>>();
|
||||
pt.putTypes(acceptedTypes);
|
||||
poiAdditionals.clear();
|
||||
if (pt instanceof PoiType && ((PoiType) pt).isAdditional() && ((PoiType) pt).getParentType() != null) {
|
||||
poiAdditionals.put(pt.getKeyName(), (PoiType) pt);
|
||||
if (pt.isAdditional()) {
|
||||
poiAdditionals.add(pt.getKeyName());
|
||||
}
|
||||
return new SearchPoiTypeFilter() {
|
||||
|
||||
|
@ -1324,6 +1369,36 @@ public class SearchCoreFactory {
|
|||
return SEARCH_BUILDING_BY_STREET_PRIORITY;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class PoiAdditionalCustomFilter extends AbstractPoiType {
|
||||
|
||||
protected List<PoiType> additionalPoiTypes = new ArrayList<PoiType>();
|
||||
|
||||
public PoiAdditionalCustomFilter(MapPoiTypes registry, PoiType pt) {
|
||||
super(pt.getKeyName(), registry);
|
||||
additionalPoiTypes.add(pt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdditional() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Map<PoiCategory, LinkedHashSet<String>> putTypes(Map<PoiCategory, LinkedHashSet<String>> acceptedTypes) {
|
||||
for (PoiType p : additionalPoiTypes) {
|
||||
if (p.getParentType() == registry.getOtherMapCategory()) {
|
||||
for (PoiCategory c : registry.getCategories(false)) {
|
||||
c.putTypes(acceptedTypes);
|
||||
}
|
||||
} else {
|
||||
p.getParentType().putTypes(acceptedTypes);
|
||||
}
|
||||
|
||||
}
|
||||
return acceptedTypes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class SearchLocationAndUrlAPI extends SearchBaseAPI {
|
||||
|
||||
|
|
|
@ -281,6 +281,11 @@ public class SearchPhrase {
|
|||
for (String s : searchWords) {
|
||||
if (s.length() > 0 && !Character.isDigit(s.charAt(0)) && !LocationParser.isValidOLC(s)) {
|
||||
mainUnknownWordToSearch = s.trim();
|
||||
if (mainUnknownWordToSearch.endsWith(".")) {
|
||||
mainUnknownWordToSearch = mainUnknownWordToSearch.substring(0,
|
||||
mainUnknownWordToSearch.length() - 1);
|
||||
mainUnknownSearchWordComplete = false;
|
||||
}
|
||||
int unknownInd = unknownSearchWords.indexOf(s);
|
||||
if (!lastUnknownSearchWordComplete && unknownSearchWords.size() - 1 == unknownInd) {
|
||||
mainUnknownSearchWordComplete = false;
|
||||
|
|
|
@ -707,13 +707,9 @@ public class Algorithms {
|
|||
}
|
||||
|
||||
public static String formatMinutesDuration(int minutes) {
|
||||
if (minutes < 60) {
|
||||
return String.valueOf(minutes);
|
||||
} else {
|
||||
int min = minutes % 60;
|
||||
int hours = minutes / 60;
|
||||
return String.format(Locale.UK, "%02d:%02d", hours, min);
|
||||
}
|
||||
int min = minutes % 60;
|
||||
int hours = minutes / 60;
|
||||
return String.format(Locale.UK, "%02d:%02d", hours, min);
|
||||
}
|
||||
|
||||
public static <T extends Enum<T>> T parseEnumValue(T[] cl, String val, T defaultValue) {
|
||||
|
|
|
@ -886,6 +886,12 @@ public class OpeningHoursParser {
|
|||
return new TIntArrayList(endTimes);
|
||||
}
|
||||
|
||||
public void setDays(boolean[] days) {
|
||||
if (this.days.length == days.length) {
|
||||
this.days = days;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the weekday of time "cal" is part of this rule
|
||||
*
|
||||
|
|
|
@ -180,14 +180,14 @@ public class SearchUICoreTest {
|
|||
SearchResultCollection collection = new SearchResultCollection(phrase);
|
||||
collection.addSearchResults(matcher.getRequestResults(), true, true);
|
||||
List<SearchResult> searchResults = collection.getCurrentSearchResults();
|
||||
int i = 0;
|
||||
for (SearchResult res : searchResults) {
|
||||
String expected = result.get(i++);
|
||||
for(int i = 0; i < result.size(); i++) {
|
||||
String expected = result.get(i);
|
||||
SearchResult res = i >= searchResults.size() ? null : searchResults.get(i);
|
||||
if (simpleTest && expected.indexOf('[') != -1) {
|
||||
expected = expected.substring(0, expected.indexOf('[')).trim();
|
||||
}
|
||||
// String present = result.toString();
|
||||
String present = formatResult(simpleTest, res, phrase);
|
||||
String present = res == null ? ("#MISSING " + (i+1)) : formatResult(simpleTest, res, phrase);
|
||||
if (!Algorithms.stringsEqual(expected, present)) {
|
||||
System.out.println(String.format("Phrase: %s", phrase));
|
||||
System.out.println(String.format("Mismatch for '%s' != '%s'. Result: ", expected, present));
|
||||
|
@ -196,9 +196,6 @@ public class SearchUICoreTest {
|
|||
}
|
||||
}
|
||||
Assert.assertEquals(expected, present);
|
||||
if (i >= result.size()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
3
OsmAnd-java/src/test/resources/.gitignore
vendored
|
@ -3,4 +3,5 @@
|
|||
/search/*
|
||||
*.obf
|
||||
*.osm
|
||||
phrases.xml
|
||||
phrases.xml
|
||||
*.obf.gz
|
||||
|
|
|
@ -124,7 +124,7 @@
|
|||
<string name="waiting_for_response_from_telegram">Warten auf Antwort von Telegram</string>
|
||||
<string name="sending_location_messages">Sende Standort</string>
|
||||
<string name="initializing">Startet</string>
|
||||
<string name="searching_for_gps">Positioniere..…</string>
|
||||
<string name="searching_for_gps">Positionierung …</string>
|
||||
<string name="connecting_to_the_internet">Verbindung zum Internet</string>
|
||||
<string name="background_work_description">Akkuoptimierungseinstellungen, um die Standortfreigabe zu stabilisieren.</string>
|
||||
<string name="password_descr">Telegram-Passwort</string>
|
||||
|
@ -180,7 +180,7 @@
|
|||
<string name="monitoring_is_disabled">Überwachung deaktiviert</string>
|
||||
<string name="time_on_the_move">Zeit in Bewegung</string>
|
||||
<string name="average_altitude">Durchschnittliche Höhe</string>
|
||||
<string name="average_speed">Durchschnittliche Geschwindigkeit</string>
|
||||
<string name="average_speed">Durchschnittsgeschwindigkeit</string>
|
||||
<string name="open_in_osmand">In OsmAnd anzeigen</string>
|
||||
<string name="end_date">Endzeit</string>
|
||||
<string name="start_date">Startzeit</string>
|
||||
|
|
110
OsmAnd-telegram/res/values-hy/strings.xml
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="search_contacts">Որոնել կոնտակտներ</string>
|
||||
<string name="bearing">Կողմնորոշում</string>
|
||||
<string name="altitude">Բարձրություն</string>
|
||||
<string name="precision">Ճշգրտություն</string>
|
||||
<string name="direction">Ուղղություն</string>
|
||||
<string name="privacy">Գաղտնիության</string>
|
||||
<string name="proxy">Պրոկսի</string>
|
||||
<string name="proxy_settings">Proxy Կարգավորումներ</string>
|
||||
<string name="proxy_disconnected">Անջատված է</string>
|
||||
<string name="proxy_connected">Միացված է</string>
|
||||
<string name="proxy_type">Proxy տեսակը</string>
|
||||
<string name="shared_string_enable">Միացնել</string>
|
||||
<string name="shared_string_connection">Կապ</string>
|
||||
<string name="proxy_server">Սերվեր</string>
|
||||
<string name="proxy_port">Պորտ</string>
|
||||
<string name="proxy_credentials">Հավատարմագրման տվյալներ</string>
|
||||
<string name="proxy_username">Օգտագործողի անունը</string>
|
||||
<string name="proxy_password">Գաղտնաբառ</string>
|
||||
<string name="proxy_key">Բանալի</string>
|
||||
<string name="gpx_settings">GPX կարգավորումներ</string>
|
||||
<string name="min_logging_speed_descr">Ֆիլտր՝ չգրանցել, քանի դեռ չի հասել անհրաժեշտ արագությանը</string>
|
||||
<string name="min_logging_speed">Նվազագույն գրանցման արագությունը</string>
|
||||
<string name="min_logging_accuracy_descr">Ֆիլտր՝ չգրանցել, քանի դեռ չի հասել անհրաժեշտ ճշգրտությանը</string>
|
||||
<string name="min_logging_accuracy">Նվազագույն գրանցման ճշգրտությունը</string>
|
||||
<string name="min_logging_distance_descr">Ֆիլտր՝ նոր կետի գրանցման համար անհրաժեշտ նվազագույն հեռավորությունը</string>
|
||||
<string name="min_logging_distance">Նվազագույն գրանցման հեռավորությունը</string>
|
||||
<string name="shared_string_select">Ընտրել</string>
|
||||
<string name="timeline_no_data">Տվյալներ չկա</string>
|
||||
<string name="timeline_no_data_descr">Ընտրված օրվա համար հավաքագրված տվյալներ չկան</string>
|
||||
<string name="start_end_date">Սկզբ - Ավարտ ամսաթիվը</string>
|
||||
<string name="set_time_timeline_descr">Ընտրեք ժամանակ ցուցադրելու համար</string>
|
||||
<string name="shared_string_apply">Կիրառել</string>
|
||||
<string name="shared_string_start">Սկիզբ</string>
|
||||
<string name="shared_string_end">Վերջ</string>
|
||||
<string name="saved_messages">Պահպանված հաղորդագրությունները</string>
|
||||
<string name="unit_of_speed_system">Արագության չափման միավոր</string>
|
||||
<string name="unit_of_speed_system_descr">Ընտրեք արագության չափման միավորը:</string>
|
||||
<string name="unit_of_length">Հեռավորության չափման միավոր</string>
|
||||
<string name="unit_of_length_descr">Փոխել հեռավորության չափման միավորը։</string>
|
||||
<string name="units_and_formats">Չափման միավորներ և ձեւաչափեր</string>
|
||||
<string name="time_zone">Ժամային գոտին</string>
|
||||
<string name="time_zone_descr">Ընտրեք ժամային գոտին, որպեսզի ցույց տա ձեր գտնվելու վայրը հաղորդագրություններում:</string>
|
||||
<string name="buffer_time">Բուֆերի ավարտման ժամկետը</string>
|
||||
<string name="buffer_time_descr">Կետերի առավելագույն պահպանման ժամանակը բուֆերում</string>
|
||||
<string name="status_widget_title">OsmAnd Tracker- ի կարգավիճակը</string>
|
||||
<string name="shared_string_suggested">Առաջարկվում է</string>
|
||||
<string name="back_to_osmand">Վերադառնալ OsmAnd</string>
|
||||
<string name="duration_ago">%1$s առաջ</string>
|
||||
<string name="last_response_duration">Վերջին պատասխանը՝ %1$s առաջ</string>
|
||||
<string name="last_update_from_telegram_duration">Վերջին թարմացումը Telegram- ից՝ %1$s առաջ</string>
|
||||
<string name="last_response_date">Վերջին պատասխանը՝ %1$s</string>
|
||||
<string name="last_update_from_telegram_date">Վերջին թարմացումը Telegram- ից.%1$s</string>
|
||||
<string name="shared_string_error_short">ՍԽԱԼ</string>
|
||||
<string name="privacy_policy_use_telegram">Telegram (հաղորդագրությունների մենեջեր) օգտագործվում է մարդկանց հետ շփվելու և հաղորդակցվելու համար:</string>
|
||||
<string name="shared_string_telegram">Telegram</string>
|
||||
<string name="app_name_short">OsmAnd Թրեկեր</string>
|
||||
<string name="timeline_description">Միացրեք մոնիտորինգը բոլոր վայրերը պատմության մեջ պահպանելու համար։</string>
|
||||
<string name="location_recording_enabled">Գտնվելու վայրի արձանագրումը միացված է</string>
|
||||
<string name="disable_monitoring">Անջատել մոնիտորինգը</string>
|
||||
<string name="timeline_available_for_free_now">Ժամանակացույցն այժմ անվճար է:</string>
|
||||
<string name="si_mi_meters">Մղոններ/մետր</string>
|
||||
<string name="si_nm">Ծովային մղոն</string>
|
||||
<string name="si_km_m">Կիլոմետր/մետր</string>
|
||||
<string name="si_mi_yard">Մղոններ/յարդեր</string>
|
||||
<string name="si_mi_feet">Մղոններ/ֆուտ</string>
|
||||
<string name="si_min_m">Րոպե մղոն</string>
|
||||
<string name="si_min_km">Րոպե կիլոմետր</string>
|
||||
<string name="si_m_s">Մետր վայրկյանում</string>
|
||||
<string name="si_mph">Մղոն ժամում</string>
|
||||
<string name="si_kmh">Կիլոմետր ժամում</string>
|
||||
<string name="mile_per_hour">մղ/ժ</string>
|
||||
<string name="km_h">կմ/ժ</string>
|
||||
<string name="m_s">մ/վ</string>
|
||||
<string name="min_km">րոպե/կմ</string>
|
||||
<string name="min_mile">րոպե/մ</string>
|
||||
<string name="nm">նմ</string>
|
||||
<string name="m">մ</string>
|
||||
<string name="km">կմ</string>
|
||||
<string name="mile">մղ</string>
|
||||
<string name="foot">ֆուտ</string>
|
||||
<string name="yard">յարդ</string>
|
||||
<string name="osmand_service">Ֆոնային ռեժիմ</string>
|
||||
<string name="shared_string_settings">Կարգավորումներ</string>
|
||||
<string name="shared_string_cancel">Չեղարկել</string>
|
||||
<string name="shared_string_continue">Շարունակել</string>
|
||||
<string name="shared_string_back">Վերադառնալ</string>
|
||||
<string name="shared_string_share">ՈՒղարկել</string>
|
||||
<string name="shared_string_install">Տեղադրել</string>
|
||||
<string name="shared_string_off">Անջատել</string>
|
||||
<string name="shared_string_all">Բոլորը</string>
|
||||
<string name="shared_string_close">Փակել</string>
|
||||
<string name="shared_string_exit">Ելք</string>
|
||||
<string name="shared_string_sort">Դասակարգել</string>
|
||||
<string name="shared_string_name">Անուն</string>
|
||||
<string name="shared_string_save">Պահպանել</string>
|
||||
<string name="shared_string_disable">Անջատել</string>
|
||||
<string name="shared_string_status">Կարգավիճակ</string>
|
||||
<string name="shared_string_enabled">Միացված է</string>
|
||||
<string name="shared_string_hide">Թաքցնել</string>
|
||||
<string name="shared_string_add">Ավելացնել</string>
|
||||
<string name="shared_string_map">Քարտեզ</string>
|
||||
<string name="average_speed">Միջին արագությունը</string>
|
||||
<string name="average_altitude">Միջին բարձրություն</string>
|
||||
<string name="shared_string_update">Թարմացնել</string>
|
||||
<string name="shared_string_appearance">Տեսակ</string>
|
||||
<string name="shared_string_ok">OK</string>
|
||||
<string name="shared_string_search">Որոնում</string>
|
||||
</resources>
|
|
@ -103,7 +103,7 @@
|
|||
<string name="show_gps_points">GPS noktalarını göster</string>
|
||||
<string name="show_gps_points_descr">Toplanan ve gönderilen GPS noktalarının miktarını göster.</string>
|
||||
<string name="please_update_osmand">Verileri haritada görüntülemek için lütfen OsmAnd\'ı güncelleyin</string>
|
||||
<string name="bearing">Konum açısı</string>
|
||||
<string name="bearing">Kerteriz</string>
|
||||
<string name="gps_points_in_buffer">gönderilen (%1$d arabellekte)</string>
|
||||
<string name="points_size">%1$d nokta</string>
|
||||
<string name="shared_string_date">Tarih</string>
|
||||
|
|
|
@ -393,6 +393,12 @@
|
|||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<receiver android:name="net.osmand.plus.audionotes.MediaRemoteControlReceiver">
|
||||
|
|
|
@ -50,6 +50,48 @@
|
|||
<asset source="voice/zh-hk/zh-hk_tts.js" destination="voice/zh-hk-tts/zh-hk_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/zh-tw/zh-tw_tts.js" destination="voice/zh-tw-tts/zh-tw_tts.js" mode="overwriteOnlyIfExists" />
|
||||
|
||||
<asset source="voice/ar/ar_tts.js" destination="voice/ar/ar_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/be/be_tts.js" destination="voice/be/be_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/cs/cs_tts.js" destination="voice/cs/cs_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/da/da_tts.js" destination="voice/da/da_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/de/de_tts.js" destination="voice/de/de_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/el/el_tts.js" destination="voice/el/el_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/en/en_tts.js" destination="voice/en/en_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/en-gb/en-gb_tts.js" destination="voice/en-gb/en-gb_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/es/es_tts.js" destination="voice/es/es_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/es-ar/es-ar_tts.js" destination="voice/es-ar/es-ar_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/et/et_tts.js" destination="voice/et/et_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/fa/fa_tts.js" destination="voice/fa/fa_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/fi/fi_tts.js" destination="voice/fi/fi_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/fr/fr_tts.js" destination="voice/fr/fr_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/gn-py/gn-py_tts.js" destination="voice/gn-py/gn-py_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/nb/nb_tts.js" destination="voice/nb/nb_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/hi/hi_tts.js" destination="voice/hi/hi_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/hr/hr_tts.js" destination="voice/hr/hr_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/hu/hu_tts.js" destination="voice/hu/hu_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/hu-formal/hu-formal_tts.js" destination="voice/hu-formal/hu-formal_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/it/it_tts.js" destination="voice/it/it_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/ja/ja_tts.js" destination="voice/ja/ja_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/ko/ko_tts.js" destination="voice/ko/ko_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/lv/lv_tts.js" destination="voice/lv/lv_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/nl/nl_tts.js" destination="voice/nl/nl_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/pl/pl_tts.js" destination="voice/pl/pl_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/pt/pt_tts.js" destination="voice/pt/pt_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/pt-br/pt-br_tts.js" destination="voice/pt-br/pt-br_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/ro/ro_tts.js" destination="voice/ro/ro_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/ru/ru_tts.js" destination="voice/ru/ru_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/sc/sc_tts.js" destination="voice/sc/sc_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/sk/sk_tts.js" destination="voice/sk/sk_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/sl/sl_tts.js" destination="voice/sl/sl_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/sr/sr_tts.js" destination="voice/sr/sr_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/sv/sv_tts.js" destination="voice/sv/sv_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/sw/sw_tts.js" destination="voice/sw/sw_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/tr/tr_tts.js" destination="voice/tr/tr_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/uk/uk_tts.js" destination="voice/uk/uk_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/zh/zh_tts.js" destination="voice/zh/zh_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/zh-hk/zh-hk_tts.js" destination="voice/zh-hk/zh-hk_tts.js" mode="overwriteOnlyIfExists" />
|
||||
<asset source="voice/zh-tw/zh-tw_tts.js" destination="voice/zh-tw/zh-tw_tts.js" mode="overwriteOnlyIfExists" />
|
||||
|
||||
<asset source="fonts/05_NotoSans-Regular.ttf" destination="fonts/05_NotoSans-Regular.ttf" mode="alwaysOverwriteOrCopy" />
|
||||
<asset source="fonts/10_NotoSans-Bold.ttf" destination="fonts/10_NotoSans-Bold.ttf" mode="alwaysOverwriteOrCopy" />
|
||||
<asset source="fonts/15_NotoSans-Italic.ttf" destination="fonts/15_NotoSans-Italic.ttf" mode="alwaysOverwriteOrCopy" />
|
||||
|
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.2 KiB |
BIN
OsmAnd/res/drawable-hdpi/map_track_point_finish.png
Normal file
After Width: | Height: | Size: 2 KiB |
BIN
OsmAnd/res/drawable-hdpi/map_track_point_start.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 903 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |