Merge branch 'r3.7'
This commit is contained in:
commit
9a2dd6f622
10 changed files with 476 additions and 628 deletions
|
@ -22,8 +22,6 @@ public class TransportStop extends MapObject {
|
|||
public int y31;
|
||||
private List<TransportStopExit> exits;
|
||||
private List<TransportRoute> routes = null;
|
||||
private LinkedHashMap<String, int[]> referencesToRoutesMap;
|
||||
|
||||
private TransportStopAggregated transportStopAggregated;
|
||||
|
||||
public TransportStop() {}
|
||||
|
@ -35,19 +33,6 @@ public class TransportStop extends MapObject {
|
|||
public boolean isMissingStop() {
|
||||
return MISSING_STOP_NAME.equals(getName());
|
||||
}
|
||||
|
||||
public LinkedHashMap<String, int[]> getReferencesToRoutesMap() {
|
||||
return referencesToRoutesMap;
|
||||
}
|
||||
|
||||
public void putReferencesToRoutes(String repositoryFileName, int[] referencesToRoutes) {
|
||||
LinkedHashMap<String, int[]> referencesToRoutesMap = this.referencesToRoutesMap;
|
||||
if (referencesToRoutesMap == null) {
|
||||
referencesToRoutesMap = new LinkedHashMap<>();
|
||||
this.referencesToRoutesMap = referencesToRoutesMap;
|
||||
}
|
||||
referencesToRoutesMap.put(repositoryFileName, referencesToRoutes);
|
||||
}
|
||||
|
||||
public void setRoutes(List<TransportRoute> routes) {
|
||||
this.routes = routes;
|
||||
|
@ -115,10 +100,6 @@ public class TransportStop extends MapObject {
|
|||
return !isDeleted() && referencesToRoutes != null && referencesToRoutes.length > 0;
|
||||
}
|
||||
|
||||
public boolean hasReferencesToRoutesMap() {
|
||||
return !isDeleted() && referencesToRoutesMap != null && !referencesToRoutesMap.isEmpty();
|
||||
}
|
||||
|
||||
public Amenity getAmenity() {
|
||||
if (transportStopAggregated != null) {
|
||||
return transportStopAggregated.getAmenity();
|
||||
|
|
|
@ -2,6 +2,7 @@ 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;
|
||||
|
@ -30,12 +31,15 @@ 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 {
|
||||
|
||||
private static final boolean MEASURE_TIME = false;
|
||||
private static final int MISSING_STOP_SEARCH_RADIUS = 15000;
|
||||
|
||||
private static final int MIN_DIST_STOP_TO_GEOMETRY = 150;
|
||||
public static final long GEOMETRY_WAY_ID = -1;
|
||||
public static final long STOPS_WAY_ID = -2;
|
||||
|
@ -440,7 +444,7 @@ public class TransportRoutePlanner {
|
|||
Node ln = startInd.way.getLastNode();
|
||||
Node fn = endInd.way.getFirstNode();
|
||||
// HERE we need to check other ways for continuation
|
||||
if (ln != null && fn != null && MapUtils.getDistance(ln.getLatLon(), fn.getLatLon()) < MISSING_STOP_SEARCH_RADIUS) {
|
||||
if (ln != null && fn != null && MapUtils.getDistance(ln.getLatLon(), fn.getLatLon()) < TransportStopsRouteReader.MISSING_STOP_SEARCH_RADIUS) {
|
||||
validContinuation = true;
|
||||
} else {
|
||||
validContinuation = false;
|
||||
|
@ -752,9 +756,7 @@ public class TransportRoutePlanner {
|
|||
// 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
|
||||
public final Map<BinaryMapIndexReader, TIntObjectHashMap<TransportRoute>> routeMap =
|
||||
new LinkedHashMap<BinaryMapIndexReader, TIntObjectHashMap<TransportRoute>>();
|
||||
|
||||
final TransportStopsRouteReader transportStopsReader;
|
||||
public int finishTimeSeconds;
|
||||
|
||||
// stats
|
||||
|
@ -779,9 +781,7 @@ public class TransportRoutePlanner {
|
|||
walkChangeRadiusIn31 = (int) (cfg.walkChangeRadius / MapUtils.getTileDistanceWidth(31));
|
||||
quadTree = new TLongObjectHashMap<List<TransportRouteSegment>>();
|
||||
this.library = library;
|
||||
for (BinaryMapIndexReader r : readers) {
|
||||
routeMap.put(r, new TIntObjectHashMap<TransportRoute>());
|
||||
}
|
||||
transportStopsReader = new TransportStopsRouteReader(Arrays.asList(readers));
|
||||
}
|
||||
|
||||
public List<TransportRouteSegment> getTransportStops(LatLon loc) throws IOException {
|
||||
|
@ -831,369 +831,11 @@ public class TransportRoutePlanner {
|
|||
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);
|
||||
|
||||
// could be global ?
|
||||
TLongObjectHashMap<TransportStop> loadedTransportStops = new TLongObjectHashMap<TransportStop>();
|
||||
|
||||
for (BinaryMapIndexReader r : routeMap.keySet()) {
|
||||
sr.clearSearchResults();
|
||||
List<TransportStop> stops = r.searchTransportIndex(sr);
|
||||
|
||||
TIntArrayList routesToLoad = mergeTransportStops(r, loadedTransportStops, stops);
|
||||
|
||||
TIntObjectHashMap<TransportRoute> loadedRoutes = routeMap.get(r);
|
||||
// localFileRoutes.clear();
|
||||
TIntObjectHashMap<TransportRoute> localFileRoutes = new TIntObjectHashMap<>(); //reference, route
|
||||
loadRoutes(r, localFileRoutes, loadedRoutes, routesToLoad);
|
||||
|
||||
for (TransportStop stop : stops) {
|
||||
// skip missing stops
|
||||
if (stop.isMissingStop()) {
|
||||
continue;
|
||||
}
|
||||
long stopId = stop.getId();
|
||||
TransportStop multifileStop = loadedTransportStops.get(stopId);
|
||||
int[] rrs = stop.getReferencesToRoutes();
|
||||
// clear up so it won't be used because there is multi file stop
|
||||
stop.setReferencesToRoutes(null);
|
||||
if (rrs != null && !multifileStop.isDeleted()) {
|
||||
for (int rr : rrs) {
|
||||
TransportRoute route = localFileRoutes.get(rr);
|
||||
if (route == null) {
|
||||
System.err.println(
|
||||
String.format("Something went wrong by loading combined route %d for stop %s",
|
||||
rr, stop));
|
||||
} else {
|
||||
TransportRoute combinedRoute = getCombinedRoute(route);
|
||||
if (multifileStop == stop || (!multifileStop.hasRoute(combinedRoute.getId()) &&
|
||||
!multifileStop.isRouteDeleted(combinedRoute.getId()))) {
|
||||
// duplicates won't be added
|
||||
multifileStop.addRouteId(combinedRoute.getId());
|
||||
multifileStop.addRoute(combinedRoute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// There should go stops with complete routes:
|
||||
loadTransportSegments(loadedTransportStops.valueCollection(), lst);
|
||||
|
||||
Collection<TransportStop> stops = transportStopsReader.readMergedTransportStops(sr);
|
||||
loadTransportSegments(stops, lst);
|
||||
readTime += System.nanoTime() - nanoTime;
|
||||
return lst;
|
||||
}
|
||||
|
||||
|
||||
public TIntArrayList mergeTransportStops(BinaryMapIndexReader reader, TLongObjectHashMap<TransportStop> loadedTransportStops,
|
||||
List<TransportStop> stops) throws IOException {
|
||||
TIntArrayList routesToLoad = new TIntArrayList();
|
||||
Iterator<TransportStop> it = stops.iterator();
|
||||
TIntArrayList localRoutesToLoad = new TIntArrayList();
|
||||
while (it.hasNext()) {
|
||||
TransportStop stop = it.next();
|
||||
long stopId = stop.getId();
|
||||
localRoutesToLoad.clear();
|
||||
TransportStop multifileStop = loadedTransportStops.get(stopId);
|
||||
long[] routesIds = stop.getRoutesIds();
|
||||
long[] delRIds = stop.getDeletedRoutesIds();
|
||||
if (multifileStop == null) {
|
||||
loadedTransportStops.put(stopId, stop);
|
||||
multifileStop = stop;
|
||||
if (!stop.isDeleted()) {
|
||||
localRoutesToLoad.addAll(stop.getReferencesToRoutes());
|
||||
}
|
||||
} else if (multifileStop.isDeleted()){
|
||||
// stop has nothing to load, so not needed
|
||||
it.remove();
|
||||
} else {
|
||||
if (delRIds != null) {
|
||||
for (long deletedRouteId : delRIds) {
|
||||
multifileStop.addDeletedRouteId(deletedRouteId);
|
||||
}
|
||||
}
|
||||
if (routesIds != null && routesIds.length > 0) {
|
||||
int[] refs = stop.getReferencesToRoutes();
|
||||
for (int i = 0; i < routesIds.length; i++) {
|
||||
long routeId = routesIds[i];
|
||||
if (!multifileStop.hasRoute(routeId) && !multifileStop.isRouteDeleted(routeId)) {
|
||||
localRoutesToLoad.add(refs[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (stop.hasReferencesToRoutes()) {
|
||||
// old format
|
||||
localRoutesToLoad.addAll(stop.getReferencesToRoutes());
|
||||
} else {
|
||||
// stop has noting to load, so not needed
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
routesToLoad.addAll(localRoutesToLoad);
|
||||
multifileStop.putReferencesToRoutes(reader.getFile().getName(), localRoutesToLoad.toArray()); //add valid stop and references to routes
|
||||
}
|
||||
return routesToLoad;
|
||||
}
|
||||
|
||||
private void loadRoutes(BinaryMapIndexReader reader, TIntObjectHashMap<TransportRoute> localFileRoutes,
|
||||
TIntObjectHashMap<TransportRoute> loadedRoutes, TIntArrayList routesToLoad) throws IOException {
|
||||
// load/combine routes
|
||||
if (routesToLoad.size() > 0) {
|
||||
routesToLoad.sort();
|
||||
TIntArrayList referencesToLoad = new TIntArrayList();
|
||||
TIntIterator itr = routesToLoad.iterator();
|
||||
int p = routesToLoad.get(0) + 1; // different
|
||||
while (itr.hasNext()) {
|
||||
int nxt = itr.next();
|
||||
if (p != nxt) {
|
||||
if (localFileRoutes != null && loadedRoutes != null && loadedRoutes.contains(nxt)) { //check if
|
||||
localFileRoutes.put(nxt, loadedRoutes.get(nxt));
|
||||
} else {
|
||||
referencesToLoad.add(nxt);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (localFileRoutes != null && loadedRoutes != null) {
|
||||
reader.loadTransportRoutes(referencesToLoad.toArray(), localFileRoutes);
|
||||
loadedRoutes.putAll(localFileRoutes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TransportRoute getCombinedRoute(TransportRoute route) throws IOException {
|
||||
if (!route.isIncomplete()) {
|
||||
return route;
|
||||
}
|
||||
TransportRoute c = combinedRoutesCache.get(route.getId());
|
||||
if (c == null) {
|
||||
c = combineRoute(route);
|
||||
combinedRoutesCache.put(route.getId(), c);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private TransportRoute combineRoute(TransportRoute route) throws IOException {
|
||||
// 1. Get all available route parts;
|
||||
List<TransportRoute> incompleteRoutes = findIncompleteRouteParts(route);
|
||||
if (incompleteRoutes == null) {
|
||||
return route;
|
||||
}
|
||||
// here could be multiple overlays between same points
|
||||
// It's better to remove them especially identical segments
|
||||
List<Way> allWays = getAllWays(incompleteRoutes);
|
||||
|
||||
|
||||
// 2. Get array of segments (each array size > 1):
|
||||
LinkedList<List<TransportStop>> stopSegments = parseRoutePartsToSegments(incompleteRoutes);
|
||||
|
||||
// 3. Merge segments and remove excess missingStops (when they are closer then MISSING_STOP_SEARCH_RADIUS):
|
||||
// + Check for missingStops. If they present in the middle/there more then one segment - we have a hole in the map data
|
||||
List<List<TransportStop>> mergedSegments = combineSegmentsOfSameRoute(stopSegments);
|
||||
|
||||
// 4. Now we need to properly sort segments, proper sorting is minimizing distance between stops
|
||||
// So it is salesman problem, we have this solution at TspAnt, but if we know last or first segment we can solve it straightforward
|
||||
List<TransportStop> firstSegment = null;
|
||||
List<TransportStop> lastSegment = null;
|
||||
for(List<TransportStop> l : mergedSegments) {
|
||||
if(!l.get(0).isMissingStop()) {
|
||||
firstSegment = l;
|
||||
}
|
||||
if(!l.get(l.size() - 1).isMissingStop()) {
|
||||
lastSegment = l;
|
||||
}
|
||||
}
|
||||
List<List<TransportStop>> sortedSegments = new ArrayList<List<TransportStop>>();
|
||||
if(firstSegment != null) {
|
||||
sortedSegments.add(firstSegment);
|
||||
mergedSegments.remove(firstSegment);
|
||||
while(!mergedSegments.isEmpty()) {
|
||||
List<TransportStop> last = sortedSegments.get(sortedSegments.size() - 1);
|
||||
List<TransportStop> add = findAndDeleteMinDistance(last.get(last.size() - 1).getLocation(), mergedSegments, true);
|
||||
sortedSegments.add(add);
|
||||
}
|
||||
|
||||
} else if(lastSegment != null) {
|
||||
sortedSegments.add(lastSegment);
|
||||
mergedSegments.remove(lastSegment);
|
||||
while(!mergedSegments.isEmpty()) {
|
||||
List<TransportStop> first = sortedSegments.get(0);
|
||||
List<TransportStop> add = findAndDeleteMinDistance(first.get(0).getLocation(), mergedSegments, false);
|
||||
sortedSegments.add(0, add);
|
||||
}
|
||||
} else {
|
||||
sortedSegments = mergedSegments;
|
||||
}
|
||||
List<TransportStop> finalList = new ArrayList<TransportStop>();
|
||||
for(List<TransportStop> s : sortedSegments) {
|
||||
finalList.addAll(s);
|
||||
}
|
||||
// 5. Create combined TransportRoute and return it
|
||||
return new TransportRoute(route, finalList, allWays);
|
||||
}
|
||||
|
||||
private List<TransportStop> findAndDeleteMinDistance(LatLon location, List<List<TransportStop>> mergedSegments,
|
||||
boolean attachToBegin) {
|
||||
int ind = attachToBegin ? 0 : mergedSegments.get(0).size() - 1;
|
||||
double minDist = MapUtils.getDistance(mergedSegments.get(0).get(ind).getLocation(), location);
|
||||
int minInd = 0;
|
||||
for(int i = 1; i < mergedSegments.size(); i++) {
|
||||
ind = attachToBegin ? 0 : mergedSegments.get(i).size() - 1;
|
||||
double dist = MapUtils.getDistance(mergedSegments.get(i).get(ind).getLocation(), location);
|
||||
if(dist < minDist) {
|
||||
minInd = i;
|
||||
}
|
||||
}
|
||||
return mergedSegments.remove(minInd);
|
||||
}
|
||||
|
||||
private List<Way> getAllWays(List<TransportRoute> parts) {
|
||||
List<Way> w = new ArrayList<Way>();
|
||||
for (TransportRoute t : parts) {
|
||||
w.addAll(t.getForwardWays());
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private List<List<TransportStop>> combineSegmentsOfSameRoute(LinkedList<List<TransportStop>> segments) {
|
||||
List<List<TransportStop>> resultSegments = new ArrayList<List<TransportStop>>();
|
||||
while (!segments.isEmpty()) {
|
||||
List<TransportStop> firstSegment = segments.poll();
|
||||
boolean merged = true;
|
||||
while (merged) {
|
||||
merged = false;
|
||||
Iterator<List<TransportStop>> it = segments.iterator();
|
||||
while (it.hasNext()) {
|
||||
List<TransportStop> segmentToMerge = it.next();
|
||||
merged = tryToMerge(firstSegment, segmentToMerge);
|
||||
|
||||
if (merged) {
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
resultSegments.add(firstSegment);
|
||||
}
|
||||
return resultSegments;
|
||||
}
|
||||
|
||||
private boolean tryToMerge(List<TransportStop> firstSegment, List<TransportStop> segmentToMerge) {
|
||||
if(firstSegment.size() < 2 || segmentToMerge.size() < 2) {
|
||||
return false;
|
||||
}
|
||||
// 1st we check that segments overlap by stop
|
||||
int commonStopFirst = 0;
|
||||
int commonStopSecond = 0;
|
||||
boolean found = false;
|
||||
for(;commonStopFirst < firstSegment.size(); commonStopFirst++) {
|
||||
for(commonStopSecond = 0; commonStopSecond < segmentToMerge.size() && !found; commonStopSecond++) {
|
||||
long lid1 = firstSegment.get(commonStopFirst).getId();
|
||||
long lid2 = segmentToMerge.get(commonStopSecond).getId();
|
||||
if(lid1 > 0 && lid2 == lid1) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found) {
|
||||
// important to increment break inside loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found && commonStopFirst < firstSegment.size()) {
|
||||
// we've found common stop so we can merge based on stops
|
||||
// merge last part first
|
||||
int leftPartFirst = firstSegment.size() - commonStopFirst;
|
||||
int leftPartSecond = segmentToMerge.size() - commonStopSecond;
|
||||
if(leftPartFirst < leftPartSecond || (leftPartFirst == leftPartSecond &&
|
||||
firstSegment.get(firstSegment.size() - 1).isMissingStop())) {
|
||||
while(firstSegment.size() > commonStopFirst) {
|
||||
firstSegment.remove(firstSegment.size() - 1);
|
||||
}
|
||||
for(int i = commonStopSecond; i < segmentToMerge.size(); i++) {
|
||||
firstSegment.add(segmentToMerge.get(i));
|
||||
}
|
||||
}
|
||||
// merge first part
|
||||
if(commonStopFirst < commonStopSecond || (commonStopFirst == commonStopSecond &&
|
||||
firstSegment.get(0).isMissingStop())) {
|
||||
for(int i = 0; i < commonStopFirst; i++) {
|
||||
firstSegment.remove(0);
|
||||
}
|
||||
for(int i = commonStopSecond; i >= 0; i--) {
|
||||
firstSegment.add(0, segmentToMerge.get(i));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
// no common stops, so try to connect to the end or beginning
|
||||
// beginning
|
||||
boolean merged = false;
|
||||
if (MapUtils.getDistance(firstSegment.get(0).getLocation(),
|
||||
segmentToMerge.get(segmentToMerge.size() - 1).getLocation()) < MISSING_STOP_SEARCH_RADIUS) {
|
||||
firstSegment.remove(0);
|
||||
for(int i = segmentToMerge.size() - 2; i >= 0; i--) {
|
||||
firstSegment.add(0, segmentToMerge.get(i));
|
||||
}
|
||||
merged = true;
|
||||
} else if(MapUtils.getDistance(firstSegment.get(firstSegment.size() - 1).getLocation(),
|
||||
segmentToMerge.get(0).getLocation()) < MISSING_STOP_SEARCH_RADIUS) {
|
||||
firstSegment.remove(firstSegment.size() - 1);
|
||||
for(int i = 1; i < segmentToMerge.size(); i++) {
|
||||
firstSegment.add(segmentToMerge.get(i));
|
||||
}
|
||||
merged = true;
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private LinkedList<List<TransportStop>> parseRoutePartsToSegments(List<TransportRoute> routeParts) {
|
||||
LinkedList<List<TransportStop>> segs = new LinkedList<List<TransportStop>>();
|
||||
// here we assume that missing stops come in pairs <A, B, C, MISSING, MISSING, D, E...>
|
||||
// we don't add segments with 1 stop cause they are irrelevant further
|
||||
for (TransportRoute part : routeParts) {
|
||||
List<TransportStop> newSeg = new ArrayList<TransportStop>();
|
||||
for (TransportStop s : part.getForwardStops()) {
|
||||
newSeg.add(s);
|
||||
if (s.isMissingStop()) {
|
||||
if (newSeg.size() > 1) {
|
||||
segs.add(newSeg);
|
||||
newSeg = new ArrayList<TransportStop>();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newSeg.size() > 1) {
|
||||
segs.add(newSeg);
|
||||
}
|
||||
}
|
||||
return segs;
|
||||
}
|
||||
|
||||
private List<TransportRoute> findIncompleteRouteParts(TransportRoute baseRoute) throws IOException {
|
||||
List<TransportRoute> allRoutes = null;
|
||||
for (BinaryMapIndexReader bmir : routeMap.keySet()) {
|
||||
// here we could limit routeMap indexes by only certain bbox around start / end (check comment on field)
|
||||
IncompleteTransportRoute ptr = bmir.getIncompleteTransportRoutes().get(baseRoute.getId());
|
||||
if (ptr != null) {
|
||||
TIntArrayList lst = new TIntArrayList();
|
||||
while(ptr != null) {
|
||||
lst.add(ptr.getRouteOffset());
|
||||
ptr = ptr.getNextLinkedRoute();
|
||||
}
|
||||
if(lst.size() > 0) {
|
||||
if(allRoutes == null) {
|
||||
allRoutes = new ArrayList<TransportRoute>();
|
||||
}
|
||||
allRoutes.addAll(bmir.getTransportRoutes(lst.toArray()).valueCollection());
|
||||
}
|
||||
}
|
||||
}
|
||||
return allRoutes;
|
||||
}
|
||||
|
||||
private void loadTransportSegments(Collection<TransportStop> stops, List<TransportRouteSegment> lst) throws IOException {
|
||||
for(TransportStop s : stops) {
|
||||
|
@ -1216,7 +858,7 @@ public class TransportRoutePlanner {
|
|||
}
|
||||
}
|
||||
if (stopIndex != -1) {
|
||||
if (cfg.useSchedule) {
|
||||
if (cfg != null && cfg.useSchedule) {
|
||||
loadScheduleRouteSegment(lst, route, stopIndex);
|
||||
} else {
|
||||
TransportRouteSegment segment = new TransportRouteSegment(route, stopIndex);
|
||||
|
@ -1230,7 +872,7 @@ public class TransportRoutePlanner {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void loadScheduleRouteSegment(List<TransportRouteSegment> lst, TransportRoute route, int stopIndex) {
|
||||
if(route.getSchedule() != null) {
|
||||
TIntArrayList ti = route.getSchedule().tripIntervals;
|
||||
|
@ -1255,10 +897,10 @@ public class TransportRoutePlanner {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static List<TransportRouteResult> convertToTransportRoutingResult(NativeTransportRoutingResult[] res,
|
||||
TransportRoutingConfiguration cfg) {
|
||||
//cache for converted TransportRoutes:
|
||||
TransportRoutingConfiguration cfg) {
|
||||
// cache for converted TransportRoutes:
|
||||
TLongObjectHashMap<TransportRoute> convertedRoutesCache = new TLongObjectHashMap<>();
|
||||
TLongObjectHashMap<TransportStop> convertedStopsCache = new TLongObjectHashMap<>();
|
||||
|
||||
|
@ -1292,8 +934,8 @@ public class TransportRoutePlanner {
|
|||
}
|
||||
|
||||
private static TransportRoute convertTransportRoute(NativeTransportRoute nr,
|
||||
TLongObjectHashMap<TransportRoute> convertedRoutesCache,
|
||||
TLongObjectHashMap<TransportStop> convertedStopsCache) {
|
||||
TLongObjectHashMap<TransportRoute> convertedRoutesCache,
|
||||
TLongObjectHashMap<TransportStop> convertedStopsCache) {
|
||||
TransportRoute r = new TransportRoute();
|
||||
r.setId(nr.id);
|
||||
r.setLocation(nr.routeLat, nr.routeLon);
|
||||
|
@ -1312,10 +954,10 @@ public class TransportRoutePlanner {
|
|||
r.setDist(nr.dist);
|
||||
r.setColor(nr.color);
|
||||
|
||||
if (nr.intervals != null && nr.intervals.length > 0 && nr.avgStopIntervals !=null
|
||||
if (nr.intervals != null && nr.intervals.length > 0 && nr.avgStopIntervals != null
|
||||
&& nr.avgStopIntervals.length > 0 && nr.avgWaitIntervals != null && nr.avgWaitIntervals.length > 0) {
|
||||
r.setSchedule(new TransportSchedule(new TIntArrayList(nr.intervals),
|
||||
new TIntArrayList(nr.avgStopIntervals), new TIntArrayList(nr.avgWaitIntervals)));
|
||||
r.setSchedule(new TransportSchedule(new TIntArrayList(nr.intervals), new TIntArrayList(nr.avgStopIntervals),
|
||||
new TIntArrayList(nr.avgWaitIntervals)));
|
||||
}
|
||||
|
||||
for (int i = 0; i < nr.waysIds.length; i++) {
|
||||
|
@ -1333,7 +975,7 @@ public class TransportRoutePlanner {
|
|||
}
|
||||
|
||||
private static List<TransportStop> convertTransportStops(NativeTransportStop[] nstops,
|
||||
TLongObjectHashMap<TransportStop> convertedStopsCache) {
|
||||
TLongObjectHashMap<TransportStop> convertedStopsCache) {
|
||||
List<TransportStop> stops = new ArrayList<>();
|
||||
for (NativeTransportStop ns : nstops) {
|
||||
if (convertedStopsCache != null && convertedStopsCache.get(ns.id) != null) {
|
||||
|
@ -1360,16 +1002,11 @@ public class TransportRoutePlanner {
|
|||
|
||||
if (ns.pTStopExit_refs != null && ns.pTStopExit_refs.length > 0) {
|
||||
for (int i = 0; i < ns.pTStopExit_refs.length; i++) {
|
||||
s.addExit(new TransportStopExit(ns.pTStopExit_x31s[i],
|
||||
ns.pTStopExit_y31s[i], ns.pTStopExit_refs[i]));
|
||||
s.addExit(
|
||||
new TransportStopExit(ns.pTStopExit_x31s[i], ns.pTStopExit_y31s[i], ns.pTStopExit_refs[i]));
|
||||
}
|
||||
}
|
||||
|
||||
if (ns.referenceToRoutesKeys != null && ns.referenceToRoutesKeys.length > 0) {
|
||||
for (int i = 0; i < ns.referenceToRoutesKeys.length; i++) {
|
||||
s.putReferencesToRoutes(ns.referenceToRoutesKeys[i], ns.referenceToRoutesVals[i]);
|
||||
}
|
||||
}
|
||||
if (convertedStopsCache == null) {
|
||||
convertedStopsCache = new TLongObjectHashMap<>();
|
||||
}
|
||||
|
@ -1380,4 +1017,7 @@ public class TransportRoutePlanner {
|
|||
}
|
||||
return stops;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,409 @@
|
|||
package net.osmand.router;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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.binary.BinaryMapIndexReader;
|
||||
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
|
||||
import net.osmand.data.IncompleteTransportRoute;
|
||||
import net.osmand.data.LatLon;
|
||||
import net.osmand.data.TransportRoute;
|
||||
import net.osmand.data.TransportStop;
|
||||
import net.osmand.osm.edit.Way;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
public class TransportStopsRouteReader {
|
||||
public static final int MISSING_STOP_SEARCH_RADIUS = 15000;
|
||||
TLongObjectHashMap<TransportRoute> combinedRoutesCache = new TLongObjectHashMap<TransportRoute>();
|
||||
Map<BinaryMapIndexReader, TIntObjectHashMap<TransportRoute>> routesFilesCache = new LinkedHashMap<BinaryMapIndexReader,
|
||||
TIntObjectHashMap<TransportRoute>>();
|
||||
|
||||
|
||||
public TransportStopsRouteReader(Collection<BinaryMapIndexReader> fls) {
|
||||
for(BinaryMapIndexReader r : fls) {
|
||||
routesFilesCache.put(r, new TIntObjectHashMap<TransportRoute>());
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<TransportStop> readMergedTransportStops(SearchRequest<TransportStop> sr) throws IOException {
|
||||
// TODO could be global ?
|
||||
TLongObjectHashMap<TransportStop> loadedTransportStops = new TLongObjectHashMap<TransportStop>();
|
||||
|
||||
for (BinaryMapIndexReader r : routesFilesCache.keySet()) {
|
||||
sr.clearSearchResults();
|
||||
List<TransportStop> stops = r.searchTransportIndex(sr);
|
||||
|
||||
TIntArrayList routesToLoad = mergeTransportStops(r, loadedTransportStops, stops);
|
||||
|
||||
TIntObjectHashMap<TransportRoute> loadedRoutes = routesFilesCache.get(r);
|
||||
// TODO localFileRoutes.clear();
|
||||
TIntObjectHashMap<TransportRoute> localFileRoutes = new TIntObjectHashMap<>(); //reference, route
|
||||
loadRoutes(r, localFileRoutes, loadedRoutes, routesToLoad);
|
||||
|
||||
for (TransportStop stop : stops) {
|
||||
// skip missing stops
|
||||
if (stop.isMissingStop()) {
|
||||
continue;
|
||||
}
|
||||
long stopId = stop.getId();
|
||||
TransportStop multifileStop = loadedTransportStops.get(stopId);
|
||||
int[] rrs = stop.getReferencesToRoutes();
|
||||
// clear up so it won't be used because there is multi file stop
|
||||
stop.setReferencesToRoutes(null);
|
||||
if (rrs != null && !multifileStop.isDeleted()) {
|
||||
for (int rr : rrs) {
|
||||
TransportRoute route = localFileRoutes.get(rr);
|
||||
if (route == null) {
|
||||
System.err.println(
|
||||
String.format("Something went wrong by loading combined route %d for stop %s",
|
||||
rr, stop));
|
||||
} else {
|
||||
TransportRoute combinedRoute = getCombinedRoute(route);
|
||||
if (multifileStop == stop || (!multifileStop.hasRoute(combinedRoute.getId()) &&
|
||||
!multifileStop.isRouteDeleted(combinedRoute.getId()))) {
|
||||
// duplicates won't be added
|
||||
multifileStop.addRouteId(combinedRoute.getId());
|
||||
multifileStop.addRoute(combinedRoute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// There should go stops with complete routes:
|
||||
return loadedTransportStops.valueCollection();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public TIntArrayList mergeTransportStops(BinaryMapIndexReader reader,
|
||||
TLongObjectHashMap<TransportStop> loadedTransportStops, List<TransportStop> stops) throws IOException {
|
||||
TIntArrayList routesToLoad = new TIntArrayList();
|
||||
Iterator<TransportStop> it = stops.iterator();
|
||||
TIntArrayList localRoutesToLoad = new TIntArrayList();
|
||||
while (it.hasNext()) {
|
||||
TransportStop stop = it.next();
|
||||
long stopId = stop.getId();
|
||||
localRoutesToLoad.clear();
|
||||
TransportStop multifileStop = loadedTransportStops.get(stopId);
|
||||
long[] routesIds = stop.getRoutesIds();
|
||||
long[] delRIds = stop.getDeletedRoutesIds();
|
||||
if (multifileStop == null) {
|
||||
loadedTransportStops.put(stopId, stop);
|
||||
multifileStop = stop;
|
||||
if (!stop.isDeleted()) {
|
||||
localRoutesToLoad.addAll(stop.getReferencesToRoutes());
|
||||
}
|
||||
} else if (multifileStop.isDeleted()) {
|
||||
// stop has nothing to load, so not needed
|
||||
it.remove();
|
||||
} else {
|
||||
if (delRIds != null) {
|
||||
for (long deletedRouteId : delRIds) {
|
||||
multifileStop.addDeletedRouteId(deletedRouteId);
|
||||
}
|
||||
}
|
||||
if (routesIds != null && routesIds.length > 0) {
|
||||
int[] refs = stop.getReferencesToRoutes();
|
||||
for (int i = 0; i < routesIds.length; i++) {
|
||||
long routeId = routesIds[i];
|
||||
if (!multifileStop.hasRoute(routeId) && !multifileStop.isRouteDeleted(routeId)) {
|
||||
localRoutesToLoad.add(refs[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (stop.hasReferencesToRoutes()) {
|
||||
// old format
|
||||
localRoutesToLoad.addAll(stop.getReferencesToRoutes());
|
||||
} else {
|
||||
// stop has noting to load, so not needed
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
routesToLoad.addAll(localRoutesToLoad);
|
||||
// // add valid
|
||||
// stop and
|
||||
// references
|
||||
// to routes
|
||||
// TODO should be here add route not references
|
||||
// multifileStop.putReferencesToRoutes(reader.getFile().getName(), localRoutesToLoad.toArray());
|
||||
}
|
||||
return routesToLoad;
|
||||
}
|
||||
|
||||
public void loadRoutes(BinaryMapIndexReader reader, TIntObjectHashMap<TransportRoute> localFileRoutes,
|
||||
TIntObjectHashMap<TransportRoute> loadedRoutes, TIntArrayList routesToLoad) throws IOException {
|
||||
// load/combine routes
|
||||
if (routesToLoad.size() > 0) {
|
||||
routesToLoad.sort();
|
||||
TIntArrayList referencesToLoad = new TIntArrayList();
|
||||
TIntIterator itr = routesToLoad.iterator();
|
||||
int prev = routesToLoad.get(0) - 1; // different
|
||||
while (itr.hasNext()) {
|
||||
int nxt = itr.next();
|
||||
if (prev != nxt) {
|
||||
if (localFileRoutes != null && loadedRoutes != null && loadedRoutes.contains(nxt)) { // check if
|
||||
localFileRoutes.put(nxt, loadedRoutes.get(nxt));
|
||||
} else {
|
||||
referencesToLoad.add(nxt);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (localFileRoutes != null && loadedRoutes != null) {
|
||||
reader.loadTransportRoutes(referencesToLoad.toArray(), localFileRoutes);
|
||||
loadedRoutes.putAll(localFileRoutes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TransportRoute getCombinedRoute(TransportRoute route) throws IOException {
|
||||
if (!route.isIncomplete()) {
|
||||
return route;
|
||||
}
|
||||
TransportRoute c = combinedRoutesCache.get(route.getId());
|
||||
if (c == null) {
|
||||
c = combineRoute(route);
|
||||
combinedRoutesCache.put(route.getId(), c);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
private TransportRoute combineRoute(TransportRoute route) throws IOException {
|
||||
// 1. Get all available route parts;
|
||||
List<TransportRoute> incompleteRoutes = findIncompleteRouteParts(route);
|
||||
if (incompleteRoutes == null) {
|
||||
return route;
|
||||
}
|
||||
// here could be multiple overlays between same points
|
||||
// It's better to remove them especially identical segments
|
||||
List<Way> allWays = getAllWays(incompleteRoutes);
|
||||
|
||||
// 2. Get array of segments (each array size > 1):
|
||||
LinkedList<List<TransportStop>> stopSegments = parseRoutePartsToSegments(incompleteRoutes);
|
||||
|
||||
// 3. Merge segments and remove excess missingStops (when they are closer then MISSING_STOP_SEARCH_RADIUS):
|
||||
// + Check for missingStops. If they present in the middle/there more then one segment - we have a hole in the
|
||||
// map data
|
||||
List<List<TransportStop>> mergedSegments = combineSegmentsOfSameRoute(stopSegments);
|
||||
|
||||
// 4. Now we need to properly sort segments, proper sorting is minimizing distance between stops
|
||||
// So it is salesman problem, we have this solution at TspAnt, but if we know last or first segment we can solve
|
||||
// it straightforward
|
||||
List<TransportStop> firstSegment = null;
|
||||
List<TransportStop> lastSegment = null;
|
||||
for (List<TransportStop> l : mergedSegments) {
|
||||
if (!l.get(0).isMissingStop()) {
|
||||
firstSegment = l;
|
||||
}
|
||||
if (!l.get(l.size() - 1).isMissingStop()) {
|
||||
lastSegment = l;
|
||||
}
|
||||
}
|
||||
List<List<TransportStop>> sortedSegments = new ArrayList<List<TransportStop>>();
|
||||
if (firstSegment != null) {
|
||||
sortedSegments.add(firstSegment);
|
||||
mergedSegments.remove(firstSegment);
|
||||
while (!mergedSegments.isEmpty()) {
|
||||
List<TransportStop> last = sortedSegments.get(sortedSegments.size() - 1);
|
||||
List<TransportStop> add = findAndDeleteMinDistance(last.get(last.size() - 1).getLocation(),
|
||||
mergedSegments, true);
|
||||
sortedSegments.add(add);
|
||||
}
|
||||
|
||||
} else if (lastSegment != null) {
|
||||
sortedSegments.add(lastSegment);
|
||||
mergedSegments.remove(lastSegment);
|
||||
while (!mergedSegments.isEmpty()) {
|
||||
List<TransportStop> first = sortedSegments.get(0);
|
||||
List<TransportStop> add = findAndDeleteMinDistance(first.get(0).getLocation(), mergedSegments, false);
|
||||
sortedSegments.add(0, add);
|
||||
}
|
||||
} else {
|
||||
sortedSegments = mergedSegments;
|
||||
}
|
||||
List<TransportStop> finalList = new ArrayList<TransportStop>();
|
||||
for (List<TransportStop> s : sortedSegments) {
|
||||
finalList.addAll(s);
|
||||
}
|
||||
// 5. Create combined TransportRoute and return it
|
||||
return new TransportRoute(route, finalList, allWays);
|
||||
}
|
||||
|
||||
private List<TransportStop> findAndDeleteMinDistance(LatLon location, List<List<TransportStop>> mergedSegments,
|
||||
boolean attachToBegin) {
|
||||
int ind = attachToBegin ? 0 : mergedSegments.get(0).size() - 1;
|
||||
double minDist = MapUtils.getDistance(mergedSegments.get(0).get(ind).getLocation(), location);
|
||||
int minInd = 0;
|
||||
for (int i = 1; i < mergedSegments.size(); i++) {
|
||||
ind = attachToBegin ? 0 : mergedSegments.get(i).size() - 1;
|
||||
double dist = MapUtils.getDistance(mergedSegments.get(i).get(ind).getLocation(), location);
|
||||
if (dist < minDist) {
|
||||
minInd = i;
|
||||
}
|
||||
}
|
||||
return mergedSegments.remove(minInd);
|
||||
}
|
||||
|
||||
private List<Way> getAllWays(List<TransportRoute> parts) {
|
||||
List<Way> w = new ArrayList<Way>();
|
||||
for (TransportRoute t : parts) {
|
||||
w.addAll(t.getForwardWays());
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
private List<List<TransportStop>> combineSegmentsOfSameRoute(LinkedList<List<TransportStop>> segments) {
|
||||
List<List<TransportStop>> resultSegments = new ArrayList<List<TransportStop>>();
|
||||
while (!segments.isEmpty()) {
|
||||
List<TransportStop> firstSegment = segments.poll();
|
||||
boolean merged = true;
|
||||
while (merged) {
|
||||
merged = false;
|
||||
Iterator<List<TransportStop>> it = segments.iterator();
|
||||
while (it.hasNext()) {
|
||||
List<TransportStop> segmentToMerge = it.next();
|
||||
merged = tryToMerge(firstSegment, segmentToMerge);
|
||||
|
||||
if (merged) {
|
||||
it.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
resultSegments.add(firstSegment);
|
||||
}
|
||||
return resultSegments;
|
||||
}
|
||||
|
||||
private boolean tryToMerge(List<TransportStop> firstSegment, List<TransportStop> segmentToMerge) {
|
||||
if (firstSegment.size() < 2 || segmentToMerge.size() < 2) {
|
||||
return false;
|
||||
}
|
||||
// 1st we check that segments overlap by stop
|
||||
int commonStopFirst = 0;
|
||||
int commonStopSecond = 0;
|
||||
boolean found = false;
|
||||
for (; commonStopFirst < firstSegment.size(); commonStopFirst++) {
|
||||
for (commonStopSecond = 0; commonStopSecond < segmentToMerge.size() && !found; commonStopSecond++) {
|
||||
long lid1 = firstSegment.get(commonStopFirst).getId();
|
||||
long lid2 = segmentToMerge.get(commonStopSecond).getId();
|
||||
if (lid1 > 0 && lid2 == lid1) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
// important to increment break inside loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found && commonStopFirst < firstSegment.size()) {
|
||||
// we've found common stop so we can merge based on stops
|
||||
// merge last part first
|
||||
int leftPartFirst = firstSegment.size() - commonStopFirst;
|
||||
int leftPartSecond = segmentToMerge.size() - commonStopSecond;
|
||||
if (leftPartFirst < leftPartSecond
|
||||
|| (leftPartFirst == leftPartSecond && firstSegment.get(firstSegment.size() - 1).isMissingStop())) {
|
||||
while (firstSegment.size() > commonStopFirst) {
|
||||
firstSegment.remove(firstSegment.size() - 1);
|
||||
}
|
||||
for (int i = commonStopSecond; i < segmentToMerge.size(); i++) {
|
||||
firstSegment.add(segmentToMerge.get(i));
|
||||
}
|
||||
}
|
||||
// merge first part
|
||||
if (commonStopFirst < commonStopSecond
|
||||
|| (commonStopFirst == commonStopSecond && firstSegment.get(0).isMissingStop())) {
|
||||
for (int i = 0; i < commonStopFirst; i++) {
|
||||
firstSegment.remove(0);
|
||||
}
|
||||
for (int i = commonStopSecond; i >= 0; i--) {
|
||||
firstSegment.add(0, segmentToMerge.get(i));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
// no common stops, so try to connect to the end or beginning
|
||||
// beginning
|
||||
boolean merged = false;
|
||||
if (MapUtils.getDistance(firstSegment.get(0).getLocation(),
|
||||
segmentToMerge.get(segmentToMerge.size() - 1).getLocation()) < MISSING_STOP_SEARCH_RADIUS) {
|
||||
firstSegment.remove(0);
|
||||
for (int i = segmentToMerge.size() - 2; i >= 0; i--) {
|
||||
firstSegment.add(0, segmentToMerge.get(i));
|
||||
}
|
||||
merged = true;
|
||||
} else if (MapUtils.getDistance(firstSegment.get(firstSegment.size() - 1).getLocation(),
|
||||
segmentToMerge.get(0).getLocation()) < MISSING_STOP_SEARCH_RADIUS) {
|
||||
firstSegment.remove(firstSegment.size() - 1);
|
||||
for (int i = 1; i < segmentToMerge.size(); i++) {
|
||||
firstSegment.add(segmentToMerge.get(i));
|
||||
}
|
||||
merged = true;
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
private LinkedList<List<TransportStop>> parseRoutePartsToSegments(List<TransportRoute> routeParts) {
|
||||
LinkedList<List<TransportStop>> segs = new LinkedList<List<TransportStop>>();
|
||||
// here we assume that missing stops come in pairs <A, B, C, MISSING, MISSING, D, E...>
|
||||
// we don't add segments with 1 stop cause they are irrelevant further
|
||||
for (TransportRoute part : routeParts) {
|
||||
List<TransportStop> newSeg = new ArrayList<TransportStop>();
|
||||
for (TransportStop s : part.getForwardStops()) {
|
||||
newSeg.add(s);
|
||||
if (s.isMissingStop()) {
|
||||
if (newSeg.size() > 1) {
|
||||
segs.add(newSeg);
|
||||
newSeg = new ArrayList<TransportStop>();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newSeg.size() > 1) {
|
||||
segs.add(newSeg);
|
||||
}
|
||||
}
|
||||
return segs;
|
||||
}
|
||||
|
||||
private List<TransportRoute> findIncompleteRouteParts(TransportRoute baseRoute) throws IOException {
|
||||
List<TransportRoute> allRoutes = null;
|
||||
for (BinaryMapIndexReader bmir : routesFilesCache.keySet()) {
|
||||
// here we could limit routeMap indexes by only certain bbox around start / end (check comment on field)
|
||||
IncompleteTransportRoute ptr = bmir.getIncompleteTransportRoutes().get(baseRoute.getId());
|
||||
if (ptr != null) {
|
||||
TIntArrayList lst = new TIntArrayList();
|
||||
while (ptr != null) {
|
||||
lst.add(ptr.getRouteOffset());
|
||||
ptr = ptr.getNextLinkedRoute();
|
||||
}
|
||||
if (lst.size() > 0) {
|
||||
if (allRoutes == null) {
|
||||
allRoutes = new ArrayList<TransportRoute>();
|
||||
}
|
||||
allRoutes.addAll(bmir.getTransportRoutes(lst.toArray()).valueCollection());
|
||||
}
|
||||
}
|
||||
}
|
||||
return allRoutes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,19 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item>
|
||||
<inset>
|
||||
<shape android:shape="oval" >
|
||||
<solid android:color="@color/feature_purchased_bg" />
|
||||
</shape>
|
||||
</inset>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@drawable/ic_action_marker_passed" />
|
||||
</item>
|
||||
<item>
|
||||
<inset>
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/feature_purchased_bg" />
|
||||
</shape>
|
||||
</inset>
|
||||
</item>
|
||||
|
||||
<item android:drawable="@drawable/ic_action_marker_passed" />
|
||||
|
||||
</layer-list>
|
|
@ -1,23 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/color_transparent"/>
|
||||
<size
|
||||
android:width="28dp"
|
||||
android:height="28dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:id="@+id/placeholder_icon"
|
||||
android:bottom="2dp"
|
||||
android:drawable="@drawable/ic_action_history"
|
||||
android:left="2dp"
|
||||
android:right="2dp"
|
||||
android:top="2dp">
|
||||
android:top="2dp" />
|
||||
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@drawable/ic_action_history"
|
||||
android:tint="@color/icon_color_default_light"/>
|
||||
</item>
|
||||
</layer-list>
|
||||
</layer-list>
|
|
@ -2,22 +2,20 @@
|
|||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/color_transparent"/>
|
||||
<solid android:color="@color/color_transparent" />
|
||||
<size
|
||||
android:width="28dp"
|
||||
android:height="28dp"/>
|
||||
android:height="28dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:id="@+id/placeholder_icon"
|
||||
android:bottom="2dp"
|
||||
android:drawable="@drawable/ic_action_placeholder_city"
|
||||
android:left="2dp"
|
||||
android:right="2dp"
|
||||
android:top="2dp">
|
||||
android:tint="@color/icon_color_default_light"
|
||||
android:top="2dp" />
|
||||
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@drawable/ic_action_placeholder_city"
|
||||
android:tint="@color/icon_color_default_light"/>
|
||||
</item>
|
||||
</layer-list>
|
||||
</layer-list>
|
|
@ -159,7 +159,7 @@ public class TransportStopController extends MenuController {
|
|||
|
||||
private void addTransportStopRoutes(OsmandApplication app, List<TransportStop> stops, List<TransportStopRoute> routes, boolean useEnglishNames) {
|
||||
for (TransportStop tstop : stops) {
|
||||
if (tstop.hasReferencesToRoutesMap()) {
|
||||
if (!tstop.isDeleted()) {
|
||||
addRoutes(app, routes, useEnglishNames, tstop, transportStop, (int) MapUtils.getDistance(tstop.getLocation(), transportStop.getLocation()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ import net.osmand.plus.resources.AsyncLoadingThread.TileLoadDownloadRequest;
|
|||
import net.osmand.plus.srtmplugin.SRTMPlugin;
|
||||
import net.osmand.plus.views.OsmandMapLayer.DrawSettings;
|
||||
import net.osmand.router.TransportRoutePlanner.TransportRoutingContext;
|
||||
import net.osmand.router.TransportStopsRouteReader;
|
||||
import net.osmand.util.Algorithms;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
|
@ -208,7 +209,7 @@ public class ResourceManager {
|
|||
private final Map<String, RegionAddressRepository> addressMap = new ConcurrentHashMap<String, RegionAddressRepository>();
|
||||
protected final Map<String, AmenityIndexRepository> amenityRepositories = new ConcurrentHashMap<String, AmenityIndexRepository>();
|
||||
// protected final Map<String, BinaryMapIndexReader> routingMapFiles = new ConcurrentHashMap<String, BinaryMapIndexReader>();
|
||||
protected final Map<String, TransportIndexRepository> transportRepositories = new ConcurrentHashMap<String, TransportIndexRepository>();
|
||||
protected final Map<String, BinaryMapReaderResource> transportRepositories = new ConcurrentHashMap<String, BinaryMapReaderResource>();
|
||||
|
||||
protected final Map<String, String> indexFileNames = new ConcurrentHashMap<String, String>();
|
||||
protected final Map<String, String> basemapFileNames = new ConcurrentHashMap<String, String>();
|
||||
|
@ -742,7 +743,7 @@ public class ResourceManager {
|
|||
addressMap.put(f.getName(), rarb);
|
||||
}
|
||||
if (mapReader.hasTransportData()) {
|
||||
transportRepositories.put(f.getName(), new TransportIndexRepositoryBinary(resource));
|
||||
transportRepositories.put(f.getName(), resource);
|
||||
}
|
||||
// disable osmc for routing temporarily due to some bugs
|
||||
if (mapReader.containsRouteData() && (!f.getParentFile().equals(liveDir) ||
|
||||
|
@ -989,51 +990,31 @@ public class ResourceManager {
|
|||
|
||||
////////////////////////////////////////////// Working with transport ////////////////////////////////////////////////
|
||||
|
||||
public LinkedHashMap<String, TransportIndexRepository> getTransportRepositories() {
|
||||
private List<BinaryMapIndexReader> getTransportRepositories(double topLat, double leftLon, double bottomLat, double rightLon) {
|
||||
List<String> fileNames = new ArrayList<>(transportRepositories.keySet());
|
||||
Collections.sort(fileNames, Algorithms.getStringVersionComparator());
|
||||
LinkedHashMap<String, TransportIndexRepository> res = new LinkedHashMap<>();
|
||||
List<BinaryMapIndexReader> res = new ArrayList<>();
|
||||
for (String fileName : fileNames) {
|
||||
TransportIndexRepository r = transportRepositories.get(fileName);
|
||||
if (r != null) {
|
||||
res.put(fileName, r);
|
||||
BinaryMapReaderResource r = transportRepositories.get(fileName);
|
||||
if (r != null && r.isUseForPublicTransport() &&
|
||||
r.getShallowReader().containTransportData(topLat, leftLon, bottomLat, rightLon)) {
|
||||
res.add(r.getReader(BinaryMapReaderResourceType.TRANSPORT));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public List<TransportIndexRepository> searchTransportRepositories(double latitude, double longitude) {
|
||||
List<TransportIndexRepository> repos = new ArrayList<>();
|
||||
for (TransportIndexRepository index : getTransportRepositories().values()) {
|
||||
if (index.isUseForPublicTransport() && index.checkContains(latitude,longitude)) {
|
||||
repos.add(index);
|
||||
}
|
||||
}
|
||||
return repos;
|
||||
}
|
||||
|
||||
public List<TransportStop> searchTransportSync(double topLat, double leftLon, double bottomLat, double rightLon,
|
||||
ResultMatcher<TransportStop> matcher) throws IOException {
|
||||
List<TransportIndexRepository> repos = new ArrayList<>();
|
||||
TLongObjectHashMap<TransportStop> loadedTransportStops = new TLongObjectHashMap<>();
|
||||
for (TransportIndexRepository index : getTransportRepositories().values()) {
|
||||
if (index.isUseForPublicTransport() && index.checkContains(topLat, leftLon, bottomLat, rightLon)) {
|
||||
repos.add(index);
|
||||
}
|
||||
}
|
||||
if (!repos.isEmpty()) {
|
||||
for (TransportIndexRepository r : repos) {
|
||||
List<TransportStop> stops = new ArrayList<>();
|
||||
r.searchTransportStops(topLat, leftLon, bottomLat, rightLon, -1, stops, matcher);
|
||||
BinaryMapIndexReader reader = ((TransportIndexRepositoryBinary) r).getOpenFile();
|
||||
if (reader != null) {
|
||||
TransportRoutingContext.mergeTransportStops(reader, loadedTransportStops, stops, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
TransportStopsRouteReader readers =
|
||||
new TransportStopsRouteReader(getTransportRepositories(topLat, leftLon, bottomLat, rightLon));
|
||||
List<TransportStop> stops = new ArrayList<>();
|
||||
for (TransportStop s : loadedTransportStops.valueCollection()) {
|
||||
if (!s.isDeleted()) {
|
||||
BinaryMapIndexReader.SearchRequest<TransportStop> req = BinaryMapIndexReader.buildSearchTransportRequest(MapUtils.get31TileNumberX(leftLon),
|
||||
MapUtils.get31TileNumberX(rightLon), MapUtils.get31TileNumberY(topLat),
|
||||
MapUtils.get31TileNumberY(bottomLat), -1, stops);
|
||||
for (TransportStop s : readers.readMergedTransportStops(req)) {
|
||||
if (!s.isDeleted() && !s.isMissingStop()) {
|
||||
stops.add(s);
|
||||
}
|
||||
}
|
||||
|
@ -1041,19 +1022,11 @@ public class ResourceManager {
|
|||
}
|
||||
|
||||
public List<TransportRoute> getRoutesForStop(TransportStop stop) {
|
||||
List<TransportRoute> routes = new ArrayList<>();
|
||||
LinkedHashMap<String, TransportIndexRepository> repositories = getTransportRepositories();
|
||||
LinkedHashMap<String, int[]> referencesToRoutes = stop.getReferencesToRoutesMap();
|
||||
if (referencesToRoutes != null) {
|
||||
for (Entry<String, int[]> refs : referencesToRoutes.entrySet()) {
|
||||
TransportIndexRepository r = repositories.get(refs.getKey());
|
||||
if (r != null) {
|
||||
List<TransportRoute> rr = r.getRoutesForReferences(refs.getValue());
|
||||
routes.addAll(rr);
|
||||
}
|
||||
}
|
||||
List<TransportRoute> rts = stop.getRoutes();
|
||||
if(rts != null) {
|
||||
return rts;
|
||||
}
|
||||
return routes;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////// Working with map ////////////////////////////////////////////////
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package net.osmand.plus.resources;
|
||||
|
||||
import net.osmand.ResultMatcher;
|
||||
import net.osmand.data.TransportRoute;
|
||||
import net.osmand.data.TransportStop;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TransportIndexRepository {
|
||||
|
||||
public boolean checkContains(double latitude, double longitude);
|
||||
|
||||
public boolean checkContains(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude);
|
||||
|
||||
public boolean acceptTransportStop(TransportStop stop);
|
||||
|
||||
public void searchTransportStops(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude,
|
||||
int limit, List<TransportStop> stops, ResultMatcher<TransportStop> matcher);
|
||||
|
||||
public List<TransportRoute> getRoutesForStop(TransportStop stop);
|
||||
|
||||
public List<TransportRoute> getRoutesForReferences(int[] referencesToRoutes);
|
||||
|
||||
public boolean isUseForPublicTransport();
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
package net.osmand.plus.resources;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.osmand.PlatformUtil;
|
||||
import net.osmand.ResultMatcher;
|
||||
import net.osmand.binary.BinaryMapIndexReader;
|
||||
import net.osmand.data.TransportRoute;
|
||||
import net.osmand.data.TransportStop;
|
||||
import net.osmand.plus.resources.ResourceManager.BinaryMapReaderResource;
|
||||
import net.osmand.plus.resources.ResourceManager.BinaryMapReaderResourceType;
|
||||
import net.osmand.util.Algorithms;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class TransportIndexRepositoryBinary implements TransportIndexRepository {
|
||||
private static final Log log = PlatformUtil.getLog(TransportIndexRepositoryBinary.class);
|
||||
private BinaryMapReaderResource resource;
|
||||
|
||||
public TransportIndexRepositoryBinary(BinaryMapReaderResource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BinaryMapIndexReader getOpenFile() {
|
||||
return resource.getReader(BinaryMapReaderResourceType.TRANSPORT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkContains(double latitude, double longitude) {
|
||||
BinaryMapIndexReader shallowReader = resource.getShallowReader();
|
||||
return shallowReader != null && shallowReader.containTransportData(latitude, longitude);
|
||||
}
|
||||
@Override
|
||||
public boolean checkContains(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude) {
|
||||
BinaryMapIndexReader shallowReader = resource.getShallowReader();
|
||||
return shallowReader != null && shallowReader.containTransportData(topLatitude, leftLongitude, bottomLatitude, rightLongitude);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void searchTransportStops(double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude,
|
||||
int limit, List<TransportStop> stops, ResultMatcher<TransportStop> matcher) {
|
||||
long now = System.currentTimeMillis();
|
||||
try {
|
||||
BinaryMapIndexReader reader = getOpenFile();
|
||||
if (reader != null) {
|
||||
reader.searchTransportIndex(BinaryMapIndexReader.buildSearchTransportRequest(MapUtils.get31TileNumberX(leftLongitude),
|
||||
MapUtils.get31TileNumberX(rightLongitude), MapUtils.get31TileNumberY(topLatitude),
|
||||
MapUtils.get31TileNumberY(bottomLatitude), limit, stops));
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Search for %s done in %s ms found %s.", //$NON-NLS-1$
|
||||
topLatitude + " " + leftLongitude, System.currentTimeMillis() - now, stops.size())); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Disk error ", e); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<TransportRoute> getRoutesForStop(TransportStop stop) {
|
||||
return getRoutesForReferences(stop.getReferencesToRoutes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TransportRoute> getRoutesForReferences(int[] referencesToRoutes) {
|
||||
try {
|
||||
BinaryMapIndexReader reader = getOpenFile();
|
||||
if (reader != null) {
|
||||
Collection<TransportRoute> res = reader.getTransportRoutes(referencesToRoutes).valueCollection();
|
||||
if (res != null) {
|
||||
List<TransportRoute> lst = new ArrayList<>(res);
|
||||
Collections.sort(lst, new Comparator<TransportRoute>() {
|
||||
@Override
|
||||
public int compare(TransportRoute o1, TransportRoute o2) {
|
||||
int i1 = Algorithms.extractFirstIntegerNumber(o1.getRef());
|
||||
int i2 = Algorithms.extractFirstIntegerNumber(o2.getRef());
|
||||
int r = Algorithms.compare(i1, i2);
|
||||
if (r == 0) {
|
||||
r = Algorithms.compare(o1.getName(), o2.getName());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
});
|
||||
return lst;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("Disk error ", e); //$NON-NLS-1$
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptTransportStop(TransportStop stop) {
|
||||
BinaryMapIndexReader shallowReader = resource.getShallowReader();
|
||||
return shallowReader != null && shallowReader.transportStopBelongsTo(stop);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseForPublicTransport() {
|
||||
return resource.isUseForPublicTransport();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue