Update transport rotues
This commit is contained in:
parent
99503fdec5
commit
b456c41b34
1 changed files with 100 additions and 172 deletions
|
@ -8,10 +8,12 @@ import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.PriorityQueue;
|
import java.util.PriorityQueue;
|
||||||
|
|
||||||
|
|
||||||
import gnu.trove.iterator.TIntIterator;
|
import gnu.trove.iterator.TIntIterator;
|
||||||
import gnu.trove.list.array.TIntArrayList;
|
import gnu.trove.list.array.TIntArrayList;
|
||||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||||
|
@ -941,31 +943,33 @@ public class TransportRoutePlanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransportRoute combineRoute(TransportRoute route) throws IOException {
|
private TransportRoute combineRoute(TransportRoute route) throws IOException {
|
||||||
|
|
||||||
// 1. Get all available route parts;
|
// 1. Get all available route parts;
|
||||||
List<TransportRoute> incompleteRoutes = findIncompleteRouteParts(route);
|
List<TransportRoute> incompleteRoutes = findIncompleteRouteParts(route);
|
||||||
if (incompleteRoutes == null) {
|
if (incompleteRoutes == null) {
|
||||||
return route;
|
return route;
|
||||||
}
|
}
|
||||||
|
// here could be multiple overlays between same points
|
||||||
|
// It's better to remove them especially identical segments
|
||||||
|
List<Way> allWays = getAllWays(incompleteRoutes);
|
||||||
|
|
||||||
List<Way> allWays = getAllWays(incompleteRoutes); //TODO check ways for right order? Artifacts during drawing.
|
|
||||||
|
|
||||||
// 2. Get array of segments:
|
// 2. Get array of segments:
|
||||||
List<List<TransportStop>> segments = parseRoutePartsToSegments(incompleteRoutes);
|
LinkedList<List<TransportStop>> stopSegments = parseRoutePartsToSegments(incompleteRoutes);
|
||||||
Collections.sort(segments, compareSegsBySize);
|
|
||||||
|
|
||||||
// 3. Merge segments and remove excess missingStops (when they are closer then MISSING_STOP_SEARCH_RADIUS):
|
// 3. Merge segments and remove excess missingStops (when they are closer then MISSING_STOP_SEARCH_RADIUS):
|
||||||
// 4. Check for missingStops. If they present in the middle/there more then one segment - we have a hole in the map data
|
// + 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 = combineSegments(segments);
|
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
|
||||||
|
|
||||||
// 5. Create combined TransportRoute and return it
|
// TODO
|
||||||
if (mergedSegments.size() > 1 || mergedSegments.size() == 0) {
|
List<TransportStop> finalList = new ArrayList<TransportStop>();
|
||||||
// TODO sort incomplete routes (and remove missingStops) and merge into one?
|
for(List<TransportStop> s : mergedSegments) {
|
||||||
return route;
|
finalList.addAll(s);
|
||||||
} else {
|
|
||||||
return new TransportRoute(route, mergedSegments.get(0), allWays);
|
|
||||||
}
|
}
|
||||||
|
// 5. Create combined TransportRoute and return it
|
||||||
|
return new TransportRoute(route, finalList, allWays);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Way> getAllWays(List<TransportRoute> parts) {
|
private List<Way> getAllWays(List<TransportRoute> parts) {
|
||||||
|
@ -976,181 +980,105 @@ public class TransportRoutePlanner {
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
Comparator<List<TransportStop>> compareSegsBySize = new Comparator<List<TransportStop>>() {
|
|
||||||
public int compare(List<TransportStop> s1, List<TransportStop> s2) {
|
|
||||||
return -Integer.compare(s1.size(), s2.size());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private List<List<TransportStop>> combineSegments(List<List<TransportStop>> segments) {
|
|
||||||
List<List<TransportStop>> rawSegments = segments;
|
|
||||||
|
|
||||||
List<List<TransportStop>> partsToDelete = new ArrayList<List<TransportStop>>();
|
private List<List<TransportStop>> combineSegmentsOfSameRoute(LinkedList<List<TransportStop>> segments) {
|
||||||
List<List<TransportStop>> partsToReturn = new ArrayList<List<TransportStop>>();
|
List<List<TransportStop>> resultSegments = new ArrayList<List<TransportStop>>();
|
||||||
List<TransportStop> base;
|
while (!segments.isEmpty()) {
|
||||||
int startSize = 0;
|
List<TransportStop> firstSegment = segments.poll();
|
||||||
|
boolean merged = true;
|
||||||
Iterator<List<TransportStop>> segItr = rawSegments.iterator();
|
while (merged) {
|
||||||
while (segItr.hasNext()) {
|
merged = false;
|
||||||
startSize = rawSegments.size();
|
Iterator<List<TransportStop>> it = segments.iterator();
|
||||||
partsToDelete.clear();
|
while (it.hasNext()) {
|
||||||
partsToReturn.clear();
|
List<TransportStop> segmentToMerge = it.next();
|
||||||
base = segItr.next();
|
merged = tryToMerge(firstSegment, segmentToMerge);
|
||||||
segItr.remove();
|
if (merged) {
|
||||||
|
it.remove();
|
||||||
TransportStop firstStopMissing = base.get(0).isMissingStop() ? base.get(0) : null;
|
|
||||||
TransportStop lastStopMissing = base.get(base.size() - 1).isMissingStop() ? base.get(base.size() - 1)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
for (int i = 0; i < segments.size(); i++) {
|
|
||||||
// compare every other piece of route with base (largest part so far)
|
|
||||||
// and if it has common stops or close missing stops try to combine
|
|
||||||
List<TransportStop> candidate = rawSegments.get(i);
|
|
||||||
TransportStop cmss = candidate.get(0).isMissingStop() ? candidate.get(0) : null;
|
|
||||||
TransportStop cmse = candidate.get(candidate.size() - 1).isMissingStop()
|
|
||||||
? candidate.get(candidate.size() - 1)
|
|
||||||
: null;
|
|
||||||
int csStopCount = candidate.size();
|
|
||||||
if (cmss != null) {
|
|
||||||
csStopCount--;
|
|
||||||
}
|
|
||||||
if (cmse != null) {
|
|
||||||
csStopCount--;
|
|
||||||
}
|
|
||||||
int csSameStopsCount = 0;
|
|
||||||
for (TransportStop s : base) {
|
|
||||||
if (!s.isMissingStop()) {
|
|
||||||
for (TransportStop cs : candidate) {
|
|
||||||
if (!cs.isMissingStop() && s.getId().equals(cs.getId())) {
|
|
||||||
csSameStopsCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (csStopCount == csSameStopsCount) {
|
|
||||||
// all stops of candidate inside base, delete candidate
|
|
||||||
partsToDelete.add(candidate);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if (csSameStopsCount > 0 && firstStopMissing != null && lastStopMissing == null
|
|
||||||
&& cmse != null) {
|
|
||||||
// parts intersecting and we know what sides to connect, attach to start
|
|
||||||
base = mergeSegments(base, candidate, false);
|
|
||||||
|
|
||||||
} else if (csSameStopsCount > 0 && lastStopMissing != null && firstStopMissing == null
|
|
||||||
&& cmss != null) {
|
|
||||||
// parts intersecting and we know what sides to connect, attach to end
|
|
||||||
base = mergeSegments(base, candidate, true);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// check for missing stops in candidate and attach accordingly
|
|
||||||
double distStartToEnd = MISSING_STOP_SEARCH_RADIUS + 1;
|
|
||||||
double distEndToStart = MISSING_STOP_SEARCH_RADIUS + 1;
|
|
||||||
if (cmss != null && lastStopMissing != null) {
|
|
||||||
distStartToEnd = MapUtils.getDistance(cmss.getLocation(),
|
|
||||||
lastStopMissing.getLocation());
|
|
||||||
}
|
|
||||||
if (cmse != null && firstStopMissing != null) {
|
|
||||||
distEndToStart = MapUtils.getDistance(cmse.getLocation(),
|
|
||||||
firstStopMissing.getLocation());
|
|
||||||
}
|
|
||||||
if (distStartToEnd < distEndToStart && distStartToEnd <= MISSING_STOP_SEARCH_RADIUS) {
|
|
||||||
base = mergeSegments(base, candidate, true);
|
|
||||||
} else if (distEndToStart < distStartToEnd && distEndToStart <= MISSING_STOP_SEARCH_RADIUS) {
|
|
||||||
base = mergeSegments(base, candidate, false);
|
|
||||||
} else {
|
|
||||||
if (csSameStopsCount == 0) {
|
|
||||||
// it's OK, we should look for other parts first
|
|
||||||
partsToReturn.add(candidate);
|
|
||||||
System.out.println("Candidate is not connected to Base, continue search");
|
|
||||||
} else {
|
|
||||||
// it's not OK, if there is intersecting stops and too long distance between
|
|
||||||
// missingStops, there is some error in data
|
|
||||||
System.out.println("MERGING ERROR. THERE IS SOMETHING WRONG WITH DATA");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
partsToDelete.add(candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (List<TransportStop> p : partsToDelete) {
|
|
||||||
rawSegments.remove(p);
|
|
||||||
}
|
|
||||||
rawSegments.addAll(partsToReturn);
|
|
||||||
rawSegments.add(base);
|
|
||||||
//Check if all is merged:
|
|
||||||
if (rawSegments.size() == 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//If we still have several segments, but after iteration they number didn't dwindle,
|
|
||||||
//check if we still could merge some of them or do we have a hole in the data
|
|
||||||
boolean hasValidCandidate = false;
|
|
||||||
if (rawSegments.size() == startSize) {
|
|
||||||
for (int i = 0; i < rawSegments.size()-1; i++) {
|
|
||||||
TransportStop ms = rawSegments.get(i).get(0).isMissingStop() ? rawSegments.get(i).get(0) : null;
|
|
||||||
TransportStop me = rawSegments.get(i).get(rawSegments.get(i).size()-1).isMissingStop()
|
|
||||||
? rawSegments.get(i).get(rawSegments.get(i).size()-1) : null;
|
|
||||||
for (int j = 1; j < rawSegments.size(); j++) {
|
|
||||||
TransportStop cms = rawSegments.get(j).get(0).isMissingStop() ? rawSegments.get(j).get(0) : null;
|
|
||||||
TransportStop cme = rawSegments.get(j).get(rawSegments.get(j).size()-1).isMissingStop()
|
|
||||||
? rawSegments.get(j).get(rawSegments.get(j).size()-1) : null;
|
|
||||||
if (ms != null && cme != null && MapUtils.getDistance(ms.getLocation(), cme.getLocation()) <= MISSING_STOP_SEARCH_RADIUS) {
|
|
||||||
hasValidCandidate = true;
|
|
||||||
}
|
|
||||||
if (me != null && cms != null && MapUtils.getDistance(me.getLocation(), cms.getLocation()) <= MISSING_STOP_SEARCH_RADIUS) {
|
|
||||||
hasValidCandidate = true;
|
|
||||||
}
|
|
||||||
//we has at least one valid pair of segments for merging
|
|
||||||
if (hasValidCandidate) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
resultSegments.add(firstSegment);
|
||||||
}
|
}
|
||||||
//if we could not merge any more segments - break;
|
return resultSegments;
|
||||||
if (rawSegments.size() == 1 || (rawSegments.size() == startSize && !hasValidCandidate)) {
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
for(;commonStopFirst < firstSegment.size(); commonStopFirst++) {
|
||||||
|
for(; commonStopSecond < segmentToMerge.size(); commonStopSecond++ ) {
|
||||||
|
long lid1 = firstSegment.get(commonStopFirst).getId();
|
||||||
|
long lid2 = segmentToMerge.get(commonStopSecond).getId();
|
||||||
|
if(lid1 > 0 && lid2 == lid1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rawSegments;
|
}
|
||||||
|
if(commonStopFirst < firstSegment.size()) {
|
||||||
|
// we've found common stop so we can merge based on stops
|
||||||
|
// merge last part first
|
||||||
|
if(firstSegment.size() - commonStopFirst < segmentToMerge.size() - commonStopSecond) {
|
||||||
|
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) {
|
||||||
|
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 List<TransportStop> mergeSegments(List<TransportStop> base, List<TransportStop> candidate, boolean forward) {
|
|
||||||
List<TransportStop> result;
|
|
||||||
if (forward) {
|
|
||||||
result = new ArrayList<>(base.subList(0, base.size()-1));
|
|
||||||
for (int i = 1; i < candidate.size(); i++) {
|
|
||||||
if (!result.contains(candidate.get(i))) {
|
|
||||||
result.add(candidate.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = new ArrayList<>(candidate.subList(0, candidate.size()-1));
|
|
||||||
for (int i = 1; i < base.size(); i++) {
|
|
||||||
if (!result.contains(base.get(i))) {
|
|
||||||
result.add(base.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<List<TransportStop>> parseRoutePartsToSegments(List<TransportRoute> routeParts) {
|
|
||||||
List<List<TransportStop>> segs = new ArrayList<List<TransportStop>>();
|
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...>
|
||||||
|
// TODO check generation that are doubles
|
||||||
for (TransportRoute part : routeParts) {
|
for (TransportRoute part : routeParts) {
|
||||||
List<TransportStop> newSeg = new ArrayList<TransportStop>();
|
List<TransportStop> newSeg = new ArrayList<TransportStop>();
|
||||||
for (TransportStop s : part.getForwardStops()) {
|
for (TransportStop s : part.getForwardStops()) {
|
||||||
|
newSeg.add(s);
|
||||||
if (s.isMissingStop()) {
|
if (s.isMissingStop()) {
|
||||||
if (newSeg.isEmpty()) {
|
if (!newSeg.isEmpty()) {
|
||||||
newSeg.add(s);
|
|
||||||
} else {
|
|
||||||
newSeg.add(s);
|
|
||||||
segs.add(newSeg);
|
segs.add(newSeg);
|
||||||
newSeg = new ArrayList<TransportStop>();
|
newSeg = new ArrayList<TransportStop>();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
newSeg.add(s);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!newSeg.isEmpty()) {
|
if (!newSeg.isEmpty()) {
|
||||||
|
|
Loading…
Reference in a new issue