Merge branch 'master' into WikipediaSearchBanner

This commit is contained in:
nazar-kutz 2020-06-30 17:58:54 +03:00 committed by GitHub
commit 352c038ee9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
462 changed files with 5890 additions and 2151 deletions

View file

@ -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;
}

View file

@ -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);
}
}
}

View file

@ -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);

View file

@ -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);

View file

@ -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();

View file

@ -1054,6 +1054,4 @@ public class RouteDataObject {
}
restrictionsVia[k] = viaWay;
}
}

View file

@ -67,7 +67,6 @@ public abstract class AbstractPoiType {
return this instanceof PoiType && this.isAdditional();
}
public String getTranslation() {
if(translation == null) {
translation = registry.getTranslation(this);

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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<>();

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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 {
}
}
}

View file

@ -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);
}
}

View file

@ -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

View file

@ -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 {

View file

@ -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;

View file

@ -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) {

View file

@ -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
*

View file

@ -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;
}
}
}

View file

@ -3,4 +3,5 @@
/search/*
*.obf
*.osm
phrases.xml
phrases.xml
*.obf.gz

View file

@ -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>

View 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>

View file

@ -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>

View file

@ -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">

View file

@ -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" />

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 903 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Some files were not shown because too many files have changed in this diff Show more