Merge pull request #510 from afischerdev/lib-update-six

Update Lib Part Six - Change Export
This commit is contained in:
afischerdev 2023-03-09 11:05:13 +01:00 committed by GitHub
commit 0cf83456f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1648 additions and 1123 deletions

View file

@ -21,9 +21,13 @@ import java.io.InputStreamReader;
import java.io.StringWriter;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import btools.mapaccess.MatchedWaypoint;
import btools.mapaccess.OsmPos;
@ -45,6 +49,9 @@ public final class OsmTrack {
public boolean showspeed;
public boolean showSpeedProfile;
public boolean showTime;
public Map<String, String> params;
public List<OsmNodeNamed> pois = new ArrayList<OsmNodeNamed>();
@ -414,6 +421,7 @@ public final class OsmTrack {
int turnInstructionMode = voiceHints != null ? voiceHints.turnInstructionMode : 0;
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
if (turnInstructionMode != 9) {
for (int i = messageList.size() - 1; i >= 0; i--) {
String message = messageList.get(i);
if (i < messageList.size() - 1)
@ -421,6 +429,7 @@ public final class OsmTrack {
if (message != null)
sb.append("<!-- ").append(message).append(" -->\n");
}
}
if (turnInstructionMode == 4) // comment style
{
@ -436,8 +445,10 @@ public final class OsmTrack {
sb.append("<gpx \n");
sb.append(" xmlns=\"http://www.topografix.com/GPX/1/1\" \n");
sb.append(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n");
if (turnInstructionMode == 2) // locus style
{
if (turnInstructionMode == 9) { // BRouter style
sb.append(" xmlns:brouter=\"Not yet documented\" \n");
}
if (turnInstructionMode == 7) { // old locus style
sb.append(" xmlns:locus=\"http://www.locusmap.eu\" \n");
}
sb.append(" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\" \n");
@ -447,32 +458,61 @@ public final class OsmTrack {
} else {
sb.append(" creator=\"BRouter-" + version + "\" version=\"1.1\">\n");
}
if (turnInstructionMode == 3) // osmand style
if (turnInstructionMode == 9) {
sb.append(" <metadata>\n");
sb.append(" <name>").append(name).append("</name>\n");
sb.append(" <extensions>\n");
sb.append(" <brouter:info>").append(messageList.get(0)).append("</brouter:info>\n");
if (params != null && params.size() > 0) {
sb.append(" <brouter:params><![CDATA[");
int i = 0;
for (Map.Entry<String, String> e : params.entrySet()) {
if (i++ != 0) sb.append("&");
sb.append(e.getKey()).append("=").append(e.getValue());
}
sb.append("]]></brouter:params>\n");
}
sb.append(" </extensions>\n");
sb.append(" </metadata>\n");
}
if (turnInstructionMode == 3 || turnInstructionMode == 8) // osmand style, cruiser
{
float lastRteTime = 0;
sb.append(" <rte>\n");
sb.append(" <rtept lat=\"").append(formatILat(nodes.get(0).getILat())).append("\" lon=\"")
float rteTime = getVoiceHintTime(0);
StringBuffer first = new StringBuffer();
// define start point
{
first.append(" <rtept lat=\"").append(formatILat(nodes.get(0).getILat())).append("\" lon=\"")
.append(formatILon(nodes.get(0).getILon())).append("\">\n")
.append(" <desc>start</desc>\n <extensions>\n");
float rteTime = getVoiceHintTime(0);
if (rteTime != lastRteTime) // add timing only if available
{
double t = rteTime - lastRteTime;
sb.append(" <time>").append("" + (int) (t + 0.5)).append("</time>\n");
first.append(" <time>").append("" + (int) (t + 0.5)).append("</time>\n");
lastRteTime = rteTime;
}
sb.append(" <offset>0</offset>\n </extensions>\n </rtept>\n");
first.append(" <offset>0</offset>\n </extensions>\n </rtept>\n");
}
if (turnInstructionMode == 8) {
if (matchedWaypoints.get(0).direct && voiceHints.list.get(0).indexInTrack == 0) {
// has a voice hint do nothing, voice hint will do
} else {
sb.append(first.toString());
}
} else {
sb.append(first.toString());
}
for (int i = 0; i < voiceHints.list.size(); i++) {
VoiceHint hint = voiceHints.list.get(i);
sb.append(" <rtept lat=\"").append(formatILat(hint.ilat)).append("\" lon=\"")
.append(formatILon(hint.ilon)).append("\">\n")
.append(" <desc>").append(hint.getMessageString()).append("</desc>\n <extensions>\n");
.append(" <desc>")
.append(turnInstructionMode == 3 ? hint.getMessageString() : hint.getCruiserMessageString())
.append("</desc>\n <extensions>\n");
rteTime = getVoiceHintTime(i + 1);
@ -482,7 +522,9 @@ public final class OsmTrack {
sb.append(" <time>").append("" + (int) (t + 0.5)).append("</time>\n");
lastRteTime = rteTime;
}
sb.append(" <turn>").append(hint.getCommandString()).append("</turn>\n <turn-angle>").append("" + (int) hint.angle)
sb.append(" <turn>")
.append(turnInstructionMode == 3 ? hint.getCommandString() : hint.getCruiserCommandString())
.append("</turn>\n <turn-angle>").append("" + (int) hint.angle)
.append("</turn-angle>\n <offset>").append("" + hint.indexInTrack).append("</offset>\n </extensions>\n </rtept>\n");
}
sb.append(" <rtept lat=\"").append(formatILat(nodes.get(nodes.size() - 1).getILat())).append("\" lon=\"")
@ -494,7 +536,7 @@ public final class OsmTrack {
sb.append("</rte>\n");
}
if (turnInstructionMode == 2) // locus style
if (turnInstructionMode == 7) // old locus style
{
float lastRteTime = getVoiceHintTime(0);
@ -537,12 +579,12 @@ public final class OsmTrack {
.append(formatILon(hint.ilon)).append("\">")
.append(hint.selev == Short.MIN_VALUE ? "" : "<ele>" + (hint.selev / 4.) + "</ele>")
.append("<extensions>\n" +
"<om:oruxmapsextensions xmlns:om=\"http://www.oruxmaps.com/oruxmapsextensions/1/0\">\n" +
"<om:ext type=\"ICON\" subtype=\"0\">").append("" + hint.getOruxAction())
" <om:oruxmapsextensions xmlns:om=\"http://www.oruxmaps.com/oruxmapsextensions/1/0\">\n" +
" <om:ext type=\"ICON\" subtype=\"0\">").append("" + hint.getOruxAction())
.append("</om:ext>\n" +
"</om:oruxmapsextensions>\n" +
"</extensions>\n" +
"</wpt>");
" </om:oruxmapsextensions>\n" +
" </extensions>\n" +
" </wpt>\n");
}
}
@ -571,34 +613,216 @@ public final class OsmTrack {
}
}
sb.append(" <trk>\n");
sb.append(" <name>").append(name).append("</name>\n");
if (turnInstructionMode == 1) // trkpt/sym style
{
if (turnInstructionMode == 9) { // brouter style
sb.append(" <src>").append(name).append("</src>\n");
sb.append(" <type>").append(voiceHints.getTransportMode()).append("</type>\n");
} else {
sb.append(" <name>").append(name).append("</name>\n");
}
if (turnInstructionMode == 2) {
if (turnInstructionMode == 7) {
sb.append(" <extensions>\n");
sb.append(" <locus:rteComputeType>").append("" + voiceHints.getLocusRouteType()).append("</locus:rteComputeType>\n");
sb.append(" <locus:rteSimpleRoundabouts>1</locus:rteSimpleRoundabouts>\n");
sb.append(" </extensions>\n");
}
// all points
sb.append(" <trkseg>\n");
String lastway = "";
boolean bNextDirect = false;
OsmPathElement nn = null;
String aSpeed;
for (int idx = 0; idx < nodes.size(); idx++) {
OsmPathElement n = nodes.get(idx);
String sele = n.getSElev() == Short.MIN_VALUE ? "" : "<ele>" + n.getElev() + "</ele>";
if (turnInstructionMode == 1) // trkpt/sym style
{
for (VoiceHint hint : voiceHints.list) {
if (hint.indexInTrack == idx) {
sele += "<sym>" + hint.getCommandString() + "</sym>";
VoiceHint hint = getVoiceHint(idx);
MatchedWaypoint mwpt = getMatchedWaypoint(idx);
if (showTime) {
sele += "<time>" + getFormattedTime3(n.getTime()) + "</time>";
}
boolean bNeedHeader = false;
if (turnInstructionMode == 9) { // trkpt/sym style
if (hint != null) {
if (mwpt != null &&
!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("end")) {
sele += "<name>" + mwpt.name + "</name>";
}
sele += "<desc>" + hint.getCruiserMessageString() + "</desc>";
sele += "<sym>" + hint.getCommandString(hint.cmd) + "</sym>";
if (mwpt != null) {
sele += "<type>Via</type>";
}
sele += "<extensions>";
if (showspeed) {
double speed = 0;
if (nn != null) {
int dist = n.calcDistance(nn);
float dt = n.getTime() - nn.getTime();
if (dt != 0.f) {
speed = ((3.6f * dist) / dt + 0.5);
}
}
sele += "<brouter:speed>" + (((int) (speed * 10)) / 10.f) + "</brouter:speed>";
}
sele += "<brouter:voicehint>" + hint.getCommandString() + ";" + (int) (hint.distanceToNext) + "," + hint.formatGeometry() + "</brouter:voicehint>";
if (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway)) {
sele += "<brouter:way>" + n.message.wayKeyValues + "</brouter:way>";
lastway = n.message.wayKeyValues;
}
if (n.message != null && n.message.nodeKeyValues != null) {
sele += "<brouter:node>" + n.message.nodeKeyValues + "</brouter:node>";
}
sele += "</extensions>";
}
if (idx == 0 && hint == null) {
if (mwpt != null && mwpt.direct) {
sele += "<desc>beeline</desc>";
} else {
sele += "<desc>start</desc>";
}
sele += "<type>Via</type>";
} else if (idx == nodes.size() - 1 && hint == null) {
sele += "<desc>end</desc>";
sele += "<type>Via</type>";
} else {
if (mwpt != null && hint == null) {
if (mwpt.direct) {
// bNextDirect = true;
sele += "<desc>beeline</desc>";
} else {
sele += "<desc>" + mwpt.name + "</desc>";
}
sele += "<type>Via</type>";
bNextDirect = false;
}
}
if (hint == null) {
bNeedHeader = (showspeed || (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway))) ||
(n.message != null && n.message.nodeKeyValues != null);
if (bNeedHeader) {
sele += "<extensions>";
if (showspeed) {
double speed = 0;
if (nn != null) {
int dist = n.calcDistance(nn);
float dt = n.getTime() - nn.getTime();
if (dt != 0.f) {
speed = ((3.6f * dist) / dt + 0.5);
}
}
sele += "<brouter:speed>" + (((int) (speed * 10)) / 10.f) + "</brouter:speed>";
}
if (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway)) {
sele += "<brouter:way>" + n.message.wayKeyValues + "</brouter:way>";
lastway = n.message.wayKeyValues;
}
if (n.message != null && n.message.nodeKeyValues != null) {
sele += "<brouter:node>" + n.message.nodeKeyValues + "</brouter:node>";
}
sele += "</extensions>";
}
}
}
if (turnInstructionMode == 2) { // locus style new
if (hint != null) {
if (mwpt != null) {
if (mwpt.direct && bNextDirect) {
sele += "<src>" + hint.getLocusSymbolString() + "</src><sym>pass_place</sym><type>Shaping</type>";
// bNextDirect = false;
} else if (mwpt.direct) {
sele += "<sym>pass_place</sym><type>Shaping</type>";
bNextDirect = true;
} else if (bNextDirect) {
sele += "<src>beeline</src><sym>" + hint.getLocusSymbolString() + "</sym><type>Shaping</type>";
bNextDirect = false;
} else {
sele += "<sym>" + hint.getLocusSymbolString() + "</sym>";
}
} else {
sele += "<sym>" + hint.getLocusSymbolString() + "</sym>";
}
} else {
if (idx == 0 && hint == null) {
int pos = sele.indexOf("<sym");
if (pos != -1) {
sele = sele.substring(0, pos);
}
if (mwpt != null && !mwpt.name.startsWith("from"))
sele += "<name>" + mwpt.name + "</name>";
if (mwpt != null && mwpt.direct) {
bNextDirect = true;
}
sele += "<sym>pass_place</sym>";
sele += "<type>Via</type>";
} else if (idx == nodes.size() - 1 && hint == null) {
int pos = sele.indexOf("<sym");
if (pos != -1) {
sele = sele.substring(0, pos);
}
if (mwpt != null && mwpt.name != null && !mwpt.name.startsWith("to"))
sele += "<name>" + mwpt.name + "</name>";
if (bNextDirect) {
sele += "<src>beeline</src>";
}
sele += "<sym>pass_place</sym>";
sele += "<type>Via</type>";
} else {
if (mwpt != null) {
if (sele.contains("sym") &&
!sele.contains("name") &&
!mwpt.name.startsWith("via") &&
!mwpt.name.startsWith("from") &&
!mwpt.name.startsWith("to")) {
int pos = sele.indexOf("<sym");
if (pos != -1)
sele = sele.substring(0, pos) + "<name>" + mwpt.name + "</name>" + sele.substring(pos) + "<type>Via</type>";
} else if (sele.contains("sym") && mwpt.name.startsWith("via")) {
sele += "<type>Via</type>";
} else if (mwpt.direct && bNextDirect) {
sele += "<src>beeline</src><sym>pass_place</sym><type>Shaping</type>";
} else if (mwpt.direct) {
sele += "<sym>pass_place</sym><type>Shaping</type>";
bNextDirect = true;
} else if (mwpt.name.startsWith("via") ||
mwpt.name.startsWith("from") ||
mwpt.name.startsWith("to")) {
if (bNextDirect) {
sele += "<src>beeline</src><sym>pass_place</sym><type>Shaping</type>";
} else {
sele += "<sym>pass_place</sym><type>Shaping</type>";
}
bNextDirect = false;
} else {
sele += "<name>" + mwpt.name + "</name>";
sele += "<sym>pass_place</sym><type>Via</type>";
}
}
}
}
}
sb.append(" <trkpt lon=\"").append(formatILon(n.getILon())).append("\" lat=\"")
.append(formatILat(n.getILat())).append("\">").append(sele).append("</trkpt>\n");
nn = n;
}
sb.append(" </trkseg>\n");
@ -921,6 +1145,18 @@ public final class OsmTrack {
return time;
}
SimpleDateFormat TIMESTAMP_FORMAT;
public String getFormattedTime3(float time) {
if (TIMESTAMP_FORMAT == null) {
TIMESTAMP_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
TIMESTAMP_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
}
// yyyy-mm-ddThh:mm:ss.SSSZ
Date d = new Date((long) (time * 1000f));
return TIMESTAMP_FORMAT.format(d);
}
public String getFormattedEnergy() {
return format1(energy / 3600000.) + "kwh";
}
@ -1060,6 +1296,17 @@ public final class OsmTrack {
input.indexInTrack = --nodeNr;
input.goodWay = node.message;
input.oldWay = node.origin.message == null ? node.message : node.origin.message;
if (rc.turnInstructionMode == 8 ||
rc.turnInstructionMode == 4 ||
rc.turnInstructionMode == 2 ||
rc.turnInstructionMode == 9) {
MatchedWaypoint mwpt = getMatchedWaypoint(nodeNr);
if (mwpt != null && mwpt.direct) {
input.cmd = VoiceHint.BL;
input.angle = (float) (nodeNr == 0 ? node.origin.message.turnangle : node.message.turnangle);
input.distanceToNext = node.calcDistance(node.origin);
}
}
OsmPathElementHolder detours = detourMap.get(node.origin.getIdFromPos());
if (nodeNr >= 0 && detours != null) {
OsmPathElementHolder h = detours;
@ -1081,8 +1328,8 @@ public final class OsmTrack {
List<VoiceHint> results = vproc.process(inputs);
double minDistance = getMinDistance();
List < VoiceHint > resultsLast = vproc.postProcess(results, rc.turnInstructionCatchingRange, minDistance);
for (VoiceHint hint: resultsLast) {
List<VoiceHint> resultsLast = vproc.postProcess(results, rc.turnInstructionCatchingRange, minDistance);
for (VoiceHint hint : resultsLast) {
voiceHints.list.add(hint);
}

View file

@ -171,6 +171,7 @@ public final class RoutingContext {
showspeed = 0.f != expctxGlobal.getVariableValue("showspeed", 0.f);
showSpeedProfile = 0.f != expctxGlobal.getVariableValue("showSpeedProfile", 0.f);
inverseRouting = 0.f != expctxGlobal.getVariableValue("inverseRouting", 0.f);
showTime = 0.f != expctxGlobal.getVariableValue("showtime", 0.f);
int tiMode = (int) expctxGlobal.getVariableValue("turnInstructionMode", 0.f);
if (tiMode != 1) // automatic selection from coordinate source
@ -233,6 +234,7 @@ public final class RoutingContext {
public boolean showspeed;
public boolean showSpeedProfile;
public boolean inverseRouting;
public boolean showTime;
public OsmPrePath firstPrePath;

View file

@ -246,9 +246,10 @@ public class RoutingEngine extends Thread {
int startIdx = 0;
int endIdx = -1;
int dist = 0;
for (int idx = 0; idx < track.nodes.size(); idx++) {
int ourSize = track.nodes.size();
for (int idx = 0; idx < ourSize; idx++) {
OsmPathElement n = track.nodes.get(idx);
if (n.getSElev() == Short.MIN_VALUE && lastElev != Short.MIN_VALUE) {
if (n.getSElev() == Short.MIN_VALUE && lastElev != Short.MIN_VALUE && idx < ourSize-1) {
// start one point before entry point to get better elevation results
if (idx > 1)
startElev = track.nodes.get(idx - 2).getSElev();
@ -511,6 +512,9 @@ public class RoutingEngine extends Thread {
totaltrack.processVoiceHints(routingContext);
totaltrack.prepareSpeedProfile(routingContext);
totaltrack.showTime = routingContext.showTime;
totaltrack.params = routingContext.keyValues;
if (routingContext.poipoints != null)
totaltrack.pois = routingContext.poipoints;
totaltrack.matchedWaypoints = matchedWaypoints;

View file

@ -102,6 +102,10 @@ public class VoiceHint {
return "RNDB" + roundaboutExit;
case RNLB:
return "RNLB" + (-roundaboutExit);
case BL:
return "BL";
case OFFR:
return "OFFR";
default:
throw new IllegalArgumentException("unknown command: " + cmd);
}
@ -140,6 +144,10 @@ public class VoiceHint {
return "RNDB" + roundaboutExit;
case RNLB:
return "RNLB" + (-roundaboutExit);
case BL:
return "BL";
case OFFR:
return "OFFR";
default:
return "unknown command: " + c;
}
@ -178,6 +186,10 @@ public class VoiceHint {
return "RNDB" + roundaboutExit;
case RNLB:
return "RNLB" + (-roundaboutExit);
case BL:
return "BL";
case OFFR:
return "OFFR";
default:
throw new IllegalArgumentException("unknown command: " + cmd);
}
@ -372,6 +384,8 @@ public class VoiceHint {
return "RNLB" + (-roundaboutExit);
case BL:
return "BL";
case OFFR:
return "OFFR";
default:
throw new IllegalArgumentException("unknown command: " + cmd);
}
@ -407,11 +421,13 @@ public class VoiceHint {
case TRU:
return "u-turn right";
case RNDB:
return "Take exit " + roundaboutExit;
return "take exit " + roundaboutExit;
case RNLB:
return "Take exit " + (-roundaboutExit);
return "take exit " + (-roundaboutExit);
case BL:
return "Beeline";
return "beeline";
case OFFR:
return "offroad";
default:
throw new IllegalArgumentException("unknown command: " + cmd);
}

View file

@ -1,9 +1,12 @@
package btools.mapaccess;
import java.util.List;
import java.util.Collections;
import java.util.Comparator;
import btools.codec.WaypointMatcher;
import btools.util.CheapRuler;
import btools.util.CheapAngleMeter;
/**
* the WaypointMatcher is feeded by the decoder with geoemtries of ways that are
@ -13,6 +16,8 @@ import btools.util.CheapRuler;
* match for each waypoint
*/
public final class WaypointMatcherImpl implements WaypointMatcher {
private static final int MAX_POINTS = 5;
private List<MatchedWaypoint> waypoints;
private OsmNodePairSet islandPairs;
@ -24,12 +29,37 @@ public final class WaypointMatcherImpl implements WaypointMatcher {
private int lonLast;
private int latLast;
private Comparator<MatchedWaypoint> comparator;
public WaypointMatcherImpl(List<MatchedWaypoint> waypoints, double maxDistance, OsmNodePairSet islandPairs) {
this.waypoints = waypoints;
this.islandPairs = islandPairs;
MatchedWaypoint last = null;
for (MatchedWaypoint mwp : waypoints) {
mwp.radius = maxDistance;
if (last != null && mwp.directionToNext == -1) {
last.directionToNext = CheapAngleMeter.getDirection(last.waypoint.ilon, last.waypoint.ilat, mwp.waypoint.ilon, mwp.waypoint.ilat);
}
last = mwp;
}
// last point has no angle so we are looking back
int lastidx = waypoints.size() - 2;
if (lastidx < 0) {
last.directionToNext = -1;
} else {
last.directionToNext = CheapAngleMeter.getDirection(last.waypoint.ilon, last.waypoint.ilat, waypoints.get(lastidx).waypoint.ilon, waypoints.get(lastidx).waypoint.ilat);
}
// sort result list
comparator = new Comparator<MatchedWaypoint>() {
@Override
public int compare(MatchedWaypoint mw1, MatchedWaypoint mw2) {
int cmpDist = Double.compare(mw1.radius, mw2.radius);
if (cmpDist != 0) return cmpDist;
return Double.compare(mw1.directionDiff, mw2.directionDiff);
}
};
}
private void checkSegment(int lon1, int lat1, int lon2, int lat2) {
@ -46,8 +76,8 @@ public final class WaypointMatcherImpl implements WaypointMatcher {
if (d == 0.)
return;
int len = waypoints.size();
for (int i = 0; i < len; i++) {
//for ( MatchedWaypoint mwp : waypoints )
for (int i = 0; i < waypoints.size(); i++) {
MatchedWaypoint mwp = waypoints.get(i);
if (mwp.direct &&
@ -65,7 +95,6 @@ public final class WaypointMatcherImpl implements WaypointMatcher {
}
OsmNode wp = mwp.waypoint;
double x1 = (lon1 - wp.ilon) * dlon2m;
double y1 = (lat1 - wp.ilat) * dlat2m;
double x2 = (lon2 - wp.ilon) * dlon2m;
@ -74,7 +103,7 @@ public final class WaypointMatcherImpl implements WaypointMatcher {
double r22 = x2 * x2 + y2 * y2;
double radius = Math.abs(r12 < r22 ? y1 * dx - x1 * dy : y2 * dx - x2 * dy) / d;
if (radius < mwp.radius) {
if (radius <= mwp.radius) {
double s1 = x1 * dx + y1 * dy;
double s2 = x2 * dx + y2 * dy;
@ -141,11 +170,67 @@ public final class WaypointMatcherImpl implements WaypointMatcher {
if (anyUpdate) {
for (MatchedWaypoint mwp : waypoints) {
if (mwp.hasUpdate) {
double angle = CheapAngleMeter.getDirection(lonStart, latStart, lonTarget, latTarget);
double diff = CheapAngleMeter.getDifferenceFromDirection(mwp.directionToNext, angle);
mwp.hasUpdate = false;
mwp.node1 = new OsmNode(lonStart, latStart);
mwp.node2 = new OsmNode(lonTarget, latTarget);
MatchedWaypoint mw = new MatchedWaypoint();
mw.waypoint = new OsmNode();
mw.waypoint.ilon = mwp.waypoint.ilon;
mw.waypoint.ilat = mwp.waypoint.ilat;
mw.crosspoint = new OsmNode();
mw.crosspoint.ilon = mwp.crosspoint.ilon;
mw.crosspoint.ilat = mwp.crosspoint.ilat;
mw.node1 = new OsmNode(lonStart, latStart);
mw.node2 = new OsmNode(lonTarget, latTarget);
mw.name = mwp.name + "_w_" + mwp.crosspoint.hashCode();
mw.radius = mwp.radius;
mw.directionDiff = diff;
mw.directionToNext = mwp.directionToNext;
updateWayList(mwp.wayNearest, mw);
// revers
angle = CheapAngleMeter.getDirection(lonTarget, latTarget, lonStart, latStart);
diff = CheapAngleMeter.getDifferenceFromDirection(mwp.directionToNext, angle);
mw = new MatchedWaypoint();
mw.waypoint = new OsmNode();
mw.waypoint.ilon = mwp.waypoint.ilon;
mw.waypoint.ilat = mwp.waypoint.ilat;
mw.crosspoint = new OsmNode();
mw.crosspoint.ilon = mwp.crosspoint.ilon;
mw.crosspoint.ilat = mwp.crosspoint.ilat;
mw.node1 = new OsmNode(lonTarget, latTarget);
mw.node2 = new OsmNode(lonStart, latStart);
mw.name = mwp.name + "_w2_" + mwp.crosspoint.hashCode();
mw.radius = mwp.radius;
mw.directionDiff = diff;
mw.directionToNext = mwp.directionToNext;
updateWayList(mwp.wayNearest, mw);
MatchedWaypoint way = mwp.wayNearest.get(0);
mwp.crosspoint.ilon = way.crosspoint.ilon;
mwp.crosspoint.ilat = way.crosspoint.ilat;
mwp.node1 = new OsmNode(way.node1.ilon, way.node1.ilat);
mwp.node2 = new OsmNode(way.node2.ilon, way.node2.ilat);
mwp.directionDiff = way.directionDiff;
mwp.radius = way.radius;
}
}
}
}
// check limit of list size (avoid long runs)
void updateWayList(List<MatchedWaypoint> ways, MatchedWaypoint mw) {
ways.add(mw);
// use only shortest distances by smallest direction difference
Collections.sort(ways, comparator);
if (ways.size() > MAX_POINTS) ways.remove(MAX_POINTS);
}
}

View file

@ -31,7 +31,7 @@ interface IBRouterService {
// "exportWaypoints" = 1 to export them (optional, default is no export)
// "pois" = lon,lat,name|... (optional)
// "extraParams" = Bundle key=value list for a profile setup (like "profile:")
// "timode" = turnInstructionMode [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-new-style, 8=cruiser-stylem, 9=brouter-intern] default 0
// "timode" = turnInstructionMode [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-old-style] default 0
// "heading" = angle (optional to give a route a start direction)
// "direction" = angle (optional, used like "heading" on a recalculation request by Locus as start direction)

View file

@ -46,4 +46,30 @@ public final class CheapAngleMeter {
}
return offset + sinp * (57.4539 + s2 * (9.57565 + s2 * (4.30904 + s2 * 2.56491)));
}
public static double getAngle(int lon1, int lat1, int lon2, int lat2) {
double res = 0;
double xdiff = lat2 - lat1;
double ydiff = lon2 - lon1;
res = Math.toDegrees(Math.atan2(ydiff, xdiff));
return res;
}
public static double getDirection(int lon1, int lat1, int lon2, int lat2) {
double res = getAngle(lon1, lat1, lon2, lat2);
return normalize(res);
}
public static double normalize(double a) {
return a >= 360 ? a - (360 * (int) (a / 360))
: a < 0 ? a - (360 * ((int) (a / 360) - 1)) : a;
}
public static double getDifferenceFromDirection(double b1, double b2) {
double r = (b2 - b1) % 360.0;
if (r < -180.0) r += 360.0;
if (r >= 180.0) r -= 360.0;
return Math.abs(r);
}
}

View file

@ -109,4 +109,149 @@ public class CheapAngleMeterTest {
}
}
@Test
public void testGetAngle() {
CheapAngleMeter am = new CheapAngleMeter();
int lon1, lat1, lon2, lat2;
lon1 = toOsmLon(10.0);
lat1 = toOsmLat(50.0);
lon2 = toOsmLon(10.0);
lat2 = toOsmLat(60.0);
double angle = am.getAngle(lon1, lat1, lon2, lat2);
assertEquals("Angle = " + angle, 0.0, angle, 0.0);
lon2 = toOsmLon(10.0);
lat2 = toOsmLat(40.0);
angle = am.getAngle(lon1, lat1, lon2, lat2);
assertEquals("Angle = " + angle, 180.0, angle, 0.0);
lon2 = toOsmLon(0.0);
lat2 = toOsmLat(50.0);
angle = am.getAngle(lon1, lat1, lon2, lat2);
assertEquals("Angle = " + angle, -90.0, angle, 0.0);
lon2 = toOsmLon(20.0);
lat2 = toOsmLat(50.0);
angle = am.getAngle(lon1, lat1, lon2, lat2);
assertEquals("Angle = " + angle, 90.0, angle, 0.0);
lon2 = toOsmLon(20.0);
lat2 = toOsmLat(60.0);
angle = am.getAngle(lon1, lat1, lon2, lat2);
assertEquals("Angle = " + angle, 45.0, angle, 0.0);
lon2 = toOsmLon(0.0);
lat2 = toOsmLat(60.0);
angle = am.getAngle(lon1, lat1, lon2, lat2);
assertEquals("Angle = " + angle, -45.0, angle, 0.0);
lon1 = 1;
lat1 = 1;
lon2 = 2;
lat2 = 2;
angle = am.getAngle(lon1, lat1, lon2, lat2);
assertEquals("Angle = " + angle, 45.0, angle, 0.0);
}
@Test
public void testGetDirection() {
CheapAngleMeter am = new CheapAngleMeter();
int lon1, lat1, lon2, lat2;
lon1 = toOsmLon(10.0);
lat1 = toOsmLat(50.0);
lon2 = toOsmLon(10.0);
lat2 = toOsmLat(60.0);
double angle = am.getDirection(lon1, lat1, lon2, lat2);
assertEquals("Direction = " + angle, 0.0, angle, 0.0);
lon2 = toOsmLon(10.0);
lat2 = toOsmLat(40.0);
angle = am.getDirection(lon1, lat1, lon2, lat2);
assertEquals("Direction = " + angle, 180.0, angle, 0.0);
lon2 = toOsmLon(0.0);
lat2 = toOsmLat(50.0);
angle = am.getDirection(lon1, lat1, lon2, lat2);
assertEquals("Direction = " + angle, 270.0, angle, 0.0);
lon2 = toOsmLon(20.0);
lat2 = toOsmLat(50.0);
angle = am.getDirection(lon1, lat1, lon2, lat2);
assertEquals("Direction = " + angle, 90.0, angle, 0.0);
lon2 = toOsmLon(20.0);
lat2 = toOsmLat(60.0);
angle = am.getDirection(lon1, lat1, lon2, lat2);
assertEquals("Direction = " + angle, 45.0, angle, 0.0);
lon2 = toOsmLon(0.0);
lat2 = toOsmLat(60.0);
angle = am.getDirection(lon1, lat1, lon2, lat2);
assertEquals("Direction = " + angle, 315.0, angle, 0.0);
lon1 = 1;
lat1 = 1;
lon2 = 2;
lat2 = 2;
angle = am.getDirection(lon1, lat1, lon2, lat2);
assertEquals("Direction = " + angle, 45.0, angle, 0.0);
}
@Test
public void testNormalize() {
CheapAngleMeter am = new CheapAngleMeter();
double n = 1;
assertEquals("Direction normalize = " + n, 1, am.normalize(n), 0.0);
n = -1;
assertEquals("Direction normalize = " + n, 359, am.normalize(n), 0.0);
n = 361;
assertEquals("Direction normalize = " + n, 1, am.normalize(n), 0.0);
n = 0;
assertEquals("Direction normalize = " + n, 0, am.normalize(n), 0.0);
n = 360;
assertEquals("Direction normalize = " + n, 0, am.normalize(n), 0.0);
}
@Test
public void testCalcAngle6() {
CheapAngleMeter am = new CheapAngleMeter();
double a1 = 90;
double a2 = 180;
assertEquals("Direction diff " + a1 + " " + a2 + " = ", 90, am.getDifferenceFromDirection(a1, a2), 0.0);
a1 = 180;
a2 = 90;
assertEquals("Direction diff " + a1 + " " + a2 + " = ", 90, am.getDifferenceFromDirection(a1, a2), 0.0);
a1 = 5;
a2 = 355;
assertEquals("Direction diff " + a1 + " " + a2 + " = ", 10, am.getDifferenceFromDirection(a1, a2), 0.0);
a1 = 355;
a2 = 5;
assertEquals("Direction diff " + a1 + " " + a2 + " = ", 10, am.getDifferenceFromDirection(a1, a2), 0.0);
a1 = 90;
a2 = 270;
assertEquals("Direction diff " + a1 + " " + a2 + " = ", 180, am.getDifferenceFromDirection(a1, a2), 0.0);
a1 = 270;
a2 = 90;
assertEquals("Direction diff " + a1 + " " + a2 + " = ", 180, am.getDifferenceFromDirection(a1, a2), 0.0);
}
}

View file

@ -97,7 +97,7 @@ Some variable names are pre-defined and accessed by the routing engine:
- 3 variables to influence the generation of turn-instructions
- `turnInstructionMode` 0=none, 1=auto-choose, 2=locus-style,
3=osmand-style
3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-old-style
- `turnInstructionCatchingRange` default=40m
- `turnInstructionRoundabouts` default=true generate explicit roundabout
hints

View file

@ -27,7 +27,7 @@ assign f_recup = 400 # %f_recup% | Newton | number
assign p_standby = 250 # %p_standby% | Watt | number
# Turn instructions settings
assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style]
assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-old-style]
# Technical parameters
assign pass1coefficient = 1.3

View file

@ -17,7 +17,7 @@
assign processUnusedTags false # set to true if you want to display all tags in the "data"
# to generate turn instructions, adapt the mode by need
assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style]
assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-old-style]
# Use the following switches to change behaviour

View file

@ -47,7 +47,7 @@ assign C_r = 0.01 # %C_r% | Rolling resistance coefficient (dimensionle
assign bikerPower = 100 # %bikerPower% | Average power (in W) provided by the biker, for travel time computation | number
# Turn instructions settings
assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style]
assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-old-style]
assign turnInstructionCatchingRange = 40 # %turnInstructionCatchingRange% | Within this distance (in m) several turning instructions are combined into one and the turning angles are better approximated to the general direction | number
assign turnInstructionRoundabouts = true # %turnInstructionRoundabouts% | Set to "false" to avoid generating special turning instructions for roundabouts | boolean

View file

@ -13,7 +13,7 @@
assign consider_elevation 1 # 0 as default, otherwise less interesting flat roads are chosen.
assign shortest_way 0 # 0 as default, duplicate shortest standard profile, SAC access limit ignored for now
assign turnInstructionMode = 1 # 0=none, 1=auto-choose, 2=locus-style, 3=osmand-style
assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-old-style]
assign turnInstructionCatchingRange 20 # V1.8.5 / default=40, but foot paths may be more distingushed, especially in cities.
assign iswet 0 # 0 as default, 1 tries to prevent muddy boots and wet buttocks

View file

@ -16,7 +16,7 @@ assign uphillcutoff 0
assign validForBikes 1
assign validForCars 1
assign turnInstructionMode = 1 # 0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style
assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-old-style]
---context:way # following code refers to way-tags

View file

@ -7,7 +7,7 @@ assign downhillcutoff 1.5
assign uphillcost 0
assign uphillcutoff 1.5
assign turnInstructionMode = 1 # 0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style
assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-old-style]
assign validForFoot 1
---context:way # following code refers to way-tags

View file

@ -35,7 +35,7 @@ assign C_r = 0.01 # %C_r% | Rolling resistance coefficient (dimensionle
assign bikerPower = 100 # %bikerPower% | Average power (in W) provided by the biker, for travel time computation | number
# Turn instructions settings
assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style]
assign turnInstructionMode = 1 # %turnInstructionMode% | Mode for the generated turn instructions | [0=none, 1=auto-choose, 2=locus-style, 3=osmand-style, 4=comment-style, 5=gpsies-style, 6=orux-style, 7=locus-old-style]
assign turnInstructionCatchingRange = 40 # %turnInstructionCatchingRange% | Within this distance (in m) several turning instructions are combined into one and the turning angles are better approximated to the general direction | number
assign turnInstructionRoundabouts = true # %turnInstructionRoundabouts% | Set to "false" to avoid generating special turning instructions for roundabouts | boolean

View file

@ -23,7 +23,7 @@ assign bikerPower = 125 # %bikerPower% | Dauerleistung in Watt, für Fahrtzei
assign ignore_bicycle_no = false # %ignore_bicycle_no% | für Regionen mit falschen bicyle=no tags können sie mit "true" ignoriert werden. Auch Shuttletransporte (bicycle=dismount + foot=no) werden geroutet | boolean
assign turnInstructionMode = 1 # %turnInstructionMode% | Modus für die Abbiegehinweise | [0=keine, 1=automatische Wahl, 2=locus-Style, 3=osmand-Style, 4=comment-Style, 5=gpsies-Style, 6=orux-Style]
assign turnInstructionMode = 1 # %turnInstructionMode% | Modus für die Abbiegehinweise | [0=keine, 1=automatische Wahl, 2=locus-Style, 3=osmand-Style, 4=comment-Style, 5=gpsies-Style, 6=orux-Style, 7=locus-old-Style]
assign turnInstructionCatchingRange 20 # innerhalb dieser Strecke werden mehrere Abiegehinweise zu einem zusammengefasst und die Abiegewinkel werden besser an die generelle Richtung angenähert
assign turnInstructionRoundabouts true # mit "false" werden keine speziellen Abiegehinweise für den Kreisverkehr generiert

View file

@ -23,7 +23,7 @@ assign bikerPower = 125 # %bikerPower% | Dauerleistung in Watt, für Fahrtzei
assign ignore_bicycle_no = false # %ignore_bicycle_no% | für Regionen mit falschen bicyle=no tags können sie mit "true" ignoriert werden. Auch Shuttletransporte (bicycle=dismount + foot=no) werden geroutet | boolean
assign turnInstructionMode = 1 # %turnInstructionMode% | Modus für die Abbiegehinweise | [0=keine, 1=automatische Wahl, 2=locus-Style, 3=osmand-Style, 4=comment-Style, 5=gpsies-Style, 6=orux-Style]
assign turnInstructionMode = 1 # %turnInstructionMode% | Modus für die Abbiegehinweise | [0=keine, 1=automatische Wahl, 2=locus-Style, 3=osmand-Style, 4=comment-Style, 5=gpsies-Style, 6=orux-Style, 7=locus-old-Style]
assign turnInstructionCatchingRange 20 # innerhalb dieser Strecke werden mehrere Abiegehinweise zu einem zusammengefasst und die Abiegewinkel werden besser an die generelle Richtung angenähert
assign turnInstructionRoundabouts true # mit "false" werden keine speziellen Abiegehinweise für den Kreisverkehr generiert