diff --git a/brouter-core/src/main/java/btools/router/AreaInfo.java b/brouter-core/src/main/java/btools/router/AreaInfo.java new file mode 100644 index 0000000..79f503e --- /dev/null +++ b/brouter-core/src/main/java/btools/router/AreaInfo.java @@ -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(); + } +} diff --git a/brouter-core/src/main/java/btools/router/AreaReader.java b/brouter-core/src/main/java/btools/router/AreaReader.java new file mode 100644 index 0000000..d661e9b --- /dev/null +++ b/brouter-core/src/main/java/btools/router/AreaReader.java @@ -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 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 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> list = new ArrayList<>(tiles.entrySet()); + Collections.sort(list, new Comparator<>() { + @Override + public int compare(Map.Entry e1, Map.Entry 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 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 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 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 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) { + } + } + } + } + +} diff --git a/brouter-core/src/main/java/btools/router/OsmPath.java b/brouter-core/src/main/java/btools/router/OsmPath.java index 94549c9..9ce8123 100644 --- a/brouter-core/src/main/java/btools/router/OsmPath.java +++ b/brouter-core/src/main/java/btools/router/OsmPath.java @@ -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(); diff --git a/brouter-core/src/main/java/btools/router/RoutingContext.java b/brouter-core/src/main/java/btools/router/RoutingContext.java index 718dd04..f5b2bf3 100644 --- a/brouter-core/src/main/java/btools/router/RoutingContext.java +++ b/brouter-core/src/main/java/btools/router/RoutingContext.java @@ -36,6 +36,7 @@ public final class RoutingContext { public Map keyValues; public String rawTrackPath; + public String rawAreaPath; public String getProfileName() { String name = localFunction == null ? "unknown" : localFunction; @@ -78,7 +79,9 @@ public final class RoutingContext { public double correctMisplacedViaPointsDistance; 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(); diff --git a/brouter-core/src/main/java/btools/router/RoutingEngine.java b/brouter-core/src/main/java/btools/router/RoutingEngine.java index 06de940..698bc7b 100644 --- a/brouter-core/src/main/java/btools/router/RoutingEngine.java +++ b/brouter-core/src/main/java/btools/router/RoutingEngine.java @@ -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 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 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 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 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 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 listStart = new ArrayList<>(); + listStart.add(wpt1); + + List wpliststart = new ArrayList<>(); + wpliststart.add(wp); + + List 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; @@ -639,21 +883,21 @@ public class RoutingEngine extends Thread { // add extra waypoints from the last broken round for (OsmNodeNamed wp : extraWaypoints) { if (wp.direct) hasDirectRouting = true; - if (wp.name.startsWith("from")) { + if (wp.name.startsWith("from")) { waypoints.add(1, wp); 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,15 +1135,11 @@ public class RoutingEngine extends Thread { } indexback--; indexfore++; + + if (routingContext.correctMisplacedViaPointsDistance > 0 && + wayDistance > routingContext.correctMisplacedViaPointsDistance) break; } - if (routingContext.correctMisplacedViaPointsDistance > 0 && - wayDistance > routingContext.correctMisplacedViaPointsDistance) { - removeVoiceHintList.clear(); - removeBackList.clear(); - removeForeList.clear(); - return false; - } // time hold float atime = 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"); diff --git a/brouter-core/src/main/java/btools/router/RoutingParamCollector.java b/brouter-core/src/main/java/btools/router/RoutingParamCollector.java index b8d7f6b..f239315 100644 --- a/brouter-core/src/main/java/btools/router/RoutingParamCollector.java +++ b/brouter-core/src/main/java/btools/router/RoutingParamCollector.java @@ -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")) { diff --git a/brouter-core/src/test/java/btools/router/OsmNodeNamedTest.java b/brouter-core/src/test/java/btools/router/OsmNodeNamedTest.java index ce2eb47..86d3279 100644 --- a/brouter-core/src/test/java/btools/router/OsmNodeNamedTest.java +++ b/brouter-core/src/test/java/btools/router/OsmNodeNamedTest.java @@ -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); + } + } } diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java index 9b5724b..2df3844 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/OsmFile.java @@ -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; diff --git a/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java b/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java index e61f469..0ea3af7 100644 --- a/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java +++ b/brouter-mapaccess/src/main/java/btools/mapaccess/PhysicalFile.java @@ -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) { + } + } + } + } diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterService.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterService.java index a1e8963..9dbdade 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterService.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterService.java @@ -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) { diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterWorker.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterWorker.java index 1c733ba..33ee802 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterWorker.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterWorker.java @@ -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) { diff --git a/brouter-server/src/main/java/btools/server/RouteServer.java b/brouter-server/src/main/java/btools/server/RouteServer.java index 36145dc..4b2be7a 100644 --- a/brouter-server/src/main/java/btools/server/RouteServer.java +++ b/brouter-server/src/main/java/btools/server/RouteServer.java @@ -222,7 +222,8 @@ public class RouteServer extends Thread implements Comparable { } 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 { 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); diff --git a/brouter-util/src/main/java/btools/util/CheapRuler.java b/brouter-util/src/main/java/btools/util/CheapRuler.java index 7a7dc8f..454b2b9 100644 --- a/brouter-util/src/main/java/btools/util/CheapRuler.java +++ b/brouter-util/src/main/java/btools/util/CheapRuler.java @@ -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; + } }