Merge pull request #759 from afischerdev/roundtrip

New Roundtrip function
This commit is contained in:
afischerdev 2025-03-31 11:10:38 +02:00 committed by GitHub
commit 063efcc3e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 705 additions and 34 deletions

View File

@ -0,0 +1,71 @@
package btools.router;
import btools.expressions.BExpressionContext;
public class AreaInfo {
final static int RESULT_TYPE_NONE = 0;
final static int RESULT_TYPE_ELEV50 = 1;
final static int RESULT_TYPE_GREEN = 4;
final static int RESULT_TYPE_RIVER = 5;
public int direction;
public int numForest = -1;
public int numRiver = -1;
public OsmNogoPolygon polygon;
public int ways = 0;
public int greenWays = 0;
public int riverWays = 0;
public double elevStart = 0;
public int elev50 = 0;
public AreaInfo(int dir) {
direction = dir;
}
void checkAreaInfo(BExpressionContext expctxWay, double elev, byte[] ab) {
ways++;
double test = elevStart - elev;
if (Math.abs(test) < 50) elev50++;
int[] ld2 = expctxWay.createNewLookupData();
expctxWay.decode(ld2, false, ab);
if (numForest != -1 && ld2[numForest] > 1) {
greenWays++;
}
if (numRiver != -1 && ld2[numRiver] > 1) {
riverWays++;
}
}
public int getElev50Weight() {
if (ways == 0) return 0;
return (int) (elev50 * 100. / ways);
}
public int getGreen() {
if (ways == 0) return 0;
return (int) (greenWays * 100. / ways);
}
public int getRiver() {
if (ways == 0) return 0;
return (int) (riverWays * 100. / ways);
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Area ").append(direction).append(" ").append(elevStart).append("m ways ").append(ways);
if (ways > 0) {
sb.append("\nArea ways <50m ").append(elev50).append(" ").append(getElev50Weight()).append("%");
sb.append("\nArea ways green ").append(greenWays).append(" ").append(getGreen()).append("%");
sb.append("\nArea ways river ").append(riverWays).append(" ").append(getRiver()).append("%");
}
return sb.toString();
}
}

View File

@ -0,0 +1,270 @@
package btools.router;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import btools.codec.DataBuffers;
import btools.codec.MicroCache;
import btools.expressions.BExpressionContextWay;
import btools.mapaccess.MatchedWaypoint;
import btools.mapaccess.NodesCache;
import btools.mapaccess.OsmFile;
import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmNodesMap;
import btools.mapaccess.PhysicalFile;
public class AreaReader {
File segmentFolder;
public void getDirectAllData(File folder, RoutingContext rc, OsmNodeNamed wp, int maxscale, BExpressionContextWay expctxWay, OsmNogoPolygon searchRect, List<AreaInfo> ais) {
this.segmentFolder = folder;
int div = 32;
int cellsize = 1000000 / div;
int scale = maxscale;
int count = 0;
int used = 0;
boolean checkBorder = maxscale > 7;
Map<Long, String> tiles = new TreeMap<>();
for (int idxLat = -scale; idxLat <= scale; idxLat++) {
for (int idxLon = -scale; idxLon <= scale; idxLon++) {
if (ignoreCenter(maxscale, idxLon, idxLat)) continue;
int tmplon = wp.ilon + cellsize * idxLon;
int tmplat = wp.ilat + cellsize * idxLat;
int lonDegree = tmplon / 1000000;
int latDegree = tmplat / 1000000;
int lonMod5 = (int) lonDegree % 5;
int latMod5 = (int) latDegree % 5;
int lon = (int) lonDegree - 180 - lonMod5;
String slon = lon < 0 ? "W" + (-lon) : "E" + lon;
int lat = (int) latDegree - 90 - latMod5;
String slat = lat < 0 ? "S" + (-lat) : "N" + lat;
String filenameBase = slon + "_" + slat;
int lonIdx = tmplon / cellsize;
int latIdx = tmplat / cellsize;
int subIdx = (latIdx - div * latDegree) * div + (lonIdx - div * lonDegree);
int subLonIdx = (lonIdx - div * lonDegree);
int subLatIdx = (latIdx - div * latDegree);
OsmNogoPolygon dataRect = new OsmNogoPolygon(true);
lon = lonDegree * 1000000;
lat = latDegree * 1000000;
int tmplon2 = lon + cellsize * (subLonIdx);
int tmplat2 = lat + cellsize * (subLatIdx);
dataRect.addVertex(tmplon2, tmplat2);
tmplon2 = lon + cellsize * (subLonIdx + 1);
tmplat2 = lat + cellsize * (subLatIdx);
dataRect.addVertex(tmplon2, tmplat2);
tmplon2 = lon + cellsize * (subLonIdx + 1);
tmplat2 = lat + cellsize * (subLatIdx + 1);
dataRect.addVertex(tmplon2, tmplat2);
tmplon2 = lon + cellsize * (subLonIdx);
tmplat2 = lat + cellsize * (subLatIdx + 1);
dataRect.addVertex(tmplon2, tmplat2);
boolean intersects = checkBorder && dataRect.intersects(searchRect.points.get(0).x, searchRect.points.get(0).y, searchRect.points.get(2).x, searchRect.points.get(2).y);
if (!intersects && checkBorder)
intersects = dataRect.intersects(searchRect.points.get(1).x, searchRect.points.get(1).y, searchRect.points.get(2).x, searchRect.points.get(3).y);
if (intersects) {
continue;
}
intersects = searchRect.intersects(dataRect.points.get(0).x, dataRect.points.get(0).y, dataRect.points.get(2).x, dataRect.points.get(2).y);
if (!intersects)
intersects = searchRect.intersects(dataRect.points.get(1).x, dataRect.points.get(1).y, dataRect.points.get(3).x, dataRect.points.get(3).y);
if (!intersects)
intersects = containsRect(searchRect, dataRect.points.get(0).x, dataRect.points.get(0).y, dataRect.points.get(2).x, dataRect.points.get(2).y);
if (!intersects) {
continue;
}
tiles.put(((long) tmplon) << 32 | tmplat, filenameBase);
count++;
}
}
List<Map.Entry<Long, String>> list = new ArrayList<>(tiles.entrySet());
Collections.sort(list, new Comparator<>() {
@Override
public int compare(Map.Entry<Long, String> e1, Map.Entry<Long, String> e2) {
return e1.getValue().compareTo(e2.getValue());
}
});
long maxmem = rc.memoryclass * 1024L * 1024L; // in MB
NodesCache nodesCache = new NodesCache(segmentFolder, expctxWay, rc.forceSecondaryData, maxmem, null, false);
PhysicalFile pf = null;
String lastFilenameBase = "";
DataBuffers dataBuffers = null;
try {
for (Map.Entry<Long, String> entry : list) {
OsmNode n = new OsmNode(entry.getKey());
// System.out.println("areareader set " + n.getILon() + "_" + n.getILat() + " " + entry.getValue());
String filenameBase = entry.getValue();
if (!filenameBase.equals(lastFilenameBase)) {
if (pf != null) pf.close();
lastFilenameBase = filenameBase;
File file = new File(segmentFolder, filenameBase + ".rd5");
dataBuffers = new DataBuffers();
pf = new PhysicalFile(file, dataBuffers, -1, -1);
}
if (getDirectData(pf, dataBuffers, n.getILon(), n.getILat(), rc, expctxWay, ais))
used++;
}
} catch (Exception e) {
System.err.println("AreaReader: after " + used + "/" + count + " " + e.getMessage());
ais.clear();
} finally {
if (pf != null)
try {
pf.close();
} catch (Exception ee) {
}
nodesCache.close();
}
}
public boolean getDirectData(PhysicalFile pf, DataBuffers dataBuffers, int inlon, int inlat, RoutingContext rc, BExpressionContextWay expctxWay, List<AreaInfo> ais) {
int lonDegree = inlon / 1000000;
int latDegree = inlat / 1000000;
OsmNodesMap nodesMap = new OsmNodesMap();
try {
int div = pf.divisor;
OsmFile osmf = new OsmFile(pf, lonDegree, latDegree, dataBuffers);
if (osmf.hasData()) {
int cellsize = 1000000 / div;
int tmplon = inlon;
int tmplat = inlat;
int lonIdx = tmplon / cellsize;
int latIdx = tmplat / cellsize;
MicroCache segment = osmf.createMicroCache(lonIdx, latIdx, dataBuffers, expctxWay, null, true, null);
if (segment != null) {
int size = segment.getSize();
for (int i = 0; i < size; i++) {
long id = segment.getIdForIndex(i);
OsmNode node = new OsmNode(id);
if (segment.getAndClear(id)) {
node.parseNodeBody(segment, nodesMap, expctxWay);
if (node.firstlink instanceof OsmLink) {
for (OsmLink link = node.firstlink; link != null; link = link.getNext(node)) {
OsmNode nextNode = link.getTarget(node);
if (nextNode.firstlink == null)
continue; // don't care about dead ends
if (nextNode.firstlink.descriptionBitmap == null)
continue;
for (AreaInfo ai : ais) {
if (ai.polygon.isWithin(node.ilon, node.ilat)) {
ai.checkAreaInfo(expctxWay, node.getElev(), nextNode.firstlink.descriptionBitmap);
break;
}
}
break;
}
}
}
}
}
return true;
}
} catch (Exception e) {
System.err.println("AreaReader: " + e.getMessage());
}
return false;
}
boolean ignoreCenter(int maxscale, int idxLon, int idxLat) {
int centerScale = (int) Math.round(maxscale * .2) - 1;
if (centerScale < 0) return false;
return idxLon >= -centerScale && idxLon <= centerScale &&
idxLat >= -centerScale && idxLat <= centerScale;
}
/*
in this case the polygon is 'only' a rectangle
*/
boolean containsRect(OsmNogoPolygon searchRect, int p1x, int p1y, int p2x, int p2y) {
return searchRect.isWithin((long) p1x, (long) p1y) &&
searchRect.isWithin(p2x, p2y);
}
public void writeAreaInfo(String filename, MatchedWaypoint wp, List<AreaInfo> ais) throws Exception {
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(filename)));
wp.writeToStream(dos);
for (AreaInfo ai : ais) {
dos.writeInt(ai.direction);
dos.writeDouble(ai.elevStart);
dos.writeInt(ai.ways);
dos.writeInt(ai.greenWays);
dos.writeInt(ai.riverWays);
dos.writeInt(ai.elev50);
}
dos.close();
}
public void readAreaInfo(File fai, MatchedWaypoint wp, List<AreaInfo> ais) {
DataInputStream dis = null;
MatchedWaypoint ep = null;
try {
dis = new DataInputStream(new BufferedInputStream(new FileInputStream(fai)));
ep = MatchedWaypoint.readFromStream(dis);
if (Math.abs(ep.waypoint.ilon - wp.waypoint.ilon) > 500 &&
Math.abs(ep.waypoint.ilat - wp.waypoint.ilat) > 500) {
return;
}
if (Math.abs(ep.radius - wp.radius) > 500) {
return;
}
for (int i = 0; i < 4; i++) {
int direction = dis.readInt();
AreaInfo ai = new AreaInfo(direction);
ai.elevStart = dis.readDouble();
ai.ways = dis.readInt();
ai.greenWays = dis.readInt();
ai.riverWays = dis.readInt();
ai.elev50 = dis.readInt();
ais.add(ai);
}
} catch (IOException e) {
ais.clear();
} finally {
if (dis != null) {
try {
dis.close();
} catch (IOException e) {
}
}
}
}
}

View File

@ -141,6 +141,10 @@ abstract class OsmPath implements OsmLinkHolder {
// evaluate the way tags
rc.expctxWay.evaluate(rc.inverseDirection ^ isReverse, description);
// and check if is useful
if (rc.ai != null && rc.ai.polygon.isWithin(lon1, lat1)) {
rc.ai.checkAreaInfo(rc.expctxWay, ele1/4., description);
}
// calculate the costfactor inputs
float costfactor = rc.expctxWay.getCostfactor();

View File

@ -36,6 +36,7 @@ public final class RoutingContext {
public Map<String, String> keyValues;
public String rawTrackPath;
public String rawAreaPath;
public String getProfileName() {
String name = localFunction == null ? "unknown" : localFunction;
@ -79,6 +80,8 @@ public final class RoutingContext {
public boolean useDynamicDistance;
public boolean buildBeelineOnRange;
public AreaInfo ai;
private void setModel(String className) {
if (className == null) {
pm = new StdModel();
@ -193,6 +196,10 @@ public final class RoutingContext {
public Integer startDirection;
public boolean startDirectionValid;
public boolean forceUseStartDirection;
public Integer roundTripDistance;
public Integer roundTripDirectionAdd;
public Integer roundTripPoints;
public boolean allowSamewayback;
public CheapAngleMeter anglemeter = new CheapAngleMeter();

View File

@ -7,6 +7,8 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@ -21,6 +23,7 @@ import btools.mapaccess.OsmLinkHolder;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmNodePairSet;
import btools.mapaccess.OsmPos;
import btools.util.CheapRuler;
import btools.util.CompactLongMap;
import btools.util.SortedHeap;
import btools.util.StackSampler;
@ -31,6 +34,7 @@ public class RoutingEngine extends Thread {
public final static int BROUTER_ENGINEMODE_SEED = 1;
public final static int BROUTER_ENGINEMODE_GETELEV = 2;
public final static int BROUTER_ENGINEMODE_GETINFO = 3;
public final static int BROUTER_ENGINEMODE_ROUNDTRIP = 4;
private NodesCache nodesCache;
private SortedHeap<OsmPath> openSet = new SortedHeap<>();
@ -48,7 +52,9 @@ public class RoutingEngine extends Thread {
private int engineMode = 0;
private int MAX_STEPS_CHECK = 10;
private int MAX_STEPS_CHECK = 500;
private int ROUNDTRIP_DEFAULT_DIRECTIONADD = 45;
private int MAX_DYNAMIC_RANGE = 60000;
@ -178,6 +184,11 @@ public class RoutingEngine extends Thread {
}
doGetInfo();
break;
case BROUTER_ENGINEMODE_ROUNDTRIP:
if (waypoints.size() < 1)
throw new IllegalArgumentException("we need one lat/lon point at least!");
doRoundTrip();
break;
default:
throw new IllegalArgumentException("not a valid engine mode");
}
@ -189,6 +200,26 @@ public class RoutingEngine extends Thread {
startTime = System.currentTimeMillis();
long startTime0 = startTime;
this.maxRunningTime = maxRunningTime;
if (routingContext.allowSamewayback) {
if (waypoints.size() == 2) {
OsmNodeNamed onn = new OsmNodeNamed(new OsmNode(waypoints.get(0).ilon, waypoints.get(0).ilat));
onn.name = "to";
waypoints.add(onn);
} else {
waypoints.get(waypoints.size() - 1).name = "via" + (waypoints.size() - 1) + "_center";
List<OsmNodeNamed> newpoints = new ArrayList<>();
for (int i = waypoints.size() - 2; i >= 0; i--) {
// System.out.println("back " + waypoints.get(i));
OsmNodeNamed onn = new OsmNodeNamed(new OsmNode(waypoints.get(i).ilon, waypoints.get(i).ilat));
onn.name = "via";
newpoints.add(onn);
}
newpoints.get(newpoints.size() - 1).name = "to";
waypoints.addAll(newpoints);
}
}
int nsections = waypoints.size() - 1;
OsmTrack[] refTracks = new OsmTrack[nsections]; // used ways for alternatives
OsmTrack[] lastTracks = new OsmTrack[nsections];
@ -196,6 +227,10 @@ public class RoutingEngine extends Thread {
List<String> messageList = new ArrayList<>();
for (int i = 0; ; i++) {
track = findTrack(refTracks, lastTracks);
// we are only looking for info
if (routingContext.ai != null) return;
track.message = "track-length = " + track.distance + " filtered ascend = " + track.ascend
+ " plain-ascend = " + track.plainAscend + " cost=" + track.cost;
if (track.energy != 0) {
@ -376,24 +411,24 @@ public class RoutingEngine extends Thread {
}
}
int otherIdx = 0;
if (minIdx == t.nodes.size()-1) {
otherIdx = minIdx-1;
if (minIdx == t.nodes.size() - 1) {
otherIdx = minIdx - 1;
} else {
otherIdx = minIdx+1;
otherIdx = minIdx + 1;
}
int otherdist = t.nodes.get(otherIdx).calcDistance(listOne.get(0).crosspoint);
int minSElev = t.nodes.get(minIdx).getSElev();
int otherSElev = t.nodes.get(otherIdx).getSElev();
int diffSElev = 0;
diffSElev = otherSElev - minSElev;
double diff = (double) mindist/(mindist + otherdist) * diffSElev;
double diff = (double) mindist / (mindist + otherdist) * diffSElev;
OsmNodeNamed n = new OsmNodeNamed(listOne.get(0).crosspoint);
n.name = wpt1.name;
n.selev = minIdx != -1 ? (short) (minSElev + (int) diff) : Short.MIN_VALUE;
if (engineMode == BROUTER_ENGINEMODE_GETINFO) {
n.nodeDescription = (start1 != null && start1.firstlink!=null ? start1.firstlink.descriptionBitmap : null);
n.nodeDescription = (start1 != null && start1.firstlink != null ? start1.firstlink.descriptionBitmap : null);
t.pois.add(n);
//t.message = "get_info";
//t.messageList.add(t.message);
@ -447,6 +482,215 @@ public class RoutingEngine extends Thread {
}
}
public void doRoundTrip() {
try {
long startTime = System.currentTimeMillis();
routingContext.useDynamicDistance = true;
double searchRadius = (routingContext.roundTripDistance == null ? 1500 :routingContext.roundTripDistance);
double direction = (routingContext.startDirection == null ? -1 :routingContext.startDirection);
double directionAdd = (routingContext.roundTripDirectionAdd == null ? ROUNDTRIP_DEFAULT_DIRECTIONADD :routingContext.roundTripDirectionAdd);
if (direction == -1) direction = getRandomDirectionFromData(waypoints.get(0), searchRadius);
if (routingContext.allowSamewayback) {
int[] pos = CheapRuler.destination(waypoints.get(0).ilon, waypoints.get(0).ilat, searchRadius, direction);
MatchedWaypoint wpt2 = new MatchedWaypoint();
wpt2.waypoint = new OsmNode(pos[0], pos[1]);
wpt2.name = "rt1_" + direction;
OsmNodeNamed onn = new OsmNodeNamed(new OsmNode(pos[0], pos[1]));
onn.name = "rt1";
waypoints.add(onn);
} else {
buildPointsFromCircle(waypoints, direction, searchRadius, routingContext.roundTripPoints == null ? 5 : routingContext.roundTripPoints);
}
routingContext.waypointCatchingRange = 250;
doRouting(0);
long endTime = System.currentTimeMillis();
logInfo("round trip execution time = " + (endTime - startTime) / 1000. + " seconds");
} catch (Exception e) {
e.getStackTrace();
logException(e);
}
}
void buildPointsFromCircle(List<OsmNodeNamed> waypoints, double startAngle, double searchRadius, int points) {
//startAngle -= 90;
for (int i = 1; i < points; i++) {
double anAngle = 90 - (180.0 * i / points);
int[] pos = CheapRuler.destination(waypoints.get(0).ilon, waypoints.get(0).ilat, searchRadius, startAngle - anAngle);
OsmNodeNamed onn = new OsmNodeNamed(new OsmNode(pos[0], pos[1]));
onn.name = "rt" + i;
waypoints.add(onn);
}
OsmNodeNamed onn = new OsmNodeNamed(waypoints.get(0));
onn.name = "to_rt";
waypoints.add(onn);
}
int getRandomDirectionFromData(OsmNodeNamed wp, double searchRadius) {
long start = System.currentTimeMillis();
int preferredRandomType = 0;
boolean consider_elevation = routingContext.expctxWay.getVariableValue("consider_elevation", 0f) == 1f;
boolean consider_forest = routingContext.expctxWay.getVariableValue("consider_forest", 0f) == 1f;
boolean consider_river = routingContext.expctxWay.getVariableValue("consider_river", 0f) == 1f;
if (consider_elevation) {
preferredRandomType = AreaInfo.RESULT_TYPE_ELEV50;
} else if (consider_forest) {
preferredRandomType = AreaInfo.RESULT_TYPE_GREEN;
} else if (consider_river) {
preferredRandomType = AreaInfo.RESULT_TYPE_RIVER;
} else {
return (int) (Math.random()*360);
}
MatchedWaypoint wpt1 = new MatchedWaypoint();
wpt1.waypoint = wp;
wpt1.name = "info";
wpt1.radius = searchRadius * 1.5;
List<AreaInfo> ais = new ArrayList<>();
AreaReader areareader = new AreaReader();
if (routingContext.rawAreaPath != null) {
File fai = new File(routingContext.rawAreaPath);
if (fai.exists()) {
areareader.readAreaInfo(fai, wpt1, ais);
}
}
if (ais.isEmpty()) {
List<MatchedWaypoint> listStart = new ArrayList<>();
listStart.add(wpt1);
List<OsmNodeNamed> wpliststart = new ArrayList<>();
wpliststart.add(wp);
List<OsmNodeNamed> listOne = new ArrayList<>();
for (int a = 45; a < 360; a += 90) {
int[] pos = CheapRuler.destination(wp.ilon, wp.ilat, searchRadius * 1.5, a);
OsmNodeNamed onn = new OsmNodeNamed(new OsmNode(pos[0], pos[1]));
onn.name = "via" + a;
listOne.add(onn);
MatchedWaypoint wpt = new MatchedWaypoint();
wpt.waypoint = onn;
wpt.name = onn.name;
listStart.add(wpt);
}
RoutingEngine re = null;
RoutingContext rc = new RoutingContext();
String name = routingContext.localFunction;
int idx = name.lastIndexOf(File.separator);
rc.localFunction = idx == -1 ? "dummy" : name.substring(0, idx + 1) + "dummy.brf";
re = new RoutingEngine(null, null, segmentDir, wpliststart, rc, BROUTER_ENGINEMODE_ROUNDTRIP);
rc.useDynamicDistance = true;
re.matchWaypointsToNodes(listStart);
re.resetCache(true);
int numForest = rc.expctxWay.getLookupKey("estimated_forest_class");
int numRiver = rc.expctxWay.getLookupKey("estimated_river_class");
OsmNode start1 = re.nodesCache.getStartNode(listStart.get(0).node1.getIdFromPos());
double elev = (start1 == null ? 0 : start1.getElev()); // listOne.get(0).crosspoint.getElev();
int maxlon = Integer.MIN_VALUE;
int minlon = Integer.MAX_VALUE;
int maxlat = Integer.MIN_VALUE;
int minlat = Integer.MAX_VALUE;
for (OsmNodeNamed on : listOne) {
maxlon = Math.max(on.ilon, maxlon);
minlon = Math.min(on.ilon, minlon);
maxlat = Math.max(on.ilat, maxlat);
minlat = Math.min(on.ilat, minlat);
}
OsmNogoPolygon searchRect = new OsmNogoPolygon(true);
searchRect.addVertex(maxlon, maxlat);
searchRect.addVertex(maxlon, minlat);
searchRect.addVertex(minlon, minlat);
searchRect.addVertex(minlon, maxlat);
for (int a = 0; a < 4; a++) {
rc.ai = new AreaInfo(a * 90 + 90);
rc.ai.elevStart = elev;
rc.ai.numForest = numForest;
rc.ai.numRiver = numRiver;
rc.ai.polygon = new OsmNogoPolygon(true);
rc.ai.polygon.addVertex(wp.ilon, wp.ilat);
rc.ai.polygon.addVertex(listOne.get(a).ilon, listOne.get(a).ilat);
if (a == 3)
rc.ai.polygon.addVertex(listOne.get(0).ilon, listOne.get(0).ilat);
else
rc.ai.polygon.addVertex(listOne.get(a + 1).ilon, listOne.get(a + 1).ilat);
ais.add(rc.ai);
}
int maxscale = Math.abs(searchRect.points.get(2).x - searchRect.points.get(0).x);
maxscale = Math.max(1, Math.round(maxscale / 31250f / 2) + 1);
areareader.getDirectAllData(segmentDir, rc, wp, maxscale, rc.expctxWay, searchRect, ais);
if (routingContext.rawAreaPath != null) {
try {
wpt1.radius = searchRadius * 1.5;
areareader.writeAreaInfo(routingContext.rawAreaPath, wpt1, ais);
} catch (Exception e) {
}
}
rc.ai = null;
}
logInfo("round trip execution time = " + (System.currentTimeMillis() - start) / 1000. + " seconds");
// for (AreaInfo ai: ais) {
// System.out.println("\n" + ai.toString());
//}
switch (preferredRandomType) {
case AreaInfo.RESULT_TYPE_ELEV50:
Collections.sort(ais, new Comparator<>() {
public int compare(AreaInfo o1, AreaInfo o2) {
return o2.getElev50Weight() - o1.getElev50Weight();
}
});
break;
case AreaInfo.RESULT_TYPE_GREEN:
Collections.sort(ais, new Comparator<>() {
public int compare(AreaInfo o1, AreaInfo o2) {
return o2.getGreen() - o1.getGreen();
}
});
break;
case AreaInfo.RESULT_TYPE_RIVER:
Collections.sort(ais, new Comparator<>() {
public int compare(AreaInfo o1, AreaInfo o2) {
return o2.getRiver() - o1.getRiver();
}
});
break;
default:
return (int) (Math.random()*360);
}
int angle = ais.get(0).direction;
return angle - 30 + (int) (Math.random() * 60);
}
private void postElevationCheck(OsmTrack track) {
OsmPathElement lastPt = null;
OsmPathElement startPt = null;
@ -644,16 +888,16 @@ public class RoutingEngine extends Thread {
waypoints.get(0).direct = true;
nUnmatched++;
} else {
waypoints.add(waypoints.size()-1, wp);
waypoints.get(waypoints.size()-2).direct = true;
waypoints.add(waypoints.size() - 1, wp);
waypoints.get(waypoints.size() - 2).direct = true;
nUnmatched++;
}
}
extraWaypoints = null;
}
if (lastTracks.length < waypoints.size()-1) {
refTracks = new OsmTrack[waypoints.size()-1]; // used ways for alternatives
lastTracks = new OsmTrack[waypoints.size()-1];
if (lastTracks.length < waypoints.size() - 1) {
refTracks = new OsmTrack[waypoints.size() - 1]; // used ways for alternatives
lastTracks = new OsmTrack[waypoints.size() - 1];
hasDirectRouting = true;
}
for (OsmNodeNamed wp : waypoints) {
@ -688,13 +932,14 @@ public class RoutingEngine extends Thread {
int startSize = matchedWaypoints.size();
matchWaypointsToNodes(matchedWaypoints);
if (startSize < matchedWaypoints.size()) {
refTracks = new OsmTrack[matchedWaypoints.size()-1]; // used ways for alternatives
lastTracks = new OsmTrack[matchedWaypoints.size()-1];
refTracks = new OsmTrack[matchedWaypoints.size() - 1]; // used ways for alternatives
lastTracks = new OsmTrack[matchedWaypoints.size() - 1];
hasDirectRouting = true;
}
for (MatchedWaypoint mwp : matchedWaypoints) {
if (hasInfo() && matchedWaypoints.size() != nUnmatched) logInfo("new wp=" + mwp.waypoint + " " + mwp.crosspoint + (mwp.direct ? " direct" : ""));
if (hasInfo() && matchedWaypoints.size() != nUnmatched)
logInfo("new wp=" + mwp.waypoint + " " + mwp.crosspoint + (mwp.direct ? " direct" : ""));
}
routingContext.checkMatchedWaypointAgainstNogos(matchedWaypoints);
@ -724,9 +969,9 @@ public class RoutingEngine extends Thread {
matchedWaypoints.add(nearbyTrack.endPoint);
}
} else {
if (lastTracks.length < matchedWaypoints.size()-1) {
refTracks = new OsmTrack[matchedWaypoints.size()-1]; // used ways for alternatives
lastTracks = new OsmTrack[matchedWaypoints.size()-1];
if (lastTracks.length < matchedWaypoints.size() - 1) {
refTracks = new OsmTrack[matchedWaypoints.size() - 1]; // used ways for alternatives
lastTracks = new OsmTrack[matchedWaypoints.size() - 1];
hasDirectRouting = true;
}
}
@ -750,12 +995,26 @@ public class RoutingEngine extends Thread {
} else {
seg = searchTrack(matchedWaypoints.get(i), matchedWaypoints.get(i + 1), i == matchedWaypoints.size() - 2 ? nearbyTrack : null, refTracks[i]);
wptIndex = i;
if (engineMode == BROUTER_ENGINEMODE_ROUNDTRIP) {
if (i < matchedWaypoints.size() - 2) {
OsmNode lastPoint = seg.containsNode(matchedWaypoints.get(i+1).node1) ? matchedWaypoints.get(i+1).node1 : matchedWaypoints.get(i+1).node2;
OsmNodeNamed nogo = new OsmNodeNamed(lastPoint);
nogo.radius = 5;
nogo.name = "nogo" + (i+1);
nogo.nogoWeight = 9999.;
nogo.isNogo = true;
if (routingContext.nogopoints == null) routingContext.nogopoints = new ArrayList<>();
routingContext.nogopoints.add(nogo);
}
}
}
if (seg == null)
return null;
if (routingContext.ai != null) return null;
boolean changed = false;
if (routingContext.correctMisplacedViaPoints && !matchedWaypoints.get(i).direct) {
if (routingContext.correctMisplacedViaPoints && !matchedWaypoints.get(i).direct && !routingContext.allowSamewayback) {
changed = snappPathConnection(totaltrack, seg, routingContext.inverseRouting ? matchedWaypoints.get(i + 1) : matchedWaypoints.get(i));
}
if (wptIndex > 0)
@ -785,7 +1044,7 @@ public class RoutingEngine extends Thread {
// check for way back on way point
private boolean snappPathConnection(OsmTrack tt, OsmTrack t, MatchedWaypoint startWp) {
if (!startWp.name.startsWith("via"))
if (!startWp.name.startsWith("via") && !startWp.name.startsWith("rt"))
return false;
int ourSize = tt.nodes.size();
@ -876,16 +1135,12 @@ public class RoutingEngine extends Thread {
}
indexback--;
indexfore++;
}
if (routingContext.correctMisplacedViaPointsDistance > 0 &&
wayDistance > routingContext.correctMisplacedViaPointsDistance) {
removeVoiceHintList.clear();
removeBackList.clear();
removeForeList.clear();
return false;
wayDistance > routingContext.correctMisplacedViaPointsDistance) break;
}
// time hold
float atime = 0;
float aenergy = 0;
@ -1111,6 +1366,7 @@ public class RoutingEngine extends Thread {
for (int i = 0; i < unmatchedWaypoints.size(); i++) {
MatchedWaypoint wp = unmatchedWaypoints.get(i);
if (wp.waypoint.calcDistance(wp.crosspoint) > routingContext.waypointCatchingRange) {
MatchedWaypoint nmw = new MatchedWaypoint();
if (i == 0) {
OsmNodeNamed onn = new OsmNodeNamed(wp.waypoint);
@ -1153,7 +1409,6 @@ public class RoutingEngine extends Thread {
}
wp.crosspoint = new OsmNode(wp.waypoint.ilon, wp.waypoint.ilat);
}
} else {
waypoints.add(wp);
}
@ -1161,6 +1416,7 @@ public class RoutingEngine extends Thread {
unmatchedWaypoints.clear();
unmatchedWaypoints.addAll(waypoints);
}
}
private OsmTrack searchTrack(MatchedWaypoint startWp, MatchedWaypoint endWp, OsmTrack nearbyTrack, OsmTrack refTrack) {
@ -1222,6 +1478,7 @@ public class RoutingEngine extends Thread {
OsmTrack t;
try {
t = findTrack(cfi == 0 ? "pass0" : "pass1", startWp, endWp, track, refTrack, false);
if (routingContext.ai != null) return t;
} catch (IllegalArgumentException iae) {
if (!terminated && matchPath != null) { // timeout, but eventually prepare a dirty ref track
logInfo("supplying dirty reference track after timeout");

View File

@ -202,6 +202,17 @@ public class RoutingParamCollector {
rctx.forceUseStartDirection = true;
} else if (key.equals("direction")) {
rctx.startDirection = Integer.valueOf(value);
} else if (key.equals("roundTripDistance")) {
rctx.roundTripDistance = Integer.valueOf(value);
} else if (key.equals("roundTripDirectionAdd")) {
rctx.roundTripDirectionAdd = Integer.valueOf(value);
} else if (key.equals("roundTripPoints")) {
rctx.roundTripPoints = Integer.valueOf(value);
if (rctx.roundTripPoints == null || rctx.roundTripPoints < 3 || rctx.roundTripPoints > 20) {
rctx.roundTripPoints = 5;
}
} else if (key.equals("allowSamewayback")) {
rctx.allowSamewayback = Integer.parseInt(value)==1;
} else if (key.equals("alternativeidx")) {
rctx.setAlternativeIdx(Integer.parseInt(value));
} else if (key.equals("turnInstructionMode")) {

View File

@ -1,6 +1,7 @@
package btools.router;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
@ -110,4 +111,22 @@ public class OsmNodeNamedTest {
0.1 * 5
);
}
@Test
public void testDestination() {
// Segment ends
int lon1, lat1, lon2, lat2;
// Circle definition
OsmNodeNamed node = new OsmNodeNamed();
// Center
node.ilon = toOsmLon(0);
node.ilat = toOsmLat(0);
double startDist = 1000;
for (int i = 0; i <= 360; i += 45) {
int[] pos = CheapRuler.destination(node.ilon, node.ilat, startDist, i);
double dist = CheapRuler.distance(node.ilon, node.ilat, pos[0], pos[1]);
assertTrue("pos " + pos[0] + " " + pos[1] + " distance (" + dist + ") should be around (" + startDist + ")", dist -1 < startDist && dist +1 > startDist);
}
}
}

View File

@ -17,7 +17,7 @@ import btools.codec.WaypointMatcher;
import btools.util.ByteDataReader;
import btools.util.Crc32;
final class OsmFile {
final public class OsmFile {
private RandomAccessFile is = null;
private long fileOffset;

View File

@ -143,4 +143,14 @@ final public class PhysicalFile {
elevationType = dis.readByte();
} catch (Exception e) {}
}
public void close(){
if (ra != null) {
try {
ra.close();
} catch (Exception ee) {
}
}
}
}

View File

@ -64,7 +64,8 @@ public class BRouterService extends Service {
worker.segmentDir = new File(baseDir, "brouter/segments4");
String errMsg = null;
if (engineMode == RoutingEngine.BROUTER_ENGINEMODE_ROUTING) {
if (engineMode == RoutingEngine.BROUTER_ENGINEMODE_ROUTING ||
engineMode == RoutingEngine.BROUTER_ENGINEMODE_ROUNDTRIP) {
String remoteProfile = params.getString("remoteProfile", null);
if (remoteProfile == null) {

View File

@ -46,6 +46,7 @@ public class BRouterWorker {
RoutingContext rc = new RoutingContext();
rc.rawTrackPath = rawTrackPath;
rc.rawAreaPath = (rawTrackPath != null ? rawTrackPath.substring(0, rawTrackPath.lastIndexOf(File.separator)+1) + "rawAreaInfo.dat" : null);
rc.localFunction = profilePath;
RoutingParamCollector routingParamCollector = new RoutingParamCollector();
@ -136,7 +137,8 @@ public class BRouterWorker {
cr.quite = true;
cr.doRun(maxRunningTime);
if (engineMode == RoutingEngine.BROUTER_ENGINEMODE_ROUTING) {
if (engineMode == RoutingEngine.BROUTER_ENGINEMODE_ROUTING ||
engineMode == RoutingEngine.BROUTER_ENGINEMODE_ROUNDTRIP) {
// store new reference track if any
// (can exist for timed-out search)
if (cr.getFoundRawTrack() != null) {

View File

@ -222,7 +222,8 @@ public class RouteServer extends Thread implements Comparable<RouteServer> {
}
String headers = encodings == null || encodings.indexOf("gzip") < 0 ? null : "Content-Encoding: gzip\n";
writeHttpHeader(bw, handler.getMimeType(), handler.getFileName(), headers, HTTP_STATUS_OK);
if (engineMode == 0) {
if (engineMode == RoutingEngine.BROUTER_ENGINEMODE_ROUTING ||
engineMode == RoutingEngine.BROUTER_ENGINEMODE_ROUNDTRIP) {
if (track != null) {
if (headers != null) { // compressed
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@ -235,7 +236,8 @@ public class RouteServer extends Thread implements Comparable<RouteServer> {
bw.write(handler.formatTrack(track));
}
}
} else if (engineMode == 2) {
} else if (engineMode == RoutingEngine.BROUTER_ENGINEMODE_GETELEV ||
engineMode == RoutingEngine.BROUTER_ENGINEMODE_GETINFO) {
String s = cr.getFoundInfo();
if (s != null) {
bw.write(s);

View File

@ -77,4 +77,21 @@ public final class CheapRuler {
double dlat = (ilat1 - ilat2) * kxky[1];
return Math.sqrt(dlat * dlat + dlon * dlon); // in m
}
public static int[] destination(int lon1, int lat1, double distance, double angle) {
double[] lonlat2m = getLonLatToMeterScales(lat1);
double lon2m = lonlat2m[0];
double lat2m = lonlat2m[1];
angle = 90. - angle;
double st = Math.sin(angle * Math.PI / 180.);
double ct = Math.cos(angle * Math.PI / 180.);
int lon2 = (int) (0.5 + lon1 + ct * distance / lon2m);
int lat2 = (int) (0.5 + lat1 + st * distance / lat2m);
int[] ret = new int[2];
ret[0] = lon2;
ret[1] = lat2;
return ret;
}
}