commit
36ae49de65
10 changed files with 8132 additions and 2732 deletions
|
@ -3,6 +3,7 @@ package net.osmand.binary;
|
|||
|
||||
import com.google.protobuf.CodedInputStream;
|
||||
import com.google.protobuf.CodedOutputStream;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.WireFormat;
|
||||
|
||||
import net.osmand.Collator;
|
||||
|
@ -26,6 +27,7 @@ import net.osmand.binary.OsmandOdb.OsmAndMapIndex.MapRootLevel;
|
|||
import net.osmand.data.Amenity;
|
||||
import net.osmand.data.Building;
|
||||
import net.osmand.data.City;
|
||||
import net.osmand.data.IncompleteTransportRoute;
|
||||
import net.osmand.data.LatLon;
|
||||
import net.osmand.data.MapObject;
|
||||
import net.osmand.data.Street;
|
||||
|
@ -109,6 +111,7 @@ public class BinaryMapIndexReader {
|
|||
/*private*/ List<TransportIndex> transportIndexes = new ArrayList<TransportIndex>();
|
||||
/*private*/ List<RouteRegion> routingIndexes = new ArrayList<RouteRegion>();
|
||||
/*private*/ List<BinaryIndexPart> indexes = new ArrayList<BinaryIndexPart>();
|
||||
TLongObjectHashMap<IncompleteTransportRoute> incompleteTransportRoutes = null;
|
||||
|
||||
protected CodedInputStream codedIS;
|
||||
|
||||
|
@ -2634,5 +2637,18 @@ public class BinaryMapIndexReader {
|
|||
}
|
||||
}
|
||||
|
||||
public TLongObjectHashMap<IncompleteTransportRoute> getIncompleteTransportRoutes() throws InvalidProtocolBufferException, IOException {
|
||||
if (incompleteTransportRoutes == null) {
|
||||
incompleteTransportRoutes = new TLongObjectHashMap<>();
|
||||
for (TransportIndex ti : transportIndexes) {
|
||||
if (ti.incompleteRoutesLength > 0) {
|
||||
transportAdapter.readIncompleteRoutesList(incompleteTransportRoutes, ti.incompleteRoutesLength,
|
||||
ti.incompleteRoutesOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
return incompleteTransportRoutes;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import com.google.protobuf.CodedInputStream;
|
|||
import com.google.protobuf.WireFormat;
|
||||
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
import gnu.trove.map.hash.TLongObjectHashMap;
|
||||
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
|
||||
import net.osmand.data.TransportSchedule;
|
||||
import net.osmand.data.TransportStop;
|
||||
|
@ -43,6 +44,8 @@ public class BinaryMapTransportReaderAdapter {
|
|||
|
||||
int stopsFileOffset = 0;
|
||||
int stopsFileLength = 0;
|
||||
int incompleteRoutesOffset = 0;
|
||||
int incompleteRoutesLength = 0;
|
||||
|
||||
public String getPartName() {
|
||||
return "Transport";
|
||||
|
@ -68,6 +71,7 @@ public class BinaryMapTransportReaderAdapter {
|
|||
return bottom;
|
||||
}
|
||||
|
||||
|
||||
IndexStringTable stringTable = null;
|
||||
}
|
||||
|
||||
|
@ -108,6 +112,12 @@ public class BinaryMapTransportReaderAdapter {
|
|||
ind.stringTable = st;
|
||||
codedIS.seek(st.length + st.fileOffset);
|
||||
break;
|
||||
case OsmandOdb.OsmAndTransportIndex.INCOMPLETEROUTES_FIELD_NUMBER :
|
||||
ind.incompleteRoutesLength = codedIS.readRawVarint32();
|
||||
ind.incompleteRoutesOffset = codedIS.getTotalBytesRead();
|
||||
codedIS.seek(ind.incompleteRoutesLength + ind.incompleteRoutesOffset);
|
||||
break;
|
||||
|
||||
default:
|
||||
skipUnknownField(t);
|
||||
break;
|
||||
|
@ -115,6 +125,7 @@ public class BinaryMapTransportReaderAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private void readTransportBounds(TransportIndex ind) throws IOException {
|
||||
while(true){
|
||||
int t = codedIS.readTag();
|
||||
|
@ -240,6 +251,79 @@ public class BinaryMapTransportReaderAdapter {
|
|||
return ((char) i)+"";
|
||||
}
|
||||
|
||||
public void readIncompleteRoutesList(TLongObjectHashMap<net.osmand.data.IncompleteTransportRoute> incompleteRoutes,
|
||||
int length, int offset) throws IOException {
|
||||
codedIS.seek(offset);
|
||||
boolean end = false;
|
||||
while (!end) {
|
||||
int t = codedIS.readTag();
|
||||
int tag = WireFormat.getTagFieldNumber(t);
|
||||
switch (tag) {
|
||||
case 0:
|
||||
end = true;
|
||||
break;
|
||||
case OsmandOdb.IncompleteTransportRoutes.ROUTES_FIELD_NUMBER:
|
||||
int l = codedIS.readRawVarint32();
|
||||
int olds = codedIS.pushLimit(l);
|
||||
net.osmand.data.IncompleteTransportRoute ir = readIncompleteRoute();
|
||||
net.osmand.data.IncompleteTransportRoute itr = incompleteRoutes.get(ir.getRouteId());
|
||||
if(itr != null) {
|
||||
itr.setNextLinkedRoute(ir);
|
||||
} else {
|
||||
incompleteRoutes.put(ir.getRouteId(), ir);
|
||||
}
|
||||
codedIS.popLimit(olds);
|
||||
break;
|
||||
default:
|
||||
skipUnknownField(t);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public net.osmand.data.IncompleteTransportRoute readIncompleteRoute() throws IOException {
|
||||
net.osmand.data.IncompleteTransportRoute dataObject = new net.osmand.data.IncompleteTransportRoute();
|
||||
boolean end = false;
|
||||
while(!end){
|
||||
int t = codedIS.readTag();
|
||||
int tag = WireFormat.getTagFieldNumber(t);
|
||||
switch (tag) {
|
||||
case 0:
|
||||
end = true;
|
||||
break;
|
||||
case OsmandOdb.IncompleteTransportRoute.ID_FIELD_NUMBER :
|
||||
dataObject.setRouteId(codedIS.readUInt64());
|
||||
break;
|
||||
case OsmandOdb.IncompleteTransportRoute.ROUTEREF_FIELD_NUMBER :
|
||||
dataObject.setRouteOffset(codedIS.readRawVarint32());
|
||||
break;
|
||||
case OsmandOdb.IncompleteTransportRoute.OPERATOR_FIELD_NUMBER :
|
||||
skipUnknownField(t);
|
||||
// dataObject.setOperator(regStr(stringTable));
|
||||
break;
|
||||
case OsmandOdb.IncompleteTransportRoute.REF_FIELD_NUMBER :
|
||||
skipUnknownField(t);
|
||||
// dataObject.setRef(regStr(stringTable));
|
||||
break;
|
||||
case OsmandOdb.IncompleteTransportRoute.TYPE_FIELD_NUMBER :
|
||||
skipUnknownField(t);
|
||||
// dataObject.setType(regStr(stringTable));
|
||||
break;
|
||||
case OsmandOdb.IncompleteTransportRoute.MISSINGSTOPS_FIELD_NUMBER :
|
||||
// dataObject.getMissingStops().add(codedIS.readSInt32()); //skip for now
|
||||
skipUnknownField(t);
|
||||
break;
|
||||
default:
|
||||
skipUnknownField(t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dataObject;
|
||||
}
|
||||
|
||||
public net.osmand.data.TransportRoute getTransportRoute(int filePointer, TIntObjectHashMap<String> stringTable,
|
||||
boolean onlyDescription) throws IOException {
|
||||
codedIS.seek(filePointer);
|
||||
|
@ -394,7 +478,6 @@ public class BinaryMapTransportReaderAdapter {
|
|||
codedIS.seek(ind.stringTable.fileOffset);
|
||||
int oldLimit = codedIS.pushLimit(ind.stringTable.length);
|
||||
int current = 0;
|
||||
int i = 0;
|
||||
while (codedIS.getBytesUntilLimit() > 0) {
|
||||
int t = codedIS.readTag();
|
||||
int tag = WireFormat.getTagFieldNumber(t);
|
||||
|
|
|
@ -26,7 +26,6 @@ import net.osmand.binary.OsmandIndex.PoiPart;
|
|||
import net.osmand.binary.OsmandIndex.RoutingPart;
|
||||
import net.osmand.binary.OsmandIndex.RoutingSubregion;
|
||||
import net.osmand.binary.OsmandIndex.TransportPart;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
|
@ -131,6 +130,10 @@ public class CachedOsmandIndexes {
|
|||
transport.setBottom(index.getBottom());
|
||||
transport.setStopsTableLength(index.stopsFileLength);
|
||||
transport.setStopsTableOffset(index.stopsFileOffset);
|
||||
// if(index.incompleteRoutesLength > 0) {
|
||||
transport.setIncompleteRoutesLength(index.incompleteRoutesLength);
|
||||
transport.setIncompleteRoutesOffset(index.incompleteRoutesOffset);
|
||||
// }
|
||||
transport.setStringTableLength(index.stringTable.length);
|
||||
transport.setStringTableOffset(index.stringTable.fileOffset);
|
||||
fileIndex.addTransportIndex(transport);
|
||||
|
@ -269,6 +272,8 @@ public class CachedOsmandIndexes {
|
|||
mi.bottom = index.getBottom();
|
||||
mi.stopsFileLength = index.getStopsTableLength();
|
||||
mi.stopsFileOffset = index.getStopsTableOffset();
|
||||
mi.incompleteRoutesLength = index.getIncompleteRoutesLength();
|
||||
mi.incompleteRoutesOffset = index.getIncompleteRoutesOffset();
|
||||
mi.stringTable = new IndexStringTable();
|
||||
mi.stringTable.fileOffset = index.getStringTableOffset();
|
||||
mi.stringTable.length = index.getStringTableLength();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -44,8 +44,8 @@ public class DataTileManager<T> {
|
|||
return x;
|
||||
}
|
||||
|
||||
private void putObjects(int tx, int ty, List<T> r){
|
||||
if(objects.containsKey(evTile(tx, ty))){
|
||||
private void putObjects(int tx, int ty, List<T> r) {
|
||||
if (objects.containsKey(evTile(tx, ty))) {
|
||||
r.addAll(objects.get(evTile(tx, ty)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package net.osmand.data;
|
||||
|
||||
public class IncompleteTransportRoute {
|
||||
private long routeId;
|
||||
private int routeOffset = -1;
|
||||
private String operator;
|
||||
private String type;
|
||||
private String ref;
|
||||
private IncompleteTransportRoute nextLinkedRoute;
|
||||
|
||||
public IncompleteTransportRoute getNextLinkedRoute() {
|
||||
return nextLinkedRoute;
|
||||
}
|
||||
|
||||
public void setNextLinkedRoute(IncompleteTransportRoute nextLinkedRoute) {
|
||||
this.nextLinkedRoute = nextLinkedRoute;
|
||||
}
|
||||
|
||||
public long getRouteId() {
|
||||
return routeId;
|
||||
}
|
||||
|
||||
public void setRouteId(long routeId) {
|
||||
this.routeId = routeId;
|
||||
}
|
||||
|
||||
public int getRouteOffset() {
|
||||
return routeOffset;
|
||||
}
|
||||
|
||||
public void setRouteOffset(int routeOffset) {
|
||||
this.routeOffset = routeOffset;
|
||||
}
|
||||
|
||||
public String getOperator() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
public void setOperator(String operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getRef() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public void setRef(String ref) {
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,10 +1,5 @@
|
|||
package net.osmand.data;
|
||||
|
||||
import net.osmand.osm.edit.Node;
|
||||
import net.osmand.osm.edit.Way;
|
||||
import net.osmand.util.Algorithms;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
@ -12,6 +7,11 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.osmand.osm.edit.Node;
|
||||
import net.osmand.osm.edit.Way;
|
||||
import net.osmand.util.Algorithms;
|
||||
import net.osmand.util.MapUtils;
|
||||
|
||||
public class TransportRoute extends MapObject {
|
||||
private List<TransportStop> forwardStops = new ArrayList<TransportStop>();
|
||||
private String ref;
|
||||
|
@ -26,6 +26,20 @@ public class TransportRoute extends MapObject {
|
|||
public TransportRoute() {
|
||||
}
|
||||
|
||||
public TransportRoute(TransportRoute r, List<TransportStop> forwardStops, List<Way> forwardWay) {
|
||||
this.name = r.name;
|
||||
this.enName = r.enName;
|
||||
this.names = r.names;
|
||||
this.id = r.id;
|
||||
this.operator = r.operator;
|
||||
this.ref = r.ref;
|
||||
this.type = r.type;
|
||||
this.color = r.color;
|
||||
this.schedule = r.schedule;
|
||||
this.forwardStops = forwardStops;
|
||||
this.forwardWays = forwardWay;
|
||||
}
|
||||
|
||||
public TransportSchedule getSchedule() {
|
||||
return schedule;
|
||||
}
|
||||
|
@ -37,6 +51,14 @@ public class TransportRoute extends MapObject {
|
|||
return schedule;
|
||||
}
|
||||
|
||||
public boolean isIncomplete() {
|
||||
for (TransportStop s : forwardStops) {
|
||||
if (s.isMissingStop()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<TransportStop> getForwardStops() {
|
||||
return forwardStops;
|
||||
|
@ -70,7 +92,8 @@ public class TransportRoute extends MapObject {
|
|||
resortWaysToStopsOrder(forwardWays, forwardStops);
|
||||
}
|
||||
|
||||
public static void mergeRouteWays(List<Way> forwardWays) {
|
||||
// intrusive operation cause it changes ways itself!
|
||||
private static void mergeRouteWays(List<Way> forwardWays) {
|
||||
boolean changed = true;
|
||||
// combine as many ways as possible
|
||||
while (changed && forwardWays != null) {
|
||||
|
@ -137,7 +160,7 @@ public class TransportRoute extends MapObject {
|
|||
}
|
||||
}
|
||||
|
||||
public static Map<Way, int[]> resortWaysToStopsOrder(List<Way> forwardWays, List<TransportStop> forwardStops) {
|
||||
private static Map<Way, int[]> resortWaysToStopsOrder(List<Way> forwardWays, List<TransportStop> forwardStops) {
|
||||
final Map<Way, int[]> orderWays = new HashMap<Way, int[]>();
|
||||
if (forwardWays != null && forwardStops.size() > 0) {
|
||||
// resort ways to stops order
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.List;
|
|||
public class TransportStop extends MapObject {
|
||||
|
||||
private static final int DELETED_STOP = -1;
|
||||
public static final String MISSING_STOP_NAME = "#Missing Stop";
|
||||
|
||||
private int[] referencesToRoutes = null;
|
||||
private long[] deletedRoutesIds;
|
||||
|
@ -31,6 +32,10 @@ public class TransportStop extends MapObject {
|
|||
return routes;
|
||||
}
|
||||
|
||||
public boolean isMissingStop() {
|
||||
return MISSING_STOP_NAME.equals(getName());
|
||||
}
|
||||
|
||||
public LinkedHashMap<String, int[]> getReferencesToRoutesMap() {
|
||||
return referencesToRoutesMap;
|
||||
}
|
||||
|
|
|
@ -54,10 +54,14 @@ public class Relation extends Entity {
|
|||
}
|
||||
|
||||
public void addMember(Long id, EntityType type, String role){
|
||||
addMember(new EntityId(type, id), role);
|
||||
}
|
||||
|
||||
public void addMember(EntityId id, String role){
|
||||
if(members == null){
|
||||
members = new ArrayList<>();
|
||||
}
|
||||
members.add(new RelationMember(new EntityId(type, id), role));
|
||||
members.add(new RelationMember(id, role));
|
||||
}
|
||||
|
||||
public List<RelationMember> getMembers(String role) {
|
||||
|
@ -116,6 +120,15 @@ public class Relation extends Entity {
|
|||
return null;
|
||||
}
|
||||
|
||||
public void update(RelationMember r, EntityId newEntityId) {
|
||||
r.entity = null;
|
||||
r.entityId = newEntityId;
|
||||
}
|
||||
|
||||
public void updateRole(RelationMember r, String newRole) {
|
||||
r.role = newRole;
|
||||
}
|
||||
|
||||
public boolean remove(EntityId key) {
|
||||
if(members != null) {
|
||||
Iterator<RelationMember> it = members.iterator();
|
||||
|
|
|
@ -5,20 +5,23 @@ import java.util.ArrayList;
|
|||
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.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;
|
||||
|
@ -32,6 +35,8 @@ 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;
|
||||
|
||||
|
@ -376,8 +381,13 @@ public class TransportRoutePlanner {
|
|||
return nodes;
|
||||
}
|
||||
|
||||
private static class SearchNodeInd {
|
||||
int ind = -1;
|
||||
Way way = null;
|
||||
double dist = MIN_DIST_STOP_TO_GEOMETRY;
|
||||
}
|
||||
|
||||
public List<Way> getGeometry() {
|
||||
List<Way> list = new ArrayList<>();
|
||||
route.mergeForwardWays();
|
||||
if (DISPLAY_FULL_SEGMENT_ROUTE) {
|
||||
System.out.println("TOTAL SEGMENTS: " + route.getForwardWays().size());
|
||||
|
@ -386,44 +396,69 @@ public class TransportRoutePlanner {
|
|||
}
|
||||
return route.getForwardWays();
|
||||
}
|
||||
List<Way> fw = route.getForwardWays();
|
||||
double minStart = 150;
|
||||
double minEnd = 150;
|
||||
LatLon str = getStart().getLocation();
|
||||
LatLon en = getEnd().getLocation();
|
||||
int endInd = -1;
|
||||
List<Node> res = new ArrayList<>();
|
||||
for (int i = 0; i < fw.size() ; i++) {
|
||||
List<Node> nodes = fw.get(i).getNodes();
|
||||
List<Way> ways = route.getForwardWays();
|
||||
|
||||
final LatLon startLoc = getStart().getLocation();
|
||||
final LatLon endLoc = getEnd().getLocation();
|
||||
SearchNodeInd startInd = new SearchNodeInd();
|
||||
SearchNodeInd endInd = new SearchNodeInd();
|
||||
for (int i = 0; i < ways.size() ; i++) {
|
||||
List<Node> nodes = ways.get(i).getNodes();
|
||||
for (int j = 0; j < nodes.size(); j++) {
|
||||
Node n = nodes.get(j);
|
||||
if (MapUtils.getDistance(str, n.getLatitude(), n.getLongitude()) < minStart) {
|
||||
minStart = MapUtils.getDistance(str, n.getLatitude(), n.getLongitude());
|
||||
res.clear();
|
||||
if (MapUtils.getDistance(startLoc, n.getLatitude(), n.getLongitude()) < startInd.dist) {
|
||||
startInd.dist = MapUtils.getDistance(startLoc, n.getLatitude(), n.getLongitude());
|
||||
startInd.ind = j;
|
||||
startInd.way = ways.get(i);
|
||||
}
|
||||
res.add(n);
|
||||
if (MapUtils.getDistance(en, n.getLatitude(), n.getLongitude()) < minEnd) {
|
||||
endInd = res.size();
|
||||
minEnd = MapUtils.getDistance(en, n.getLatitude(), n.getLongitude());
|
||||
if (MapUtils.getDistance(endLoc, n.getLatitude(), n.getLongitude()) < endInd.dist) {
|
||||
endInd.dist = MapUtils.getDistance(endLoc, n.getLatitude(), n.getLongitude());
|
||||
endInd.ind = j;
|
||||
endInd.way = ways.get(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
Way way;
|
||||
if (res.isEmpty() || endInd == -1) {
|
||||
way = new Way(STOPS_WAY_ID);
|
||||
boolean validOneWay = startInd.way != null && startInd.way == endInd.way && startInd.ind <= endInd.ind;
|
||||
if (validOneWay) {
|
||||
Way way = new Way(GEOMETRY_WAY_ID);
|
||||
for (int k = startInd.ind; k <= endInd.ind; k++) {
|
||||
way.addNode(startInd.way.getNodes().get(k));
|
||||
}
|
||||
return Collections.singletonList(way);
|
||||
}
|
||||
boolean validContinuation = startInd.way != null && endInd.way != null &&
|
||||
startInd.way != endInd.way;
|
||||
if (validContinuation) {
|
||||
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) {
|
||||
validContinuation = true;
|
||||
} else {
|
||||
validContinuation = false;
|
||||
}
|
||||
}
|
||||
if (validContinuation) {
|
||||
List<Way> two = new ArrayList<Way>();
|
||||
Way way = new Way(GEOMETRY_WAY_ID);
|
||||
for (int k = startInd.ind; k < startInd.way.getNodes().size(); k++) {
|
||||
way.addNode(startInd.way.getNodes().get(k));
|
||||
}
|
||||
two.add(way);
|
||||
way = new Way(GEOMETRY_WAY_ID);
|
||||
for (int k = 0; k <= endInd.ind; k++) {
|
||||
way.addNode(endInd.way.getNodes().get(k));
|
||||
}
|
||||
two.add(way);
|
||||
return two;
|
||||
}
|
||||
Way way = new Way(STOPS_WAY_ID);
|
||||
for (int i = start; i <= end; i++) {
|
||||
LatLon l = getStop(i).getLocation();
|
||||
Node n = new Node(l.getLatitude(), l.getLongitude(), -1);
|
||||
way.addNode(n);
|
||||
}
|
||||
} else {
|
||||
way = new Way(GEOMETRY_WAY_ID);
|
||||
for(int k = 0; k < res.size() && k < endInd; k++) {
|
||||
way.addNode(res.get(k));
|
||||
}
|
||||
}
|
||||
list.add(way);
|
||||
return list;
|
||||
return Collections.singletonList(way);
|
||||
}
|
||||
|
||||
public double getTravelDist() {
|
||||
|
@ -574,8 +609,8 @@ public class TransportRoutePlanner {
|
|||
if(aTime != -1) {
|
||||
arriveTime = String.format("and arrive at %s", formatTransporTime(aTime));
|
||||
}
|
||||
bld.append(String.format(" %d. %s: walk %.1f m to '%s' and travel %s to '%s' by %s %d stops %s\n",
|
||||
i + 1, s.route.getRef(), s.walkDist, s.getStart().getName(),
|
||||
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));
|
||||
|
@ -701,12 +736,17 @@ public class TransportRoutePlanner {
|
|||
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
|
||||
public final Map<BinaryMapIndexReader, TIntObjectHashMap<TransportRoute>> routeMap =
|
||||
new LinkedHashMap<BinaryMapIndexReader, TIntObjectHashMap<TransportRoute>>();
|
||||
|
||||
|
||||
// stats
|
||||
public long startCalcTime;
|
||||
public int visitedRoutesCount;
|
||||
|
@ -723,7 +763,6 @@ public class TransportRoutePlanner {
|
|||
|
||||
|
||||
|
||||
|
||||
public TransportRoutingContext(TransportRoutingConfiguration cfg, NativeLibrary library, BinaryMapIndexReader... readers) {
|
||||
this.cfg = cfg;
|
||||
walkRadiusIn31 = (int) (cfg.walkRadius / MapUtils.getTileDistanceWidth(31));
|
||||
|
@ -785,7 +824,7 @@ public class TransportRoutePlanner {
|
|||
|
||||
// could be global ?
|
||||
TLongObjectHashMap<TransportStop> loadedTransportStops = new TLongObjectHashMap<TransportStop>();
|
||||
TIntObjectHashMap<TransportRoute> localFileRoutes = new TIntObjectHashMap<>();
|
||||
TIntObjectHashMap<TransportRoute> localFileRoutes = new TIntObjectHashMap<>(); //reference, route
|
||||
for (BinaryMapIndexReader r : routeMap.keySet()) {
|
||||
sr.clearSearchResults();
|
||||
List<TransportStop> stops = r.searchTransportIndex(sr);
|
||||
|
@ -794,44 +833,49 @@ public class TransportRoutePlanner {
|
|||
mergeTransportStops(r, loadedTransportStops, stops, localFileRoutes, routeMap.get(r));
|
||||
|
||||
for (TransportStop stop : stops) {
|
||||
// skip missing stops
|
||||
if (stop.isMissingStop()) {
|
||||
continue;
|
||||
}
|
||||
long stopId = stop.getId();
|
||||
TransportStop multifileStop = loadedTransportStops.get(stopId);
|
||||
int[] rrs = stop.getReferencesToRoutes();
|
||||
if (multifileStop == stop) {
|
||||
// clear up so it won't be used as it is multi file stop
|
||||
stop.setReferencesToRoutes(null);
|
||||
} else {
|
||||
// add other routes
|
||||
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 route %d for stop %s", rr, stop));
|
||||
} else if (multifileStop == stop ||
|
||||
(!multifileStop.hasRoute(route.getId()) &&
|
||||
!multifileStop.isRouteDeleted(route.getId()))) {
|
||||
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(route.getId());
|
||||
multifileStop.addRoute(route);
|
||||
multifileStop.addRouteId(combinedRoute.getId());
|
||||
multifileStop.addRoute(combinedRoute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// There should go stops with complete routes:
|
||||
loadTransportSegments(loadedTransportStops.valueCollection(), lst);
|
||||
|
||||
readTime += System.nanoTime() - nanoTime;
|
||||
return lst;
|
||||
}
|
||||
|
||||
|
||||
public static List<TransportStop> mergeTransportStops(BinaryMapIndexReader reader,
|
||||
TLongObjectHashMap<TransportStop> loadedTransportStops,
|
||||
List<TransportStop> stops,
|
||||
TIntObjectHashMap<TransportRoute> localFileRoutes,
|
||||
TIntObjectHashMap<TransportRoute> loadedRoutes) throws IOException {
|
||||
TIntObjectHashMap<TransportRoute> loadedRoutes
|
||||
) throws IOException {
|
||||
TIntArrayList routesToLoad = new TIntArrayList();
|
||||
TIntArrayList localRoutesToLoad = new TIntArrayList();
|
||||
Iterator<TransportStop> it = stops.iterator();
|
||||
|
@ -849,7 +893,7 @@ public class TransportRoutePlanner {
|
|||
localRoutesToLoad.addAll(stop.getReferencesToRoutes());
|
||||
}
|
||||
} else if (multifileStop.isDeleted()){
|
||||
// stop has noting to load, so not needed
|
||||
// stop has nothing to load, so not needed
|
||||
it.remove();
|
||||
} else {
|
||||
if (delRIds != null) {
|
||||
|
@ -876,10 +920,10 @@ public class TransportRoutePlanner {
|
|||
}
|
||||
}
|
||||
routesToLoad.addAll(localRoutesToLoad);
|
||||
multifileStop.putReferencesToRoutes(reader.getFile().getName(), localRoutesToLoad.toArray());
|
||||
multifileStop.putReferencesToRoutes(reader.getFile().getName(), localRoutesToLoad.toArray()); //add valid stop and references to routes
|
||||
}
|
||||
|
||||
// load routes
|
||||
// load/combine routes
|
||||
if (routesToLoad.size() > 0) {
|
||||
routesToLoad.sort();
|
||||
TIntArrayList referencesToLoad = new TIntArrayList();
|
||||
|
@ -888,7 +932,7 @@ public class TransportRoutePlanner {
|
|||
while (itr.hasNext()) {
|
||||
int nxt = itr.next();
|
||||
if (p != nxt) {
|
||||
if (localFileRoutes != null && loadedRoutes != null && loadedRoutes.contains(nxt)) {
|
||||
if (localFileRoutes != null && loadedRoutes != null && loadedRoutes.contains(nxt)) { //check if
|
||||
localFileRoutes.put(nxt, loadedRoutes.get(nxt));
|
||||
} else {
|
||||
referencesToLoad.add(nxt);
|
||||
|
@ -904,6 +948,231 @@ public class TransportRoutePlanner {
|
|||
return stops;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 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) {
|
||||
if (s.isDeleted() || s.getRoutes() == null) {
|
||||
|
@ -1089,5 +1358,4 @@ public class TransportRoutePlanner {
|
|||
}
|
||||
return stops;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue