Merge branch 'master' into Material_migration

This commit is contained in:
Vitaliy 2020-08-10 19:03:55 +03:00
commit 9eb8dc66a7
448 changed files with 21541 additions and 9009 deletions

View file

@ -0,0 +1,10 @@
name: "Validate Gradle Wrapper"
on: [push, pull_request]
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1

View file

@ -51,4 +51,9 @@ interface IOsmAndAidlCallback {
* Callback for {@link IOsmAndAidlInterface} registerForVoiceRouterMessages() method.
*/
void onVoiceRouterNotify(in OnVoiceNavigationParams params);
/**
* Callback for {@link IOsmAndAidlInterface} registerForKeyEvents() method.
*/
void onKeyEvent(in KeyEvent params);
}

View file

@ -96,6 +96,12 @@ import net.osmand.aidlapi.mapmarker.RemoveMapMarkersParams;
import net.osmand.aidlapi.quickaction.QuickActionParams;
import net.osmand.aidlapi.quickaction.QuickActionInfoParams;
import net.osmand.aidlapi.lock.SetLockStateParams;
import net.osmand.aidlapi.events.AKeyEventsParams;
import net.osmand.aidlapi.info.AppInfoParams;
// NOTE: Add new methods at the end of file!!!
interface IOsmAndAidlInterface {
@ -841,4 +847,21 @@ interface IOsmAndAidlInterface {
boolean executeQuickAction(in QuickActionParams params);
boolean getQuickActionsInfo(out List<QuickActionInfoParams> quickActions);
/**
* Toggle Lock/Unlock screen.
*/
boolean setLockState(in SetLockStateParams params);
/**
* Method to register for key events.
*
* @params subscribeToUpdates (boolean) - boolean flag to subscribe or unsubscribe from key events
* @params callbackId (long) - id of callback, needed to unsubscribe key events
* @params callback (IOsmAndAidlCallback) - callback to notify user on key event
* @params keyEventList (List<Integer>) - list of requested key events
*/
long registerForKeyEvents(in AKeyEventsParams params, IOsmAndAidlCallback callback);
AppInfoParams getAppInfo();
}

View file

@ -0,0 +1,2 @@
package net.osmand.aidlapi.events;
parcelable AKeyEventsParams;

View file

@ -0,0 +1,73 @@
package net.osmand.aidlapi.events;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import net.osmand.aidlapi.AidlParams;
import java.util.ArrayList;
public class AKeyEventsParams extends AidlParams {
private long callbackId = -1L;
private boolean subscribeToUpdates = true;
private ArrayList<Integer> keyEventList;
public AKeyEventsParams() {
}
protected AKeyEventsParams(Parcel in) {
readFromParcel(in);
}
public static final Parcelable.Creator<AKeyEventsParams> CREATOR = new Parcelable.Creator<AKeyEventsParams>() {
@Override
public AKeyEventsParams createFromParcel(Parcel in) {
return new AKeyEventsParams(in);
}
@Override
public AKeyEventsParams[] newArray(int size) {
return new AKeyEventsParams[size];
}
};
public long getCallbackId() {
return callbackId;
}
public void setCallbackId(long callbackId) {
this.callbackId = callbackId;
}
public void setSubscribeToUpdates(boolean subscribeToUpdates) {
this.subscribeToUpdates = subscribeToUpdates;
}
public boolean isSubscribeToUpdates() {
return subscribeToUpdates;
}
public void setKeyEventList(ArrayList<Integer> keyEventList) {
this.keyEventList = keyEventList;
}
public ArrayList<Integer> getKeyEventList() {
return keyEventList;
}
@Override
protected void readFromBundle(Bundle bundle) {
callbackId = bundle.getLong("callbackId");
subscribeToUpdates = bundle.getBoolean("subscribeToUpdates");
keyEventList = bundle.getIntegerArrayList("keyEventList");
}
@Override
public void writeToBundle(Bundle bundle) {
bundle.putLong("callbackId", callbackId);
bundle.putBoolean("subscribeToUpdates", subscribeToUpdates);
bundle.putIntegerArrayList("keyEventList", keyEventList);
}
}

View file

@ -0,0 +1,3 @@
package net.osmand.aidlapi.info;
parcelable AppInfoParams;

View file

@ -0,0 +1,95 @@
package net.osmand.aidlapi.info;
import android.os.Bundle;
import android.os.Parcel;
import net.osmand.aidlapi.AidlParams;
import net.osmand.aidlapi.map.ALatLon;
public class AppInfoParams extends AidlParams {
private ALatLon lastKnownLocation;
private ALatLon mapLocation;
private Bundle turnInfo;
private int leftTime;
private int leftDistance;
private long arrivalTime;
private boolean mapVisible;
public AppInfoParams(ALatLon lastKnownLocation, ALatLon mapLocation, Bundle turnInfo, int leftTime, int leftDistance, long arrivalTime, boolean mapVisible) {
this.lastKnownLocation = lastKnownLocation;
this.mapLocation = mapLocation;
this.leftTime = leftTime;
this.leftDistance = leftDistance;
this.arrivalTime = arrivalTime;
this.turnInfo = turnInfo;
this.mapVisible = mapVisible;
}
public AppInfoParams(Parcel in) {
readFromParcel(in);
}
public static final Creator<AppInfoParams> CREATOR = new Creator<AppInfoParams>() {
@Override
public AppInfoParams createFromParcel(Parcel in) {
return new AppInfoParams(in);
}
@Override
public AppInfoParams[] newArray(int size) {
return new AppInfoParams[size];
}
};
public ALatLon getLastKnownLocation() {
return lastKnownLocation;
}
public ALatLon getMapLocation() {
return mapLocation;
}
public int getLeftTime() {
return leftTime;
}
public long getArrivalTime() {
return arrivalTime;
}
public int getLeftDistance() {
return leftDistance;
}
public boolean isMapVisible() {
return mapVisible;
}
public Bundle getTurnInfo() {
return turnInfo;
}
@Override
public void writeToBundle(Bundle bundle) {
bundle.putParcelable("lastKnownLocation", lastKnownLocation);
bundle.putParcelable("mapLocation", mapLocation);
bundle.putInt("leftTime", leftTime);
bundle.putLong("arrivalTime", arrivalTime);
bundle.putInt("leftDistance", leftDistance);
bundle.putBundle("turnInfo", turnInfo);
bundle.putBoolean("mapVisible", mapVisible);
}
@Override
protected void readFromBundle(Bundle bundle) {
lastKnownLocation = bundle.getParcelable("lastKnownLocation");
mapLocation = bundle.getParcelable("mapLocation");
leftTime = bundle.getInt("leftTime");
arrivalTime = bundle.getLong("arrivalTime");
leftDistance = bundle.getInt("leftDistance");
turnInfo = bundle.getBundle("turnInfo");
mapVisible = bundle.getBoolean("mapVisible");
}
}

View file

@ -0,0 +1,3 @@
package net.osmand.aidlapi.lock;
parcelable SetLockStateParams;

View file

@ -0,0 +1,45 @@
package net.osmand.aidlapi.lock;
import android.os.Bundle;
import android.os.Parcel;
import net.osmand.aidlapi.AidlParams;
public class SetLockStateParams extends AidlParams {
private boolean lock;
public SetLockStateParams(boolean lock) {
this.lock = lock;
}
public SetLockStateParams(Parcel in) {
readFromParcel(in);
}
public static final Creator<SetLockStateParams> CREATOR = new Creator<SetLockStateParams>() {
@Override
public SetLockStateParams createFromParcel(Parcel in) {
return new SetLockStateParams(in);
}
@Override
public SetLockStateParams[] newArray(int size) {
return new SetLockStateParams[size];
}
};
public boolean getLockState() {
return lock;
}
@Override
public void writeToBundle(Bundle bundle) {
bundle.putBoolean("lock", this.lock);
}
@Override
protected void readFromBundle(Bundle bundle) {
lock = bundle.getBoolean("lock");
}
}

View file

@ -10,12 +10,14 @@ public class SetMapLocationParams extends AidlParams {
private double latitude;
private double longitude;
private int zoom;
private float rotation;
private boolean animated;
public SetMapLocationParams(double latitude, double longitude, int zoom, boolean animated) {
public SetMapLocationParams(double latitude, double longitude, int zoom, float rotation, boolean animated) {
this.latitude = latitude;
this.longitude = longitude;
this.zoom = zoom;
this.rotation = rotation;
this.animated = animated;
}
@ -47,6 +49,10 @@ public class SetMapLocationParams extends AidlParams {
return zoom;
}
public float getRotation() {
return rotation;
}
public boolean isAnimated() {
return animated;
}
@ -56,6 +62,7 @@ public class SetMapLocationParams extends AidlParams {
bundle.putDouble("latitude", latitude);
bundle.putDouble("longitude", longitude);
bundle.putInt("zoom", zoom);
bundle.putFloat("rotation", rotation);
bundle.putBoolean("animated", animated);
}
@ -64,6 +71,7 @@ public class SetMapLocationParams extends AidlParams {
latitude = bundle.getDouble("latitude");
longitude = bundle.getDouble("longitude");
zoom = bundle.getInt("zoom");
rotation = bundle.getFloat("rotation");
animated = bundle.getBoolean("animated");
}
}

View file

@ -1,6 +1,7 @@
package net.osmand;
import net.osmand.data.QuadRect;
import net.osmand.util.Algorithms;
@ -42,10 +43,14 @@ import java.util.Stack;
import java.util.TimeZone;
public class GPXUtilities {
public final static Log log = PlatformUtil.getLog(GPXUtilities.class);
private static final String ICON_NAME_EXTENSION = "icon";
private static final String DEFAULT_ICON_NAME = "special_star";
private static final String BACKGROUND_TYPE_EXTENSION = "background";
private static final String PROFILE_TYPE_EXTENSION = "profile";
private static final String TRKPT_INDEX_EXTENSION = "trkpt_idx";
private final static String GPX_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; //$NON-NLS-1$
private final static String GPX_TIME_FORMAT_MILLIS = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; //$NON-NLS-1$
@ -115,6 +120,13 @@ public class GPXUtilities {
return extensions;
}
public Map<String, String> getExtensionsToWrite() {
if (extensions == null) {
extensions = new LinkedHashMap<>();
}
return extensions;
}
public GPXExtensionsWriter getExtensionsWriter() {
return extensionsWriter;
}
@ -148,14 +160,7 @@ public class GPXUtilities {
getExtensionsToWrite().remove("color");
}
public Map<String, String> getExtensionsToWrite() {
if (extensions == null) {
extensions = new LinkedHashMap<>();
}
return extensions;
}
private int parseColor(String colorString, int defColor) {
protected int parseColor(String colorString, int defColor) {
if (!Algorithms.isEmpty(colorString)) {
if (colorString.charAt(0) == '#') {
long color = Long.parseLong(colorString.substring(1), 16);
@ -302,6 +307,26 @@ public class GPXUtilities {
getExtensionsToWrite().put(BACKGROUND_TYPE_EXTENSION, backType);
}
public String getProfileType() {
return getExtensionsToRead().get(PROFILE_TYPE_EXTENSION);
}
public void setProfileType(String profileType) {
getExtensionsToWrite().put(PROFILE_TYPE_EXTENSION, profileType);
}
public int getTrkPtIndex() {
try {
return Integer.parseInt(getExtensionsToRead().get(TRKPT_INDEX_EXTENSION));
}catch(NumberFormatException e){
return -1;
}
}
public void setTrkPtIndex(int index) {
getExtensionsToWrite().put(TRKPT_INDEX_EXTENSION, String.valueOf(index));
}
@Override
public int hashCode() {
final int prime = 31;
@ -932,7 +957,6 @@ public class GPXUtilities {
sp = new SplitSegment(segment, k - 1, cf);
currentMetricEnd += metricLimit;
prev = sp.get(0);
}
total += currentSegment;
}
@ -1511,6 +1535,97 @@ public class GPXUtilities {
}
return new QuadRect(left, top, right, bottom);
}
public int getGradientScaleColor(String gradientScaleType, int defColor) {
String clrValue = null;
if (extensions != null) {
clrValue = extensions.get(gradientScaleType);
}
return parseColor(clrValue, defColor);
}
public void setGradientScaleColor(String gradientScaleType, int gradientScaleColor) {
getExtensionsToWrite().put(gradientScaleType, Algorithms.colorToString(gradientScaleColor));
}
public String getGradientScaleType() {
if (extensions != null) {
return extensions.get("gradient_scale_type");
}
return null;
}
public void setGradientScaleType(String gradientScaleType) {
getExtensionsToWrite().put("gradient_scale_type", gradientScaleType);
}
public void removeGradientScaleType() {
getExtensionsToWrite().remove("gradient_scale_type");
}
public String getSplitType() {
if (extensions != null) {
return extensions.get("split_type");
}
return null;
}
public void setSplitType(String gpxSplitType) {
getExtensionsToWrite().put("split_type", gpxSplitType);
}
public double getSplitInterval() {
if (extensions != null) {
String splitIntervalStr = extensions.get("split_interval");
if (!Algorithms.isEmpty(splitIntervalStr)) {
try {
return Double.parseDouble(splitIntervalStr);
} catch (NumberFormatException e) {
log.error("Error reading split_interval", e);
}
}
}
return 0;
}
public void setSplitInterval(double splitInterval) {
getExtensionsToWrite().put("split_interval", String.valueOf(splitInterval));
}
public String getWidth(String defWidth) {
String widthValue = null;
if (extensions != null) {
widthValue = extensions.get("width");
}
return widthValue != null ? widthValue : defWidth;
}
public void setWidth(String width) {
getExtensionsToWrite().put("width", width);
}
public boolean isShowArrows() {
String showArrows = null;
if (extensions != null) {
showArrows = extensions.get("show_arrows");
}
return Boolean.parseBoolean(showArrows);
}
public void setShowArrows(boolean showArrows) {
getExtensionsToWrite().put("show_arrows", String.valueOf(showArrows));
}
public boolean isShowStartFinish() {
if (extensions != null && extensions.containsKey("show_start_finish")) {
return Boolean.parseBoolean(extensions.get("show_start_finish"));
}
return true;
}
public void setShowStartFinish(boolean showStartFinish) {
getExtensionsToWrite().put("show_start_finish", String.valueOf(showStartFinish));
}
}
public static String asString(GPXFile file) {

View file

@ -28,9 +28,6 @@ public class IndexConstants {
public static final String EXTRA_EXT = ".extra";
public static final String EXTRA_ZIP_EXT = ".extra.zip";
public static final String TOUR_INDEX_EXT = ".tour"; //$NON-NLS-1$
public static final String TOUR_INDEX_EXT_ZIP = ".tour.zip"; //$NON-NLS-1$
public static final String GEN_LOG_EXT = ".gen.log"; //$NON-NLS-1$
public static final String VOICE_INDEX_EXT_ZIP = ".voice.zip"; //$NON-NLS-1$

View file

@ -0,0 +1,159 @@
package net.osmand;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.data.LatLon;
import net.osmand.util.Algorithms;
import java.util.ArrayList;
import java.util.List;
public class LocationsHolder {
private static final int LOCATION_TYPE_UNKNOWN = -1;
private static final int LOCATION_TYPE_LATLON = 0;
private static final int LOCATION_TYPE_LOCATION = 1;
private static final int LOCATION_TYPE_WPTPT = 2;
private List<LatLon> latLonList;
private List<Location> locationList;
private List<WptPt> wptPtList;
private int locationType;
private int size;
@SuppressWarnings("unchecked")
public LocationsHolder(List<?> locations) {
this.locationType = resolveLocationType(locations);
switch (locationType) {
case LOCATION_TYPE_LATLON:
latLonList = (List<LatLon>) locations;
size = locations.size();
break;
case LOCATION_TYPE_LOCATION:
locationList = (List<Location>) locations;
size = locations.size();
break;
case LOCATION_TYPE_WPTPT:
wptPtList = (List<WptPt>) locations;
size = locations.size();
break;
}
}
private int resolveLocationType(List<?> locations) {
if (!Algorithms.isEmpty(locations)) {
Object locationObj = locations.get(0);
if (locationObj instanceof LatLon) {
return LOCATION_TYPE_LATLON;
} else if (locationObj instanceof WptPt) {
return LOCATION_TYPE_WPTPT;
} else if (locationObj instanceof Location) {
return LOCATION_TYPE_LOCATION;
} else {
throw new IllegalArgumentException("Unsupported location type: " + locationObj.getClass().getSimpleName());
}
}
return LOCATION_TYPE_UNKNOWN;
}
public double getLatitude(int index) {
switch (locationType) {
case LOCATION_TYPE_LATLON:
return latLonList.get(index).getLatitude();
case LOCATION_TYPE_LOCATION:
return locationList.get(index).getLatitude();
case LOCATION_TYPE_WPTPT:
return wptPtList.get(index).getLatitude();
default:
return 0;
}
}
public double getLongitude(int index) {
switch (locationType) {
case LOCATION_TYPE_LATLON:
return latLonList.get(index).getLongitude();
case LOCATION_TYPE_LOCATION:
return locationList.get(index).getLongitude();
case LOCATION_TYPE_WPTPT:
return wptPtList.get(index).getLongitude();
default:
return 0;
}
}
public int getSize() {
return size;
}
@SuppressWarnings("unchecked")
private <T> List<T> getList(int locationType) {
List<T> res = new ArrayList<>();
if (size > 0) {
for (int i = 0; i < size; i++) {
switch (locationType) {
case LOCATION_TYPE_LATLON:
res.add((T) getLatLon(i));
break;
case LOCATION_TYPE_LOCATION:
res.add((T) getLocation(i));
break;
case LOCATION_TYPE_WPTPT:
res.add((T) getWptPt(i));
break;
}
}
}
return res;
}
public List<LatLon> getLatLonList() {
if (this.locationType == LOCATION_TYPE_LATLON) {
return latLonList;
} else {
return getList(LOCATION_TYPE_LATLON);
}
}
public List<WptPt> getWptPtList() {
if (this.locationType == LOCATION_TYPE_WPTPT) {
return wptPtList;
} else {
return getList(LOCATION_TYPE_WPTPT);
}
}
public List<Location> getLocationsList() {
if (this.locationType == LOCATION_TYPE_LOCATION) {
return locationList;
} else {
return getList(LOCATION_TYPE_LOCATION);
}
}
public LatLon getLatLon(int index) {
if (this.locationType == LOCATION_TYPE_LATLON) {
return latLonList.get(index);
} else {
return new LatLon(getLatitude(index), getLongitude(index));
}
}
public WptPt getWptPt(int index) {
if (this.locationType == LOCATION_TYPE_WPTPT) {
return wptPtList.get(index);
} else {
WptPt wptPt = new WptPt();
wptPt.lat = getLatitude(index);
wptPt.lon = getLongitude(index);
return wptPt;
}
}
public Location getLocation(int index) {
if (this.locationType == LOCATION_TYPE_LOCATION) {
return locationList.get(index);
} else {
return new Location("", getLatitude(index), getLongitude(index));
}
}
}

View file

@ -26,10 +26,9 @@ import net.osmand.data.QuadRect;
import net.osmand.render.RenderingRuleSearchRequest;
import net.osmand.render.RenderingRulesStorage;
import net.osmand.router.NativeTransportRoutingResult;
import net.osmand.router.PrecalculatedRouteDirection;
import net.osmand.router.RouteCalculationProgress;
import net.osmand.router.RouteSegmentResult;
import net.osmand.router.RoutingConfiguration;
import net.osmand.router.RoutingContext;
import net.osmand.router.TransportRoutingConfiguration;
import net.osmand.util.Algorithms;
@ -135,12 +134,10 @@ public class NativeLibrary {
return nativeTransportRouting(new int[] { sx31, sy31, ex31, ey31 }, cfg, progress);
}
public RouteSegmentResult[] runNativeRouting(int sx31, int sy31, int ex31, int ey31, RoutingConfiguration config,
RouteRegion[] regions, RouteCalculationProgress progress, PrecalculatedRouteDirection precalculatedRouteDirection,
boolean basemap, boolean publicTransport, boolean startTransportStop, boolean targetTransportStop) {
public RouteSegmentResult[] runNativeRouting(RoutingContext c, RouteRegion[] regions, boolean basemap) {
// config.router.printRules(System.out);
return nativeRouting(new int[] { sx31, sy31, ex31, ey31 }, config, config.initialDirection == null ? -360 : config.initialDirection.floatValue(),
regions, progress, precalculatedRouteDirection, basemap, publicTransport, startTransportStop, targetTransportStop);
return nativeRouting(c, c.config.initialDirection == null ? -360 : c.config.initialDirection.floatValue(),
regions, basemap);
}
@ -156,16 +153,15 @@ public class NativeLibrary {
protected static native NativeRouteSearchResult loadRoutingData(RouteRegion reg, String regName, int regfp, RouteSubregion subreg,
boolean loadObjects);
public static native void deleteNativeRoutingContext(long handle);
protected static native void deleteRenderingContextHandle(long handle);
protected static native void deleteRouteSearchResult(long searchResultHandle);
protected static native RouteDataObject[] getRouteDataObjects(RouteRegion reg, long rs, int x31, int y31);
protected static native RouteSegmentResult[] nativeRouting(int[] coordinates, RoutingConfiguration r,
float initDirection, RouteRegion[] regions, RouteCalculationProgress progress,
PrecalculatedRouteDirection precalculatedRouteDirection, boolean basemap,
boolean publicTransport, boolean startTransportStop, boolean targetTransportStop);
protected static native RouteSegmentResult[] nativeRouting(RoutingContext c, float initDirection, RouteRegion[] regions, boolean basemap);
protected static native NativeTransportRoutingResult[] nativeTransportRouting(int[] coordinates, TransportRoutingConfiguration cfg,
RouteCalculationProgress progress);

View file

@ -696,6 +696,7 @@ public class BinaryMapIndexReader {
private void readMapIndex(MapIndex index, boolean onlyInitEncodingRules) throws IOException {
int defaultId = 1;
int oldLimit;
int encodingRulesSize = 0;
while (true) {
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
@ -712,10 +713,14 @@ public class BinaryMapIndexReader {
break;
case OsmandOdb.OsmAndMapIndex.RULES_FIELD_NUMBER :
if (onlyInitEncodingRules) {
if(encodingRulesSize == 0) {
encodingRulesSize = codedIS.getTotalBytesRead();
}
int len = codedIS.readInt32();
oldLimit = codedIS.pushLimit(len);
readMapEncodingRule(index, defaultId++);
codedIS.popLimit(oldLimit);
index.encodingRulesSizeBytes = (codedIS.getTotalBytesRead() - encodingRulesSize);
} else {
skipUnknownField(t);
}
@ -1829,10 +1834,13 @@ public class BinaryMapIndexReader {
public int onewayReverseAttribute = -1;
public TIntHashSet positiveLayers = new TIntHashSet(2);
public TIntHashSet negativeLayers = new TIntHashSet(2);
public int encodingRulesSizeBytes;
// to speed up comparision
private MapIndex referenceMapIndex;
public Integer getRule(String t, String v) {
Map<String, Integer> m = encodingRules.get(t);
if (m != null) {

View file

@ -292,6 +292,7 @@ public class BinaryMapRouteReaderAdapter {
public static class RouteRegion extends BinaryIndexPart {
public int regionsRead;
public List<RouteTypeRule> routeEncodingRules = new ArrayList<BinaryMapRouteReaderAdapter.RouteTypeRule>();
public int routeEncodingRulesBytes = 0;
public Map<String, Integer> decodingRules = null;
List<RouteSubregion> subregions = new ArrayList<RouteSubregion>();
List<RouteSubregion> basesubregions = new ArrayList<RouteSubregion>();
@ -614,6 +615,7 @@ public class BinaryMapRouteReaderAdapter {
protected void readRouteIndex(RouteRegion region) throws IOException {
int routeEncodingRule = 1;
int routeEncodingRulesSize = 0;
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
@ -626,10 +628,13 @@ public class BinaryMapRouteReaderAdapter {
break;
case OsmandOdb.OsmAndRoutingIndex.RULES_FIELD_NUMBER: {
int len = codedIS.readInt32();
if(routeEncodingRulesSize == 0) {
routeEncodingRulesSize = codedIS.getTotalBytesRead();
}
int oldLimit = codedIS.pushLimit(len);
readRouteEncodingRule(region, routeEncodingRule++);
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
codedIS.popLimit(oldLimit);
region.routeEncodingRulesBytes = codedIS.getTotalBytesRead() - routeEncodingRulesSize;
} break;
case OsmandOdb.OsmAndRoutingIndex.ROOTBOXES_FIELD_NUMBER :
case OsmandOdb.OsmAndRoutingIndex.BASEMAPBOXES_FIELD_NUMBER :{

View file

@ -4,6 +4,7 @@ import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher;
import net.osmand.CollatorStringMatcher.StringMatcherMode;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.data.Building;
import net.osmand.data.City;
import net.osmand.data.LatLon;
@ -23,9 +24,11 @@ import java.text.Collator;
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.List;
import java.util.Map;
import java.util.Set;
import gnu.trove.set.hash.TLongHashSet;
@ -40,7 +43,7 @@ public class GeocodingUtilities {
public static final float STOP_SEARCHING_STREET_WITH_MULTIPLIER_RADIUS = 250;
public static final float STOP_SEARCHING_STREET_WITHOUT_MULTIPLIER_RADIUS = 400;
public static final int DISTANCE_STREET_NAME_PROXIMITY_BY_NAME = 15000;
public static final int DISTANCE_STREET_NAME_PROXIMITY_BY_NAME = 45000;
public static final float DISTANCE_STREET_FROM_CLOSEST_WITH_SAME_NAME = 1000;
public static final float THRESHOLD_MULTIPLIER_SKIP_BUILDINGS_AFTER = 1.5f;
@ -142,15 +145,12 @@ public class GeocodingUtilities {
RoutePlannerFrontEnd rp = new RoutePlannerFrontEnd();
List<GeocodingResult> lst = new ArrayList<GeocodingUtilities.GeocodingResult>();
List<RouteSegmentPoint> listR = new ArrayList<BinaryRoutePlanner.RouteSegmentPoint>();
rp.findRouteSegment(lat, lon, ctx, listR);
// we allow duplications to search in both files for boundary regions
rp.findRouteSegment(lat, lon, ctx, listR, false, true);
double distSquare = 0;
TLongHashSet set = new TLongHashSet();
Set<String> streetNames = new HashSet<String>();
Map<String, List<RouteRegion>> streetNames = new HashMap<>();
for (RouteSegmentPoint p : listR) {
RouteDataObject road = p.getRoad();
if (!set.add(road.getId())) {
continue;
}
// System.out.println(road.toString() + " " + Math.sqrt(p.distSquare));
String name = Algorithms.isEmpty(road.getName()) ? road.getRef("", false, true) : road.getName();
if (allowEmptyNames || !Algorithms.isEmpty(name)) {
@ -164,7 +164,13 @@ public class GeocodingUtilities {
sr.connectionPoint = new LatLon(MapUtils.get31LatitudeY(p.preciseY), MapUtils.get31LongitudeX(p.preciseX));
sr.regionFP = road.region.getFilePointer();
sr.regionLen = road.region.getLength();
if (streetNames.add(sr.streetName)) {
List<RouteRegion> plst = streetNames.get(sr.streetName);
if (plst == null) {
plst = new ArrayList<BinaryMapRouteReaderAdapter.RouteRegion>();
streetNames.put(sr.streetName, plst);
}
if (!plst.contains(road.region)) {
plst.add(road.region);
lst.add(sr);
}
}
@ -308,6 +314,48 @@ public class GeocodingUtilities {
return res;
}
public void filterDuplicateRegionResults(final List<GeocodingResult> res) {
Collections.sort(res, DISTANCE_COMPARATOR);
// filter duplicate city results (when building is in both regions on boundary)
for (int i = 0; i < res.size() - 1;) {
int cmp = cmpResult(res.get(i), res.get(i + 1));
if (cmp > 0) {
res.remove(i);
} else if (cmp < 0) {
res.remove(i + 1);
} else {
// nothing to delete
i++;
}
}
}
private int cmpResult(GeocodingResult gr1, GeocodingResult gr2) {
boolean eqStreet = Algorithms.stringsEqual(gr1.streetName, gr2.streetName);
if (eqStreet) {
boolean sameObj = false;
if (gr1.building != null && gr2.building != null) {
if (Algorithms.stringsEqual(gr1.building.getName(), gr2.building.getName())) {
// same building
sameObj = true;
}
} else if (gr1.building == null && gr2.building == null) {
// same street
sameObj = true;
}
if (sameObj) {
double cityDist1 = MapUtils.getDistance(gr1.searchPoint, gr1.city.getLocation());
double cityDist2 = MapUtils.getDistance(gr2.searchPoint, gr2.city.getLocation());
if (cityDist1 < cityDist2) {
return -1;
} else {
return 1;
}
}
}
return 0;
}
private List<GeocodingResult> loadStreetBuildings(final GeocodingResult road, BinaryMapIndexReader reader,
GeocodingResult street) throws IOException {
final List<GeocodingResult> streetBuildings = new ArrayList<GeocodingResult>();

View file

@ -1,12 +1,15 @@
package net.osmand.binary;
import net.osmand.Location;
import net.osmand.PlatformUtil;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import net.osmand.util.TransliterationHelper;
import org.apache.commons.logging.Log;
import java.text.MessageFormat;
import java.util.Arrays;
@ -34,7 +37,7 @@ public class RouteDataObject {
public int[] nameIds;
// mixed array [0, height, cumulative_distance height, cumulative_distance, height, ...] - length is length(points)*2
public float[] heightDistanceArray = null;
private static final Log LOG = PlatformUtil.getLog(RouteDataObject.class);
public RouteDataObject(RouteRegion region) {
this.region = region;
}
@ -56,6 +59,7 @@ public class RouteDataObject {
this.pointsY = copy.pointsY;
this.types = copy.types;
this.names = copy.names;
this.nameIds = copy.nameIds;
this.restrictions = copy.restrictions;
this.restrictionsVia = copy.restrictionsVia;
this.pointTypes = copy.pointTypes;
@ -426,12 +430,19 @@ public class RouteDataObject {
int[] opointsX = pointsX;
int[] opointsY = pointsY;
int[][] opointTypes = pointTypes;
String[][] opointNames = pointNames;
int[][] opointNameTypes = pointNameTypes;
pointsX = new int[pointsX.length + 1];
pointsY = new int[pointsY.length + 1];
boolean insTypes = this.pointTypes != null && this.pointTypes.length > pos;
boolean insNames = this.pointNames != null && this.pointNames.length > pos;
if (insTypes) {
pointTypes = new int[opointTypes.length + 1][];
}
if (insNames) {
pointNames = new String[opointNames.length + 1][];
pointNameTypes = new int[opointNameTypes.length +1][];
}
int i = 0;
for (; i < pos; i++) {
pointsX[i] = opointsX[i];
@ -439,18 +450,32 @@ public class RouteDataObject {
if (insTypes) {
pointTypes[i] = opointTypes[i];
}
if (insNames) {
pointNames[i] = opointNames[i];
pointNameTypes[i] = opointNameTypes[i];
}
}
pointsX[i] = x31;
pointsY[i] = y31;
if (insTypes) {
pointTypes[i] = null;
}
if (insNames) {
pointNames[i] = null;
pointNameTypes[i] = null;
}
for (i = i + 1; i < pointsX.length; i++) {
pointsX[i] = opointsX[i - 1];
pointsY[i] = opointsY[i - 1];
if (insTypes && i < pointTypes.length) {
pointTypes[i] = opointTypes[i - 1];
}
if (insNames && i < pointNames.length) {
pointNames[i] = opointNames[i - 1];
}
if (insNames && i < pointNameTypes.length) {
pointNameTypes[i] = opointNameTypes[i - 1];
}
}
}

View file

@ -38,6 +38,7 @@ public class Amenity extends MapObject {
public static final String COLLECTION_TIMES = "collection_times";
public static final String CONTENT = "content";
public static final String CUISINE = "cuisine";
public static final String WIKIDATA = "wikidata";
public static final String DISH = "dish";
public static final String REF = "ref";
public static final String OSM_DELETE_VALUE = "delete";

View file

@ -7,7 +7,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.ArrayList;

View file

@ -4,6 +4,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@ -153,6 +154,34 @@ public abstract class MapRenderingTypes {
return a;
}
protected MapRulType checkOrCreateTextRule(String targetTag, MapRulType ref) {
MapRulType mt = types.get(constructRuleKey(targetTag, null));
if (mt == null) {
MapRulType ct = MapRulType.createText(targetTag, ref);
mt = registerRuleType(ct);
}
return mt;
}
protected MapRulType checkOrMainRule(String tag, String value, int minzoom) {
MapRulType mt = types.get(constructRuleKey(tag, value));
if (mt == null) {
mt = registerRuleType(MapRulType.createMainEntity(tag, value));
mt.minzoom = minzoom;
mt.maxzoom = 21;
}
return mt;
}
protected MapRulType checkOrCreateAdditional(String tag, String value, MapRulType ref) {
MapRulType mt = types.get(constructRuleKey(tag, value));
if (mt == null) {
MapRulType ct = MapRulType.createAdditional(tag, value, ref);
mt = registerRuleType(ct);
}
return mt;
}
protected MapRulType getRuleType(String tag, String val, boolean poi, boolean map) {
Map<String, MapRulType> types = getEncodingRuleTypes();
tag = lc(tag);
@ -171,7 +200,6 @@ public abstract class MapRenderingTypes {
rType.map = parent.map;
rType.poi = parent.poi;
rType.onlyPoint = parent.onlyPoint;
rType.namePrefix = parent.namePrefix;
rType = registerRuleType(rType);
}
return rType;
@ -265,37 +293,112 @@ public abstract class MapRenderingTypes {
rtype.onlyPoint = Boolean.parseBoolean(parser.getAttributeValue("", "point")); //$NON-NLS-1$
rtype.relation = Boolean.parseBoolean(parser.getAttributeValue("", "relation")); //$NON-NLS-1$
rtype.relationGroup = Boolean.parseBoolean(parser.getAttributeValue("", "relationGroup")); //$NON-NLS-1$
if (rtype.isMain()) {
rtype.namePrefix = parser.getAttributeValue("", "namePrefix"); //$NON-NLS-1$
if (rtype.namePrefix == null) {
rtype.namePrefix = "";
if (rtype.isMain()) {
if (rtype.relationGroup) {
MapRulType mrt = MapRulType.createMainEntity(tag + "_" + value, null);
mrt.order = rtype.order;
mrt.category = rtype.category;
mrt.poi = rtype.poi;
mrt.map = rtype.map;
registerMapRule(parser, mrt);
}
String v = parser.getAttributeValue("", "nameTags");
if (v != null) {
String[] names = v.split(",");
rtype.names = new MapRulType[names.length * (langs.length + 1)];
int j = 0;
for (int i = 0; i < names.length; i++) {
String tagName = names[i];
if (rtype.namePrefix.length() > 0) {
tagName = rtype.namePrefix + tagName;
String groupSort = parser.getAttributeValue("", "relationGroupSort");
if (groupSort != null) {
rtype.relationSortTags = new LinkedHashMap<String, List<String>>();
String[] ls = groupSort.split(";");
for(String l : ls) {
int sp = l.indexOf('=');
String key = l;
String[] values = new String[0];
if(sp >= 0) {
key = l.substring(0, sp);
values = l.substring(sp +1).split(",");
}
MapRulType mt = MapRulType.createText(tagName);
mt = registerRuleType(mt);
rtype.names[j++] = mt;
for(String lng : langs) {
mt = MapRulType.createText(tagName + ":" + lng);
mt = registerRuleType(mt);
rtype.names[j++] = mt;
rtype.relationSortTags.put(key, Arrays.asList(values));
}
}
String additionalTags = parser.getAttributeValue("", "additionalTags");
String additionalPrefix = parser.getAttributeValue("", "additionalPrefix");
if (additionalTags != null) {
rtype.additionalTags = new LinkedHashMap<String, String>();
for(String tg : additionalTags.split(",")) {
String targetTag = tg;
if(!Algorithms.isEmpty(additionalPrefix)) {
targetTag = additionalPrefix + tg;
}
rtype.additionalTags.put(tg, targetTag);
}
}
rtype.relationGroupPrefix = parser.getAttributeValue("", "relationGroupPrefix"); //$NON-NLS-1$
String relationGroupAdditionalTags = parser.getAttributeValue("", "relationGroupAdditionalTags");
if (relationGroupAdditionalTags != null) {
rtype.relationGroupAdditionalTags = new LinkedHashMap<String, String>();
for(String tg : relationGroupAdditionalTags.split(",")) {
rtype.relationGroupAdditionalTags.put(tg, tg);
}
}
String nmts = parser.getAttributeValue("", "nameTags");
if (nmts != null) {
if (rtype.relation || rtype.relationGroup) {
String namePrefix = parser.getAttributeValue("", "namePrefix"); //$NON-NLS-1$
if (namePrefix == null) {
namePrefix = "";
}
rtype.relationNames = new LinkedHashMap<String, String>();
putNameTags(nmts, rtype.relationNames, namePrefix);
} else {
String[] nameSplit = nmts.split(",");
for (String nameTag : nameSplit) {
checkOrCreateTextRule(nameTag, null);
}
}
}
String rnmts = parser.getAttributeValue("", "relationGroupNameTags");
if (rnmts != null) {
rtype.relationGroupNameTags = new LinkedHashMap<String, String>();
putNameTags(rnmts, rtype.relationGroupNameTags, "");
}
}
return rtype;
}
private void putNameTags(String namesList, Map<String, String> names, String namePrefix) {
if (namesList != null) {
String[] nameSplit = namesList.split(",");
for (int i = 0; i < nameSplit.length; i++) {
String tagName = nameSplit[i];
String tagTargetName = tagName;
if (namePrefix.length() > 0) {
tagTargetName = namePrefix + tagName;
}
names.put(tagName, tagTargetName);
for (String lng : langs) {
names.put(tagName + ":" + lng, tagTargetName + ":" + lng);
}
}
}
}
protected void registerMapRule(XmlPullParser parser, MapRulType rtype) {
String val = parser.getAttributeValue("", "minzoom"); //$NON-NLS-1$
if (rtype.isMain()) {
rtype.minzoom = 15;
}
if (val != null) {
rtype.minzoom = Integer.parseInt(val);
}
val = parser.getAttributeValue("", "maxzoom"); //$NON-NLS-1$
rtype.maxzoom = 31;
if (val != null) {
rtype.maxzoom = Integer.parseInt(val);
}
registerRuleType(rtype);
}
protected MapRulType registerRuleType(MapRulType rt) {
String tag = rt.tagValuePattern.tag;
String val = rt.tagValuePattern.value;
@ -422,7 +525,14 @@ public abstract class MapRenderingTypes {
}
public static class MapRulType {
protected MapRulType[] names;
// relation part
protected Map<String, String> relationNames;
protected Map<String, String> additionalTags;
protected Map<String, List<String>> relationSortTags;
protected String relationGroupPrefix;
protected Map<String, String> relationGroupNameTags;
protected Map<String, String> relationGroupAdditionalTags;
protected TagValuePattern tagValuePattern;
protected boolean additional;
protected boolean additionalText;
@ -440,7 +550,6 @@ public abstract class MapRenderingTypes {
protected int minzoom;
protected int maxzoom;
protected boolean onlyPoint;
protected String namePrefix ="";
// inner id
@ -452,6 +561,16 @@ public abstract class MapRenderingTypes {
private MapRulType(){
}
private void copyMetadata(MapRulType ref) {
minzoom = ref.minzoom;
maxzoom = ref.maxzoom;
order = ref.order;
category = ref.category;
onlyPoint = ref.onlyPoint;
}
public boolean isPOI(){
return poi;
}
@ -471,24 +590,37 @@ public abstract class MapRenderingTypes {
return rt;
}
public static MapRulType createText(String tag) {
public static MapRulType createText(String tag, MapRulType ref) {
MapRulType rt = new MapRulType();
rt.additionalText = true;
rt.minzoom = 2;
rt.maxzoom = 31;
rt.tagValuePattern = new TagValuePattern(tag, null);
if (ref != null) {
rt.copyMetadata(ref);
}
rt.additionalText = true;
rt.tagValuePattern = new TagValuePattern(tag, null);
return rt;
}
public static MapRulType createAdditional(String tag, String value) {
public static MapRulType createAdditional(String tag, String value, MapRulType ref) {
MapRulType rt = new MapRulType();
rt.additional = true;
rt.minzoom = 2;
rt.maxzoom = 31;
if (ref != null) {
rt.copyMetadata(ref);
}
rt.additional = true;
rt.tagValuePattern = new TagValuePattern(tag, value);
return rt;
}
public static MapRulType createText(String tag) {
return createText(tag, null);
}
public static MapRulType createAdditional(String tag, String value) {
return createAdditional(tag, value, null);
}
public String getTag() {
return tagValuePattern.tag;
@ -549,14 +681,6 @@ public abstract class MapRenderingTypes {
return onlyPoint;
}
public boolean isRelation() {
return relation;
}
public boolean isRelationGroup() {
return relationGroup;
}
public int getFreq() {
return freq;

View file

@ -7,7 +7,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@ -18,6 +17,7 @@ import java.util.Map.Entry;
import java.util.Stack;
import net.osmand.PlatformUtil;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
import org.xmlpull.v1.XmlPullParser;
@ -26,7 +26,7 @@ import org.xmlpull.v1.XmlPullParserException;
public class RenderingRulesStorage {
private final static Log log = PlatformUtil.getLog(RenderingRulesStorage.class);
static boolean STORE_ATTTRIBUTES = false;
static boolean STORE_ATTRIBUTES = false;
// keep sync !
// keep sync ! not change values
@ -40,6 +40,10 @@ public class RenderingRulesStorage {
private final static int SHIFT_TAG_VAL = 16;
private final static String SEQ_ATTR_KEY = "seq";
private final static String SEQ_PLACEHOLDER = "#SEQ";
// C++
List<String> dictionary = new ArrayList<String>();
Map<String, Integer> dictionaryMap = new LinkedHashMap<String, Integer>();
@ -183,31 +187,86 @@ public class RenderingRulesStorage {
}
}
private class XmlTreeSequence {
XmlTreeSequence parent;
String seqOrder;
Map<String, String> attrsMap = new LinkedHashMap<String, String>();
String name;
List<XmlTreeSequence> children = new ArrayList<RenderingRulesStorage.XmlTreeSequence>();
private void process(RenderingRulesHandler handler, int el) throws XmlPullParserException, IOException {
Map<String, String> seqAttrsMap = new HashMap<String, String>(attrsMap);
if (attrsMap.containsKey(SEQ_ATTR_KEY)) {
attrsMap.remove(SEQ_ATTR_KEY);
}
for (Entry<String, String> attr: attrsMap.entrySet()) {
if (attr.getValue().contains(SEQ_PLACEHOLDER)) {
seqAttrsMap.put(attr.getKey(), attr.getValue().replace(SEQ_PLACEHOLDER, el+""));
} else {
seqAttrsMap.put(attr.getKey(), attr.getValue());
}
}
handler.startElement(seqAttrsMap, name);
for(XmlTreeSequence s : children) {
s.process(handler, el);
}
handler.endElement(name);
}
}
private class RenderingRulesHandler {
private final XmlPullParser parser;
private int state;
Stack<RenderingRule> stack = new Stack<RenderingRule>();
Map<String, String> attrsMap = new LinkedHashMap<String, String>();
private final RenderingRulesStorageResolver resolver;
private RenderingRulesStorage dependsStorage;
public RenderingRulesHandler(XmlPullParser parser, RenderingRulesStorageResolver resolver){
this.parser = parser;
this.resolver = resolver;
}
public void parse(InputStream is) throws XmlPullParserException, IOException {
XmlPullParser parser = this.parser;
Map<String, String> attrsMap = new LinkedHashMap<String, String>();
parser.setInput(is, "UTF-8");
int tok;
XmlTreeSequence currentSeqElement = null;
while ((tok = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (tok == XmlPullParser.START_TAG) {
startElement(parser.getName());
attrsMap.clear();
parseAttributes(parser, attrsMap);
String name = parser.getName();
if (!Algorithms.isEmpty(parser.getAttributeValue("", SEQ_ATTR_KEY)) || currentSeqElement != null) {
XmlTreeSequence seq = new XmlTreeSequence();
seq.name = name;
seq.attrsMap = new HashMap<String, String>(attrsMap);
seq.parent = currentSeqElement;
if (currentSeqElement == null) {
seq.seqOrder = parser.getAttributeValue("", SEQ_ATTR_KEY);
} else {
currentSeqElement.children.add(seq);
seq.seqOrder = currentSeqElement.seqOrder;
}
currentSeqElement = seq;
} else {
startElement(attrsMap, name);
}
} else if (tok == XmlPullParser.END_TAG) {
endElement(parser.getName());
if(currentSeqElement == null) {
endElement(parser.getName());
} else {
XmlTreeSequence process = currentSeqElement;
currentSeqElement = currentSeqElement.parent;
if (currentSeqElement == null) {
// Here we process sequence element
int seqEnd = Integer.parseInt(process.seqOrder.substring(process.seqOrder.indexOf(':') + 1, process.seqOrder.length()));
for(int i = 1; i < seqEnd; i++) {
process.process(this, i);
}
}
}
}
}
@ -226,16 +285,14 @@ public class RenderingRulesStorage {
return true;
}
public void startElement(String name) throws XmlPullParserException, IOException {
public void startElement(Map<String, String> attrsMap, String name) throws XmlPullParserException, IOException {
boolean stateChanged = false;
final boolean isCase = isCase(name);
final boolean isSwitch = isSwitch(name);
if(isCase || isSwitch){ //$NON-NLS-1$
attrsMap.clear();
boolean top = stack.size() == 0 || isTopCase();
parseAttributes(attrsMap);
RenderingRule renderingRule = new RenderingRule(attrsMap, isSwitch, RenderingRulesStorage.this);
if(top || STORE_ATTTRIBUTES){
if(top || STORE_ATTRIBUTES){
renderingRule.storeAttributes(attrsMap);
}
if (stack.size() > 0 && stack.peek() instanceof RenderingRule) {
@ -244,10 +301,8 @@ public class RenderingRulesStorage {
}
stack.push(renderingRule);
} else if(isApply(name)){ //$NON-NLS-1$
attrsMap.clear();
parseAttributes(attrsMap);
RenderingRule renderingRule = new RenderingRule(attrsMap, false, RenderingRulesStorage.this);
if(STORE_ATTTRIBUTES) {
if(STORE_ATTRIBUTES) {
renderingRule.storeAttributes(attrsMap);
}
if (stack.size() > 0 && stack.peek() instanceof RenderingRule) {
@ -272,14 +327,14 @@ public class RenderingRulesStorage {
state = POLYGON_RULES;
stateChanged = true;
} else if("renderingAttribute".equals(name)){ //$NON-NLS-1$
String attr = parser.getAttributeValue("", "name");
String attr = attrsMap.get("name");
RenderingRule root = new RenderingRule(new HashMap<String, String>(), false, RenderingRulesStorage.this);
renderingAttributes.put(attr, root);
stack.push(root);
} else if("renderingProperty".equals(name)){ //$NON-NLS-1$
String attr = parser.getAttributeValue("", "attr");
String attr = attrsMap.get("attr");
RenderingRuleProperty prop;
String type = parser.getAttributeValue("", "type");
String type = attrsMap.get("type");
if("boolean".equalsIgnoreCase(type)){
prop = RenderingRuleProperty.createInputBooleanProperty(attr);
} else if("string".equalsIgnoreCase(type)){
@ -287,20 +342,20 @@ public class RenderingRulesStorage {
} else {
prop = RenderingRuleProperty.createInputIntProperty(attr);
}
prop.setDescription(parser.getAttributeValue("", "description"));
prop.setDefaultValueDescription(parser.getAttributeValue("", "defaultValueDescription"));
prop.setCategory(parser.getAttributeValue("", "category"));
prop.setName(parser.getAttributeValue("", "name"));
if(parser.getAttributeValue("", "possibleValues") != null){
prop.setPossibleValues(parser.getAttributeValue("", "possibleValues").split(","));
prop.setDescription(attrsMap.get("description"));
prop.setDefaultValueDescription(attrsMap.get("defaultValueDescription"));
prop.setCategory(attrsMap.get("category"));
prop.setName(attrsMap.get("name"));
if (attrsMap.get("possibleValues") != null) {
prop.setPossibleValues(attrsMap.get("possibleValues").split(","));
}
PROPS.registerRule(prop);
} else if("renderingConstant".equals(name)){ //$NON-NLS-1$
if(!renderingConstants.containsKey(parser.getAttributeValue("", "name"))){
renderingConstants.put(parser.getAttributeValue("", "name"), parser.getAttributeValue("", "value"));
if(!renderingConstants.containsKey(attrsMap.get("name"))){
renderingConstants.put(attrsMap.get("name"), attrsMap.get("value"));
}
} else if("renderingStyle".equals(name)){ //$NON-NLS-1$
String depends = parser.getAttributeValue("", "depends");
String depends = attrsMap.get("depends");
if(depends != null && depends.length()> 0){
this.dependsStorage = resolver.resolve(depends, resolver);
}
@ -311,7 +366,7 @@ public class RenderingRulesStorage {
PROPS = new RenderingRuleStorageProperties(dependsStorage.PROPS);
}
internalRenderingName = parser.getAttributeValue("", "name");
internalRenderingName = attrsMap.get("name");
} else if("renderer".equals(name)){ //$NON-NLS-1$
throw new XmlPullParserException("Rendering style is deprecated and no longer supported.");
@ -337,7 +392,7 @@ public class RenderingRulesStorage {
return "group".equals(name) || "switch".equals(name);
}
private Map<String, String> parseAttributes(Map<String, String> m) {
private Map<String, String> parseAttributes(XmlPullParser parser, Map<String, String> m) {
for (int i = 0; i < parser.getAttributeCount(); i++) {
String name = parser.getAttributeName(i);
String vl = parser.getAttributeValue(i);
@ -396,7 +451,7 @@ public class RenderingRulesStorage {
vl = ns.remove("value");
// reset rendering rule attributes
renderingRule.init(ns);
if(STORE_ATTTRIBUTES) {
if(STORE_ATTRIBUTES) {
renderingRule.storeAttributes(ns);
}
@ -469,7 +524,7 @@ public class RenderingRulesStorage {
public static void main(String[] args) throws XmlPullParserException, IOException {
STORE_ATTTRIBUTES = true;
STORE_ATTRIBUTES = true;
// InputStream is = RenderingRulesStorage.class.getResourceAsStream("default.render.xml");
final String loc = "/Users/victorshcherb/osmand/repos/resources/rendering_styles/";
String defaultFile = loc + "UniRS.render.xml";

View file

@ -21,7 +21,7 @@ import org.xmlpull.v1.XmlPullParserException;
public class RenderingRulesStoragePrinter {
public static void main(String[] args) throws XmlPullParserException, IOException {
RenderingRulesStorage.STORE_ATTTRIBUTES = true;
RenderingRulesStorage.STORE_ATTRIBUTES = true;
// InputStream is = RenderingRulesStorage.class.getResourceAsStream("default.render.xml");
String defaultFile = "/Users/victorshcherb/osmand/repos/resources/rendering_styles/default.render.xml";
if(args.length > 0) {

View file

@ -65,13 +65,10 @@ public class BinaryRoutePlanner {
* Calculate route between start.segmentEnd and end.segmentStart (using A* algorithm)
* return list of segments
*/
@SuppressWarnings("unused")
FinalRouteSegment searchRouteInternal(final RoutingContext ctx, RouteSegmentPoint start, RouteSegmentPoint end,
RouteSegment recalculationEnd ) throws InterruptedException, IOException {
// measure time
ctx.timeToLoad = 0;
ctx.memoryOverhead = 1000;
ctx.visitedSegments = 0;
// Initializing priority queue to visit way segments
Comparator<RouteSegment> nonHeuristicSegmentsComparator = new NonHeuristicSegmentsComparator();
@ -122,7 +119,9 @@ public class BinaryRoutePlanner {
if (ctx.memoryOverhead > ctx.config.memoryLimitation * 0.95) {
throw new IllegalStateException("There is not enough memory " + ctx.config.memoryLimitation / (1 << 20) + " Mb");
}
ctx.visitedSegments ++;
if (ctx.calculationProgress != null) {
ctx.calculationProgress.visitedSegments++;
}
if (forwardSearch) {
boolean doNotAddIntersections = onlyBackward;
processRouteSegment(ctx, false, graphDirectSegments, visitedDirectSegments,
@ -166,8 +165,14 @@ public class BinaryRoutePlanner {
throw new InterruptedException("Route calculation interrupted");
}
}
ctx.visitedSegments = visitedDirectSegments.size() + visitedOppositeSegments.size();
printDebugMemoryInformation(ctx, graphDirectSegments, graphReverseSegments, visitedDirectSegments, visitedOppositeSegments);
if (ctx.calculationProgress != null) {
ctx.calculationProgress.visitedDirectSegments += visitedDirectSegments.size();
ctx.calculationProgress.visitedOppositeSegments += visitedOppositeSegments.size();
ctx.calculationProgress.directQueueSize += graphDirectSegments.size(); // Math.max(ctx.directQueueSize,
// graphDirectSegments.size());
ctx.calculationProgress.oppositeQueueSize += graphReverseSegments.size();
ctx.calculationProgress.visitedOppositeSegments += visitedOppositeSegments.size();
}
return finalSegment;
}
@ -368,23 +373,21 @@ public class BinaryRoutePlanner {
log.warn(logMsg);
}
public void printDebugMemoryInformation(RoutingContext ctx, PriorityQueue<RouteSegment> graphDirectSegments, PriorityQueue<RouteSegment> graphReverseSegments,
TLongObjectHashMap<RouteSegment> visitedDirectSegments,TLongObjectHashMap<RouteSegment> visitedOppositeSegments) {
printInfo(String.format("Time. Total: %.2f, to load: %.2f, to load headers: %.2f, to calc dev: %.2f ",
(System.nanoTime() - ctx.timeToCalculate) / 1e6, ctx.timeToLoad / 1e6,
ctx.timeToLoadHeaders / 1e6, ctx.timeNanoToCalcDeviation / 1e6));
// GeneralRouter.TIMER = 0;
int maxLoadedTiles = Math.max(ctx.maxLoadedTiles, ctx.getCurrentlyLoadedTiles());
printInfo("Current loaded tiles : " + ctx.getCurrentlyLoadedTiles() + ", maximum loaded tiles " + maxLoadedTiles);
printInfo("Loaded tiles " + ctx.loadedTiles + " (distinct " + ctx.distinctLoadedTiles + "), unloaded tiles " + ctx.unloadedTiles +
", loaded more than once same tiles "
+ ctx.loadedPrevUnloadedTiles);
printInfo("Visited segments " + ctx.visitedSegments + ", relaxed roads " + ctx.relaxedSegments);
if (graphDirectSegments != null && graphReverseSegments != null) {
printInfo("Priority queues sizes : " + graphDirectSegments.size() + "/" + graphReverseSegments.size());
}
if (visitedDirectSegments != null && visitedOppositeSegments != null) {
printInfo("Visited interval sizes: " + visitedDirectSegments.size() + "/" + visitedOppositeSegments.size());
public static void printDebugMemoryInformation(RoutingContext ctx) {
if (ctx.calculationProgress != null) {
RouteCalculationProgress p = ctx.calculationProgress;
printInfo(String.format("Time. Total: %.2f, to load: %.2f, to load headers: %.2f, to find start/end: %.2f, extra: %.2f ",
p.timeToCalculate / 1e6, p.timeToLoad / 1e6, p.timeToLoadHeaders / 1e6,
p.timeToFindInitialSegments / 1e6, p.timeNanoToCalcDeviation / 1e6));
// GeneralRouter.TIMER = 0;
int maxLoadedTiles = Math.max(p.maxLoadedTiles, ctx.getCurrentlyLoadedTiles());
printInfo("Current loaded tiles : " + ctx.getCurrentlyLoadedTiles() + ", maximum loaded tiles "
+ maxLoadedTiles);
printInfo("Loaded tiles " + p.loadedTiles + " (distinct " + p.distinctLoadedTiles + "), unloaded tiles "
+ p.unloadedTiles + ", loaded more than once same tiles " + p.loadedPrevUnloadedTiles);
printInfo("Visited segments: " + ctx.getVisitedSegments() + ", relaxed roads " + p.relaxedSegments);
printInfo("Priority queues sizes : " + p.directQueueSize + "/" + p.oppositeQueueSize);
printInfo("Visited interval sizes: " + p.visitedDirectSegments + "/" + p.visitedOppositeSegments);
}
}
@ -852,9 +855,12 @@ public class BinaryRoutePlanner {
}
public static class RouteSegmentPoint extends RouteSegment {
public RouteSegmentPoint(RouteDataObject road, int segmentStart, double distSquare) {
super(road, segmentStart);
this.distSquare = distSquare;
this.preciseX = road.getPoint31XTile(segmentStart);
this.preciseY = road.getPoint31YTile(segmentStart);
}
public RouteSegmentPoint(RouteSegmentPoint pnt) {
@ -874,6 +880,11 @@ public class BinaryRoutePlanner {
}
@Override
public String toString() {
return String.format("%d (%s): %s", segStart, getPreciseLatLon(), road);
}
}
public static class RouteSegment {

View file

@ -72,6 +72,8 @@ public class GeneralRouter implements VehicleRouter {
private float defaultSpeed = 1f;
// speed in m/s
private float maxSpeed = 10f;
// speed in m/s (used for shortest route)
private float maxVehicleSpeed;
private TLongHashSet impassableRoads;
private GeneralRouterProfile profile;
@ -149,6 +151,7 @@ public class GeneralRouter implements VehicleRouter {
if (params.containsKey(MAX_SPEED)) {
maxSpeed = parseSilentFloat(params.get(MAX_SPEED), maxSpeed);
}
maxVehicleSpeed = maxSpeed;
if (shortestRoute) {
maxSpeed = Math.min(CAR_SHORTEST_DEFAULT_SPEED, maxSpeed);
}
@ -489,10 +492,15 @@ public class GeneralRouter implements VehicleRouter {
@Override
public float defineVehicleSpeed(RouteDataObject road) {
// don't use cache cause max/min is different for routing speed
if (maxVehicleSpeed != maxSpeed) {
float spd = getObjContext(RouteDataObjectAttribute.ROAD_SPEED).evaluateFloat(road, defaultSpeed);
return Math.max(Math.min(spd, maxVehicleSpeed), minSpeed);
}
Float sp = getCache(RouteDataObjectAttribute.ROAD_SPEED, road);
if (sp == null) {
float spd = getObjContext(RouteDataObjectAttribute.ROAD_SPEED).evaluateFloat(road, defaultSpeed);
sp = Math.max(Math.min(spd, maxSpeed), minSpeed);
sp = Math.max(Math.min(spd, maxVehicleSpeed), minSpeed);
putCache(RouteDataObjectAttribute.ROAD_SPEED, road, sp);
}
return sp;

View file

@ -12,12 +12,29 @@ public class RouteCalculationProgress {
public float totalEstimatedDistance = 0;
public float routingCalculatedTime = 0;
public int loadedTiles = 0;
public int relaxedSegments = 0;
public int visitedSegments = 0;
public int visitedDirectSegments = 0;
public int visitedOppositeSegments = 0;
public int directQueueSize = 0;
public int oppositeQueueSize = 0;
public int totalIterations = 1;
public int iteration = -1;
public long timeNanoToCalcDeviation = 0;
public long timeToLoad = 0;
public long timeToLoadHeaders = 0;
public long timeToFindInitialSegments = 0;
public long timeToCalculate = 0;
public int distinctLoadedTiles = 0;
public int maxLoadedTiles = 0;
public int loadedPrevUnloadedTiles = 0;
public int unloadedTiles = 0;
public int loadedTiles = 0;
public boolean isCancelled;
public boolean requestPrivateAccessRouting;

View file

@ -1,14 +1,7 @@
package net.osmand.router;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import net.osmand.LocationsHolder;
import net.osmand.NativeLibrary;
import net.osmand.PlatformUtil;
import net.osmand.binary.BinaryMapIndexReader;
@ -23,6 +16,14 @@ import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import gnu.trove.list.array.TIntArrayList;
public class RoutePlannerFrontEnd {
@ -31,7 +32,7 @@ public class RoutePlannerFrontEnd {
// Check issue #8649
protected static final double GPS_POSSIBLE_ERROR = 7;
public boolean useSmartRouteRecalculation = true;
public RoutePlannerFrontEnd() {
}
@ -50,13 +51,14 @@ public class RoutePlannerFrontEnd {
// don't search subsegments shorter than specified distance (also used to step back for car turns)
public double MINIMUM_STEP_APPROXIMATION = 100;
// Parameter to smoother the track itself (could be 0 if it's not recorded track)
public double SMOOTHEN_POINTS_NO_ROUTE = 2;
public double SMOOTHEN_POINTS_NO_ROUTE = 5;
public final RoutingContext ctx;
public int routeCalculations = 0;
public int routePointsSearched = 0;
public int routeDistCalculations = 0;
public List<RouteSegmentResult> res = new ArrayList<RouteSegmentResult>();
public List<GpxPoint> finalPoints = new ArrayList<GpxPoint>();
public List<RouteSegmentResult> result = new ArrayList<RouteSegmentResult>();
public int routeDistance;
public int routeGapDistance;
public int routeDistanceUnmatched;
@ -72,27 +74,29 @@ public class RoutePlannerFrontEnd {
}
public double distFromLastPoint(LatLon startPoint) {
if(res.size() > 0) {
if (result.size() > 0) {
return MapUtils.getDistance(getLastPoint(), startPoint);
}
return 0;
}
public LatLon getLastPoint() {
if(res.size() > 0) {
return res.get(res.size() - 1).getEndPoint();
if (result.size() > 0) {
return result.get(result.size() - 1).getEndPoint();
}
return null;
}
}
private static class GpxPoint {
public static class GpxPoint {
public int ind;
public LatLon loc;
public double cumDist;
public RouteSegmentPoint pnt;
public List<RouteSegmentResult> routeToTarget;
public List<RouteSegmentResult> stepBackRoute;
public int targetInd = -1;
public boolean straightLine = false;
}
public RoutingContext buildRoutingContext(RoutingConfiguration config, NativeLibrary nativeLibrary, BinaryMapIndexReader[] map, RouteCalculationMode rm) {
@ -116,15 +120,21 @@ public class RoutePlannerFrontEnd {
}
public RouteSegmentPoint findRouteSegment(double lat, double lon, RoutingContext ctx, List<RouteSegmentPoint> list, boolean transportStop) throws IOException {
return findRouteSegment(lat, lon, ctx, list, false, false);
}
public RouteSegmentPoint findRouteSegment(double lat, double lon, RoutingContext ctx, List<RouteSegmentPoint> list, boolean transportStop,
boolean allowDuplications) throws IOException {
long now = System.nanoTime();
int px = MapUtils.get31TileNumberX(lon);
int py = MapUtils.get31TileNumberY(lat);
ArrayList<RouteDataObject> dataObjects = new ArrayList<RouteDataObject>();
ctx.loadTileData(px, py, 17, dataObjects);
ctx.loadTileData(px, py, 17, dataObjects, allowDuplications);
if (dataObjects.isEmpty()) {
ctx.loadTileData(px, py, 15, dataObjects);
ctx.loadTileData(px, py, 15, dataObjects, allowDuplications);
}
if (dataObjects.isEmpty()) {
ctx.loadTileData(px, py, 14, dataObjects);
ctx.loadTileData(px, py, 14, dataObjects, allowDuplications);
}
if (list == null) {
list = new ArrayList<BinaryRoutePlanner.RouteSegmentPoint>();
@ -166,6 +176,9 @@ public class RoutePlannerFrontEnd {
return Double.compare(o1.distSquare, o2.distSquare);
}
});
if (ctx.calculationProgress != null) {
ctx.calculationProgress.timeToFindInitialSegments += (System.nanoTime() - now);
}
if (list.size() > 0) {
RouteSegmentPoint ps = null;
if (ctx.publicTransport) {
@ -202,38 +215,36 @@ public class RoutePlannerFrontEnd {
useSmartRouteRecalculation = use;
}
// TODO last segment not correct (cut) before end point and point of straight line
// TODO add missing turns for straight lines (compare code)
// TODO native matches less roads
// TODO fix progress - next iteration
// TODO fix timings and remove logging every iteration
public GpxRouteApproximation searchGpxRoute(GpxRouteApproximation gctx, List<LatLon> points) throws IOException, InterruptedException {
gctx.ctx.timeToCalculate = System.nanoTime();
public GpxRouteApproximation searchGpxRoute(GpxRouteApproximation gctx, List<GpxPoint> gpxPoints) throws IOException, InterruptedException {
long timeToCalculate = System.nanoTime();
if (gctx.ctx.calculationProgress == null) {
gctx.ctx.calculationProgress = new RouteCalculationProgress();
}
List<GpxPoint> gpxPoints = generageGpxPoints(points, gctx);
GpxPoint start = gpxPoints.size() > 0 ? gpxPoints.get(0) : null;
boolean prevRouteFound = false;
gctx.ctx.keepNativeRoutingContext = true;
GpxPoint start = null;
GpxPoint prev = null;
if(gpxPoints.size() > 0) {
gctx.ctx.calculationProgress.totalIterations = (int) (gpxPoints.get(gpxPoints.size() - 1).cumDist / gctx.MAXIMUM_STEP_APPROXIMATION + 1);
start = gpxPoints.get(0);
}
while (start != null) {
double routeDist = gctx.MAXIMUM_STEP_APPROXIMATION;
GpxPoint next = findNextGpxPointWithin(gctx, gpxPoints, start, routeDist);
boolean routeFound = false;
gctx.ctx.calculationProgress.nextIteration();
if (next != null && initRoutingPoint(start, gctx, gctx.MINIMUM_POINT_APPROXIMATION)) {
while (routeDist >= gctx.MINIMUM_STEP_APPROXIMATION && !routeFound) {
routeFound = initRoutingPoint(next, gctx, gctx.MINIMUM_POINT_APPROXIMATION);
if (routeFound) {
routeFound = findGpxRouteSegment(gctx, gpxPoints, start, next, prevRouteFound);
routeFound = findGpxRouteSegment(gctx, gpxPoints, start, next, prev != null);
if (routeFound) {
// route is found - cut the end of the route and move to next iteration
// start.stepBackRoute = new ArrayList<RouteSegmentResult>();
// boolean stepBack = true;
boolean stepBack = stepBackAndFindPrevPointInRoute(gctx, gpxPoints, start, next);
if (!stepBack) {
// not supported case (workaround increase MAXIMUM_STEP_APPROXIMATION)
log.info("Consider to increase MAXIMUM_STEP_APPROXIMATION to: " + routeDist*2);
log.info("Consider to increase MAXIMUM_STEP_APPROXIMATION to: " + routeDist * 2);
start.routeToTarget = null;
routeFound = false;
break;
@ -257,23 +268,27 @@ public class RoutePlannerFrontEnd {
if (!routeFound) {
// route is not found, move start point by
next = findNextGpxPointWithin(gctx, gpxPoints, start, gctx.MINIMUM_STEP_APPROXIMATION);
if (prevRouteFound) {
if (next == null) {
// TODO finish
// makeSegmentPointPrecise(prev.routeToTarget.get(prev.routeToTarget.size() - 1), start.loc, false);
} else {
if (prev != null) {
prev.routeToTarget.addAll(prev.stepBackRoute);
makeSegmentPointPrecise(prev.routeToTarget.get(prev.routeToTarget.size() - 1), start.loc, false);
if (next != null) {
log.warn("NOT found route from: " + start.pnt.getRoad() + " at " + start.pnt.getSegmentStart());
}
}
prev = null;
} else {
prev = start;
}
prevRouteFound = routeFound;
start = next;
}
if(gctx.ctx.calculationProgress != null) {
gctx.ctx.calculationProgress.timeToCalculate = System.nanoTime() - timeToCalculate;
}
gctx.ctx.deleteNativeRoutingContext();
BinaryRoutePlanner.printDebugMemoryInformation(gctx.ctx);
calculateGpxRoute(gctx, gpxPoints);
if (!gctx.res.isEmpty()) {
new RouteResultPreparation().printResults(gctx.ctx, points.get(0), points.get(points.size() - 1), gctx.res);
if (!gctx.result.isEmpty()) {
new RouteResultPreparation().printResults(gctx.ctx, gpxPoints.get(0).loc, gpxPoints.get(gpxPoints.size() - 1).loc, gctx.result);
System.out.println(gctx);
}
return gctx;
@ -289,6 +304,7 @@ public class RoutePlannerFrontEnd {
double d = 0;
int segmendInd = start.routeToTarget.size() - 1;
boolean search = true;
start.stepBackRoute = new ArrayList<RouteSegmentResult>();
mainLoop: for (; segmendInd >= 0 && search; segmendInd--) {
RouteSegmentResult rr = start.routeToTarget.get(segmendInd);
boolean minus = rr.getStartPointIndex() < rr.getEndPointIndex();
@ -300,6 +316,7 @@ public class RoutePlannerFrontEnd {
if (nextInd == rr.getStartPointIndex()) {
segmendInd--;
} else {
start.stepBackRoute.add(new RouteSegmentResult(rr.getObject(), nextInd, rr.getEndPointIndex()));
rr.setEndPointIndex(nextInd);
}
search = false;
@ -311,8 +328,10 @@ public class RoutePlannerFrontEnd {
// here all route segments - 1 is longer than needed distance to step back
return false;
}
while (start.routeToTarget.size() > segmendInd + 1) {
start.routeToTarget.remove(segmendInd + 1);
RouteSegmentResult removed = start.routeToTarget.remove(segmendInd + 1);
start.stepBackRoute.add(removed);
}
RouteSegmentResult res = start.routeToTarget.get(segmendInd);
next.pnt = new RouteSegmentPoint(res.getObject(), res.getEndPointIndex(), 0);
@ -321,30 +340,33 @@ public class RoutePlannerFrontEnd {
private void calculateGpxRoute(GpxRouteApproximation gctx, List<GpxPoint> gpxPoints) {
RouteRegion reg = new RouteRegion();
reg.initRouteEncodingRule(0, "highway", "unmatched");
reg.initRouteEncodingRule(0, "highway", RouteResultPreparation.UNMATCHED_HIGHWAY_TYPE);
List<LatLon> lastStraightLine = null;
GpxPoint straightPointStart = 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(startPoint);
addStraightLine(gctx.res, lastStraightLine, reg, gctx);
addStraightLine(gctx, lastStraightLine, straightPointStart, reg);
lastStraightLine = null;
}
if (gctx.distFromLastPoint(startPoint) > 1) {
gctx.routeGapDistance += gctx.distFromLastPoint(startPoint);
System.out.println(String.format("????? gap of route point = %f, gap of actual gpxPoint = %f ",
gctx.distFromLastPoint(startPoint), gctx.distFromLastPoint(pnt.loc)));
System.out.println(String.format("????? gap of route point = %f, gap of actual gpxPoint = %f, %s ",
gctx.distFromLastPoint(startPoint), gctx.distFromLastPoint(pnt.loc), pnt.loc));
}
gctx.res.addAll(pnt.routeToTarget);
gctx.finalPoints.add(pnt);
gctx.result.addAll(pnt.routeToTarget);
i = pnt.targetInd;
} else {
// add straight line from i -> i+1
if (lastStraightLine == null) {
lastStraightLine = new ArrayList<LatLon>();
straightPointStart = pnt;
// make smooth connection
if(gctx.distFromLastPoint(pnt.loc) > 1) {
if (gctx.distFromLastPoint(pnt.loc) > 1) {
lastStraightLine.add(gctx.getLastPoint());
}
}
@ -353,20 +375,20 @@ public class RoutePlannerFrontEnd {
}
}
if (lastStraightLine != null) {
addStraightLine(gctx.res, lastStraightLine, reg, gctx);
addStraightLine(gctx, lastStraightLine, straightPointStart, reg);
lastStraightLine = null;
}
// clean turns to recaculate them
cleanupResultAndAddTurns(gctx);
}
private List<GpxPoint> generageGpxPoints(List<LatLon> points, GpxRouteApproximation gctx) {
List<GpxPoint> gpxPoints = new ArrayList<>(points.size());
public List<GpxPoint> generateGpxPoints(GpxRouteApproximation gctx, LocationsHolder locationsHolder) {
List<GpxPoint> gpxPoints = new ArrayList<>(locationsHolder.getSize());
GpxPoint prev = null;
for(int i = 0; i < points.size(); i++) {
for(int i = 0; i < locationsHolder.getSize(); i++) {
GpxPoint p = new GpxPoint();
p.ind = i;
p.loc = points.get(i);
p.loc = locationsHolder.getLatLon(i);
if (prev != null) {
p.cumDist = MapUtils.getDistance(p.loc, prev.loc) + prev.cumDist;
}
@ -380,27 +402,27 @@ public class RoutePlannerFrontEnd {
private void cleanupResultAndAddTurns(GpxRouteApproximation 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);
for(int i = 0; i < gctx.result.size(); i++) {
RouteSegmentResult s = gctx.result.get(i);
for(int j = i + 2; j <= i + LOOK_AHEAD && j < gctx.result.size(); j++) {
RouteSegmentResult e = gctx.result.get(j);
if (e.getStartPoint().equals(s.getEndPoint())) {
while ((--j) != i) {
gctx.result.remove(j);
}
break;
}
}
}
RouteResultPreparation preparation = new RouteResultPreparation();
for (RouteSegmentResult r : gctx.res) {
for (RouteSegmentResult r : gctx.result) {
r.setTurnType(null);
r.setDescription("");
}
preparation.prepareTurnResults(gctx.ctx, gctx.res);
preparation.prepareTurnResults(gctx.ctx, gctx.result);
}
private void addStraightLine(List<RouteSegmentResult> res, List<LatLon> lastStraightLine, RouteRegion reg, GpxRouteApproximation gctx) {
private void addStraightLine(GpxRouteApproximation gctx, List<LatLon> lastStraightLine, GpxPoint strPnt, RouteRegion reg) {
RouteDataObject rdo = new RouteDataObject(reg);
if(gctx.SMOOTHEN_POINTS_NO_ROUTE > 0) {
simplifyDouglasPeucker(lastStraightLine, gctx.SMOOTHEN_POINTS_NO_ROUTE, 0, lastStraightLine.size() - 1);
@ -424,8 +446,19 @@ public class RoutePlannerFrontEnd {
rdo.pointsY = y.toArray();
rdo.types = new int[] { 0 } ;
rdo.id = -1;
// comment to see road without straight connections
res.add(new RouteSegmentResult(rdo, 0, rdo.getPointsLength() - 1));
strPnt.routeToTarget = new ArrayList<>();
strPnt.straightLine = true;
strPnt.routeToTarget.add(new RouteSegmentResult(rdo, 0, rdo.getPointsLength() - 1));
RouteResultPreparation preparation = new RouteResultPreparation();
try {
preparation.prepareResult(gctx.ctx, strPnt.routeToTarget, false);
} catch (IOException e) {
throw new IllegalStateException(e);
}
// VIEW: comment to see road without straight connections
gctx.finalPoints.add(strPnt);
gctx.result.addAll(strPnt.routeToTarget);
}
@ -493,6 +526,7 @@ public class RoutePlannerFrontEnd {
gctx.routeDistCalculations += (target.cumDist - start.cumDist);
gctx.routeCalculations++;
res = searchRouteInternalPrepare(gctx.ctx, start.pnt, target.pnt, null);
//BinaryRoutePlanner.printDebugMemoryInformation(gctx.ctx);
routeIsCorrect = res != null && !res.isEmpty();
for (int k = start.ind + 1; routeIsCorrect && k < target.ind; k++) {
GpxPoint ipoint = gpxPoints.get(k);
@ -506,9 +540,16 @@ public class RoutePlannerFrontEnd {
// make first position precise
makeSegmentPointPrecise(res.get(0), start.loc, true);
} else {
assert res.get(0).getObject().getId() == start.pnt.getRoad().getId();
// start point could shift to +-1 due to direction
res.get(0).setStartPointIndex(start.pnt.getSegmentStart());
if(res.get(0).getObject().getId() == start.pnt.getRoad().getId()) {
// start point could shift to +-1 due to direction
res.get(0).setStartPointIndex(start.pnt.getSegmentStart());
} else {
// for native routing this is possible when point lies on intersection of 2 lines
// solution here could be to pass to native routing id of the route
// though it should not create any issue
System.out.println("??? not found " + start.pnt.getRoad().getId() + " instead "
+ res.get(0).getObject().getId());
}
}
start.routeToTarget = res;
start.targetInd = target.ind;
@ -568,7 +609,7 @@ public class RoutePlannerFrontEnd {
public List<RouteSegmentResult> searchRoute(final RoutingContext ctx, LatLon start, LatLon end, List<LatLon> intermediates,
PrecalculatedRouteDirection routeDirection) throws IOException, InterruptedException {
ctx.timeToCalculate = System.nanoTime();
long timeToCalculate = System.nanoTime();
if (ctx.calculationProgress == null) {
ctx.calculationProgress = new RouteCalculationProgress();
}
@ -600,6 +641,7 @@ public class RoutePlannerFrontEnd {
}
routeDirection = PrecalculatedRouteDirection.build(ls, ctx.config.DEVIATION_RADIUS, ctx.getRouter().getMaxSpeed());
}
List<RouteSegmentResult> res ;
if (intermediatesEmpty && ctx.nativeLib != null) {
ctx.startX = MapUtils.get31TileNumberX(start.getLongitude());
ctx.startY = MapUtils.get31TileNumberY(start.getLatitude());
@ -613,31 +655,32 @@ public class RoutePlannerFrontEnd {
ctx.precalculatedRouteDirection = routeDirection.adopt(ctx);
}
ctx.calculationProgress.nextIteration();
List<RouteSegmentResult> res = runNativeRouting(ctx, recalculationEnd);
if (res != null) {
new RouteResultPreparation().printResults(ctx, start, end, res);
}
res = runNativeRouting(ctx, recalculationEnd);
makeStartEndPointsPrecise(res, start, end, intermediates);
return res;
}
int indexNotFound = 0;
List<RouteSegmentPoint> points = new ArrayList<RouteSegmentPoint>();
if (!addSegment(start, ctx, indexNotFound++, points, ctx.startTransportStop)) {
return null;
}
if (intermediates != null) {
for (LatLon l : intermediates) {
if (!addSegment(l, ctx, indexNotFound++, points, false)) {
System.out.println(points.get(points.size() - 1).getRoad().toString());
return null;
} else {
int indexNotFound = 0;
List<RouteSegmentPoint> points = new ArrayList<RouteSegmentPoint>();
if (!addSegment(start, ctx, indexNotFound++, points, ctx.startTransportStop)) {
return null;
}
if (intermediates != null) {
for (LatLon l : intermediates) {
if (!addSegment(l, ctx, indexNotFound++, points, false)) {
System.out.println(points.get(points.size() - 1).getRoad().toString());
return null;
}
}
}
if (!addSegment(end, ctx, indexNotFound++, points, ctx.targetTransportStop)) {
return null;
}
ctx.calculationProgress.nextIteration();
res = searchRouteImpl(ctx, points, routeDirection);
}
if (!addSegment(end, ctx, indexNotFound++, points, ctx.targetTransportStop)) {
return null;
if (ctx.calculationProgress != null) {
ctx.calculationProgress.timeToCalculate += (System.nanoTime() - timeToCalculate);
}
ctx.calculationProgress.nextIteration();
List<RouteSegmentResult> res = searchRouteImpl(ctx, points, routeDirection);
BinaryRoutePlanner.printDebugMemoryInformation(ctx);
if (res != null) {
new RouteResultPreparation().printResults(ctx, start, end, res);
}
@ -752,8 +795,12 @@ public class RoutePlannerFrontEnd {
if (ctx.nativeLib != null) {
ctx.startX = start.preciseX;
ctx.startY = start.preciseY;
ctx.startRoadId = start.road.id;
ctx.startSegmentInd = start.segStart;
ctx.targetX = end.preciseX;
ctx.targetY = end.preciseY;
ctx.targetRoadId = end.road.id;
ctx.targetSegmentInd = end.segStart;
return runNativeRouting(ctx, recalculationEnd);
} else {
refreshProgressDistance(ctx);
@ -817,11 +864,9 @@ public class RoutePlannerFrontEnd {
ctx.checkOldRoutingFiles(ctx.startX, ctx.startY);
ctx.checkOldRoutingFiles(ctx.targetX, ctx.targetY);
long time = System.currentTimeMillis();
RouteSegmentResult[] res = ctx.nativeLib.runNativeRouting(ctx.startX, ctx.startY, ctx.targetX, ctx.targetY,
ctx.config, regions, ctx.calculationProgress, ctx.precalculatedRouteDirection, ctx.calculationMode == RouteCalculationMode.BASE,
ctx.publicTransport, ctx.startTransportStop, ctx.targetTransportStop);
log.info("Native routing took " + (System.currentTimeMillis() - time) / 1000f + " seconds");
// long time = System.currentTimeMillis();
RouteSegmentResult[] res = ctx.nativeLib.runNativeRouting(ctx, regions, ctx.calculationMode == RouteCalculationMode.BASE);
// log.info("Native routing took " + (System.currentTimeMillis() - time) / 1000f + " seconds");
ArrayList<RouteSegmentResult> result = new ArrayList<RouteSegmentResult>(Arrays.asList(res));
if (recalculationEnd != null) {
log.info("Native routing use precalculated route");
@ -832,9 +877,7 @@ public class RoutePlannerFrontEnd {
current = pr;
}
}
ctx.routingTime = ctx.calculationProgress.routingCalculatedTime;
ctx.visitedSegments = ctx.calculationProgress.visitedSegments;
ctx.loadedTiles = ctx.calculationProgress.loadedTiles;
ctx.routingTime += ctx.calculationProgress.routingCalculatedTime;
return new RouteResultPreparation().prepareResult(ctx, result, recalculationEnd != null);
}
@ -894,16 +937,7 @@ public class RoutePlannerFrontEnd {
List<RouteSegmentResult> res = searchRouteInternalPrepare(local, points.get(i), points.get(i + 1), routeDirection);
makeStartEndPointsPrecise(res, points.get(i).getPreciseLatLon(), points.get(i + 1).getPreciseLatLon(), null);
results.addAll(res);
ctx.distinctLoadedTiles += local.distinctLoadedTiles;
ctx.loadedTiles += local.loadedTiles;
ctx.visitedSegments += local.visitedSegments;
ctx.loadedPrevUnloadedTiles += local.loadedPrevUnloadedTiles;
ctx.timeToCalculate += local.timeToCalculate;
ctx.timeToLoad += local.timeToLoad;
ctx.timeToLoadHeaders += local.timeToLoadHeaders;
ctx.relaxedSegments += local.relaxedSegments;
ctx.routingTime += local.routingTime;
// local.unloadAllData(ctx);
if (restPartRecalculatedRoute != null) {
results.addAll(restPartRecalculatedRoute);

View file

@ -44,8 +44,11 @@ public class RouteResultPreparation {
public static boolean PRINT_TO_CONSOLE_ROUTE_INFORMATION_TO_TEST = false;
public static String PRINT_TO_GPX_FILE = null;
private static final float TURN_DEGREE_MIN = 45;
private static final float UNMATCHED_TURN_DEGREE_MINIMUM = 45;
private static final float SPLIT_TURN_DEGREE_NOT_STRAIGHT = 100;
public static final int SHIFT_ID = 6;
private Log log = PlatformUtil.getLog(RouteResultPreparation.class);
public static final String UNMATCHED_HIGHWAY_TYPE = "unmatched";
/**
* Helper method to prepare final result
*/
@ -222,7 +225,7 @@ public class RouteResultPreparation {
private void justifyUTurns(boolean leftSide, List<RouteSegmentResult> result) {
int next;
for (int i = 0; i < result.size() - 1; i = next) {
for (int i = 1; i < result.size() - 1; i = next) {
next = i + 1;
TurnType t = result.get(i).getTurnType();
// justify turn
@ -313,6 +316,7 @@ public class RouteResultPreparation {
RouteSegmentResult rr = result.get(i);
boolean plus = rr.getStartPointIndex() < rr.getEndPointIndex();
int next;
boolean unmatched = UNMATCHED_HIGHWAY_TYPE.equals(rr.getObject().getHighway());
for (int j = rr.getStartPointIndex(); j != rr.getEndPointIndex(); j = next) {
next = plus ? j + 1 : j - 1;
if (j == rr.getStartPointIndex()) {
@ -323,27 +327,33 @@ public class RouteResultPreparation {
}
List<RouteSegmentResult> attachedRoutes = rr.getAttachedRoutes(next);
boolean tryToSplit = next != rr.getEndPointIndex() && !rr.getObject().roundabout() && attachedRoutes != null;
if(rr.getDistance(next, plus ) == 0) {
if (rr.getDistance(next, plus) == 0) {
// same point will be processed next step
tryToSplit = false;
}
if (tryToSplit) {
float distBearing = unmatched ? RouteSegmentResult.DIST_BEARING_DETECT_UNMATCHED : RouteSegmentResult.DIST_BEARING_DETECT;
// avoid small zigzags
float before = rr.getBearing(next, !plus);
float after = rr.getBearing(next, plus);
if(rr.getDistance(next, plus ) < 5) {
after = before + 180;
} else if(rr.getDistance(next, !plus ) < 5) {
before = after - 180;
float before = rr.getBearingEnd(next, distBearing);
float after = rr.getBearingBegin(next, distBearing);
if (rr.getDistance(next, plus) < distBearing) {
after = before;
} else if (rr.getDistance(next, !plus) < distBearing) {
before = after;
}
boolean straight = Math.abs(MapUtils.degreesDiff(before + 180, after)) < TURN_DEGREE_MIN;
double contAngle = Math.abs(MapUtils.degreesDiff(before, after));
boolean straight = contAngle < TURN_DEGREE_MIN;
boolean isSplit = false;
if (unmatched && Math.abs(contAngle) >= UNMATCHED_TURN_DEGREE_MINIMUM) {
isSplit = true;
}
// split if needed
for (RouteSegmentResult rs : attachedRoutes) {
double diff = MapUtils.degreesDiff(before + 180, rs.getBearingBegin());
double diff = MapUtils.degreesDiff(before, rs.getBearingBegin());
if (Math.abs(diff) <= TURN_DEGREE_MIN) {
isSplit = true;
} else if (!straight && Math.abs(diff) < 100) {
} else if (!straight && Math.abs(diff) < SPLIT_TURN_DEGREE_NOT_STRAIGHT) {
isSplit = true;
}
}
@ -384,8 +394,8 @@ public class RouteResultPreparation {
private List<RouteSegmentResult> convertFinalSegmentToResults(RoutingContext ctx, FinalRouteSegment finalSegment) {
List<RouteSegmentResult> result = new ArrayList<RouteSegmentResult>();
if (finalSegment != null) {
ctx.routingTime = finalSegment.distanceFromStart;
println("Routing calculated time distance " + finalSegment.distanceFromStart);
ctx.routingTime += finalSegment.distanceFromStart;
// println("Routing calculated time distance " + finalSegment.distanceFromStart);
// Get results from opposite direction roads
RouteSegment segment = finalSegment.reverseWaySearch ? finalSegment :
finalSegment.opposite.getParentRoute();
@ -486,8 +496,9 @@ public class RouteResultPreparation {
String msg = String.format("<test regions=\"\" description=\"\" best_percent=\"\" vehicle=\"%s\" \n"
+ " start_lat=\"%.5f\" start_lon=\"%.5f\" target_lat=\"%.5f\" target_lon=\"%.5f\" "
+ " routing_time=\"%.2f\" loadedTiles=\"%d\" visitedSegments=\"%d\" complete_distance=\"%.2f\" complete_time=\"%.2f\" >",
ctx.config.routerName, startLat, startLon, endLat, endLon, ctx.routingTime, ctx.loadedTiles,
ctx.visitedSegments, completeDistance, completeTime);
ctx.config.routerName, startLat, startLon, endLat, endLon, ctx.routingTime,
ctx.getLoadedTiles(),
ctx.getVisitedSegments(), completeDistance, completeTime);
// String msg = MessageFormat.format("<test regions=\"\" description=\"\" best_percent=\"\" vehicle=\"{4}\" \n"
// + " start_lat=\"{0}\" start_lon=\"{1}\" target_lat=\"{2}\" target_lon=\"{3}\" {5} >",
// startLat + "", startLon + "", endLat + "", endLon + "", ctx.config.routerName,
@ -1061,18 +1072,17 @@ public class RouteResultPreparation {
}
TurnType t = null;
if (prev != null) {
boolean noAttachedRoads = rr.getAttachedRoutes(rr.getStartPointIndex()).size() == 0;
// add description about turn
double mpi = MapUtils.degreesDiff(prev.getBearingEnd(), rr.getBearingBegin());
if(noAttachedRoads){
// VICTOR : look at the comment inside direction route
// ? avoid small zigzags is covered at (search for "zigzags")
// double begin = rr.getObject().directionRoute(rr.getStartPointIndex(), rr.getStartPointIndex() <
// rr.getEndPointIndex(), 25);
// mpi = MapUtils.degreesDiff(prev.getBearingEnd(), begin);
// avoid small zigzags is covered at (search for "zigzags")
float bearingDist = RouteSegmentResult.DIST_BEARING_DETECT;
// could be || noAttachedRoads, boolean noAttachedRoads = rr.getAttachedRoutes(rr.getStartPointIndex()).size() == 0;
if (UNMATCHED_HIGHWAY_TYPE.equals(rr.getObject().getHighway())) {
bearingDist = RouteSegmentResult.DIST_BEARING_DETECT_UNMATCHED;
}
double mpi = MapUtils.degreesDiff(prev.getBearingEnd(prev.getEndPointIndex(), bearingDist),
rr.getBearingBegin(rr.getStartPointIndex(), bearingDist));
if (mpi >= TURN_DEGREE_MIN) {
if (mpi < 45) {
if (mpi < TURN_DEGREE_MIN) {
// Slight turn detection here causes many false positives where drivers would expect a "normal" TL. Best use limit-angle=TURN_DEGREE_MIN, this reduces TSL to the turn-lanes cases.
t = TurnType.valueOf(TurnType.TSLL, leftSide);
} else if (mpi < 120) {
@ -1085,7 +1095,7 @@ public class RouteResultPreparation {
int[] lanes = getTurnLanesInfo(prev, t.getValue());
t.setLanes(lanes);
} else if (mpi < -TURN_DEGREE_MIN) {
if (mpi > -45) {
if (mpi > -TURN_DEGREE_MIN) {
t = TurnType.valueOf(TurnType.TSLR, leftSide);
} else if (mpi > -120) {
t = TurnType.valueOf(TurnType.TR, leftSide);
@ -1467,32 +1477,39 @@ public class RouteResultPreparation {
protected TurnType createSimpleKeepLeftRightTurn(boolean leftSide, RouteSegmentResult prevSegm,
RouteSegmentResult currentSegm, RoadSplitStructure rs) {
double devation = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), currentSegm.getBearingBegin()));
boolean makeSlightTurn = devation > 5 && (!isMotorway(prevSegm) || !isMotorway(currentSegm));
TurnType t = null;
int laneType = TurnType.C;
if (rs.keepLeft && rs.keepRight) {
t = TurnType.valueOf(TurnType.C, leftSide);
} else if (rs.keepLeft) {
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLL : TurnType.KL, leftSide);
if (makeSlightTurn) {
laneType = TurnType.TSLL;
}
} else if (rs.keepRight) {
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLR : TurnType.KR, leftSide);
if (makeSlightTurn) {
laneType = TurnType.TSLR;
}
} else {
return null;
}
int current = countLanesMinOne(currentSegm);
int ls = current + rs.leftLanes + rs.rightLanes;
int[] lanes = new int[ls];
for (int it = 0; it < ls; it++) {
if (it < rs.leftLanes || it >= rs.leftLanes + current) {
lanes[it] = 0;
lanes[it] = TurnType.C << 1;
} else {
lanes[it] = 1;
lanes[it] = (laneType << 1) + 1;
}
}
// sometimes links are
if ((current <= rs.leftLanes + rs.rightLanes) && (rs.leftLanes > 1 || rs.rightLanes > 1)) {
rs.speak = true;
}
double devation = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), currentSegm.getBearingBegin()));
boolean makeSlightTurn = devation > 5 && (!isMotorway(prevSegm) || !isMotorway(currentSegm));
TurnType t = null;
if (rs.keepLeft && rs.keepRight) {
t = TurnType.valueOf(TurnType.C, leftSide);
} else if (rs.keepLeft) {
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLL : TurnType.KL, leftSide);
} else if (rs.keepRight) {
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLR : TurnType.KR, leftSide);
} else {
return t;
}
t.setSkipToSpeak(!rs.speak);
t.setLanes(lanes);
return t;

View file

@ -22,7 +22,10 @@ import gnu.trove.map.hash.TIntObjectHashMap;
public class RouteSegmentResult implements StringExternalizable<RouteDataBundle> {
// this should be bigger (50-80m) but tests need to be fixed first
private static final float DIST_BEARING_DETECT = 5;
public static final float DIST_BEARING_DETECT = 5;
public static final float DIST_BEARING_DETECT_UNMATCHED = 50;
private RouteDataObject object;
private int startPointIndex;
private int endPointIndex;
@ -446,19 +449,32 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
}
public float getBearingBegin() {
return (float) (object.directionRoute(startPointIndex, startPointIndex < endPointIndex, DIST_BEARING_DETECT) / Math.PI * 180);
return getBearingBegin(startPointIndex, DIST_BEARING_DETECT);
}
public float getBearing(int point, boolean plus) {
return (float) (object.directionRoute(point, plus, DIST_BEARING_DETECT) / Math.PI * 180);
}
public float getDistance(int point, boolean plus) {
return (float) (plus? object.distance(point, endPointIndex): object.distance(startPointIndex, point));
public float getBearingBegin(int point, float dist) {
return getBearing(point, true, dist);
}
public float getBearingEnd() {
return (float) (MapUtils.alignAngleDifference(object.directionRoute(endPointIndex, startPointIndex > endPointIndex, DIST_BEARING_DETECT) - Math.PI) / Math.PI * 180);
return getBearingEnd(endPointIndex, DIST_BEARING_DETECT);
}
public float getBearingEnd(int point, float dist) {
return getBearing(point, false, dist);
}
public float getBearing(int point, boolean begin, float dist) {
if (begin) {
return (float) (object.directionRoute(point, startPointIndex < endPointIndex, dist) / Math.PI * 180);
} else {
double dr = object.directionRoute(point, startPointIndex > endPointIndex, dist);
return (float) (MapUtils.alignAngleDifference(dr - Math.PI) / Math.PI * 180);
}
}
public float getDistance(int point, boolean plus) {
return (float) (plus ? object.distance(point, endPointIndex) : object.distance(startPointIndex, point));
}
public void setSegmentTime(float segmentTime) {
@ -497,7 +513,7 @@ public class RouteSegmentResult implements StringExternalizable<RouteDataBundle>
return endPointIndex - startPointIndex > 0;
}
private LatLon convertPoint(RouteDataObject o, int ind){
return new LatLon(MapUtils.get31LatitudeY(o.getPoint31YTile(ind)), MapUtils.get31LongitudeX(o.getPoint31XTile(ind)));
}

View file

@ -40,8 +40,6 @@ public class RoutingContext {
private final static Log log = PlatformUtil.getLog(RoutingContext.class);
// Final context variables
public final RoutingConfiguration config;
@ -50,23 +48,31 @@ public class RoutingContext {
public final Map<BinaryMapIndexReader, List<RouteSubregion>> map = new LinkedHashMap<BinaryMapIndexReader, List<RouteSubregion>>();
public final Map<RouteRegion, BinaryMapIndexReader> reverseMap = new LinkedHashMap<RouteRegion, BinaryMapIndexReader>();
// 0. Reference to native routingcontext for multiple routes
public long nativeRoutingContext;
public boolean keepNativeRoutingContext;
// 1. Initial variables
public int startX;
public int startY;
public long startRoadId;
public int startSegmentInd;
public boolean startTransportStop;
public int targetX;
public int targetY;
public long targetRoadId;
public int targetSegmentInd;
public boolean targetTransportStop;
public boolean publicTransport;
// deprecated
public long firstRoadId;
public int firstRoadDirection;
public RouteCalculationProgress calculationProgress;
public boolean leftSideNavigation;
public List<RouteSegmentResult> previouslyCalculatedRoute;
public PrecalculatedRouteDirection precalculatedRouteDirection;
// 2. Routing memory cache (big objects)
TLongObjectHashMap<List<RoutingSubregionTile>> indexedSubregions = new TLongObjectHashMap<List<RoutingSubregionTile>>();
@ -82,27 +88,15 @@ public class RoutingContext {
public TileStatistics global = new TileStatistics();
// updated by route planner in bytes
public int memoryOverhead = 0;
long timeNanoToCalcDeviation = 0;
long timeToLoad = 0;
long timeToLoadHeaders = 0;
long timeToFindInitialSegments = 0;
public long timeToCalculate = 0;
int distinctLoadedTiles = 0;
int maxLoadedTiles = 0;
int loadedPrevUnloadedTiles = 0;
int unloadedTiles = 0;
public float routingTime = 0;
public int loadedTiles = 0;
public int visitedSegments = 0;
public int relaxedSegments = 0;
// callback of processing segments
RouteSegmentVisitor visitor = null;
// old planner
public FinalRouteSegment finalRouteSegment;
RoutingContext(RoutingContext cp) {
this.config = cp.config;
@ -201,12 +195,16 @@ public class RoutingContext {
public void initStartAndTargetPoints(RouteSegment start, RouteSegment end) {
initTargetPoint(end);
startX = start.road.getPoint31XTile(start.getSegmentStart());
startY = start.road.getPoint31YTile(start.getSegmentStart());
startY = start.road.getPoint31YTile(start.getSegmentStart());
startRoadId = start.road.getId();
startSegmentInd = start.getSegmentStart();
}
public void initTargetPoint(RouteSegment end) {
targetX = end.road.getPoint31XTile(end.getSegmentStart());
targetY = end.road.getPoint31YTile(end.getSegmentStart());
targetRoadId = end.road.getId();
targetSegmentInd = end.getSegmentStart();
}
public void unloadAllData() {
@ -218,7 +216,9 @@ public class RoutingContext {
if (tl.isLoaded()) {
if(except == null || except.searchSubregionTile(tl.subregion) < 0){
tl.unload();
unloadedTiles ++;
if(calculationProgress != null) {
calculationProgress.unloadedTiles ++;
}
global.size -= tl.tileStatistics.size;
}
}
@ -252,18 +252,6 @@ public class RoutingContext {
return ind;
}
public void newRoutingPoints() {
int middleX = startX / 2 + targetX / 2;
int middleY = startY / 2 + targetY;
List<RouteDataObject> dataObjects = new ArrayList<RouteDataObject>();
loadTileData(middleX, middleY, 17, dataObjects);
System.out.println("Size of data objects " + dataObjects.size());
}
public RouteSegment loadRouteSegment(int x31, int y31, long memoryLimit) {
long tileId = getRoutingTile(x31, y31, memoryLimit);
@ -314,27 +302,37 @@ public class RoutingContext {
} catch (IOException e) {
throw new RuntimeException("Loading data exception", e);
}
timeToLoad += (System.nanoTime() - now);
if (calculationProgress != null) {
calculationProgress.timeToLoad += (System.nanoTime() - now);
}
} else {
long now = System.nanoTime();
NativeRouteSearchResult ns = nativeLib.loadRouteRegion(ts.subregion, loadObjectsInMemory);
// System.out.println(ts.subregion.shiftToData + " " + Arrays.toString(ns.objects));
ts.setLoadedNative(ns, this);
timeToLoad += (System.nanoTime() - now);
if (calculationProgress != null) {
calculationProgress.timeToLoad += (System.nanoTime() - now);
}
}
loadedTiles++;
if (calculationProgress != null) {
calculationProgress.loadedTiles++;
}
if (wasUnloaded) {
if(ucount == 1) {
loadedPrevUnloadedTiles++;
if(calculationProgress != null) {
calculationProgress.loadedPrevUnloadedTiles++;
}
}
} else {
if(global != null) {
global.allRoutes += ts.tileStatistics.allRoutes;
global.coordinates += ts.tileStatistics.coordinates;
}
distinctLoadedTiles++;
if (calculationProgress != null) {
calculationProgress.distinctLoadedTiles++;
}
}
global.size += ts.tileStatistics.size;
}
@ -408,7 +406,9 @@ public class RoutingContext {
}
collection.add(found);
}
timeToLoadHeaders += (System.nanoTime() - now);
if (calculationProgress != null) {
calculationProgress.timeToLoadHeaders += (System.nanoTime() - now);
}
}
} catch (IOException e) {
throw new RuntimeException("Loading data exception", e);
@ -418,6 +418,10 @@ public class RoutingContext {
}
public void loadTileData(int x31, int y31, int zoomAround, final List<RouteDataObject> toFillIn) {
loadTileData(x31, y31, zoomAround, toFillIn, false);
}
public void loadTileData(int x31, int y31, int zoomAround, final List<RouteDataObject> toFillIn, boolean allowDuplications) {
int t = config.ZOOM_TO_LOAD_TILES - zoomAround;
int coordinatesShift = (1 << (31 - config.ZOOM_TO_LOAD_TILES));
if(t <= 0) {
@ -428,7 +432,6 @@ public class RoutingContext {
}
TLongHashSet ts = new TLongHashSet();
long now = System.nanoTime();
for(int i = -t; i <= t; i++) {
for(int j = -t; j <= t; j++) {
ts.add(getRoutingTile(x31 +i*coordinatesShift, y31 + j*coordinatesShift, 0));
@ -438,8 +441,10 @@ public class RoutingContext {
TLongObjectHashMap<RouteDataObject> excludeDuplications = new TLongObjectHashMap<RouteDataObject>();
while (it.hasNext()) {
getAllObjects(it.next(), toFillIn, excludeDuplications);
if (allowDuplications) {
excludeDuplications.clear();
}
}
timeToFindInitialSegments += (System.nanoTime() - now);
}
@SuppressWarnings("unused")
@ -522,7 +527,9 @@ public class RoutingContext {
loaded++;
}
}
maxLoadedTiles = Math.max(maxLoadedTiles, getCurrentlyLoadedTiles());
if(calculationProgress != null) {
calculationProgress.maxLoadedTiles = Math.max(calculationProgress.maxLoadedTiles, getCurrentlyLoadedTiles());
}
Collections.sort(list, new Comparator<RoutingSubregionTile>() {
private int pow(int base, int pw) {
int r = 1;
@ -544,7 +551,9 @@ public class RoutingContext {
i++;
// System.out.println("Unload " + unload);
unload.unload();
unloadedTiles ++;
if(calculationProgress != null) {
calculationProgress.unloadedTiles ++;
}
global.size -= unload.tileStatistics.size;
// tile could be cleaned from routing tiles and deleted from whole list
@ -795,7 +804,31 @@ public class RoutingContext {
return map.keySet().toArray(new BinaryMapIndexReader[map.size()]);
}
public int getVisitedSegments() {
if(calculationProgress != null) {
return calculationProgress.visitedSegments;
}
return 0;
}
public int getLoadedTiles() {
if (calculationProgress != null) {
return calculationProgress.loadedTiles;
}
return 0;
}
public synchronized void deleteNativeRoutingContext() {
if (nativeRoutingContext != 0) {
NativeLibrary.deleteNativeRoutingContext(nativeRoutingContext);
}
nativeRoutingContext = 0;
}
@Override
protected void finalize() throws Throwable {
deleteNativeRoutingContext();
super.finalize();
}
}

View file

@ -236,13 +236,13 @@ public class TestRouting {
throw new IllegalArgumentException(MessageFormat.format("Complete routing time (expected) {0} != {1} (original) : {2}", routing_time, calcRoutingTime, testDescription));
}
if (visitedSegments > 0 && !isInOrLess(visitedSegments, ctx.visitedSegments, percent)) {
if (visitedSegments > 0 && !isInOrLess(visitedSegments, ctx.getVisitedSegments(), percent)) {
throw new IllegalArgumentException(MessageFormat.format("Visited segments (expected) {0} != {1} (original) : {2}", visitedSegments,
ctx.visitedSegments, testDescription));
ctx.getVisitedSegments(), testDescription));
}
if (loadedTiles > 0 && !isInOrLess(loadedTiles, ctx.loadedTiles, percent)) {
if (loadedTiles > 0 && !isInOrLess(loadedTiles, ctx.getLoadedTiles(), percent)) {
throw new IllegalArgumentException(MessageFormat.format("Loaded tiles (expected) {0} != {1} (original) : {2}", loadedTiles,
ctx.loadedTiles, testDescription));
ctx.getLoadedTiles(), testDescription));
}
if(TEST_BOTH_DIRECTION){

View file

@ -2,27 +2,16 @@ package net.osmand.router;
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;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Locale;
import java.util.PriorityQueue;
import gnu.trove.iterator.TIntIterator;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import net.osmand.NativeLibrary;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.data.IncompleteTransportRoute;
import net.osmand.data.LatLon;
import net.osmand.data.QuadRect;
import net.osmand.data.TransportRoute;
@ -31,9 +20,6 @@ import net.osmand.data.TransportStop;
import net.osmand.data.TransportStopExit;
import net.osmand.osm.edit.Node;
import net.osmand.osm.edit.Way;
import net.osmand.router.TransportRoutePlanner.TransportRouteResult;
import net.osmand.router.TransportRoutePlanner.TransportRouteResultSegment;
import net.osmand.router.TransportRoutePlanner.TransportRouteSegment;
import net.osmand.util.MapUtils;
public class TransportRoutePlanner {
@ -224,11 +210,10 @@ public class TransportRoutePlanner {
}
}
private List<TransportRouteResult> prepareResults(TransportRoutingContext ctx, List<TransportRouteSegment> results) {
Collections.sort(results, new SegmentsComparator(ctx));
List<TransportRouteResult> lst = new ArrayList<TransportRouteResult>();
System.out.println(String.format("Calculated %.1f seconds, found %d results, visited %d routes / %d stops, loaded %d tiles (%d ms read, %d ms total), loaded ways %d (%d wrong)",
System.out.println(String.format(Locale.US, "Calculated %.1f seconds, found %d results, visited %d routes / %d stops, loaded %d tiles (%d ms read, %d ms total), loaded ways %d (%d wrong)",
(System.currentTimeMillis() - ctx.startCalcTime) / 1000.0, results.size(),
ctx.visitedRoutesCount, ctx.visitedStops,
ctx.quadTree.size(), ctx.readTime / (1000 * 1000), ctx.loadTime / (1000 * 1000),
@ -314,7 +299,6 @@ public class TransportRoutePlanner {
}
}
public static class TransportRouteResultSegment {
private static final boolean DISPLAY_FULL_SEGMENT_ROUTE = false;
@ -486,157 +470,13 @@ public class TransportRoutePlanner {
return route.getForwardStops().get(i);
}
}
public static class TransportRouteResult {
List<TransportRouteResultSegment> segments = new ArrayList<TransportRouteResultSegment>(4);
double finishWalkDist;
double routeTime;
private final TransportRoutingConfiguration cfg;
public TransportRouteResult(TransportRoutingContext ctx) {
cfg = ctx.cfg;
}
public TransportRouteResult(TransportRoutingConfiguration cfg) {
this.cfg = cfg;
}
public List<TransportRouteResultSegment> getSegments() {
return segments;
}
public void setFinishWalkDist(double finishWalkDist) {
this.finishWalkDist = finishWalkDist;
}
public void setRouteTime(double routeTime) {
this.routeTime = routeTime;
}
public void addSegment(TransportRouteResultSegment seg) {
segments.add(seg);
}
public double getWalkDist() {
double d = finishWalkDist;
for (TransportRouteResultSegment s : segments) {
d += s.walkDist;
}
return d;
}
public double getFinishWalkDist() {
return finishWalkDist;
}
public double getWalkSpeed() {
return cfg.walkSpeed;
}
public double getRouteTime() {
return routeTime;
}
public int getStops() {
int stops = 0;
for(TransportRouteResultSegment s : segments) {
stops += (s.end - s.start);
}
return stops;
}
public boolean isRouteStop(TransportStop stop) {
for(TransportRouteResultSegment s : segments) {
if (s.getTravelStops().contains(stop)) {
return true;
}
}
return false;
}
public TransportRouteResultSegment getRouteStopSegment(TransportStop stop) {
for(TransportRouteResultSegment s : segments) {
if (s.getTravelStops().contains(stop)) {
return s;
}
}
return null;
}
public double getTravelDist() {
double d = 0;
for (TransportRouteResultSegment s : segments) {
d += s.getTravelDist();
}
return d;
}
public double getTravelTime() {
double t = 0;
for (TransportRouteResultSegment s : segments) {
if (cfg.useSchedule) {
TransportSchedule sts = s.route.getSchedule();
for (int k = s.start; k < s.end; k++) {
t += sts.getAvgStopIntervals()[k] * 10;
}
} else {
t += cfg.getBoardingTime();
t += s.getTravelTime();
}
}
return t;
}
public double getWalkTime() {
return getWalkDist() / cfg.walkSpeed;
}
public double getChangeTime() {
return cfg.getChangeTime();
}
public double getBoardingTime() {
return cfg.getBoardingTime();
}
public int getChanges() {
return segments.size() - 1;
}
@Override
public String toString() {
StringBuilder bld = new StringBuilder();
bld.append(String.format("Route %d stops, %d changes, %.2f min: %.2f m (%.1f min) to walk, %.2f m (%.1f min) to travel\n",
getStops(), getChanges(), routeTime / 60, getWalkDist(), getWalkTime() / 60.0,
getTravelDist(), getTravelTime() / 60.0));
for(int i = 0; i < segments.size(); i++) {
TransportRouteResultSegment s = segments.get(i);
String time = "";
String arriveTime = "";
if(s.depTime != -1) {
time = String.format("at %s", formatTransporTime(s.depTime));
}
int aTime = s.getArrivalTime();
if(aTime != -1) {
arriveTime = String.format("and arrive at %s", formatTransporTime(aTime));
}
bld.append(String.format(" %d. %s [%d]: walk %.1f m to '%s' and travel %s to '%s' by %s %d stops %s\n",
i + 1, s.route.getRef(), s.route.getId() / 2, s.walkDist, s.getStart().getName(),
time, s.getEnd().getName(),s.route.getName(), (s.end - s.start), arriveTime));
}
bld.append(String.format(" F. Walk %.1f m to reach your destination", finishWalkDist));
return bld.toString();
}
}
public static String formatTransporTime(int i) {
public static String formatTransportTime(int i) {
int h = i / 60 / 6;
int mh = i - h * 60 * 6;
int m = mh / 6;
int s = (mh - m * 6) * 10;
String tm = String.format("%02d:%02d:%02d ", h, m, s);
return tm;
return String.format(Locale.US, "%02d:%02d:%02d ", h, m, s);
}
public static class TransportRouteSegment {
@ -653,13 +493,9 @@ public class TransportRoutePlanner {
double parentTravelDist; // travel distance for parent route (inaccurate)
// walk distance to start route location (or finish in case last segment)
double walkDist = 0;
// main field accumulated all time spent from beginning of journey
double distFromStart = 0;
public TransportRouteSegment(TransportRoute road, int stopIndex) {
this.road = road;
this.segStart = (short) stopIndex;
@ -677,8 +513,7 @@ public class TransportRoutePlanner {
this.segStart = c.segStart;
this.departureTime = c.departureTime;
}
public boolean wasVisited(TransportRouteSegment rrs) {
if (rrs.road.getId().longValue() == road.getId().longValue() &&
rrs.departureTime == departureTime) {
@ -690,22 +525,18 @@ public class TransportRoutePlanner {
return false;
}
public TransportStop getStop(int i) {
return road.getForwardStops().get(i);
}
public LatLon getLocation() {
return road.getForwardStops().get(segStart).getLocation();
}
public int getLength() {
return road.getForwardStops().size();
}
public long getId() {
long l = road.getId();
@ -727,7 +558,6 @@ public class TransportRoutePlanner {
return l ;
}
public int getDepth() {
if(parentRoute != null) {
return parentRoute.getDepth() + 1;
@ -738,165 +568,10 @@ public class TransportRoutePlanner {
@Override
public String toString() {
return String.format("Route: %s, stop: %s %s", road.getName(), road.getForwardStops().get(segStart).getName(),
departureTime == -1 ? "" : formatTransporTime(departureTime) );
}
}
public static class TransportRoutingContext {
public NativeLibrary library;
public RouteCalculationProgress calculationProgress;
public TLongObjectHashMap<TransportRouteSegment> visitedSegments = new TLongObjectHashMap<TransportRouteSegment>();
public TransportRoutingConfiguration cfg;
public TLongObjectHashMap<TransportRoute> combinedRoutesCache = new TLongObjectHashMap<TransportRoute>();
public Map<TransportStop, List<TransportRoute>> missingStopsCache = new HashMap<TransportStop, List<TransportRoute>>();
public TLongObjectHashMap<List<TransportRouteSegment>> quadTree;
// Here we don't limit files by bbox, so it could be an issue while searching for multiple unused files
// Incomplete routes usually don't need more files than around Max-BBOX of start/end,
// so here an improvement could be introduced
final TransportStopsRouteReader transportStopsReader;
public int finishTimeSeconds;
// stats
public long startCalcTime;
public int visitedRoutesCount;
public int visitedStops;
public int wrongLoadedWays;
public int loadedWays;
public long loadTime;
public long readTime;
private final int walkRadiusIn31;
private final int walkChangeRadiusIn31;
public TransportRoutingContext(TransportRoutingConfiguration cfg, NativeLibrary library, BinaryMapIndexReader... readers) {
this.cfg = cfg;
walkRadiusIn31 = (int) (cfg.walkRadius / MapUtils.getTileDistanceWidth(31));
walkChangeRadiusIn31 = (int) (cfg.walkChangeRadius / MapUtils.getTileDistanceWidth(31));
quadTree = new TLongObjectHashMap<List<TransportRouteSegment>>();
this.library = library;
transportStopsReader = new TransportStopsRouteReader(Arrays.asList(readers));
}
public List<TransportRouteSegment> getTransportStops(LatLon loc) throws IOException {
int y = MapUtils.get31TileNumberY(loc.getLatitude());
int x = MapUtils.get31TileNumberX(loc.getLongitude());
return getTransportStops(x, y, false, new ArrayList<TransportRouteSegment>());
}
public List<TransportRouteSegment> getTransportStops(int x, int y, boolean change, List<TransportRouteSegment> res) throws IOException {
return loadNativeTransportStops(x, y, change, res);
}
private List<TransportRouteSegment> loadNativeTransportStops(int sx, int sy, boolean change, List<TransportRouteSegment> res) throws IOException {
long nanoTime = System.nanoTime();
int d = change ? walkChangeRadiusIn31 : walkRadiusIn31;
int lx = (sx - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
int rx = (sx + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
int ty = (sy - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
int by = (sy + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
for(int x = lx; x <= rx; x++) {
for(int y = ty; y <= by; y++) {
long tileId = (((long)x) << (cfg.ZOOM_TO_LOAD_TILES + 1)) + y;
List<TransportRouteSegment> list = quadTree.get(tileId);
if(list == null) {
list = loadTile(x, y);
quadTree.put(tileId, list);
}
for(TransportRouteSegment r : list) {
TransportStop st = r.getStop(r.segStart);
if (Math.abs(st.x31 - sx) > walkRadiusIn31 || Math.abs(st.y31 - sy) > walkRadiusIn31) {
wrongLoadedWays++;
} else {
loadedWays++;
res.add(r);
}
}
}
}
loadTime += System.nanoTime() - nanoTime;
return res;
}
private List<TransportRouteSegment> loadTile(int x, int y) throws IOException {
long nanoTime = System.nanoTime();
List<TransportRouteSegment> lst = new ArrayList<TransportRouteSegment>();
int pz = (31 - cfg.ZOOM_TO_LOAD_TILES);
SearchRequest<TransportStop> sr = BinaryMapIndexReader.buildSearchTransportRequest(x << pz, (x + 1) << pz,
y << pz, (y + 1) << pz, -1, null);
Collection<TransportStop> stops = transportStopsReader.readMergedTransportStops(sr);
loadTransportSegments(stops, lst);
readTime += System.nanoTime() - nanoTime;
return lst;
}
private void loadTransportSegments(Collection<TransportStop> stops, List<TransportRouteSegment> lst) throws IOException {
for(TransportStop s : stops) {
if (s.isDeleted() || s.getRoutes() == null) {
continue;
}
for (TransportRoute route : s.getRoutes()) {
int stopIndex = -1;
double dist = TransportRoute.SAME_STOP;
for (int k = 0; k < route.getForwardStops().size(); k++) {
TransportStop st = route.getForwardStops().get(k);
if(st.getId().longValue() == s.getId().longValue() ) {
stopIndex = k;
break;
}
double d = MapUtils.getDistance(st.getLocation(), s.getLocation());
if (d < dist) {
stopIndex = k;
dist = d;
}
}
if (stopIndex != -1) {
if (cfg != null && cfg.useSchedule) {
loadScheduleRouteSegment(lst, route, stopIndex);
} else {
TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex);
lst.add(segment);
}
} else {
System.err.println(String.format("Routing error: missing stop '%s' in route '%s' id: %d",
s.toString(), route.getRef(), route.getId() / 2));
}
}
}
}
private void loadScheduleRouteSegment(List<TransportRouteSegment> lst, TransportRoute route, int stopIndex) {
if(route.getSchedule() != null) {
TIntArrayList ti = route.getSchedule().tripIntervals;
int cnt = ti.size();
int t = 0;
// improve by using exact data
int stopTravelTime = 0;
TIntArrayList avgStopIntervals = route.getSchedule().avgStopIntervals;
for (int i = 0; i < stopIndex; i++) {
if (avgStopIntervals.size() > i) {
stopTravelTime += avgStopIntervals.getQuick(i);
}
}
for(int i = 0; i < cnt; i++) {
t += ti.getQuick(i);
int startTime = t + stopTravelTime;
if(startTime >= cfg.scheduleTimeOfDay && startTime <= cfg.scheduleTimeOfDay + cfg.scheduleMaxTime ) {
TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex, startTime);
lst.add(segment);
}
}
}
departureTime == -1 ? "" : formatTransportTime(departureTime) );
}
}
public static List<TransportRouteResult> convertToTransportRoutingResult(NativeTransportRoutingResult[] res,
TransportRoutingConfiguration cfg) {
// cache for converted TransportRoutes:

View file

@ -0,0 +1,151 @@
package net.osmand.router;
import net.osmand.data.TransportSchedule;
import net.osmand.data.TransportStop;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class TransportRouteResult {
List<TransportRoutePlanner.TransportRouteResultSegment> segments = new ArrayList<TransportRoutePlanner.TransportRouteResultSegment>(4);
double finishWalkDist;
double routeTime;
private final TransportRoutingConfiguration cfg;
public TransportRouteResult(TransportRoutingContext ctx) {
cfg = ctx.cfg;
}
public TransportRouteResult(TransportRoutingConfiguration cfg) {
this.cfg = cfg;
}
public List<TransportRoutePlanner.TransportRouteResultSegment> getSegments() {
return segments;
}
public void setFinishWalkDist(double finishWalkDist) {
this.finishWalkDist = finishWalkDist;
}
public void setRouteTime(double routeTime) {
this.routeTime = routeTime;
}
public void addSegment(TransportRoutePlanner.TransportRouteResultSegment seg) {
segments.add(seg);
}
public double getWalkDist() {
double d = finishWalkDist;
for (TransportRoutePlanner.TransportRouteResultSegment s : segments) {
d += s.walkDist;
}
return d;
}
public double getFinishWalkDist() {
return finishWalkDist;
}
public double getWalkSpeed() {
return cfg.walkSpeed;
}
public double getRouteTime() {
return routeTime;
}
public int getStops() {
int stops = 0;
for(TransportRoutePlanner.TransportRouteResultSegment s : segments) {
stops += (s.end - s.start);
}
return stops;
}
public boolean isRouteStop(TransportStop stop) {
for(TransportRoutePlanner.TransportRouteResultSegment s : segments) {
if (s.getTravelStops().contains(stop)) {
return true;
}
}
return false;
}
public TransportRoutePlanner.TransportRouteResultSegment getRouteStopSegment(TransportStop stop) {
for(TransportRoutePlanner.TransportRouteResultSegment s : segments) {
if (s.getTravelStops().contains(stop)) {
return s;
}
}
return null;
}
public double getTravelDist() {
double d = 0;
for (TransportRoutePlanner.TransportRouteResultSegment s : segments) {
d += s.getTravelDist();
}
return d;
}
public double getTravelTime() {
double t = 0;
for (TransportRoutePlanner.TransportRouteResultSegment s : segments) {
if (cfg.useSchedule) {
TransportSchedule sts = s.route.getSchedule();
for (int k = s.start; k < s.end; k++) {
t += sts.getAvgStopIntervals()[k] * 10;
}
} else {
t += cfg.getBoardingTime();
t += s.getTravelTime();
}
}
return t;
}
public double getWalkTime() {
return getWalkDist() / cfg.walkSpeed;
}
public double getChangeTime() {
return cfg.getChangeTime();
}
public double getBoardingTime() {
return cfg.getBoardingTime();
}
public int getChanges() {
return segments.size() - 1;
}
@Override
public String toString() {
StringBuilder bld = new StringBuilder();
bld.append(String.format(Locale.US, "Route %d stops, %d changes, %.2f min: %.2f m (%.1f min) to walk, %.2f m (%.1f min) to travel\n",
getStops(), getChanges(), routeTime / 60, getWalkDist(), getWalkTime() / 60.0,
getTravelDist(), getTravelTime() / 60.0));
for(int i = 0; i < segments.size(); i++) {
TransportRoutePlanner.TransportRouteResultSegment s = segments.get(i);
String time = "";
String arriveTime = "";
if(s.depTime != -1) {
time = String.format("at %s", TransportRoutePlanner.formatTransportTime(s.depTime));
}
int aTime = s.getArrivalTime();
if(aTime != -1) {
arriveTime = String.format("and arrive at %s", TransportRoutePlanner.formatTransportTime(aTime));
}
bld.append(String.format(Locale.US, " %d. %s [%d]: walk %.1f m to '%s' and travel %s to '%s' by %s %d stops %s\n",
i + 1, s.route.getRef(), s.route.getId() / 2, s.walkDist, s.getStart().getName(),
time, s.getEnd().getName(),s.route.getName(), (s.end - s.start), arriveTime));
}
bld.append(String.format(" F. Walk %.1f m to reach your destination", finishWalkDist));
return bld.toString();
}
}

View file

@ -0,0 +1,171 @@
package net.osmand.router;
import net.osmand.NativeLibrary;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.data.LatLon;
import net.osmand.data.TransportRoute;
import net.osmand.data.TransportStop;
import net.osmand.router.TransportRoutePlanner.TransportRouteSegment;
import net.osmand.util.MapUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.map.hash.TLongObjectHashMap;
public class TransportRoutingContext {
public NativeLibrary library;
public RouteCalculationProgress calculationProgress;
public TLongObjectHashMap<TransportRouteSegment> visitedSegments = new TLongObjectHashMap<TransportRouteSegment>();
public TransportRoutingConfiguration cfg;
public TLongObjectHashMap<TransportRoute> combinedRoutesCache = new TLongObjectHashMap<TransportRoute>();
public Map<TransportStop, List<TransportRoute>> missingStopsCache = new HashMap<TransportStop, List<TransportRoute>>();
public TLongObjectHashMap<List<TransportRouteSegment>> quadTree;
// Here we don't limit files by bbox, so it could be an issue while searching for multiple unused files
// Incomplete routes usually don't need more files than around Max-BBOX of start/end,
// so here an improvement could be introduced
final TransportStopsRouteReader transportStopsReader;
public int finishTimeSeconds;
// stats
public long startCalcTime;
public int visitedRoutesCount;
public int visitedStops;
public int wrongLoadedWays;
public int loadedWays;
public long loadTime;
public long readTime;
private final int walkRadiusIn31;
private final int walkChangeRadiusIn31;
public TransportRoutingContext(TransportRoutingConfiguration cfg, NativeLibrary library, BinaryMapIndexReader... readers) {
this.cfg = cfg;
walkRadiusIn31 = (int) (cfg.walkRadius / MapUtils.getTileDistanceWidth(31));
walkChangeRadiusIn31 = (int) (cfg.walkChangeRadius / MapUtils.getTileDistanceWidth(31));
quadTree = new TLongObjectHashMap<List<TransportRouteSegment>>();
this.library = library;
transportStopsReader = new TransportStopsRouteReader(Arrays.asList(readers));
}
public List<TransportRouteSegment> getTransportStops(LatLon loc) throws IOException {
int y = MapUtils.get31TileNumberY(loc.getLatitude());
int x = MapUtils.get31TileNumberX(loc.getLongitude());
return getTransportStops(x, y, false, new ArrayList<TransportRouteSegment>());
}
public List<TransportRouteSegment> getTransportStops(int x, int y, boolean change, List<TransportRouteSegment> res) throws IOException {
return loadNativeTransportStops(x, y, change, res);
}
private List<TransportRouteSegment> loadNativeTransportStops(int sx, int sy, boolean change, List<TransportRouteSegment> res) throws IOException {
long nanoTime = System.nanoTime();
int d = change ? walkChangeRadiusIn31 : walkRadiusIn31;
int lx = (sx - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
int rx = (sx + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
int ty = (sy - d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
int by = (sy + d ) >> (31 - cfg.ZOOM_TO_LOAD_TILES);
for(int x = lx; x <= rx; x++) {
for(int y = ty; y <= by; y++) {
long tileId = (((long)x) << (cfg.ZOOM_TO_LOAD_TILES + 1)) + y;
List<TransportRouteSegment> list = quadTree.get(tileId);
if(list == null) {
list = loadTile(x, y);
quadTree.put(tileId, list);
}
for(TransportRouteSegment r : list) {
TransportStop st = r.getStop(r.segStart);
if (Math.abs(st.x31 - sx) > walkRadiusIn31 || Math.abs(st.y31 - sy) > walkRadiusIn31) {
wrongLoadedWays++;
} else {
loadedWays++;
res.add(r);
}
}
}
}
loadTime += System.nanoTime() - nanoTime;
return res;
}
private List<TransportRouteSegment> loadTile(int x, int y) throws IOException {
long nanoTime = System.nanoTime();
List<TransportRouteSegment> lst = new ArrayList<TransportRouteSegment>();
int pz = (31 - cfg.ZOOM_TO_LOAD_TILES);
BinaryMapIndexReader.SearchRequest<TransportStop> sr = BinaryMapIndexReader.buildSearchTransportRequest(x << pz, (x + 1) << pz,
y << pz, (y + 1) << pz, -1, null);
Collection<TransportStop> stops = transportStopsReader.readMergedTransportStops(sr);
loadTransportSegments(stops, lst);
readTime += System.nanoTime() - nanoTime;
return lst;
}
private void loadTransportSegments(Collection<TransportStop> stops, List<TransportRouteSegment> lst) throws IOException {
for(TransportStop s : stops) {
if (s.isDeleted() || s.getRoutes() == null) {
continue;
}
for (TransportRoute route : s.getRoutes()) {
int stopIndex = -1;
double dist = TransportRoute.SAME_STOP;
for (int k = 0; k < route.getForwardStops().size(); k++) {
TransportStop st = route.getForwardStops().get(k);
if(st.getId().longValue() == s.getId().longValue() ) {
stopIndex = k;
break;
}
double d = MapUtils.getDistance(st.getLocation(), s.getLocation());
if (d < dist) {
stopIndex = k;
dist = d;
}
}
if (stopIndex != -1) {
if (cfg != null && cfg.useSchedule) {
loadScheduleRouteSegment(lst, route, stopIndex);
} else {
TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex);
lst.add(segment);
}
} else {
System.err.println(String.format(Locale.US, "Routing error: missing stop '%s' in route '%s' id: %d",
s.toString(), route.getRef(), route.getId() / 2));
}
}
}
}
private void loadScheduleRouteSegment(List<TransportRouteSegment> lst, TransportRoute route, int stopIndex) {
if(route.getSchedule() != null) {
TIntArrayList ti = route.getSchedule().tripIntervals;
int cnt = ti.size();
int t = 0;
// improve by using exact data
int stopTravelTime = 0;
TIntArrayList avgStopIntervals = route.getSchedule().avgStopIntervals;
for (int i = 0; i < stopIndex; i++) {
if (avgStopIntervals.size() > i) {
stopTravelTime += avgStopIntervals.getQuick(i);
}
}
for(int i = 0; i < cnt; i++) {
t += ti.getQuick(i);
int startTime = t + stopTravelTime;
if(startTime >= cfg.scheduleTimeOfDay && startTime <= cfg.scheduleTimeOfDay + cfg.scheduleMaxTime ) {
TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex, startTime);
lst.add(segment);
}
}
}
}
}

View file

@ -160,7 +160,7 @@ public class SearchPhrase {
public SearchPhrase generateNewPhrase(String text, SearchSettings settings) {
String textToSearch = text;
String textToSearch = Algorithms.normalizeSearchText(text);
List<SearchWord> leftWords = this.words;
String thisTxt = getText(true);
List<SearchWord> foundWords = new ArrayList<>();
@ -183,6 +183,7 @@ public class SearchPhrase {
}
public static SearchPhrase emptyPhrase() {
return emptyPhrase(null);
}

View file

@ -41,11 +41,34 @@ public class Algorithms {
private static final int BUFFER_SIZE = 1024;
private static final Log log = PlatformUtil.getLog(Algorithms.class);
public static boolean isEmpty(Collection c) {
public static boolean isEmpty(Collection<?> c) {
return c == null || c.size() == 0;
}
private static char[] CHARS_TO_NORMALIZE_KEY = new char[''];
private static char[] CHARS_TO_NORMALIZE_VALUE = new char['\''];
public static boolean isEmpty(Map map) {
public static String normalizeSearchText(String s) {
boolean norm = false;
for (int i = 0; i < s.length() && !norm; i++) {
char ch = s.charAt(i);
for (int j = 0; j < CHARS_TO_NORMALIZE_KEY.length; j++) {
if (ch == CHARS_TO_NORMALIZE_KEY[j]) {
norm = true;
break;
}
}
}
if (!norm) {
return s;
}
for (int k = 0; k < CHARS_TO_NORMALIZE_KEY.length; k++) {
s = s.replace(CHARS_TO_NORMALIZE_KEY[k], CHARS_TO_NORMALIZE_VALUE[k]);
}
return s;
}
public static boolean isEmpty(Map<?, ?> map) {
return map == null || map.size() == 0;
}
@ -320,10 +343,8 @@ public class Algorithms {
* exception. Supported formats are:
* #RRGGBB
* #AARRGGBB
* 'red', 'blue', 'green', 'black', 'white', 'gray', 'cyan', 'magenta',
* 'yellow', 'lightgray', 'darkgray'
*/
public static int parseColor(String colorString) {
public static int parseColor(String colorString) throws IllegalArgumentException {
if (colorString.charAt(0) == '#') {
// Use a long to avoid rollovers on #ffXXXXXX
if (colorString.length() == 4) {
@ -484,6 +505,10 @@ public class Algorithms {
}
public static StringBuilder readFromInputStream(InputStream i) throws IOException {
return readFromInputStream(i, true);
}
public static StringBuilder readFromInputStream(InputStream i, boolean autoclose) throws IOException {
StringBuilder responseBody = new StringBuilder();
responseBody.setLength(0);
if (i != null) {
@ -498,6 +523,9 @@ public class Algorithms {
}
responseBody.append(s);
}
if (autoclose) {
i.close();
}
}
return responseBody;
}

View file

@ -85,7 +85,7 @@
<string name="get_telegram_title">Рэгістрацыя ў Telegram</string>
<string name="get_telegram_account_first">Для абмену вам неабходны акаўнт Тэлеграм.</string>
<string name="get_telegram_description_continue">Калі ласка, ўсталюйце Тэлеграм і наладзьце акаўнт.</string>
<string name="get_telegram_after_creating_account">Пасля гэтага вы зможаце выкарыстоўваць дадатак.</string>
<string name="get_telegram_after_creating_account">Пасля гэтага вы зможаце выкарыстоўваць праграму.</string>
<string name="shared_string_all">Усе</string>
<string name="shared_string_off">Выкл</string>
<string name="already_registered_in_telegram">Вам неабходна мець акаўнт Тэлеграм і нумар тэлефона</string>
@ -125,7 +125,7 @@
<string name="shared_string_continue">Працягнуць</string>
<string name="shared_string_cancel">Скасаваць</string>
<string name="shared_string_settings">Налады</string>
<string name="no_location_permission">Дадатак не мае дазволу на доступ да даных аб месцазнаходжанні.</string>
<string name="no_location_permission">Праграма не мае дазволу на доступ да даных аб месцазнаходжанні.</string>
<string name="gps_not_available">Калі ласка, ўключыце \"Месцазнаходжанне\" ў сістэмных наладах</string>
<string name="location_service_no_gps_available">Абярыце аднаго пастаўшчыка месцазнаходжання, каб падзяліцца сваім месцазнаходжаннем.</string>
<string name="osmand_service">Фонавы рэжым</string>
@ -168,7 +168,7 @@
<string name="shared_string_hour_short">г</string>
<string name="shared_string_minute_short">хвіл</string>
<string name="shared_string_second_short">сек</string>
<string name="welcome_descr"><b>Назіральнік OsmAnd</b> Дае магчымасць дзяліцца сваім месцазнаходжаннем і бачыць месцазнаходжанне іншых у OsmAnd.<br/> <br/> Дадатак выкарыстоўвае Telegram API, таму вам неабходны акаўнт Тэлеграм.</string>
<string name="welcome_descr"><b>Назіральнік OsmAnd</b> Дае магчымасць дзяліцца сваім месцазнаходжаннем і бачыць месцазнаходжанне іншых у OsmAnd.<br/> <br/> Праграма выкарыстоўвае Telegram API, таму вам неабходны акаўнт Тэлеграм.</string>
<string name="my_location">Маё месцазнаходжанне</string>
<string name="live_now">Зараз дзейнічае</string>
<string name="send_location_as">Адправіць месцазнаходжанне як</string>
@ -200,7 +200,7 @@
<string name="timeline_description">Уключыць маніторынг, каб захоўваць пункты месцазнаходжання ў гісторыі.</string>
<string name="app_name_short">Назіральнік OsmAnd</string>
<string name="shared_string_telegram">Telegram</string>
<string name="privacy_policy_use_telegram">Telegram (дадатак для ліставання) выкарыстоўваецца для зносін паміж людзьмі.</string>
<string name="privacy_policy_use_telegram">Telegram (праграма для ліставання) выкарыстоўваецца для зносін паміж людзьмі.</string>
<string name="privacy_policy_telegram_client">Назіральнік OsmAnd - адзін з кліентаў для адкрытай платформы Telegram. Вашыя кантакты могуць выкарыстоўваць іншы кліент.</string>
<string name="privacy_policy_agree">Націскаючы \"Працягнуць\" вы пагаджаецеся з палітыкай прыватнасці Telegram і OsmAnd.</string>
<string name="shared_string_accept">Ухваліць</string>

View file

@ -248,4 +248,21 @@
<string name="start_location_sharing">Partager la position</string>
<string name="show_on_map">Afficher sur la carte</string>
<string name="already_registered_in_telegram">Vous avez besoin d\'un compte Telegram enregistré et d\'un numéro de téléphone</string>
<string name="app_name">OsmAnd Online GPS Tracker</string>
<string name="location_service_no_gps_available">Sélectionnez lun des services de localisation pour partager votre position.</string>
<string name="welcome_descr"><b>OsmAnd Tracker</b> vous permet de partager votre position et voir celle des autres dans OsmAnd.<br/><br/>Cette application utilise l\'API de Telegram, et donc vous avez besoin d\'un compte Telegram.</string>
<string name="install_osmand_dialog_message">Vous avez besoin d\'installer la version gratuite ou payante d\'OsmAnd d\'abord</string>
<string name="osmand_service_descr">OsmAnd Tracker s\'exécute en arrière-plan, écran éteint.</string>
<string name="gps_not_available">Merci d\'activer la géolocalisation dans les paramètres du système</string>
<string name="phone_number_descr">Numéro de téléphone au format international</string>
<string name="phone_number_title">Numéro de téléphone</string>
<string name="my_location_search_hint">Recherche : groupe ou contact</string>
<string name="location_sharing_description">Sélectionner les contacts et les groupes avec lesquels vous souhaitez partager votre position.</string>
<string name="set_time">Définir l\'heure</string>
<string name="visible_time_for_all">Heure visible par tous</string>
<string name="set_visible_time_for_all">Rendre l\'heure visible par tous</string>
<string name="send_my_location_desc">Définir l\'intervalle minimum pour partager sa position.</string>
<string name="stale_location_desc">La dernière fois qu\'un contact s\'est déplacé.</string>
<string name="location_history_desc">Cacher les contacts qui ne se sont pas déplacés depuis un temps donné.</string>
<string name="set_time_description">Définissez l\'heure à laquelle les contacts et groupes sélectionnés verront votre position en temps réel.</string>
</resources>

View file

@ -196,7 +196,7 @@
<string name="shared_string_telegram">Telegram</string>
<string name="privacy_policy_use_telegram">Telegram (l\'applicazione di messaggistica) viene usato per connettersi e comunicare con le persone.</string>
<string name="privacy_policy_telegram_client">Il tracker OsmAnd è uno dei client utilizzati dalla piattaforma aperta Telegram. I tuoi contatti possono usare un qualsiasi cliente Telegram.</string>
<string name="privacy_policy_agree">Cliccando continua tu sottoscrivi le condizioni di Privacy di Telegram e quelle di OsmAnd.</string>
<string name="privacy_policy_agree">Cliccando \"Continua\" tu sottoscrivi le condizioni di privacy di Telegram e quelle di OsmAnd.</string>
<string name="shared_string_accept">Accettazione</string>
<string name="how_it_works">Come funziona</string>
<string name="telegram_privacy_policy">Informativa sulla privacy di Telegram</string>
@ -257,4 +257,12 @@
<string name="time_zone">Fuso orario</string>
<string name="unit_of_speed_system">Unità di misura della velocità</string>
<string name="back_to_osmand">Torna a OsmAnd</string>
<string name="status_widget_title">Stato di OsmAnd Tracker</string>
<string name="shared_string_suggested">Suggerito</string>
<string name="duration_ago">%1$s fa</string>
<string name="last_response_duration">Ultima risposta: %1$s fa</string>
<string name="last_update_from_telegram_duration">Ultimo aggiornamento da Telegram: %1$s fa</string>
<string name="last_response_date">Ultima risposta: %1$s</string>
<string name="last_update_from_telegram_date">Ultimo aggiornamento da Telegram: %1$s</string>
<string name="shared_string_error_short">ERR</string>
</resources>

View file

@ -70,7 +70,7 @@
<string name="saved_messages">Iznan yettwaskelsen</string>
<string name="unit_of_speed_system">Tayunt n urured</string>
<string name="unit_of_speed_system_descr">Ad isbadu tayunt n urured.</string>
<string name="unit_of_length">Tayunin n umeccaq</string>
<string name="unit_of_length">Tayunin n teɣzef</string>
<string name="unit_of_length_descr">Beddel tayunt n uktili n umeccaq.</string>
<string name="units_and_formats">Tayunin d yimasalen</string>
<string name="time_zone_descr">Fren izḍi usrig ara d-tsekneḍ deg yiznan-ik n usideg.</string>
@ -137,7 +137,7 @@
<string name="shared_string_group">Agraw</string>
<string name="last_response">Dernier temps de réponse</string>
<string name="time_ago">aya</string>
<string name="shared_string_exit">Tufɣa</string>
<string name="shared_string_exit">Tuffɣa</string>
<string name="shared_string_sort">Smizzwer</string>
<string name="by_name">S yisem</string>
<string name="share_location_as">Bḍu adɣar</string>

View file

@ -142,9 +142,7 @@
<string name="shared_string_hour_short">h</string>
<string name="shared_string_minute_short">min</string>
<string name="shared_string_second_short">sek</string>
<string name="welcome_descr">
<b>Nadajnik OsmAnd</b> pozwala Ci udostępniać swoją lokalizację i widzieć ją w OsmAnd.<br/>
<br/>Aplikacja używa API Telegram, a więc potrzebujesz konta Telegram.</string>
<string name="welcome_descr"><b>Nadajnik OsmAnd</b> pozwala Ci udostępniać swoją lokalizację i widzieć ją w OsmAnd.<br/> <br/>Aplikacja używa API Telegram, a więc potrzebujesz konta Telegram.</string>
<string name="my_location">Moja lokalizacja</string>
<string name="live_now">Na żywo</string>
<string name="enter_another_device_name">Wybierz nazwę, której jeszcze nie używasz</string>

View file

@ -102,7 +102,7 @@
<string name="minutes_format">%1$d m</string>
<string name="hours_format">%1$d h</string>
<string name="shared_string_install">Instalar</string>
<string name="shared_string_share">Enviar</string>
<string name="shared_string_share">Compartilhe</string>
<string name="shared_string_back">Voltar</string>
<string name="visible_time_for_all">Hora visível para todos</string>
<string name="set_time_description">Defina a hora em que seus contatos e grupos selecionados verão sua localização em tempo real.</string>

View file

@ -25,11 +25,11 @@
<string name="active_chats">Bate-papos ativos</string>
<string name="si_min_km">Minutos por quilômetro</string>
<string name="background_work">Funcionamento em segundo plano</string>
<string name="location_history_desc">Ocultar contatos que não foram movidos em um determinado momento.</string>
<string name="location_history_desc">Ocultar contactos que não foram movidos num determinado momento.</string>
<string name="gpx_settings">Configurações de GPX</string>
<string name="type_contact_or_group_name">Digite o nome do contato ou do grupo</string>
<string name="min_mile">min/m</string>
<string name="password_descr">Senha do telegrama</string>
<string name="password_descr">Palavra-passe do telegrama</string>
<string name="last_updated_location">Última localização atualizada:</string>
<string name="stale_location">Parado</string>
<string name="show_gps_points_descr">Mostrar quantidade de pontos de GPS coletados e enviados.</string>
@ -71,7 +71,7 @@
<string name="osmand_connect">Conectar OsmAnd</string>
<string name="get_telegram_description_continue">Por favor, instale o Telegram e configure uma conta.</string>
<string name="si_kmh">Quilômetros por hora</string>
<string name="shared_string_password">Senha</string>
<string name="shared_string_password">Palavra-passe</string>
<string name="min_logging_distance">Distância mínima de registro</string>
<string name="shared_string_save">Gravar</string>
<string name="shared_string_start">Iniciar</string>
@ -92,7 +92,7 @@
<string name="shared_string_later">Mais tarde</string>
<string name="my_location">Minha localização</string>
<string name="install_osmand">Instalar OsmAnd</string>
<string name="privacy_policy_use_telegram">Telegrama (o aplicativo de mensagens) é usado para conectar e se comunicar com as pessoas.</string>
<string name="privacy_policy_use_telegram">Telegrama (a app de mensagens) é usado para conectar e se comunicar com as pessoas.</string>
<string name="not_possible_to_send_to_telegram_chats">Não é possível enviar para bate-papo do Telegram:</string>
<string name="not_sent_yet">Ainda não enviado</string>
<string name="points_size">%1$d pontos</string>
@ -108,7 +108,7 @@
<string name="expire_at">Expira</string>
<string name="send_location_as">Enviar localização como</string>
<string name="shared_string_exit">Sair</string>
<string name="no_location_permission">O aplicativo não tem permissão para acessar os dados de localização.</string>
<string name="no_location_permission">A app não tem permissão para acessar os dados de localização.</string>
<string name="si_mph">Milhas por hora</string>
<string name="share_location_as_description_second_line">Pode criar e visualizar o ID do aparelho no cliente de telegrama usando o bot de bate-papo %1$s. %2$s</string>
<string name="shared_string_back">Voltar</string>
@ -208,7 +208,7 @@
<string name="average_speed">Velocidade média</string>
<string name="shared_string_status">Situação</string>
<string name="km_h">km/h</string>
<string name="get_telegram_after_creating_account">Então você pode usar este aplicativo.</string>
<string name="get_telegram_after_creating_account">Então pode usar esta app.</string>
<string name="shared_string_settings">Configurações</string>
<string name="go_to_settings">Vá para as configurações</string>
<string name="authentication_code_descr">Uma faixa GPX é salva automaticamente durante a navegação.</string>

View file

@ -58,7 +58,7 @@
<string name="disable_monitoring">Desativar monitoramento</string>
<string name="location_recording_enabled">Gravação de localização ativada</string>
<string name="timeline_description">Ative o monitoramento para salvar todos os locais no histórico.</string>
<string name="privacy_policy_use_telegram">Telegrama (o aplicativo de mensagens) é usado para conectar e se comunicar com as pessoas.</string>
<string name="privacy_policy_use_telegram">Telegrama (a app de mensagens) é usado para conectar e se comunicar com as pessoas.</string>
<string name="privacy_policy_telegram_client">OsmAnd tracker é um dos clientes que usam a Plataforma aberta do Telegram . Seus contatos podem usar qualquer outro cliente Telegram.</string>
<string name="privacy_policy_agree">Ao clicar em \"Continuar\" você concorda com as condições da política de privacidade do Telegram e OsmAnd.</string>
<string name="shared_string_accept">Aceitar</string>
@ -144,7 +144,7 @@
<string name="in_time">no %1$s</string>
<string name="osmand_connect_desc">Escolha a versão OsmAnd que OsmAnd Tracker usa para exibir posições.</string>
<string name="osmand_connect">Conectar OsmAnd</string>
<string name="location_history_desc">Ocultar contatos que não foram movidos em um determinado momento.</string>
<string name="location_history_desc">Ocultar contactos que não foram movidos num determinado momento.</string>
<string name="location_history">Histórico de localização</string>
<string name="stale_location_desc">A última vez que um contato foi movido.</string>
<string name="stale_location">Parado</string>
@ -161,7 +161,7 @@
<string name="get_telegram_title">Registro no Telegram</string>
<string name="get_telegram_account_first">Você precisa de uma conta do Telegram para usar o compartilhamento de local.</string>
<string name="get_telegram_description_continue">Por favor, instale o Telegram e configure uma conta.</string>
<string name="get_telegram_after_creating_account">Então você pode usar este aplicativo.</string>
<string name="get_telegram_after_creating_account">Então pode usar esta app.</string>
<string name="shared_string_all">Todos</string>
<string name="shared_string_off">Desligado</string>
<string name="already_registered_in_telegram">Você precisa de uma conta e número de telefone registrados no Telegram</string>
@ -190,7 +190,7 @@
<string name="authentication_code">Código de Autenticação</string>
<string name="authentication_code_descr">Uma faixa GPX é salva automaticamente durante a navegação.</string>
<string name="enter_password">Digite a palavra-passe</string>
<string name="password_descr">Senha do telegrama</string>
<string name="password_descr">Palavra-passe do Telegram</string>
<string name="shared_string_login">Entrar</string>
<string name="shared_string_logout">Sair</string>
<string name="initialization">Iniciando</string>
@ -201,7 +201,7 @@
<string name="shared_string_continue">Continuar</string>
<string name="shared_string_cancel">Cancelar</string>
<string name="shared_string_settings">Configurações</string>
<string name="no_location_permission">O aplicativo não tem permissão para acessar os dados de localização.</string>
<string name="no_location_permission">A app não tem permissão para acessar os dados de localização.</string>
<string name="gps_not_available">Por favor, ligue \"Localização\" nas configurações do sistema</string>
<string name="location_service_no_gps_available">Selecione um dos provedores de localização para compartilhar sua localização.</string>
<string name="osmand_service">Modo de fundo</string>

View file

@ -156,7 +156,7 @@
<string name="telegram_privacy_policy">Политика конфиденциальности Telegram</string>
<string name="osmand_privacy_policy">Политика конфиденциальности OsmAnd</string>
<string name="received_gps_points">Получено точек GPX: %1$s</string>
<string name="shared_string_appearance">Вид</string>
<string name="shared_string_appearance">Внешний вид</string>
<string name="show_gps_points_descr">Показать количество собранных и отправленных точек GPS.</string>
<string name="please_update_osmand">Обновите OsmAnd для просмотра данных на карте</string>
<string name="shared_string_update">Обновить</string>
@ -246,7 +246,7 @@
<string name="min_logging_speed">Минимальная скорость регистрации</string>
<string name="min_logging_speed_descr">Фильтр: не регистрировать, пока не достигнута необходимая скорость</string>
<string name="shared_string_end">Конец</string>
<string name="shared_string_start">Начало</string>
<string name="shared_string_start">Старт</string>
<string name="set_time_timeline_descr">Выберите время для отображения</string>
<string name="saved_messages">Сохраненные сообщения</string>
<string name="time_zone_descr">Выберите часовой пояс, чтобы показывать время вашего местоположения в сообщениях.</string>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="last_update_from_telegram_date">Senaste uppdatering från Telegram: %1$s</string>
</resources>

View file

@ -251,7 +251,7 @@
<string name="saved_messages">Збережені повідомлення</string>
<string name="time_zone_descr">Оберіть часовий пояс щоб відобразити повідомлення у вашому розташуванні.</string>
<string name="time_zone">Часовий пояс</string>
<string name="units_and_formats">Одиниця вимірювання та формати</string>
<string name="units_and_formats">Одиниці виміру й формати</string>
<string name="unit_of_length">Одиниці вимірювання довжини</string>
<string name="unit_of_length_descr">Змінити одиницю вимірювання відстані.</string>
<string name="unit_of_speed_system_descr">Визначити одиницю швидкості.</string>

View file

@ -8,12 +8,15 @@ public class SetMapLocationParams implements Parcelable {
private double latitude;
private double longitude;
private int zoom;
private boolean animated;
private float rotation;
public SetMapLocationParams(double latitude, double longitude, int zoom, boolean animated) {
private boolean animated;
public SetMapLocationParams(double latitude, double longitude, int zoom, float rotation, boolean animated) {
this.latitude = latitude;
this.longitude = longitude;
this.zoom = zoom;
this.rotation = rotation;
this.animated = animated;
}
@ -44,6 +47,10 @@ public class SetMapLocationParams implements Parcelable {
return zoom;
}
public float getRotation() {
return rotation;
}
public boolean isAnimated() {
return animated;
}
@ -53,6 +60,7 @@ public class SetMapLocationParams implements Parcelable {
out.writeDouble(longitude);
out.writeInt(zoom);
out.writeByte((byte) (animated ? 1 : 0));
out.writeFloat(rotation);
}
private void readFromParcel(Parcel in) {
@ -60,6 +68,7 @@ public class SetMapLocationParams implements Parcelable {
longitude = in.readDouble();
zoom = in.readInt();
animated = in.readByte() != 0;
rotation = in.readFloat();
}
public int describeContents() {

View file

@ -939,7 +939,7 @@ class OsmandAidlHelper(private val app: TelegramApplication) {
if (mIOsmAndAidlInterface != null) {
try {
return mIOsmAndAidlInterface!!.setMapLocation(
SetMapLocationParams(latitude, longitude, zoom, animated))
SetMapLocationParams(latitude, longitude, zoom, Float.NaN, animated))
} catch (e: RemoteException) {
e.printStackTrace()
}

View file

@ -955,6 +955,7 @@
android:process="net.osmand.plus"
android:label="@string/process_navigation_service"
android:name="net.osmand.plus.NavigationService"
android:foregroundServiceType="location"
android:stopWithTask="false">
<intent-filter>
<action android:name="net.osmand.plus.NavigationService" />

View file

@ -22,8 +22,8 @@ task printc {
}
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
compileSdkVersion 29
buildToolsVersion "29.0.3"
// compileNdkVersion "android-ndk-r17b"
signingConfigs {
@ -563,6 +563,7 @@ dependencies {
implementation ("com.github.HITGIF:TextFieldBoxes:1.4.5"){
exclude group: 'com.android.support'
}
implementation 'com.jaredrummler:colorpicker:1.1.0'
huaweiImplementation files('libs/huawei-android-drm_v2.5.2.300.jar')
freehuaweiImplementation files('libs/huawei-android-drm_v2.5.2.300.jar')

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.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 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 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 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: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/icon_color_secondary_dark" />
<corners android:radius="30dp" />
</shape>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/icon_color_secondary_light" />
<corners android:radius="30dp" />
</shape>

View file

@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M5,15C5,13.8954 5.8954,13 7,13H10C10.5523,13 11,12.5523 11,12C11,11.4477 10.5523,11 10,11H7C4.7909,11 3,12.7909 3,15C3,17.2091 4.7909,19 7,19H15.1707C15.5825,20.1652 16.6938,21 18,21C19.6569,21 21,19.6569 21,18C21,16.3431 19.6569,15 18,15C16.6938,15 15.5825,15.8348 15.1707,17H7C5.8954,17 5,16.1046 5,15ZM17,18C17,18.5523 17.4477,19 18,19C18.5523,19 19,18.5523 19,18C19,17.4477 18.5523,17 18,17C17.4477,17 17,17.4477 17,18Z"
android:strokeAlpha="0.7"
android:fillColor="#ffffff"
android:fillType="evenOdd"
android:fillAlpha="0.7"/>
<path
android:pathData="M3,6C3,4.3432 4.3432,3 6,3C7.3062,3 8.4175,3.8348 8.8293,5H17C19.2091,5 21,6.7909 21,9C21,11.2091 19.2091,13 17,13H14C13.4477,13 13,12.5523 13,12C13,11.4477 13.4477,11 14,11H17C18.1046,11 19,10.1046 19,9C19,7.8954 18.1046,7 17,7H8.8293C8.4175,8.1652 7.3062,9 6,9C4.3432,9 3,7.6568 3,6ZM7,6C7,5.4477 6.5523,5 6,5C5.4477,5 5,5.4477 5,6C5,6.5523 5.4477,7 6,7C6.5523,7 7,6.5523 7,6Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="6dp"
android:viewportWidth="12"
android:viewportHeight="6">
<path
android:pathData="M0,6L6,0L12,6H0Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M16.9285,4.6286C16.7318,4.137 16.1861,3.8839 15.6838,4.0513L12.6838,5.0513L13.3162,6.9487L15.4217,6.2468L16.2071,8.2102L13.8377,9H9.5811C8.6414,9 7.7078,8.8485 6.8162,8.5513C5.7208,8.1861 4.5736,8 3.4189,8H3C2.4477,8 2,8.4477 2,9C2,9.5523 2.4477,10 3,10H3.4189C4.3586,10 5.2922,10.1515 6.1838,10.4487C6.3323,10.4982 6.4818,10.5444 6.6321,10.5873C7.5714,11.0587 8.2953,11.8858 8.6325,12.8974L8.7408,13.2223C8.7467,13.24 8.7531,13.2576 8.7599,13.2748L7.3486,13.4865C7.2195,13.4558 7.1027,13.3741 7.0311,13.25C6.8982,13.0199 6.9691,12.728 7.1877,12.5833C6.967,12.2926 6.7074,12.033 6.4167,11.8123C6.272,12.031 5.9801,12.1018 5.75,11.969C5.5196,11.836 5.4351,11.5473 5.5527,11.3125C5.2218,11.173 4.8682,11.0768 4.4991,11.0308C4.4831,11.2926 4.2658,11.5 4,11.5C3.7342,11.5 3.5169,11.2926 3.5009,11.0308C3.1318,11.0768 2.7782,11.173 2.4473,11.3125C2.5649,11.5473 2.4804,11.8359 2.25,11.9689C2.0199,12.1018 1.728,12.0309 1.5833,11.8123C1.2926,12.033 1.0331,12.2926 0.8123,12.5833C1.0309,12.728 1.1018,13.0198 0.9689,13.2499C0.8359,13.4803 0.5473,13.5647 0.3125,13.4473C0.173,13.7782 0.0768,14.1317 0.0308,14.5009C0.2926,14.5169 0.5,14.7342 0.5,15C0.5,15.2658 0.2926,15.4831 0.0308,15.4991C0.0768,15.8682 0.173,16.2218 0.3125,16.5527C0.5473,16.4352 0.8359,16.5196 0.9689,16.75C1.1018,16.9801 1.0309,17.272 0.8123,17.4167C1.033,17.7074 1.2926,17.967 1.5833,18.1877C1.7281,17.9691 2.0199,17.8983 2.25,18.0311C2.4803,18.1641 2.5648,18.4527 2.4474,18.6875C2.7782,18.827 3.1318,18.9232 3.5009,18.9692C3.5169,18.7074 3.7342,18.5 4,18.5C4.2658,18.5 4.4831,18.7074 4.4991,18.9692C4.8682,18.9232 5.2218,18.827 5.5526,18.6875C5.4351,18.4527 5.5196,18.1641 5.75,18.0311C5.9801,17.8982 6.2719,17.9691 6.4167,18.1877C6.7074,17.967 6.967,17.7073 7.1877,17.4166C6.9691,17.2719 6.8982,16.98 7.0311,16.7499C7.1641,16.5195 7.4527,16.435 7.6875,16.5526C7.827,16.2217 7.9232,15.8682 7.9692,15.4991C7.8986,15.4948 7.832,15.4759 7.7724,15.4453L13.4317,14.5964C14.1262,14.4922 14.7623,14.1476 15.2289,13.6227L17.3966,11.184L17.6962,11.9331C17.6531,11.9002 17.6147,11.8597 17.5833,11.8122C17.2926,12.033 17.0331,12.2925 16.8123,12.5832C17.0309,12.7279 17.1018,13.0197 16.9689,13.2498C16.8359,13.4802 16.5473,13.5647 16.3125,13.4472C16.173,13.7781 16.0768,14.1317 16.0308,14.5009C16.2926,14.5168 16.5,14.7341 16.5,14.9999C16.5,15.2657 16.2926,15.4831 16.0308,15.499C16.0768,15.8682 16.173,16.2217 16.3125,16.5526C16.5473,16.4351 16.8359,16.5196 16.9689,16.7499C17.1018,16.9801 17.0309,17.2719 16.8123,17.4166C17.033,17.7073 17.2926,17.9669 17.5833,18.1876C17.7281,17.9691 18.0199,17.8982 18.25,18.0311C18.4803,18.1641 18.5648,18.4527 18.4474,18.6874C18.7782,18.8269 19.1318,18.9232 19.5009,18.9691C19.5169,18.7073 19.7342,18.4999 20,18.4999C20.2658,18.4999 20.4831,18.7073 20.4991,18.9691C20.8682,18.9232 21.2218,18.8269 21.5527,18.6874C21.4351,18.4526 21.5196,18.164 21.75,18.031C21.9801,17.8982 22.2719,17.969 22.4167,18.1876C22.7074,17.9669 22.967,17.7073 23.1877,17.4165C22.9691,17.2718 22.8982,16.98 23.0311,16.7498C23.1641,16.5195 23.4527,16.435 23.6875,16.5525C23.827,16.2217 23.9232,15.8681 23.9692,15.499C23.7074,15.4831 23.5,15.2657 23.5,14.9999C23.5,14.7341 23.7074,14.5168 23.9692,14.5009C23.9232,14.1317 23.827,13.7781 23.6875,13.4473C23.4527,13.5648 23.1641,13.4803 23.0311,13.2499C22.8982,13.0198 22.9691,12.728 23.1877,12.5833C22.967,12.2926 22.7074,12.033 22.4167,11.8122C22.272,12.0309 21.9801,12.1018 21.75,11.9689C21.5196,11.8359 21.4351,11.5472 21.5527,11.3125C21.2218,11.173 20.8682,11.0767 20.4991,11.0308C20.4831,11.2925 20.2658,11.4999 20,11.4999C19.8325,11.4999 19.6842,11.4176 19.5935,11.2911L19.5048,11.0693C19.503,11.0566 19.5017,11.0438 19.5009,11.0308C19.4973,11.0312 19.4936,11.0317 19.4899,11.0322L16.9285,4.6286ZM18.4282,13.7631L19.0715,15.3714C19.2766,15.8841 19.8586,16.1336 20.3714,15.9284C20.8842,15.7233 21.1336,15.1414 20.9285,14.6286L20.2851,13.0201C21.2546,13.1585 22,13.9921 22,14.9999C22,16.1045 21.1046,16.9999 20,16.9999C18.8954,16.9999 18,16.1045 18,14.9999C18,14.533 18.16,14.1034 18.4282,13.7631ZM5.5646,13.7541C5.1982,13.2945 4.6335,13 4,13C2.8954,13 2,13.8954 2,15C2,16.1046 2.8954,17 4,17C4.8462,17 5.5697,16.4744 5.8618,15.7319L4.1483,15.9889C3.6022,16.0708 3.093,15.6945 3.0111,15.1483C2.9291,14.6021 3.3055,14.093 3.8517,14.011L5.5646,13.7541Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M14.1159,9.6799L16.6159,6.6799L17.3841,7.3201L16.6441,8.2081L24,14L24,16H21.95C21.7184,17.1411 20.7095,18 19.5,18C18.2905,18 17.2816,17.1411 17.05,16H5.95C5.7183,17.1411 4.7095,18 3.5,18C2.2905,18 1.2816,17.1411 1.05,16H1C0.4477,16 0,15.5523 0,15V11H4V8H6L8,13.0343H18V11.5L15.5808,9.484L14.8841,10.3201L14.1159,9.6799ZM5.0001,15.5C5.0001,16.3284 4.3285,17 3.5001,17C2.6717,17 2.0001,16.3284 2.0001,15.5C2.0001,14.6716 2.6717,14 3.5001,14C4.3285,14 5.0001,14.6716 5.0001,15.5ZM21.0001,15.5C21.0001,16.3284 20.3285,17 19.5001,17C18.6717,17 18.0001,16.3284 18.0001,15.5C18.0001,14.6716 18.6717,14 19.5001,14C20.3285,14 21.0001,14.6716 21.0001,15.5Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M16,6L14,6L14,2H16V6Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M16,6L16,13.5858L18.2929,11.2929L19.7071,12.7071L15,17.4142L10.2929,12.7071L11.7071,11.2929L14,13.5858V6H12L10,4H4C2.8954,4 2,4.8954 2,6V18C2,19.1046 2.8954,20 4,20H20C21.1046,20 22,19.1046 22,18V8C22,6.8954 21.1046,6 20,6L16,6Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -4,7 +4,12 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M7.9665536 4.070941C7.0068803 4.072229 6.0149912 4.2865025 5.1455896 4.5791361 5.0603526 4.6078099 4.954574 4.6755988 4.9331717 4.7455086L4.4546022 6.4097024c-0.09638 0.276188 -2.2440733 5.4113916 -0.6413745 7.2190636 1.8562508 2.093638 3.9856671 0.756751 5.7439389 0.878178 6.0284454 0.416328 9.1646024 0.719802 10.7497374 -0.350348 0.07881 -0.27154 0.167915 -0.774966 -0.04706 -1.265918 -0.197091 -0.449986 -0.313908 -0.78041 -0.911789 -0.982077 -0.282059 -0.09516 -0.538381 -0.165061 -0.809702 -0.239042 -0.546449 -0.149007 -1.12557 -0.307169 -1.996593 -0.697203l-1.142303 1.195054c-0.135847 0.142129 -0.359638 0.14724 -0.501766 0.01138l-0.01955 -0.01871c-0.142128 -0.135848 -0.147169 -0.359639 -0.01132 -0.501767l0.974683 -1.019741c-0.276445 -0.138894 -0.578762 -0.298709 -0.911513 -0.48325l-1.110788 1.16215c-0.135848 0.14213 -0.359639 0.147163 -0.501766 0.01132l-0.01955 -0.01871c-0.142128 -0.135847 -0.147169 -0.359637 -0.01132 -0.501766L14.269833 9.7795574C13.99751 9.6208066 13.706502 9.4467709 13.396156 9.2568292l-1.157188 1.2106388c-0.135847 0.142128 -0.359573 0.147168 -0.501702 0.01132l-0.01961 -0.01871C11.575529 10.32423 11.570495 10.10051 11.706336 9.9583831L12.7564 8.8597385C12.486211 8.6897723 12.202108 8.5083044 11.903655 8.3150003l-1.24488 1.3024534c-0.135847 0.1421277 -0.359638 0.1472397 -0.501767 0.01138l-0.01955 -0.01871C9.9953356 9.4742772 9.9902246 9.2504871 10.126085 9.1083586L11.278591 7.9026053C11.115203 7.7652924 10.274809 6.9496638 10.398949 4.7963435c0.0032 -0.056447 -0.02893 -0.11177 -0.07365 -0.1391902C9.6350936 4.2343495 8.8134596 4.069803 7.9666947 4.0709795ZM4.4388766 15.318046A2.3390127 2.3390127 0 0 0 2.1008871 17.657013 2.3390127 2.3390127 0 0 0 4.43986 19.996057 2.3390127 2.3390127 0 0 0 6.7788983 17.657013 2.3390127 2.3390127 0 0 0 4.43986 15.318046a2.3390127 2.3390127 0 0 0 -0.00133 0zm4.993565 0.0019a2.3390127 2.3390127 0 0 0 -2.3379198 2.339039 2.3390127 2.3390127 0 0 0 2.3389678 2.338973 2.3390127 2.3390127 0 0 0 2.3390454 -2.338973 2.3390127 2.3390127 0 0 0 -2.3390454 -2.339039 2.3390127 2.3390127 0 0 0 -0.0013 0zm4.9936354 0.0038a2.3390127 2.3390127 0 0 0 -2.337989 2.339037 2.3390127 2.3390127 0 0 0 2.339038 2.338974 2.3390127 2.3390127 0 0 0 2.339043 -2.338974 2.3390127 2.3390127 0 0 0 -2.339043 -2.339037 2.3390127 2.3390127 0 0 0 -0.0012 0zm4.993635 0.0019a2.3390127 2.3390127 0 0 0 -2.337996 2.338974 2.3390127 2.3390127 0 0 0 2.339045 2.339037 2.3390127 2.3390127 0 0 0 2.338972 -2.339037 2.3390127 2.3390127 0 0 0 -2.338972 -2.338974 2.3390127 2.3390127 0 0 0 -0.0012 0zM4.4327307 16.429285a1.227233 1.227233 0 0 1 0.00716 0 1.227233 1.227233 0 0 1 1.2272714 1.2272 1.227233 1.227233 0 0 1 -1.2272714 1.227273 1.227233 1.227233 0 0 1 -1.227207 -1.227273 1.227233 1.227233 0 0 1 1.2200766 -1.2272zm4.9935649 0.0019a1.227233 1.227233 0 0 1 0.0072 0 1.227233 1.227233 0 0 1 1.2272724 1.227272 1.227233 1.227233 0 0 1 -1.2272724 1.2272 1.227233 1.227233 0 0 1 -1.2272007 -1.2272 1.227233 1.227233 0 0 1 1.2200067 -1.227272zm4.9936354 0.0038a1.227233 1.227233 0 0 1 0.0073 0 1.227233 1.227233 0 0 1 1.227201 1.22727 1.227233 1.227233 0 0 1 -1.227201 1.227202 1.227233 1.227233 0 0 1 -1.227201 -1.227202 1.227233 1.227233 0 0 1 1.220007 -1.22727zm4.993629 0.0019a1.227233 1.227233 0 0 1 0.0073 0 1.227233 1.227233 0 0 1 1.227201 1.2272 1.227233 1.227233 0 0 1 -1.227201 1.227272 1.227233 1.227233 0 0 1 -1.227272 -1.227272 1.227233 1.227233 0 0 1 1.220071 -1.2272z"
android:fillColor="#727272"
android:pathData="M4,17.5C4,17.2239 4.2239,17 4.5,17H19.5C19.7761,17 20,17.2239 20,17.5C20,17.7761 19.7761,18 19.5,18H4.5C4.2239,18 4,17.7761 4,17.5Z"
android:strokeAlpha="0.2"
android:fillColor="#ffffff"
android:fillAlpha="0.2"/>
<path
android:pathData="M11,5C11,4.4477 10.5523,4 10,4H5.8346C5.3513,4 4.9372,4.3456 4.8507,4.8211L3.643,11.4633C3.3082,13.3049 4.7229,15 6.5946,15H9.5C8.1193,15 7,16.1193 7,17.5C7,16.1193 5.8807,15 4.5,15C3.1193,15 2,16.1193 2,17.5C2,18.8807 3.1193,20 4.5,20C5.8807,20 7,18.8807 7,17.5C7,18.8807 8.1193,20 9.5,20C10.8807,20 12,18.8807 12,17.5C12,18.8807 13.1193,20 14.5,20C15.8807,20 17,18.8807 17,17.5C17,18.8807 18.1193,20 19.5,20C20.8807,20 22,18.8807 22,17.5C22,16.1193 20.8807,15 19.5,15C18.1193,15 17,16.1193 17,17.5C17,16.1193 15.8807,15 14.5,15H18.7065C19.9731,15 21,13.9731 21,12.7065C21,11.6936 20.3356,10.8007 19.3655,10.5097L17.3233,9.897L16.9552,10.7069C16.8409,10.9583 16.5445,11.0695 16.2931,10.9552C16.0417,10.8409 15.9305,10.5445 16.0448,10.2931L16.3567,9.607L15.3991,9.3197L14.9472,10.2236C14.8237,10.4706 14.5234,10.5707 14.2764,10.4472C14.0294,10.3237 13.9293,10.0234 14.0528,9.7764L14.4269,9.0281L13.4903,8.7471L12.9371,9.7428C12.803,9.9842 12.4986,10.0712 12.2572,9.9371C12.0158,9.803 11.9288,9.4986 12.0629,9.2572L12.5097,8.4529L11.7127,8.2138C11.2897,8.0869 11,7.6976 11,7.256V5ZM14.5,15C13.1193,15 12,16.1193 12,17.5C12,16.1193 10.8807,15 9.5,15H14.5ZM4.5,18C4.7761,18 5,17.7761 5,17.5C5,17.2239 4.7761,17 4.5,17C4.2239,17 4,17.2239 4,17.5C4,17.7761 4.2239,18 4.5,18ZM9.5,18C9.7761,18 10,17.7761 10,17.5C10,17.2239 9.7761,17 9.5,17C9.2239,17 9,17.2239 9,17.5C9,17.7761 9.2239,18 9.5,18ZM15,17.5C15,17.7761 14.7761,18 14.5,18C14.2239,18 14,17.7761 14,17.5C14,17.2239 14.2239,17 14.5,17C14.7761,17 15,17.2239 15,17.5ZM19.5,18C19.7761,18 20,17.7761 20,17.5C20,17.2239 19.7761,17 19.5,17C19.2239,17 19,17.2239 19,17.5C19,17.7761 19.2239,18 19.5,18Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,21 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M5,17C5.5523,17 6,16.5523 6,16C6,15.4477 5.5523,15 5,15C4.4477,15 4,15.4477 4,16C4,16.5523 4.4477,17 5,17ZM5,19C6.6568,19 8,17.6569 8,16C8,14.3431 6.6568,13 5,13C3.3431,13 2,14.3431 2,16C2,17.6569 3.3431,19 5,19Z"
android:strokeAlpha="0.9"
android:fillColor="#ffffff"
android:fillType="evenOdd"
android:fillAlpha="0.9"/>
<path
android:pathData="M19,17C19.5523,17 20,16.5523 20,16C20,15.4477 19.5523,15 19,15C18.4477,15 18,15.4477 18,16C18,16.5523 18.4477,17 19,17ZM19,19C20.6569,19 22,17.6569 22,16C22,14.3431 20.6569,13 19,13C17.3431,13 16,14.3431 16,16C16,17.6569 17.3431,19 19,19Z"
android:strokeAlpha="0.9"
android:fillColor="#ffffff"
android:fillType="evenOdd"
android:fillAlpha="0.9"/>
<path
android:pathData="M13,7H15.1654L16,11.5902V14H10V11C10.5523,11 11,10.5523 11,10C11,9.4477 10.5523,9 10,9H6C5.4477,9 5,9.4477 5,10C5,10.5523 5.4477,11 6,11H5C3.3431,11 2,12.3431 2,14V16H22V15C22,12.7909 20.2091,11 18,11C17.9751,11 17.9504,11.0005 17.9257,11.0014L17.5417,8.889C17.679,8.9599 17.8348,9 18,9H19V7H18C17.7057,7 17.4411,7.1271 17.2581,7.3295L17.1332,6.6422C16.9603,5.6912 16.132,5 15.1654,5H13V7Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -4,7 +4,7 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M13.5858,7H12V5H14.4142L18.415,9.0008C18.4433,9.0003 18.4716,9 18.5,9C20.9853,9 23,11.0147 23,13.5C23,15.9853 20.9853,18 18.5,18C16.244,18 14.3758,16.3399 14.0502,14.1746L13.5,15H8.7439C8.1262,16.7478 6.4593,18 4.5,18C2.0147,18 0,15.9853 0,13.5C0,11.0147 2.0147,9 4.5,9C4.6749,9 4.8474,9.01 5.0171,9.0294L5,9H6V8H9V9H15.5858L13.5858,7ZM7,13.5C7,14.8807 5.8807,16 4.5,16C3.1193,16 2,14.8807 2,13.5C2,12.1193 3.1193,11 4.5,11C5.8807,11 7,12.1193 7,13.5ZM21,13.5C21,14.8807 19.8807,16 18.5,16C17.1193,16 16,14.8807 16,13.5C16,12.1193 17.1193,11 18.5,11C19.8807,11 21,12.1193 21,13.5Z"
android:fillColor="#727272"
android:pathData="M13,7H15.3501L15.8603,8.148L13.6379,10H11V9C11,8.4477 10.5523,8 10,8H5C4.4477,8 4,8.4477 4,9H3C2.4477,9 2,9.4477 2,10C2,10.5523 2.4477,11 3,11H3.7639L3.7762,11.0062C1.6712,11.1222 0,12.8659 0,15C0,17.2091 1.7909,19 4,19C5.8638,19 7.4299,17.7252 7.874,16H11.9296C12.9327,16 13.8694,15.4987 14.4258,14.6641L17.0242,10.7666L17.5132,11.8668C16.5912,12.5996 16,13.7308 16,15C16,17.2091 17.7909,19 20,19C22.2091,19 24,17.2091 24,15C24,12.7909 22.2091,11 20,11C19.7754,11 19.5551,11.0185 19.3406,11.0541L18.8535,9.9581C19.5113,9.7992 20,9.2067 20,8.5C20,7.6716 19.3284,7 18.5,7C18.1864,7 17.8953,7.0962 17.6546,7.2607L17.1777,6.1877C16.8567,5.4655 16.1405,5 15.3501,5H13V7ZM18.3827,13.8232L19.0862,15.4061C19.3105,15.9108 19.9015,16.1381 20.4061,15.9138C20.9108,15.6895 21.1381,15.0985 20.9138,14.5939L20.2103,13.0109C21.216,13.116 22,13.9664 22,15C22,16.1046 21.1046,17 20,17C18.8954,17 18,16.1046 18,15C18,14.5601 18.142,14.1534 18.3827,13.8232ZM5.7324,14H4C3.4477,14 3,14.4477 3,15C3,15.5523 3.4477,16 4,16H5.7324C5.3866,16.5978 4.7403,17 4,17C2.8954,17 2,16.1046 2,15C2,13.8954 2.8954,13 4,13C4.7403,13 5.3866,13.4022 5.7324,14ZM7.874,14H9V12H6.6458C7.2362,12.5211 7.6716,13.2138 7.874,14Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M0,12C0,8.6863 2.6863,6 6,6H18C21.3137,6 24,8.6863 24,12C24,15.3137 21.3137,18 18,18H6C2.6863,18 0,15.3137 0,12Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M11,12L8,9V15L11,12Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M18,12L15,9V15L18,12Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M6,6H18C21.3137,6 24,8.6863 24,12C24,15.3137 21.3137,18 18,18H6C2.6863,18 0,15.3137 0,12C0,8.6863 2.6863,6 6,6ZM6,8C3.7909,8 2,9.7909 2,12C2,14.2091 3.7909,16 6,16H18C20.2091,16 22,14.2091 22,12C22,9.7909 20.2091,8 18,8H6Z"
android:strokeAlpha="0.3"
android:fillColor="#ffffff"
android:fillType="evenOdd"
android:fillAlpha="0.3"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3.3542,9C3.2289,9.1106 3.1106,9.2289 3,9.3542V12H2C2,13.0144 2.3776,13.9407 3,14.6458V12H6V15H3.3542C4.0593,15.6224 4.9855,16 6,16V15H9V16H12V15H15V16H18L18,15H20.6458C20.7711,14.8894 20.8894,14.7711 21,14.6458V12H22C22,10.9856 21.6224,10.0593 21,9.3542V12H18V9H20.6458C19.9407,8.3776 19.0144,8 18,8L18,9H15V8H12V9H9V8H6V9H3.3542ZM9,12H6V9H9V12ZM12,12V9H15V12H12ZM12,12V15H9V12H12ZM15,12H18V15H15V12Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M5,7L19,7A5,5 0,0 1,24 12L24,12A5,5 0,0 1,19 17L5,17A5,5 0,0 1,0 12L0,12A5,5 0,0 1,5 7z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M11,12L8,9V15L11,12Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M18,12L15,9V15L18,12Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M5,7H19C21.7614,7 24,9.2386 24,12C24,14.7614 21.7614,17 19,17H5C2.2386,17 0,14.7614 0,12C0,9.2386 2.2386,7 5,7ZM5,9C3.3431,9 2,10.3431 2,12C2,13.6569 3.3431,15 5,15H19C20.6569,15 22,13.6569 22,12C22,10.3431 20.6569,9 19,9H5Z"
android:strokeAlpha="0.3"
android:fillColor="#ffffff"
android:fillType="evenOdd"
android:fillAlpha="0.3"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M5,9C4.2316,9 3.5308,9.2888 3,9.7639V12H2C2,12.8885 2.3863,13.6868 3,14.2361V12H6V15H9V12H12V15H15V12H18V15H19C19.7684,15 20.4692,14.7111 21,14.2361V12H22C22,11.1115 21.6137,10.3132 21,9.7639V12H18V9H15V12H12V9H9V12H6V9H5Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3,9L21,9A3,3 0,0 1,24 12L24,12A3,3 0,0 1,21 15L3,15A3,3 0,0 1,0 12L0,12A3,3 0,0 1,3 9z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M8,12L6,11V13L8,12Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M11,11L13,12L11,13V11Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M16,11L18,12L16,13V11Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3,9H21C22.6569,9 24,10.3431 24,12C24,13.6569 22.6569,15 21,15H3C1.3432,15 0,13.6569 0,12C0,10.3431 1.3432,9 3,9ZM3,11C2.4477,11 2,11.4477 2,12C2,12.5523 2.4477,13 3,13H21C21.5523,13 22,12.5523 22,12C22,11.4477 21.5523,11 21,11H3Z"
android:strokeAlpha="0.3"
android:fillColor="#ffffff"
android:fillType="evenOdd"
android:fillAlpha="0.3"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M6,11H3L3,12H2C2,12.5523 2.4477,13 3,13V12H6V13H9V12H12V13H15V12H18V13H21V12H22C22,11.4477 21.5523,11 21,11V12H18V11H15V12H12V11H9V12H6V11Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,6C13.1046,6 14,5.1046 14,4C14,2.8954 13.1046,2 12,2C10.8954,2 10,2.8954 10,4C10,5.1046 10.8954,6 12,6Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M19,12.9999V10.9999C17.46,11.0199 15.91,10.2499 14.93,9.1699L13.64,7.7399C13.47,7.5499 13.26,7.3999 13.03,7.2899C13.02,7.2899 13.02,7.2799 13.01,7.2799H13C12.65,7.0799 12.25,6.9799 11.81,7.0199C10.76,7.1099 10,8.0399 10,9.0899V14.9999C10,16.0999 10.9,16.9999 12,16.9999H17V21.9999H19V16.4999C19,15.3999 18.1,14.4999 17,14.4999H14V11.0499C15.29,12.1199 17.25,12.9899 19,12.9999ZM12.83,17.9999C12.42,19.1599 11.31,19.9999 10,19.9999C8.34,19.9999 7,18.6599 7,16.9999C7,15.6899 7.84,14.5899 9,14.1699V12.0999C6.72,12.5599 5,14.5799 5,16.9999C5,19.7599 7.24,21.9999 10,21.9999C12.42,21.9999 14.44,20.2799 14.9,17.9999H12.83Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M17,6.54C18.1046,6.54 19,5.6446 19,4.54C19,3.4355 18.1046,2.54 17,2.54C15.8954,2.54 15,3.4355 15,4.54C15,5.6446 15.8954,6.54 17,6.54Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M14,17H12C12,18.65 10.65,20 9,20C7.35,20 6,18.65 6,17C6,15.35 7.35,14 9,14V12C6.24,12 4,14.24 4,17C4,19.76 6.24,22 9,22C11.76,22 14,19.76 14,17ZM17,13.5H15.14L16.81,9.83C17.42,8.5 16.44,7 14.96,7H9.76C8.95,7 8.22,7.47 7.89,8.2L7.22,10L9.14,10.53L9.79,9H12L10.17,13.1C9.57,14.43 10.56,16 12.02,16H17V21H19V15.5C19,14.4 18.1,13.5 17,13.5Z"
android:fillColor="#ffffff"/>
</vector>

View file

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="36dp"
android:height="36dp"
android:viewportWidth="36"
android:viewportHeight="36">
<path
android:pathData="M18,18V24H12V18H18Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M12,1.0242C9.7643,1.8145 7.7313,3.0338 6,4.5834V6H4.5834C3.0338,7.7313 1.8145,9.7643 1.0242,12H6V18H0C0,20.1038 0.3609,22.1233 1.0242,24H6V30H4.5834C5.029,30.498 5.502,30.971 6,31.4166V30H12V34.9758C13.8767,35.6391 15.8962,36 18,36V30H24V34.9758C26.2357,34.1856 28.2687,32.9662 30,31.4166V30H31.4166C32.9662,28.2687 34.1856,26.2357 34.9758,24H30V18H36C36,15.8962 35.6391,13.8767 34.9758,12H30V6H31.4166C30.971,5.502 30.498,5.029 30,4.5834V6H24V1.0242C22.1233,0.3609 20.1038,0 18,0V6H12V1.0242ZM12,12H6V6H12V12ZM18,12V6H24V12H18ZM18,18V12H12V18H6V24H12V30H18V24H24V30H30V24H24V18H30V12H24V18H18Z"
android:fillColor="#ffffff"
android:fillType="evenOdd"/>
</vector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/radio_button_selected_center" android:state_checked="true" />
<item android:drawable="@drawable/radio_button_regular_center" />
</selector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/radio_button_selected_center_dark" android:state_checked="true" />
<item android:drawable="@drawable/radio_button_regular_center_dark" />
</selector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/radio_button_selected_center_light" android:state_checked="true" />
<item android:drawable="@drawable/radio_button_regular_center_light" />
</selector>

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