Merge pull request #494 from afischerdev/update-version

Update lib part three - ignore misplaced via points
This commit is contained in:
afischerdev 2023-01-15 16:44:56 +01:00 committed by GitHub
commit 2b9a9d5bdd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 245 additions and 15 deletions

View file

@ -48,7 +48,7 @@ public final class OsmTrack {
public List<OsmNodeNamed> pois = new ArrayList<OsmNodeNamed>(); public List<OsmNodeNamed> pois = new ArrayList<OsmNodeNamed>();
private static class OsmPathElementHolder { public static class OsmPathElementHolder {
public OsmPathElement node; public OsmPathElement node;
public OsmPathElementHolder nextHolder; public OsmPathElementHolder nextHolder;
} }
@ -928,6 +928,12 @@ public final class OsmTrack {
return true; return true;
} }
public OsmPathElementHolder getFromDetourMap(long id) {
if (detourMap == null)
return null;
return detourMap.get(id);
}
public void prepareSpeedProfile(RoutingContext rc) { public void prepareSpeedProfile(RoutingContext rc) {
// sendSpeedProfile = rc.keyValues != null && rc.keyValues.containsKey( "vmax" ); // sendSpeedProfile = rc.keyValues != null && rc.keyValues.containsKey( "vmax" );
} }
@ -987,6 +993,17 @@ public final class OsmTrack {
return nodes.get(nodes.size() - 1).getTime(); return nodes.get(nodes.size() - 1).getTime();
} }
public void removeVoiceHint(int i) {
if (voiceHints != null) {
VoiceHint remove = null;
for (VoiceHint vh : voiceHints.list) {
if (vh.indexInTrack == i)
remove = vh;
}
if (remove != null)
voiceHints.list.remove(remove);
}
}
private MessageData startSection(OsmPathElement element, OsmPathElement root) { private MessageData startSection(OsmPathElement element, OsmPathElement root) {
OsmPathElement e = element; OsmPathElement e = element;

View file

@ -78,6 +78,8 @@ public final class RoutingContext {
public boolean transitonly; public boolean transitonly;
public double waypointCatchingRange; public double waypointCatchingRange;
public boolean correctMisplacedViaPoints;
public double correctMisplacedViaPointsDistance;
private void setModel(String className) { private void setModel(String className) {
if (className == null) { if (className == null) {
@ -114,8 +116,8 @@ public final class RoutingContext {
// add parameter to context // add parameter to context
for (Map.Entry<String, String> e : keyValues.entrySet()) { for (Map.Entry<String, String> e : keyValues.entrySet()) {
float f = Float.parseFloat(e.getValue()); float f = Float.parseFloat(e.getValue());
expctxWay.setVariableValue(e.getKey(), f, false); expctxWay.setVariableValue(e.getKey(), f, true);
expctxNode.setVariableValue(e.getKey(), f, false); expctxNode.setVariableValue(e.getKey(), f, true);
} }
} }
@ -136,6 +138,9 @@ public final class RoutingContext {
// turn-restrictions not used per default for foot profiles // turn-restrictions not used per default for foot profiles
considerTurnRestrictions = 0.f != expctxGlobal.getVariableValue("considerTurnRestrictions", footMode ? 0.f : 1.f); considerTurnRestrictions = 0.f != expctxGlobal.getVariableValue("considerTurnRestrictions", footMode ? 0.f : 1.f);
correctMisplacedViaPoints = 0.f != expctxGlobal.getVariableValue("correctMisplacedViaPoints", 1.f);
correctMisplacedViaPointsDistance = expctxGlobal.getVariableValue("correctMisplacedViaPointsDistance", 40.f);
// process tags not used in the profile (to have them in the data-tab) // process tags not used in the profile (to have them in the data-tab)
processUnusedTags = 0.f != expctxGlobal.getVariableValue("processUnusedTags", 0.f); processUnusedTags = 0.f != expctxGlobal.getVariableValue("processUnusedTags", 0.f);
@ -457,7 +462,7 @@ public final class RoutingContext {
} }
} }
} }
return (int) (d + 1.0); return (int) (d + 0.5);
} }
public OsmPathModel pm; public OsmPathModel pm;

View file

@ -16,6 +16,7 @@ import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmLinkHolder; import btools.mapaccess.OsmLinkHolder;
import btools.mapaccess.OsmNode; import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmNodePairSet; import btools.mapaccess.OsmNodePairSet;
import btools.util.CompactLongMap;
import btools.util.SortedHeap; import btools.util.SortedHeap;
import btools.util.StackSampler; import btools.util.StackSampler;
@ -32,6 +33,8 @@ public class RoutingEngine extends Thread {
private int MAXNODES_ISLAND_CHECK = 500; private int MAXNODES_ISLAND_CHECK = 500;
private OsmNodePairSet islandNodePairs = new OsmNodePairSet(MAXNODES_ISLAND_CHECK); private OsmNodePairSet islandNodePairs = new OsmNodePairSet(MAXNODES_ISLAND_CHECK);
private int MAX_STEPS_CHECK = 10;
protected OsmTrack foundTrack = new OsmTrack(); protected OsmTrack foundTrack = new OsmTrack();
private OsmTrack foundRawTrack = null; private OsmTrack foundRawTrack = null;
private int alternativeIndex = 0; private int alternativeIndex = 0;
@ -358,15 +361,26 @@ public class RoutingEngine extends Thread {
} }
OsmTrack seg; OsmTrack seg;
int wptIndex;
if (routingContext.inverseRouting) { if (routingContext.inverseRouting) {
routingContext.inverseDirection = true; routingContext.inverseDirection = true;
seg = searchTrack(matchedWaypoints.get(i + 1), matchedWaypoints.get(i), null, refTracks[i]); seg = searchTrack(matchedWaypoints.get(i + 1), matchedWaypoints.get(i), null, refTracks[i]);
routingContext.inverseDirection = false; routingContext.inverseDirection = false;
wptIndex = i + 1;
} else { } else {
seg = searchTrack(matchedWaypoints.get(i), matchedWaypoints.get(i + 1), i == matchedWaypoints.size() - 2 ? nearbyTrack : null, refTracks[i]); seg = searchTrack(matchedWaypoints.get(i), matchedWaypoints.get(i + 1), i == matchedWaypoints.size() - 2 ? nearbyTrack : null, refTracks[i]);
wptIndex = i;
} }
if (seg == null)
return null;
boolean changed = false;
if (routingContext.correctMisplacedViaPoints && !matchedWaypoints.get(i).direct) {
changed = snappPathConnection(totaltrack, seg, routingContext.inverseRouting ? matchedWaypoints.get(i + 1) : matchedWaypoints.get(i));
}
// if (wptIndex > 0)
// matchedWaypoints.get(wptIndex).indexInTrack = totaltrack.nodes.size() - 1;
if (seg == null) return null;
totaltrack.appendTrack(seg); totaltrack.appendTrack(seg);
lastTracks[i] = seg; lastTracks[i] = seg;
} }
@ -375,6 +389,172 @@ public class RoutingEngine extends Thread {
return totaltrack; return totaltrack;
} }
// check for way back on way point
private boolean snappPathConnection(OsmTrack tt, OsmTrack t, MatchedWaypoint startWp) {
if (!startWp.name.startsWith("via"))
return false;
int ourSize = tt.nodes.size();
if (ourSize > 0) {
OsmPathElement testPoint = tt.nodes.get(ourSize - 1);
if (routingContext.poipoints != null) {
for (OsmNodeNamed node : routingContext.poipoints) {
int lon0 = tt.nodes.get(ourSize - 2).getILon();
int lat0 = tt.nodes.get(ourSize - 2).getILat();
int lon1 = startWp.crosspoint.ilon;
int lat1 = startWp.crosspoint.ilat;
int lon2 = node.ilon;
int lat2 = node.ilat;
double angle3 = routingContext.anglemeter.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2);
int dist = node.calcDistance(startWp.crosspoint);
if (dist < routingContext.waypointCatchingRange)
return false;
}
}
ArrayList<OsmPathElement> removeBackList = new ArrayList<>();
ArrayList<OsmPathElement> removeForeList = new ArrayList<>();
ArrayList<Integer> removeVoiceHintList = new ArrayList<>();
int lon0,
lat0,
lon1,
lat1,
lon2,
lat2;
OsmPathElement last = null;
OsmPathElement lastJunction = null;
CompactLongMap<OsmTrack.OsmPathElementHolder> lastJunctions = new CompactLongMap<>();
OsmPathElement newJunction = null;
OsmPathElement newTarget = null;
OsmPathElement tmpback = null;
OsmPathElement tmpfore = null;
int indexback = ourSize - 1;
int indexfore = 0;
int stop = (indexback - MAX_STEPS_CHECK > 1 ? indexback - MAX_STEPS_CHECK : 1);
double wayDistance = 0;
double nextDist = 0;
while (indexback >= 1 && indexback >= stop && indexfore < t.nodes.size()) {
int junctions = 0;
tmpback = tt.nodes.get(indexback);
tmpfore = t.nodes.get(indexfore);
int dist = tmpback.calcDistance(tmpfore);
if (1 == 1) {
OsmTrack.OsmPathElementHolder detours = tt.getFromDetourMap(tmpback.getIdFromPos());
OsmTrack.OsmPathElementHolder h = detours;
while (h != null) {
junctions++;
lastJunctions.put(h.node.getIdFromPos(), h);
h = h.nextHolder;
}
}
if (dist == 1 && indexfore > 0) {
if (indexfore == 1) {
removeBackList.add(tt.nodes.get(tt.nodes.size() - 1)); // last and first should be equal, so drop only on second also equal
removeForeList.add(t.nodes.get(0));
removeBackList.add(tmpback);
removeForeList.add(tmpfore);
removeVoiceHintList.add(tt.nodes.size() - 1);
removeVoiceHintList.add(indexback);
} else {
removeBackList.add(tmpback);
removeForeList.add(tmpfore);
removeVoiceHintList.add(indexback);
}
nextDist = t.nodes.get(indexfore - 1).calcDistance(tmpfore);
wayDistance += nextDist;
}
if (dist > 1 || indexback == 1) {
if (removeBackList.size() != 0) {
// recover last - should be the cross point
removeBackList.remove(removeBackList.get(removeBackList.size() - 1));
removeForeList.remove(removeForeList.get(removeForeList.size() - 1));
break;
} else {
return false;
}
}
indexback--;
indexfore++;
}
if (routingContext.correctMisplacedViaPointsDistance > 0 &&
wayDistance > routingContext.correctMisplacedViaPointsDistance) {
removeVoiceHintList.clear();
removeBackList.clear();
removeForeList.clear();
return false;
}
// time hold
float atime = 0;
float aenergy = 0;
if (removeForeList.size() > 1) {
atime = t.nodes.get(removeForeList.size() - 2).getTime();
aenergy = t.nodes.get(removeForeList.size() - 2).getEnergy();
}
for (OsmPathElement e : removeBackList) {
tt.nodes.remove(e);
}
for (OsmPathElement e : removeForeList) {
t.nodes.remove(e);
}
for (Integer e : removeVoiceHintList) {
tt.removeVoiceHint(e);
}
removeVoiceHintList.clear();
removeBackList.clear();
removeForeList.clear();
if (atime > 0f) {
for (OsmPathElement e : t.nodes) {
e.setTime(e.getTime() - atime);
e.setEnergy(e.getEnergy() - aenergy);
}
}
if (t.nodes.size() < 2)
return true;
if (tt.nodes.size() < 1)
return true;
if (tt.nodes.size() == 1) {
last = tt.nodes.get(0);
} else {
last = tt.nodes.get(tt.nodes.size() - 2);
}
newJunction = t.nodes.get(0);
newTarget = t.nodes.get(1);
setNewVoiceHint(t, last, lastJunctions, newJunction, newTarget);
return true;
}
return false;
}
private void setNewVoiceHint(OsmTrack t, OsmPathElement last, CompactLongMap<OsmTrack.OsmPathElementHolder> lastJunctiona, OsmPathElement newJunction, OsmPathElement newTarget) {
if (last == null || newJunction == null || newTarget == null)
return;
int lon0,
lat0,
lon1,
lat1,
lon2,
lat2;
lon0 = last.getILon();
lat0 = last.getILat();
lon1 = newJunction.getILon();
lat1 = newJunction.getILat();
lon2 = newTarget.getILon();
lat2 = newTarget.getILat();
// get a new angle
double angle = routingContext.anglemeter.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2);
newTarget.message.turnangle = (float) angle;
}
// geometric position matching finding the nearest routable way-section // geometric position matching finding the nearest routable way-section
private void matchWaypointsToNodes(List<MatchedWaypoint> unmatchedWaypoints) { private void matchWaypointsToNodes(List<MatchedWaypoint> unmatchedWaypoints) {
resetCache(false); resetCache(false);
@ -386,7 +566,8 @@ public class RoutingEngine extends Thread {
try { try {
boolean calcBeeline = startWp.direct; boolean calcBeeline = startWp.direct;
if (!calcBeeline) return searchRoutedTrack(startWp, endWp, nearbyTrack, refTrack); if (!calcBeeline)
return searchRoutedTrack(startWp, endWp, nearbyTrack, refTrack);
// we want a beeline-segment // we want a beeline-segment
OsmPath path = routingContext.createPath(new OsmLink(null, startWp.crosspoint)); OsmPath path = routingContext.createPath(new OsmLink(null, startWp.crosspoint));
@ -399,7 +580,10 @@ public class RoutingEngine extends Thread {
private OsmTrack searchRoutedTrack(MatchedWaypoint startWp, MatchedWaypoint endWp, OsmTrack nearbyTrack, OsmTrack refTrack) { private OsmTrack searchRoutedTrack(MatchedWaypoint startWp, MatchedWaypoint endWp, OsmTrack nearbyTrack, OsmTrack refTrack) {
OsmTrack track = null; OsmTrack track = null;
double[] airDistanceCostFactors = new double[]{routingContext.pass1coefficient, routingContext.pass2coefficient}; double[] airDistanceCostFactors = new double[]{
routingContext.pass1coefficient,
routingContext.pass2coefficient
};
boolean isDirty = false; boolean isDirty = false;
IllegalArgumentException dirtyMessage = null; IllegalArgumentException dirtyMessage = null;
@ -526,10 +710,14 @@ public class RoutingEngine extends Thread {
double minradius = 1e10; double minradius = 1e10;
for (OsmLink link = n1.firstlink; link != null; link = link.getNext(n1)) { for (OsmLink link = n1.firstlink; link != null; link = link.getNext(n1)) {
OsmNode nextNode = link.getTarget(n1); OsmNode nextNode = link.getTarget(n1);
if (nextNode.isHollow()) continue; // border node? if (nextNode.isHollow())
if (nextNode.firstlink == null) continue; // don't care about dead ends continue; // border node?
if (nextNode == n1) continue; // ? if (nextNode.firstlink == null)
if (nextNode != n2) continue; // just that link continue; // don't care about dead ends
if (nextNode == n1)
continue; // ?
if (nextNode != n2)
continue; // just that link
wp.radius = 1.5; wp.radius = 1.5;
OsmPath testPath = routingContext.createPath(startPath, link, null, guideTrack != null); OsmPath testPath = routingContext.createPath(startPath, link, null, guideTrack != null);
@ -686,7 +874,6 @@ public class RoutingEngine extends Thread {
if (directWeaving && nodesCache.hasHollowLinkTargets(path.getTargetNode())) { if (directWeaving && nodesCache.hasHollowLinkTargets(path.getTargetNode())) {
if (!memoryPanicMode) { if (!memoryPanicMode) {
if (!nodesCache.nodesMap.isInMemoryBounds(openSet.getSize(), false)) { if (!nodesCache.nodesMap.isInMemoryBounds(openSet.getSize(), false)) {
// System.out.println( "collecting..." );
int nodesBefore = nodesCache.nodesMap.nodesCreated; int nodesBefore = nodesCache.nodesMap.nodesCreated;
int pathsBefore = openSet.getSize(); int pathsBefore = openSet.getSize();
@ -901,7 +1088,8 @@ public class RoutingEngine extends Thread {
routingContext.setWaypoint(endPos, true); routingContext.setWaypoint(endPos, true);
} }
OsmPath testPath = routingContext.createPath(otherPath, link, refTrack, guideTrack != null); OsmPath testPath = routingContext.createPath(otherPath, link, refTrack, guideTrack != null);
if (testPath.cost >= 0 && (bestPath == null || testPath.cost < bestPath.cost)) { if (testPath.cost >= 0 && (bestPath == null || testPath.cost < bestPath.cost) &&
(testPath.sourceNode.getIdFromPos() != testPath.targetNode.getIdFromPos())) {
bestPath = testPath; bestPath = testPath;
} }
} finally { } finally {
@ -962,7 +1150,7 @@ public class RoutingEngine extends Thread {
OsmPathElement element = OsmPathElement.create(path, false); OsmPathElement element = OsmPathElement.create(path, false);
// for final track, cut endnode // for final track, cut endnode
if (guideTrack != null) { if (guideTrack != null && element.origin != null) {
element = element.origin; element = element.origin;
} }

View file

@ -834,6 +834,15 @@ public abstract class BExpressionContext implements IByteArrayUnifier {
Integer num = variableNumbers.get(name); Integer num = variableNumbers.get(name);
if (num != null) { if (num != null) {
variableData[num.intValue()] = value; variableData[num.intValue()] = value;
} else if (create) {
num = getVariableIdx(name, create);
float[] readOnlyData = variableData;
int minWriteIdx = readOnlyData.length;
variableData = new float[variableNumbers.size()];
for (int i = 0; i < minWriteIdx; i++) {
variableData[i] = readOnlyData[i];
}
variableData[num.intValue()] = value;
} }
} }

View file

@ -103,7 +103,6 @@ public class RouteServerTest {
InputStream inputStream = httpConnection.getInputStream(); InputStream inputStream = httpConnection.getInputStream();
JSONObject geoJson = new JSONObject(new String(inputStream.readAllBytes(), StandardCharsets.UTF_8)); JSONObject geoJson = new JSONObject(new String(inputStream.readAllBytes(), StandardCharsets.UTF_8));
Assert.assertEquals(2, geoJson.query("/features/0/properties/voicehints/0/1")); // TL Assert.assertEquals(2, geoJson.query("/features/0/properties/voicehints/0/1")); // TL
Assert.assertEquals(1, geoJson.query("/features/0/properties/voicehints/1/1")); // C
} }
@Test @Test
@ -145,6 +144,18 @@ public class RouteServerTest {
Assert.assertEquals("350", geoJson.query("/features/0/properties/track-length")); Assert.assertEquals("350", geoJson.query("/features/0/properties/track-length"));
} }
@Test
public void misplacedPoints() throws IOException {
URL requestUrl = new URL(baseUrl + "brouter?lonlats=8.708678,49.999188|8.71145,49.999761|8.715801,50.00065&nogos=&profile=trekking&alternativeidx=0&format=geojson&correctMisplacedViaPoints=1&timode=3");
HttpURLConnection httpConnection = (HttpURLConnection) requestUrl.openConnection();
httpConnection.connect();
Assert.assertEquals(HttpURLConnection.HTTP_OK, httpConnection.getResponseCode());
InputStream inputStream = httpConnection.getInputStream();
JSONObject geoJson = new JSONObject(new String(inputStream.readAllBytes(), StandardCharsets.UTF_8));
Assert.assertEquals("598", geoJson.query("/features/0/properties/track-length"));
}
@Test @Test
public void uploadValidProfile() throws IOException { public void uploadValidProfile() throws IOException {