Merge branch 'abrensch:master' into rework-voicehint
This commit is contained in:
commit
4c310bf4b4
31 changed files with 1600 additions and 1650 deletions
43
brouter-core/src/main/java/btools/router/FormatCsv.java
Normal file
43
brouter-core/src/main/java/btools/router/FormatCsv.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
package btools.router;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
public class FormatCsv extends Formatter {
|
||||
|
||||
|
||||
public FormatCsv(RoutingContext rc) {
|
||||
super(rc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(OsmTrack t) {
|
||||
try {
|
||||
StringWriter sw = new StringWriter();
|
||||
BufferedWriter bw = new BufferedWriter(sw);
|
||||
writeMessages(bw, t);
|
||||
return sw.toString();
|
||||
} catch (Exception ex) {
|
||||
return "Error: " + ex.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public void writeMessages(BufferedWriter bw, OsmTrack t) throws Exception {
|
||||
dumpLine(bw, MESSAGES_HEADER);
|
||||
for (String m : t.aggregateMessages()) {
|
||||
dumpLine(bw, m);
|
||||
}
|
||||
if (bw != null)
|
||||
bw.close();
|
||||
}
|
||||
|
||||
private void dumpLine(BufferedWriter bw, String s) throws Exception {
|
||||
if (bw == null) {
|
||||
System.out.println(s);
|
||||
} else {
|
||||
bw.write(s);
|
||||
bw.write("\n");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
532
brouter-core/src/main/java/btools/router/FormatGpx.java
Normal file
532
brouter-core/src/main/java/btools/router/FormatGpx.java
Normal file
|
@ -0,0 +1,532 @@
|
|||
package btools.router;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Map;
|
||||
|
||||
import btools.mapaccess.MatchedWaypoint;
|
||||
import btools.util.StringUtils;
|
||||
|
||||
public class FormatGpx extends Formatter {
|
||||
public FormatGpx(RoutingContext rc) {
|
||||
super(rc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(OsmTrack t) {
|
||||
try {
|
||||
StringWriter sw = new StringWriter(8192);
|
||||
BufferedWriter bw = new BufferedWriter(sw);
|
||||
formatAsGpx(bw, t);
|
||||
bw.close();
|
||||
return sw.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String formatAsGpx(BufferedWriter sb, OsmTrack t) throws IOException {
|
||||
int turnInstructionMode = t.voiceHints != null ? t.voiceHints.turnInstructionMode : 0;
|
||||
|
||||
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
if (turnInstructionMode != 9) {
|
||||
for (int i = t.messageList.size() - 1; i >= 0; i--) {
|
||||
String message = t.messageList.get(i);
|
||||
if (i < t.messageList.size() - 1)
|
||||
message = "(alt-index " + i + ": " + message + " )";
|
||||
if (message != null)
|
||||
sb.append("<!-- ").append(message).append(" -->\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (turnInstructionMode == 4) { // comment style
|
||||
sb.append("<!-- $transport-mode$").append(t.voiceHints.getTransportMode()).append("$ -->\n");
|
||||
sb.append("<!-- cmd idx lon lat d2next geometry -->\n");
|
||||
sb.append("<!-- $turn-instruction-start$\n");
|
||||
for (VoiceHint hint : t.voiceHints.list) {
|
||||
sb.append(String.format(" $turn$%6s;%6d;%10s;%10s;%6d;%s$\n", hint.getCommandString(), hint.indexInTrack,
|
||||
formatILon(hint.ilon), formatILat(hint.ilat), (int) (hint.distanceToNext), hint.formatGeometry()));
|
||||
}
|
||||
sb.append(" $turn-instruction-end$ -->\n");
|
||||
}
|
||||
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 == 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");
|
||||
|
||||
if (turnInstructionMode == 3) {
|
||||
sb.append(" creator=\"OsmAndRouter\" version=\"1.1\">\n");
|
||||
} else {
|
||||
sb.append(" creator=\"BRouter-" + t.version + "\" version=\"1.1\">\n");
|
||||
}
|
||||
if (turnInstructionMode == 9) {
|
||||
sb.append(" <metadata>\n");
|
||||
sb.append(" <name>").append(t.name).append("</name>\n");
|
||||
sb.append(" <extensions>\n");
|
||||
sb.append(" <brouter:info>").append(t.messageList.get(0)).append("</brouter:info>\n");
|
||||
if (t.params != null && t.params.size() > 0) {
|
||||
sb.append(" <brouter:params><![CDATA[");
|
||||
int i = 0;
|
||||
for (Map.Entry<String, String> e : t.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");
|
||||
|
||||
float rteTime = t.getVoiceHintTime(0);
|
||||
StringBuffer first = new StringBuffer();
|
||||
// define start point
|
||||
{
|
||||
first.append(" <rtept lat=\"").append(formatILat(t.nodes.get(0).getILat())).append("\" lon=\"")
|
||||
.append(formatILon(t.nodes.get(0).getILon())).append("\">\n")
|
||||
.append(" <desc>start</desc>\n <extensions>\n");
|
||||
if (rteTime != lastRteTime) { // add timing only if available
|
||||
double ti = rteTime - lastRteTime;
|
||||
first.append(" <time>").append("" + (int) (ti + 0.5)).append("</time>\n");
|
||||
lastRteTime = rteTime;
|
||||
}
|
||||
first.append(" <offset>0</offset>\n </extensions>\n </rtept>\n");
|
||||
}
|
||||
if (turnInstructionMode == 8) {
|
||||
if (t.matchedWaypoints.get(0).direct && t.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 < t.voiceHints.list.size(); i++) {
|
||||
VoiceHint hint = t.voiceHints.list.get(i);
|
||||
sb.append(" <rtept lat=\"").append(formatILat(hint.ilat)).append("\" lon=\"")
|
||||
.append(formatILon(hint.ilon)).append("\">\n")
|
||||
.append(" <desc>")
|
||||
.append(turnInstructionMode == 3 ? hint.getMessageString() : hint.getCruiserMessageString())
|
||||
.append("</desc>\n <extensions>\n");
|
||||
|
||||
rteTime = t.getVoiceHintTime(i + 1);
|
||||
|
||||
if (rteTime != lastRteTime) { // add timing only if available
|
||||
double ti = rteTime - lastRteTime;
|
||||
sb.append(" <time>").append("" + (int) (ti + 0.5)).append("</time>\n");
|
||||
lastRteTime = rteTime;
|
||||
}
|
||||
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(t.nodes.get(t.nodes.size() - 1).getILat())).append("\" lon=\"")
|
||||
.append(formatILon(t.nodes.get(t.nodes.size() - 1).getILon())).append("\">\n")
|
||||
.append(" <desc>destination</desc>\n <extensions>\n");
|
||||
sb.append(" <time>0</time>\n");
|
||||
sb.append(" <offset>").append("" + (t.nodes.size() - 1)).append("</offset>\n </extensions>\n </rtept>\n");
|
||||
|
||||
sb.append("</rte>\n");
|
||||
}
|
||||
|
||||
if (turnInstructionMode == 7) { // old locus style
|
||||
float lastRteTime = t.getVoiceHintTime(0);
|
||||
|
||||
for (int i = 0; i < t.voiceHints.list.size(); i++) {
|
||||
VoiceHint hint = t.voiceHints.list.get(i);
|
||||
sb.append(" <wpt lon=\"").append(formatILon(hint.ilon)).append("\" lat=\"")
|
||||
.append(formatILat(hint.ilat)).append("\">")
|
||||
.append(hint.selev == Short.MIN_VALUE ? "" : "<ele>" + (hint.selev / 4.) + "</ele>")
|
||||
.append("<name>").append(hint.getMessageString()).append("</name>")
|
||||
.append("<extensions><locus:rteDistance>").append("" + hint.distanceToNext).append("</locus:rteDistance>");
|
||||
float rteTime = t.getVoiceHintTime(i + 1);
|
||||
if (rteTime != lastRteTime) { // add timing only if available
|
||||
double ti = rteTime - lastRteTime;
|
||||
double speed = hint.distanceToNext / ti;
|
||||
sb.append("<locus:rteTime>").append("" + ti).append("</locus:rteTime>")
|
||||
.append("<locus:rteSpeed>").append("" + speed).append("</locus:rteSpeed>");
|
||||
lastRteTime = rteTime;
|
||||
}
|
||||
sb.append("<locus:rtePointAction>").append("" + hint.getLocusAction()).append("</locus:rtePointAction></extensions>")
|
||||
.append("</wpt>\n");
|
||||
}
|
||||
}
|
||||
if (turnInstructionMode == 5) { // gpsies style
|
||||
for (VoiceHint hint : t.voiceHints.list) {
|
||||
sb.append(" <wpt lon=\"").append(formatILon(hint.ilon)).append("\" lat=\"")
|
||||
.append(formatILat(hint.ilat)).append("\">")
|
||||
.append("<name>").append(hint.getMessageString()).append("</name>")
|
||||
.append("<sym>").append(hint.getSymbolString().toLowerCase()).append("</sym>")
|
||||
.append("<type>").append(hint.getSymbolString()).append("</type>")
|
||||
.append("</wpt>\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (turnInstructionMode == 6) { // orux style
|
||||
for (VoiceHint hint : t.voiceHints.list) {
|
||||
sb.append(" <wpt lat=\"").append(formatILat(hint.ilat)).append("\" lon=\"")
|
||||
.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())
|
||||
.append("</om:ext>\n" +
|
||||
" </om:oruxmapsextensions>\n" +
|
||||
" </extensions>\n" +
|
||||
" </wpt>\n");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i <= t.pois.size() - 1; i++) {
|
||||
OsmNodeNamed poi = t.pois.get(i);
|
||||
sb.append(" <wpt lon=\"").append(formatILon(poi.ilon)).append("\" lat=\"")
|
||||
.append(formatILat(poi.ilat)).append("\">\n")
|
||||
.append(" <name>").append(StringUtils.escapeXml10(poi.name)).append("</name>\n")
|
||||
.append(" </wpt>\n");
|
||||
}
|
||||
|
||||
if (t.exportWaypoints) {
|
||||
for (int i = 0; i <= t.matchedWaypoints.size() - 1; i++) {
|
||||
MatchedWaypoint wt = t.matchedWaypoints.get(i);
|
||||
sb.append(" <wpt lon=\"").append(formatILon(wt.waypoint.ilon)).append("\" lat=\"")
|
||||
.append(formatILat(wt.waypoint.ilat)).append("\">\n")
|
||||
.append(" <name>").append(StringUtils.escapeXml10(wt.name)).append("</name>\n");
|
||||
if (i == 0) {
|
||||
sb.append(" <type>from</type>\n");
|
||||
} else if (i == t.matchedWaypoints.size() - 1) {
|
||||
sb.append(" <type>to</type>\n");
|
||||
} else {
|
||||
sb.append(" <type>via</type>\n");
|
||||
}
|
||||
sb.append(" </wpt>\n");
|
||||
}
|
||||
}
|
||||
sb.append(" <trk>\n");
|
||||
if (turnInstructionMode == 9
|
||||
|| turnInstructionMode == 2
|
||||
|| turnInstructionMode == 8
|
||||
|| turnInstructionMode == 4) { // Locus, comment, cruise, brouter style
|
||||
sb.append(" <src>").append(t.name).append("</src>\n");
|
||||
sb.append(" <type>").append(t.voiceHints.getTransportMode()).append("</type>\n");
|
||||
} else {
|
||||
sb.append(" <name>").append(t.name).append("</name>\n");
|
||||
}
|
||||
|
||||
if (turnInstructionMode == 7) {
|
||||
sb.append(" <extensions>\n");
|
||||
sb.append(" <locus:rteComputeType>").append("" + t.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 < t.nodes.size(); idx++) {
|
||||
OsmPathElement n = t.nodes.get(idx);
|
||||
String sele = n.getSElev() == Short.MIN_VALUE ? "" : "<ele>" + n.getElev() + "</ele>";
|
||||
VoiceHint hint = t.getVoiceHint(idx);
|
||||
MatchedWaypoint mwpt = t.getMatchedWaypoint(idx);
|
||||
|
||||
if (t.showTime) {
|
||||
sele += "<time>" + getFormattedTime3(n.getTime()) + "</time>";
|
||||
}
|
||||
if (turnInstructionMode == 8) {
|
||||
if (mwpt != null &&
|
||||
!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
|
||||
sele += "<name>" + mwpt.name + "</name>";
|
||||
}
|
||||
}
|
||||
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("to")) {
|
||||
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 (t.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 == t.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 = (t.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 (t.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.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
|
||||
sele += "<name>" + mwpt.name + "</name>";
|
||||
}
|
||||
if (mwpt.direct && bNextDirect) {
|
||||
sele += "<src>" + hint.getLocusSymbolString() + "</src><sym>pass_place</sym><type>Shaping</type>";
|
||||
// bNextDirect = false;
|
||||
} else if (mwpt.direct) {
|
||||
if (idx == 0)
|
||||
sele += "<sym>pass_place</sym><type>Via</type>";
|
||||
else
|
||||
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><type>Via</type>";
|
||||
}
|
||||
} 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 == t.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 (!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
|
||||
sele += "<name>" + mwpt.name + "</name>";
|
||||
}
|
||||
if (mwpt.direct && bNextDirect) {
|
||||
sele += "<src>beeline</src><sym>pass_place</sym><type>Shaping</type>";
|
||||
} else if (mwpt.direct) {
|
||||
if (idx == 0)
|
||||
sele += "<sym>pass_place</sym><type>Via</type>";
|
||||
else
|
||||
sele += "<sym>pass_place</sym><type>Shaping</type>";
|
||||
bNextDirect = true;
|
||||
} else if (bNextDirect) {
|
||||
sele += "<src>beeline</src><sym>pass_place</sym><type>Shaping</type>";
|
||||
bNextDirect = false;
|
||||
} 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>Via</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");
|
||||
sb.append(" </trk>\n");
|
||||
sb.append("</gpx>\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String formatAsWaypoint(OsmNodeNamed n) {
|
||||
try {
|
||||
StringWriter sw = new StringWriter(8192);
|
||||
BufferedWriter bw = new BufferedWriter(sw);
|
||||
formatGpxHeader(bw);
|
||||
formatWaypointGpx(bw, n);
|
||||
formatGpxFooter(bw);
|
||||
bw.close();
|
||||
sw.close();
|
||||
return sw.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void formatGpxHeader(BufferedWriter sb) throws IOException {
|
||||
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
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");
|
||||
sb.append(" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\" \n");
|
||||
sb.append(" creator=\"BRouter-" + OsmTrack.version + "\" version=\"1.1\">\n");
|
||||
}
|
||||
|
||||
public void formatGpxFooter(BufferedWriter sb) throws IOException {
|
||||
sb.append("</gpx>\n");
|
||||
}
|
||||
|
||||
public void formatWaypointGpx(BufferedWriter sb, OsmNodeNamed n) throws IOException {
|
||||
sb.append(" <wpt lon=\"").append(formatILon(n.ilon)).append("\" lat=\"")
|
||||
.append(formatILat(n.ilat)).append("\">");
|
||||
if (n.getSElev() != Short.MIN_VALUE) {
|
||||
sb.append("<ele>").append("" + n.getElev()).append("</ele>");
|
||||
}
|
||||
if (n.name != null) {
|
||||
sb.append("<name>").append(StringUtils.escapeXml10(n.name)).append("</name>");
|
||||
}
|
||||
if (n.nodeDescription != null && rc != null) {
|
||||
sb.append("<desc>").append(rc.expctxWay.getKeyValueDescription(false, n.nodeDescription)).append("</desc>");
|
||||
}
|
||||
sb.append("</wpt>\n");
|
||||
}
|
||||
|
||||
public static String getWaypoint(int ilon, int ilat, String name, String desc) {
|
||||
return "<wpt lon=\"" + formatILon(ilon) + "\" lat=\"" + formatILat(ilat) + "\"><name>" + name + "</name>" + (desc != null ? "<desc>" + desc + "</desc>" : "") + "</wpt>";
|
||||
}
|
||||
|
||||
public OsmTrack read(String filename) throws Exception {
|
||||
File f = new File(filename);
|
||||
if (!f.exists()) {
|
||||
return null;
|
||||
}
|
||||
OsmTrack track = new OsmTrack();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
|
||||
|
||||
for (; ; ) {
|
||||
String line = br.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
|
||||
int idx0 = line.indexOf("<trkpt ");
|
||||
if (idx0 >= 0) {
|
||||
idx0 = line.indexOf(" lon=\"");
|
||||
idx0 += 6;
|
||||
int idx1 = line.indexOf('"', idx0);
|
||||
int ilon = (int) ((Double.parseDouble(line.substring(idx0, idx1)) + 180.) * 1000000. + 0.5);
|
||||
int idx2 = line.indexOf(" lat=\"");
|
||||
if (idx2 < 0)
|
||||
continue;
|
||||
idx2 += 6;
|
||||
int idx3 = line.indexOf('"', idx2);
|
||||
int ilat = (int) ((Double.parseDouble(line.substring(idx2, idx3)) + 90.) * 1000000. + 0.5);
|
||||
track.nodes.add(OsmPathElement.create(ilon, ilat, (short) 0, null, false));
|
||||
}
|
||||
}
|
||||
br.close();
|
||||
return track;
|
||||
}
|
||||
|
||||
}
|
246
brouter-core/src/main/java/btools/router/FormatJson.java
Normal file
246
brouter-core/src/main/java/btools/router/FormatJson.java
Normal file
|
@ -0,0 +1,246 @@
|
|||
package btools.router;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import btools.mapaccess.MatchedWaypoint;
|
||||
import btools.util.StringUtils;
|
||||
|
||||
public class FormatJson extends Formatter {
|
||||
|
||||
public FormatJson(RoutingContext rc) {
|
||||
super(rc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(OsmTrack t) {
|
||||
int turnInstructionMode = t.voiceHints != null ? t.voiceHints.turnInstructionMode : 0;
|
||||
|
||||
StringBuilder sb = new StringBuilder(8192);
|
||||
|
||||
sb.append("{\n");
|
||||
sb.append(" \"type\": \"FeatureCollection\",\n");
|
||||
sb.append(" \"features\": [\n");
|
||||
sb.append(" {\n");
|
||||
sb.append(" \"type\": \"Feature\",\n");
|
||||
sb.append(" \"properties\": {\n");
|
||||
sb.append(" \"creator\": \"BRouter-" + t.version + "\",\n");
|
||||
sb.append(" \"name\": \"").append(t.name).append("\",\n");
|
||||
sb.append(" \"track-length\": \"").append(t.distance).append("\",\n");
|
||||
sb.append(" \"filtered ascend\": \"").append(t.ascend).append("\",\n");
|
||||
sb.append(" \"plain-ascend\": \"").append(t.plainAscend).append("\",\n");
|
||||
sb.append(" \"total-time\": \"").append(t.getTotalSeconds()).append("\",\n");
|
||||
sb.append(" \"total-energy\": \"").append(t.energy).append("\",\n");
|
||||
sb.append(" \"cost\": \"").append(t.cost).append("\",\n");
|
||||
if (t.voiceHints != null && !t.voiceHints.list.isEmpty()) {
|
||||
sb.append(" \"voicehints\": [\n");
|
||||
for (VoiceHint hint : t.voiceHints.list) {
|
||||
sb.append(" [");
|
||||
sb.append(hint.indexInTrack);
|
||||
sb.append(',').append(hint.getJsonCommandIndex());
|
||||
sb.append(',').append(hint.getExitNumber());
|
||||
sb.append(',').append(hint.distanceToNext);
|
||||
sb.append(',').append((int) hint.angle);
|
||||
|
||||
// not always include geometry because longer and only needed for comment style
|
||||
if (turnInstructionMode == 4) { // comment style
|
||||
sb.append(",\"").append(hint.formatGeometry()).append("\"");
|
||||
}
|
||||
|
||||
sb.append("],\n");
|
||||
}
|
||||
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||
sb.append(" ],\n");
|
||||
}
|
||||
if (t.showSpeedProfile) { // set in profile
|
||||
List<String> sp = t.aggregateSpeedProfile();
|
||||
if (sp.size() > 0) {
|
||||
sb.append(" \"speedprofile\": [\n");
|
||||
for (int i = sp.size() - 1; i >= 0; i--) {
|
||||
sb.append(" [").append(sp.get(i)).append(i > 0 ? "],\n" : "]\n");
|
||||
}
|
||||
sb.append(" ],\n");
|
||||
}
|
||||
}
|
||||
// ... traditional message list
|
||||
{
|
||||
sb.append(" \"messages\": [\n");
|
||||
sb.append(" [\"").append(MESSAGES_HEADER.replaceAll("\t", "\", \"")).append("\"],\n");
|
||||
for (String m : t.aggregateMessages()) {
|
||||
sb.append(" [\"").append(m.replaceAll("\t", "\", \"")).append("\"],\n");
|
||||
}
|
||||
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||
sb.append(" ],\n");
|
||||
}
|
||||
|
||||
if (t.getTotalSeconds() > 0) {
|
||||
sb.append(" \"times\": [");
|
||||
DecimalFormat decimalFormat = (DecimalFormat) NumberFormat.getInstance(Locale.ENGLISH);
|
||||
decimalFormat.applyPattern("0.###");
|
||||
for (OsmPathElement n : t.nodes) {
|
||||
sb.append(decimalFormat.format(n.getTime())).append(",");
|
||||
}
|
||||
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||
sb.append("]\n");
|
||||
} else {
|
||||
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||
}
|
||||
|
||||
sb.append(" },\n");
|
||||
|
||||
if (t.iternity != null) {
|
||||
sb.append(" \"iternity\": [\n");
|
||||
for (String s : t.iternity) {
|
||||
sb.append(" \"").append(s).append("\",\n");
|
||||
}
|
||||
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||
sb.append(" ],\n");
|
||||
}
|
||||
sb.append(" \"geometry\": {\n");
|
||||
sb.append(" \"type\": \"LineString\",\n");
|
||||
sb.append(" \"coordinates\": [\n");
|
||||
|
||||
OsmPathElement nn = null;
|
||||
for (OsmPathElement n : t.nodes) {
|
||||
String sele = n.getSElev() == Short.MIN_VALUE ? "" : ", " + n.getElev();
|
||||
if (t.showspeed) { // hack: show speed instead of elevation
|
||||
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 = ", " + (((int) (speed * 10)) / 10.f);
|
||||
}
|
||||
sb.append(" [").append(formatILon(n.getILon())).append(", ").append(formatILat(n.getILat()))
|
||||
.append(sele).append("],\n");
|
||||
nn = n;
|
||||
}
|
||||
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||
|
||||
sb.append(" ]\n");
|
||||
sb.append(" }\n");
|
||||
if (t.exportWaypoints || !t.pois.isEmpty()) {
|
||||
sb.append(" },\n");
|
||||
for (int i = 0; i <= t.pois.size() - 1; i++) {
|
||||
OsmNodeNamed poi = t.pois.get(i);
|
||||
addFeature(sb, "poi", poi.name, poi.ilat, poi.ilon);
|
||||
if (i < t.matchedWaypoints.size() - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(" \n");
|
||||
}
|
||||
if (t.exportWaypoints) {
|
||||
for (int i = 0; i <= t.matchedWaypoints.size() - 1; i++) {
|
||||
String type;
|
||||
if (i == 0) {
|
||||
type = "from";
|
||||
} else if (i == t.matchedWaypoints.size() - 1) {
|
||||
type = "to";
|
||||
} else {
|
||||
type = "via";
|
||||
}
|
||||
|
||||
MatchedWaypoint wp = t.matchedWaypoints.get(i);
|
||||
addFeature(sb, type, wp.name, wp.waypoint.ilat, wp.waypoint.ilon);
|
||||
if (i < t.matchedWaypoints.size() - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(" \n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sb.append(" }\n");
|
||||
}
|
||||
sb.append(" ]\n");
|
||||
sb.append("}\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void addFeature(StringBuilder sb, String type, String name, int ilat, int ilon) {
|
||||
sb.append(" {\n");
|
||||
sb.append(" \"type\": \"Feature\",\n");
|
||||
sb.append(" \"properties\": {\n");
|
||||
sb.append(" \"name\": \"" + StringUtils.escapeJson(name) + "\",\n");
|
||||
sb.append(" \"type\": \"" + type + "\"\n");
|
||||
sb.append(" },\n");
|
||||
sb.append(" \"geometry\": {\n");
|
||||
sb.append(" \"type\": \"Point\",\n");
|
||||
sb.append(" \"coordinates\": [\n");
|
||||
sb.append(" " + formatILon(ilon) + ",\n");
|
||||
sb.append(" " + formatILat(ilat) + "\n");
|
||||
sb.append(" ]\n");
|
||||
sb.append(" }\n");
|
||||
sb.append(" }");
|
||||
}
|
||||
|
||||
public String formatAsWaypoint(OsmNodeNamed n) {
|
||||
try {
|
||||
StringWriter sw = new StringWriter(8192);
|
||||
BufferedWriter bw = new BufferedWriter(sw);
|
||||
addJsonHeader(bw);
|
||||
addJsonFeature(bw, "info", "wpinfo", n.ilon, n.ilat, n.getElev(), (n.nodeDescription != null ? rc.expctxWay.getKeyValueDescription(false, n.nodeDescription) : null));
|
||||
addJsonFooter(bw);
|
||||
bw.close();
|
||||
sw.close();
|
||||
return sw.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addJsonFeature(BufferedWriter sb, String type, String name, int ilon, int ilat, double elev, String desc) {
|
||||
try {
|
||||
sb.append(" {\n");
|
||||
sb.append(" \"type\": \"Feature\",\n");
|
||||
sb.append(" \"properties\": {\n");
|
||||
sb.append(" \"creator\": \"BRouter-" + OsmTrack.version + "\",\n");
|
||||
sb.append(" \"name\": \"" + StringUtils.escapeJson(name) + "\",\n");
|
||||
sb.append(" \"type\": \"" + type + "\"");
|
||||
if (desc != null) {
|
||||
sb.append(",\n \"message\": \"" + desc + "\"\n");
|
||||
} else {
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.append(" },\n");
|
||||
sb.append(" \"geometry\": {\n");
|
||||
sb.append(" \"type\": \"Point\",\n");
|
||||
sb.append(" \"coordinates\": [\n");
|
||||
sb.append(" " + formatILon(ilon) + ",\n");
|
||||
sb.append(" " + formatILat(ilat) + ",\n");
|
||||
sb.append(" " + elev + "\n");
|
||||
sb.append(" ]\n");
|
||||
sb.append(" }\n");
|
||||
sb.append(" }\n");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addJsonHeader(BufferedWriter sb) {
|
||||
try {
|
||||
sb.append("{\n");
|
||||
sb.append(" \"type\": \"FeatureCollection\",\n");
|
||||
sb.append(" \"features\": [\n");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addJsonFooter(BufferedWriter sb) {
|
||||
try {
|
||||
sb.append(" ]\n");
|
||||
sb.append("}\n");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
91
brouter-core/src/main/java/btools/router/FormatKml.java
Normal file
91
brouter-core/src/main/java/btools/router/FormatKml.java
Normal file
|
@ -0,0 +1,91 @@
|
|||
package btools.router;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import btools.mapaccess.MatchedWaypoint;
|
||||
import btools.util.StringUtils;
|
||||
|
||||
public class FormatKml extends Formatter {
|
||||
public FormatKml(RoutingContext rc) {
|
||||
super(rc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String format(OsmTrack t) {
|
||||
StringBuilder sb = new StringBuilder(8192);
|
||||
|
||||
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
|
||||
sb.append("<kml xmlns=\"http://earth.google.com/kml/2.0\">\n");
|
||||
sb.append(" <Document>\n");
|
||||
sb.append(" <name>KML Samples</name>\n");
|
||||
sb.append(" <open>1</open>\n");
|
||||
sb.append(" <distance>3.497064</distance>\n");
|
||||
sb.append(" <traveltime>872</traveltime>\n");
|
||||
sb.append(" <description>To enable simple instructions add: 'instructions=1' as parameter to the URL</description>\n");
|
||||
sb.append(" <Folder>\n");
|
||||
sb.append(" <name>Paths</name>\n");
|
||||
sb.append(" <visibility>0</visibility>\n");
|
||||
sb.append(" <description>Examples of paths.</description>\n");
|
||||
sb.append(" <Placemark>\n");
|
||||
sb.append(" <name>Tessellated</name>\n");
|
||||
sb.append(" <visibility>0</visibility>\n");
|
||||
sb.append(" <description><![CDATA[If the <tessellate> tag has a value of 1, the line will contour to the underlying terrain]]></description>\n");
|
||||
sb.append(" <LineString>\n");
|
||||
sb.append(" <tessellate>1</tessellate>\n");
|
||||
sb.append(" <coordinates>");
|
||||
|
||||
for (OsmPathElement n : t.nodes) {
|
||||
sb.append(formatILon(n.getILon())).append(",").append(formatILat(n.getILat())).append("\n");
|
||||
}
|
||||
|
||||
sb.append(" </coordinates>\n");
|
||||
sb.append(" </LineString>\n");
|
||||
sb.append(" </Placemark>\n");
|
||||
sb.append(" </Folder>\n");
|
||||
if (t.exportWaypoints || !t.pois.isEmpty()) {
|
||||
if (!t.pois.isEmpty()) {
|
||||
sb.append(" <Folder>\n");
|
||||
sb.append(" <name>poi</name>\n");
|
||||
for (int i = 0; i < t.pois.size(); i++) {
|
||||
OsmNodeNamed poi = t.pois.get(i);
|
||||
createPlaceMark(sb, poi.name, poi.ilat, poi.ilon);
|
||||
}
|
||||
sb.append(" </Folder>\n");
|
||||
}
|
||||
|
||||
if (t.exportWaypoints) {
|
||||
int size = t.matchedWaypoints.size();
|
||||
createFolder(sb, "start", t.matchedWaypoints.subList(0, 1));
|
||||
if (t.matchedWaypoints.size() > 2) {
|
||||
createFolder(sb, "via", t.matchedWaypoints.subList(1, size - 1));
|
||||
}
|
||||
createFolder(sb, "end", t.matchedWaypoints.subList(size - 1, size));
|
||||
}
|
||||
}
|
||||
sb.append(" </Document>\n");
|
||||
sb.append("</kml>\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void createFolder(StringBuilder sb, String type, List<MatchedWaypoint> waypoints) {
|
||||
sb.append(" <Folder>\n");
|
||||
sb.append(" <name>" + type + "</name>\n");
|
||||
for (int i = 0; i < waypoints.size(); i++) {
|
||||
MatchedWaypoint wp = waypoints.get(i);
|
||||
createPlaceMark(sb, wp.name, wp.waypoint.ilat, wp.waypoint.ilon);
|
||||
}
|
||||
sb.append(" </Folder>\n");
|
||||
}
|
||||
|
||||
private void createPlaceMark(StringBuilder sb, String name, int ilat, int ilon) {
|
||||
sb.append(" <Placemark>\n");
|
||||
sb.append(" <name>" + StringUtils.escapeXml10(name) + "</name>\n");
|
||||
sb.append(" <Point>\n");
|
||||
sb.append(" <coordinates>" + formatILon(ilon) + "," + formatILat(ilat) + "</coordinates>\n");
|
||||
sb.append(" </Point>\n");
|
||||
sb.append(" </Placemark>\n");
|
||||
}
|
||||
|
||||
}
|
110
brouter-core/src/main/java/btools/router/Formatter.java
Normal file
110
brouter-core/src/main/java/btools/router/Formatter.java
Normal file
|
@ -0,0 +1,110 @@
|
|||
package btools.router;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public abstract class Formatter {
|
||||
|
||||
static final String MESSAGES_HEADER = "Longitude\tLatitude\tElevation\tDistance\tCostPerKm\tElevCost\tTurnCost\tNodeCost\tInitialCost\tWayTags\tNodeTags\tTime\tEnergy";
|
||||
|
||||
RoutingContext rc;
|
||||
|
||||
Formatter() {
|
||||
}
|
||||
|
||||
Formatter(RoutingContext rc) {
|
||||
this.rc = rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* writes the track in gpx-format to a file
|
||||
*
|
||||
* @param filename the filename to write to
|
||||
* @param t the track to write
|
||||
*/
|
||||
public void write(String filename, OsmTrack t) throws Exception {
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
|
||||
bw.write(format(t));
|
||||
bw.close();
|
||||
}
|
||||
|
||||
public OsmTrack read(String filename) throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* writes the track in a selected output format to a string
|
||||
*
|
||||
* @param t the track to format
|
||||
* @return the formatted string
|
||||
*/
|
||||
public abstract String format(OsmTrack t);
|
||||
|
||||
|
||||
static String formatILon(int ilon) {
|
||||
return formatPos(ilon - 180000000);
|
||||
}
|
||||
|
||||
static String formatILat(int ilat) {
|
||||
return formatPos(ilat - 90000000);
|
||||
}
|
||||
|
||||
private static String formatPos(int p) {
|
||||
boolean negative = p < 0;
|
||||
if (negative)
|
||||
p = -p;
|
||||
char[] ac = new char[12];
|
||||
int i = 11;
|
||||
while (p != 0 || i > 3) {
|
||||
ac[i--] = (char) ('0' + (p % 10));
|
||||
p /= 10;
|
||||
if (i == 5)
|
||||
ac[i--] = '.';
|
||||
}
|
||||
if (negative)
|
||||
ac[i--] = '-';
|
||||
return new String(ac, i + 1, 11 - i);
|
||||
}
|
||||
|
||||
public static String getFormattedTime2(int s) {
|
||||
int seconds = (int) (s + 0.5);
|
||||
int hours = seconds / 3600;
|
||||
int minutes = (seconds - hours * 3600) / 60;
|
||||
seconds = seconds - hours * 3600 - minutes * 60;
|
||||
String time = "";
|
||||
if (hours != 0)
|
||||
time = "" + hours + "h ";
|
||||
if (minutes != 0)
|
||||
time = time + minutes + "m ";
|
||||
if (seconds != 0)
|
||||
time = time + seconds + "s";
|
||||
return time;
|
||||
}
|
||||
|
||||
static public String getFormattedEnergy(int energy) {
|
||||
return format1(energy / 3600000.) + "kwh";
|
||||
}
|
||||
|
||||
static private String format1(double n) {
|
||||
String s = "" + (long) (n * 10 + 0.5);
|
||||
int len = s.length();
|
||||
return s.substring(0, len - 1) + "." + s.charAt(len - 1);
|
||||
}
|
||||
|
||||
|
||||
static final String dateformat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
|
||||
|
||||
static public String getFormattedTime3(float time) {
|
||||
SimpleDateFormat TIMESTAMP_FORMAT = new SimpleDateFormat(dateformat, 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -7,33 +7,20 @@ package btools.router;
|
|||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
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;
|
||||
import btools.util.CompactLongMap;
|
||||
import btools.util.FrozenLongMap;
|
||||
import btools.util.StringUtils;
|
||||
|
||||
public final class OsmTrack {
|
||||
final public static String version = "1.7.3";
|
||||
|
@ -66,7 +53,7 @@ public final class OsmTrack {
|
|||
|
||||
private CompactLongMap<OsmPathElementHolder> detourMap;
|
||||
|
||||
private VoiceHintList voiceHints;
|
||||
public VoiceHintList voiceHints;
|
||||
|
||||
public String message = null;
|
||||
public List<String> messageList = null;
|
||||
|
@ -178,7 +165,7 @@ public final class OsmTrack {
|
|||
nodesMap = new FrozenLongMap<>(nodesMap);
|
||||
}
|
||||
|
||||
private List<String> aggregateMessages() {
|
||||
public List<String> aggregateMessages() {
|
||||
ArrayList<String> res = new ArrayList<>();
|
||||
MessageData current = null;
|
||||
for (OsmPathElement n : nodes) {
|
||||
|
@ -200,7 +187,7 @@ public final class OsmTrack {
|
|||
return res;
|
||||
}
|
||||
|
||||
private List<String> aggregateSpeedProfile() {
|
||||
public List<String> aggregateSpeedProfile() {
|
||||
ArrayList<String> res = new ArrayList<>();
|
||||
int vmax = -1;
|
||||
int vmaxe = -1;
|
||||
|
@ -395,752 +382,9 @@ public final class OsmTrack {
|
|||
public int plainAscend;
|
||||
public int cost;
|
||||
public int energy;
|
||||
|
||||
/**
|
||||
* writes the track in gpx-format to a file
|
||||
*
|
||||
* @param filename the filename to write to
|
||||
*/
|
||||
public void writeGpx(String filename) throws Exception {
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
|
||||
formatAsGpx(bw);
|
||||
bw.close();
|
||||
}
|
||||
|
||||
public String formatAsGpx() {
|
||||
try {
|
||||
StringWriter sw = new StringWriter(8192);
|
||||
BufferedWriter bw = new BufferedWriter(sw);
|
||||
formatAsGpx(bw);
|
||||
bw.close();
|
||||
return sw.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String formatAsGpx(BufferedWriter sb) throws IOException {
|
||||
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)
|
||||
message = "(alt-index " + i + ": " + message + " )";
|
||||
if (message != null)
|
||||
sb.append("<!-- ").append(message).append(" -->\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (turnInstructionMode == 4) { // comment style
|
||||
sb.append("<!-- $transport-mode$").append(voiceHints.getTransportMode()).append("$ -->\n");
|
||||
sb.append("<!-- cmd idx lon lat d2next geometry -->\n");
|
||||
sb.append("<!-- $turn-instruction-start$\n");
|
||||
for (VoiceHint hint : voiceHints.list) {
|
||||
sb.append(String.format(" $turn$%6s;%6d;%10s;%10s;%6d;%s$\n", hint.getCommandString(), hint.indexInTrack,
|
||||
formatILon(hint.ilon), formatILat(hint.ilat), (int) (hint.distanceToNext), hint.formatGeometry()));
|
||||
}
|
||||
sb.append(" $turn-instruction-end$ -->\n");
|
||||
}
|
||||
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 == 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");
|
||||
|
||||
if (turnInstructionMode == 3) {
|
||||
sb.append(" creator=\"OsmAndRouter\" version=\"1.1\">\n");
|
||||
} else {
|
||||
sb.append(" creator=\"BRouter-" + version + "\" version=\"1.1\">\n");
|
||||
}
|
||||
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");
|
||||
|
||||
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");
|
||||
if (rteTime != lastRteTime) { // add timing only if available
|
||||
double t = rteTime - lastRteTime;
|
||||
first.append(" <time>").append("" + (int) (t + 0.5)).append("</time>\n");
|
||||
lastRteTime = rteTime;
|
||||
}
|
||||
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(turnInstructionMode == 3 ? hint.getMessageString() : hint.getCruiserMessageString())
|
||||
.append("</desc>\n <extensions>\n");
|
||||
|
||||
rteTime = getVoiceHintTime(i + 1);
|
||||
|
||||
if (rteTime != lastRteTime) { // add timing only if available
|
||||
double t = rteTime - lastRteTime;
|
||||
sb.append(" <time>").append("" + (int) (t + 0.5)).append("</time>\n");
|
||||
lastRteTime = rteTime;
|
||||
}
|
||||
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=\"")
|
||||
.append(formatILon(nodes.get(nodes.size() - 1).getILon())).append("\">\n")
|
||||
.append(" <desc>destination</desc>\n <extensions>\n");
|
||||
sb.append(" <time>0</time>\n");
|
||||
sb.append(" <offset>").append("" + (nodes.size() - 1)).append("</offset>\n </extensions>\n </rtept>\n");
|
||||
|
||||
sb.append("</rte>\n");
|
||||
}
|
||||
|
||||
if (turnInstructionMode == 7) { // old locus style
|
||||
float lastRteTime = getVoiceHintTime(0);
|
||||
|
||||
for (int i = 0; i < voiceHints.list.size(); i++) {
|
||||
VoiceHint hint = voiceHints.list.get(i);
|
||||
sb.append(" <wpt lon=\"").append(formatILon(hint.ilon)).append("\" lat=\"")
|
||||
.append(formatILat(hint.ilat)).append("\">")
|
||||
.append(hint.selev == Short.MIN_VALUE ? "" : "<ele>" + (hint.selev / 4.) + "</ele>")
|
||||
.append("<name>").append(hint.getMessageString()).append("</name>")
|
||||
.append("<extensions><locus:rteDistance>").append("" + hint.distanceToNext).append("</locus:rteDistance>");
|
||||
float rteTime = getVoiceHintTime(i + 1);
|
||||
if (rteTime != lastRteTime) { // add timing only if available
|
||||
double t = rteTime - lastRteTime;
|
||||
double speed = hint.distanceToNext / t;
|
||||
sb.append("<locus:rteTime>").append("" + t).append("</locus:rteTime>")
|
||||
.append("<locus:rteSpeed>").append("" + speed).append("</locus:rteSpeed>");
|
||||
lastRteTime = rteTime;
|
||||
}
|
||||
sb.append("<locus:rtePointAction>").append("" + hint.getLocusAction()).append("</locus:rtePointAction></extensions>")
|
||||
.append("</wpt>\n");
|
||||
}
|
||||
}
|
||||
if (turnInstructionMode == 5) { // gpsies style
|
||||
for (VoiceHint hint : voiceHints.list) {
|
||||
sb.append(" <wpt lon=\"").append(formatILon(hint.ilon)).append("\" lat=\"")
|
||||
.append(formatILat(hint.ilat)).append("\">")
|
||||
.append("<name>").append(hint.getMessageString()).append("</name>")
|
||||
.append("<sym>").append(hint.getSymbolString().toLowerCase()).append("</sym>")
|
||||
.append("<type>").append(hint.getSymbolString()).append("</type>")
|
||||
.append("</wpt>\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (turnInstructionMode == 6) { // orux style
|
||||
for (VoiceHint hint : voiceHints.list) {
|
||||
sb.append(" <wpt lat=\"").append(formatILat(hint.ilat)).append("\" lon=\"")
|
||||
.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())
|
||||
.append("</om:ext>\n" +
|
||||
" </om:oruxmapsextensions>\n" +
|
||||
" </extensions>\n" +
|
||||
" </wpt>\n");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i <= pois.size() - 1; i++) {
|
||||
OsmNodeNamed poi = pois.get(i);
|
||||
sb.append(" <wpt lon=\"").append(formatILon(poi.ilon)).append("\" lat=\"")
|
||||
.append(formatILat(poi.ilat)).append("\">\n")
|
||||
.append(" <name>").append(StringUtils.escapeXml10(poi.name)).append("</name>\n")
|
||||
.append(" </wpt>\n");
|
||||
}
|
||||
|
||||
if (exportWaypoints) {
|
||||
for (int i = 0; i <= matchedWaypoints.size() - 1; i++) {
|
||||
MatchedWaypoint wt = matchedWaypoints.get(i);
|
||||
sb.append(" <wpt lon=\"").append(formatILon(wt.waypoint.ilon)).append("\" lat=\"")
|
||||
.append(formatILat(wt.waypoint.ilat)).append("\">\n")
|
||||
.append(" <name>").append(StringUtils.escapeXml10(wt.name)).append("</name>\n");
|
||||
if (i == 0) {
|
||||
sb.append(" <type>from</type>\n");
|
||||
} else if (i == matchedWaypoints.size() - 1) {
|
||||
sb.append(" <type>to</type>\n");
|
||||
} else {
|
||||
sb.append(" <type>via</type>\n");
|
||||
}
|
||||
sb.append(" </wpt>\n");
|
||||
}
|
||||
}
|
||||
sb.append(" <trk>\n");
|
||||
if (turnInstructionMode == 9
|
||||
|| turnInstructionMode == 2
|
||||
|| turnInstructionMode == 8
|
||||
|| turnInstructionMode == 4) { // Locus, comment, cruise, 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 == 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>";
|
||||
VoiceHint hint = getVoiceHint(idx);
|
||||
MatchedWaypoint mwpt = getMatchedWaypoint(idx);
|
||||
|
||||
if (showTime) {
|
||||
sele += "<time>" + getFormattedTime3(n.getTime()) + "</time>";
|
||||
}
|
||||
if (turnInstructionMode == 8) {
|
||||
if (mwpt != null &&
|
||||
!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
|
||||
sele += "<name>" + mwpt.name + "</name>";
|
||||
}
|
||||
}
|
||||
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("to")) {
|
||||
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.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
|
||||
sele += "<name>" + mwpt.name + "</name>";
|
||||
}
|
||||
if (mwpt.direct && bNextDirect) {
|
||||
sele += "<src>" + hint.getLocusSymbolString() + "</src><sym>pass_place</sym><type>Shaping</type>";
|
||||
// bNextDirect = false;
|
||||
} else if (mwpt.direct) {
|
||||
if (idx == 0)
|
||||
sele += "<sym>pass_place</sym><type>Via</type>";
|
||||
else
|
||||
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><type>Via</type>";
|
||||
}
|
||||
} 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 (!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
|
||||
sele += "<name>" + mwpt.name + "</name>";
|
||||
}
|
||||
if (mwpt.direct && bNextDirect) {
|
||||
sele += "<src>beeline</src><sym>pass_place</sym><type>Shaping</type>";
|
||||
} else if (mwpt.direct) {
|
||||
if (idx == 0)
|
||||
sele += "<sym>pass_place</sym><type>Via</type>";
|
||||
else
|
||||
sele += "<sym>pass_place</sym><type>Shaping</type>";
|
||||
bNextDirect = true;
|
||||
} else if (bNextDirect) {
|
||||
sele += "<src>beeline</src><sym>pass_place</sym><type>Shaping</type>";
|
||||
bNextDirect = false;
|
||||
} 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>Via</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");
|
||||
sb.append(" </trk>\n");
|
||||
sb.append("</gpx>\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static public String formatAsGpxWaypoint(OsmNodeNamed n) {
|
||||
try {
|
||||
StringWriter sw = new StringWriter(8192);
|
||||
BufferedWriter bw = new BufferedWriter(sw);
|
||||
formatGpxHeader(bw);
|
||||
formatWaypointGpx(bw, n);
|
||||
formatGpxFooter(bw);
|
||||
bw.close();
|
||||
sw.close();
|
||||
return sw.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static public void formatGpxHeader(BufferedWriter sb) throws IOException {
|
||||
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
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");
|
||||
sb.append(" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\" \n");
|
||||
sb.append(" creator=\"BRouter-" + version + "\" version=\"1.1\">\n");
|
||||
}
|
||||
|
||||
static public void formatGpxFooter(BufferedWriter sb) throws IOException {
|
||||
sb.append("</gpx>\n");
|
||||
}
|
||||
|
||||
static public void formatWaypointGpx(BufferedWriter sb, OsmNodeNamed n) throws IOException {
|
||||
sb.append(" <wpt lon=\"").append(formatILon(n.ilon)).append("\" lat=\"")
|
||||
.append(formatILat(n.ilat)).append("\">");
|
||||
if (n.getSElev() != Short.MIN_VALUE) {
|
||||
sb.append("<ele>").append("" + n.getElev()).append("</ele>");
|
||||
}
|
||||
if (n.name != null) {
|
||||
sb.append("<name>").append(StringUtils.escapeXml10(n.name)).append("</name>");
|
||||
}
|
||||
if (n.nodeDescription != null) {
|
||||
sb.append("<desc>").append("hat desc").append("</desc>");
|
||||
}
|
||||
sb.append("</wpt>\n");
|
||||
}
|
||||
|
||||
public void writeKml(String filename) throws Exception {
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
|
||||
|
||||
bw.write(formatAsKml());
|
||||
bw.close();
|
||||
}
|
||||
|
||||
public String formatAsKml() {
|
||||
StringBuilder sb = new StringBuilder(8192);
|
||||
|
||||
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
||||
|
||||
sb.append("<kml xmlns=\"http://earth.google.com/kml/2.0\">\n");
|
||||
sb.append(" <Document>\n");
|
||||
sb.append(" <name>KML Samples</name>\n");
|
||||
sb.append(" <open>1</open>\n");
|
||||
sb.append(" <distance>3.497064</distance>\n");
|
||||
sb.append(" <traveltime>872</traveltime>\n");
|
||||
sb.append(" <description>To enable simple instructions add: 'instructions=1' as parameter to the URL</description>\n");
|
||||
sb.append(" <Folder>\n");
|
||||
sb.append(" <name>Paths</name>\n");
|
||||
sb.append(" <visibility>0</visibility>\n");
|
||||
sb.append(" <description>Examples of paths.</description>\n");
|
||||
sb.append(" <Placemark>\n");
|
||||
sb.append(" <name>Tessellated</name>\n");
|
||||
sb.append(" <visibility>0</visibility>\n");
|
||||
sb.append(" <description><![CDATA[If the <tessellate> tag has a value of 1, the line will contour to the underlying terrain]]></description>\n");
|
||||
sb.append(" <LineString>\n");
|
||||
sb.append(" <tessellate>1</tessellate>\n");
|
||||
sb.append(" <coordinates>");
|
||||
|
||||
for (OsmPathElement n : nodes) {
|
||||
sb.append(formatILon(n.getILon())).append(",").append(formatILat(n.getILat())).append("\n");
|
||||
}
|
||||
|
||||
sb.append(" </coordinates>\n");
|
||||
sb.append(" </LineString>\n");
|
||||
sb.append(" </Placemark>\n");
|
||||
sb.append(" </Folder>\n");
|
||||
if (exportWaypoints || !pois.isEmpty()) {
|
||||
if (!pois.isEmpty()) {
|
||||
sb.append(" <Folder>\n");
|
||||
sb.append(" <name>poi</name>\n");
|
||||
for (int i = 0; i < pois.size(); i++) {
|
||||
OsmNodeNamed poi = pois.get(i);
|
||||
createPlaceMark(sb, poi.name, poi.ilat, poi.ilon);
|
||||
}
|
||||
sb.append(" </Folder>\n");
|
||||
}
|
||||
|
||||
if (exportWaypoints) {
|
||||
int size = matchedWaypoints.size();
|
||||
createFolder(sb, "start", matchedWaypoints.subList(0, 1));
|
||||
if (matchedWaypoints.size() > 2) {
|
||||
createFolder(sb, "via", matchedWaypoints.subList(1, size - 1));
|
||||
}
|
||||
createFolder(sb, "end", matchedWaypoints.subList(size - 1, size));
|
||||
}
|
||||
}
|
||||
sb.append(" </Document>\n");
|
||||
sb.append("</kml>\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void createFolder(StringBuilder sb, String type, List<MatchedWaypoint> waypoints) {
|
||||
sb.append(" <Folder>\n");
|
||||
sb.append(" <name>" + type + "</name>\n");
|
||||
for (int i = 0; i < waypoints.size(); i++) {
|
||||
MatchedWaypoint wp = waypoints.get(i);
|
||||
createPlaceMark(sb, wp.name, wp.waypoint.ilat, wp.waypoint.ilon);
|
||||
}
|
||||
sb.append(" </Folder>\n");
|
||||
}
|
||||
|
||||
private void createPlaceMark(StringBuilder sb, String name, int ilat, int ilon) {
|
||||
sb.append(" <Placemark>\n");
|
||||
sb.append(" <name>" + StringUtils.escapeXml10(name) + "</name>\n");
|
||||
sb.append(" <Point>\n");
|
||||
sb.append(" <coordinates>" + formatILon(ilon) + "," + formatILat(ilat) + "</coordinates>\n");
|
||||
sb.append(" </Point>\n");
|
||||
sb.append(" </Placemark>\n");
|
||||
}
|
||||
|
||||
public List<String> iternity;
|
||||
|
||||
public void writeJson(String filename) throws Exception {
|
||||
BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
|
||||
|
||||
bw.write(formatAsGeoJson());
|
||||
bw.close();
|
||||
}
|
||||
|
||||
|
||||
public String formatAsGeoJson() {
|
||||
int turnInstructionMode = voiceHints != null ? voiceHints.turnInstructionMode : 0;
|
||||
|
||||
StringBuilder sb = new StringBuilder(8192);
|
||||
|
||||
sb.append("{\n");
|
||||
sb.append(" \"type\": \"FeatureCollection\",\n");
|
||||
sb.append(" \"features\": [\n");
|
||||
sb.append(" {\n");
|
||||
sb.append(" \"type\": \"Feature\",\n");
|
||||
sb.append(" \"properties\": {\n");
|
||||
sb.append(" \"creator\": \"BRouter-" + version + "\",\n");
|
||||
sb.append(" \"name\": \"").append(name).append("\",\n");
|
||||
sb.append(" \"track-length\": \"").append(distance).append("\",\n");
|
||||
sb.append(" \"filtered ascend\": \"").append(ascend).append("\",\n");
|
||||
sb.append(" \"plain-ascend\": \"").append(plainAscend).append("\",\n");
|
||||
sb.append(" \"total-time\": \"").append(getTotalSeconds()).append("\",\n");
|
||||
sb.append(" \"total-energy\": \"").append(energy).append("\",\n");
|
||||
sb.append(" \"cost\": \"").append(cost).append("\",\n");
|
||||
if (voiceHints != null && !voiceHints.list.isEmpty()) {
|
||||
sb.append(" \"voicehints\": [\n");
|
||||
for (VoiceHint hint : voiceHints.list) {
|
||||
sb.append(" [");
|
||||
sb.append(hint.indexInTrack);
|
||||
sb.append(',').append(hint.getJsonCommandIndex());
|
||||
sb.append(',').append(hint.getExitNumber());
|
||||
sb.append(',').append(hint.distanceToNext);
|
||||
sb.append(',').append((int) hint.angle);
|
||||
|
||||
// not always include geometry because longer and only needed for comment style
|
||||
if (turnInstructionMode == 4) { // comment style
|
||||
sb.append(",\"").append(hint.formatGeometry()).append("\"");
|
||||
}
|
||||
|
||||
sb.append("],\n");
|
||||
}
|
||||
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||
sb.append(" ],\n");
|
||||
}
|
||||
if (showSpeedProfile) { // set in profile
|
||||
List<String> sp = aggregateSpeedProfile();
|
||||
if (sp.size() > 0) {
|
||||
sb.append(" \"speedprofile\": [\n");
|
||||
for (int i = sp.size() - 1; i >= 0; i--) {
|
||||
sb.append(" [").append(sp.get(i)).append(i > 0 ? "],\n" : "]\n");
|
||||
}
|
||||
sb.append(" ],\n");
|
||||
}
|
||||
}
|
||||
// ... traditional message list
|
||||
{
|
||||
sb.append(" \"messages\": [\n");
|
||||
sb.append(" [\"").append(MESSAGES_HEADER.replaceAll("\t", "\", \"")).append("\"],\n");
|
||||
for (String m : aggregateMessages()) {
|
||||
sb.append(" [\"").append(m.replaceAll("\t", "\", \"")).append("\"],\n");
|
||||
}
|
||||
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||
sb.append(" ],\n");
|
||||
}
|
||||
|
||||
if (getTotalSeconds() > 0) {
|
||||
sb.append(" \"times\": [");
|
||||
DecimalFormat decimalFormat = (DecimalFormat) NumberFormat.getInstance(Locale.ENGLISH);
|
||||
decimalFormat.applyPattern("0.###");
|
||||
for (OsmPathElement n : nodes) {
|
||||
sb.append(decimalFormat.format(n.getTime())).append(",");
|
||||
}
|
||||
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||
sb.append("]\n");
|
||||
} else {
|
||||
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||
}
|
||||
|
||||
sb.append(" },\n");
|
||||
|
||||
if (iternity != null) {
|
||||
sb.append(" \"iternity\": [\n");
|
||||
for (String s : iternity) {
|
||||
sb.append(" \"").append(s).append("\",\n");
|
||||
}
|
||||
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||
sb.append(" ],\n");
|
||||
}
|
||||
sb.append(" \"geometry\": {\n");
|
||||
sb.append(" \"type\": \"LineString\",\n");
|
||||
sb.append(" \"coordinates\": [\n");
|
||||
|
||||
OsmPathElement nn = null;
|
||||
for (OsmPathElement n : nodes) {
|
||||
String sele = n.getSElev() == Short.MIN_VALUE ? "" : ", " + n.getElev();
|
||||
if (showspeed) { // hack: show speed instead of elevation
|
||||
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 = ", " + (((int) (speed * 10)) / 10.f);
|
||||
}
|
||||
sb.append(" [").append(formatILon(n.getILon())).append(", ").append(formatILat(n.getILat()))
|
||||
.append(sele).append("],\n");
|
||||
nn = n;
|
||||
}
|
||||
sb.deleteCharAt(sb.lastIndexOf(","));
|
||||
|
||||
sb.append(" ]\n");
|
||||
sb.append(" }\n");
|
||||
if (exportWaypoints || !pois.isEmpty()) {
|
||||
sb.append(" },\n");
|
||||
for (int i = 0; i <= pois.size() - 1; i++) {
|
||||
OsmNodeNamed poi = pois.get(i);
|
||||
addFeature(sb, "poi", poi.name, poi.ilat, poi.ilon);
|
||||
if (i < matchedWaypoints.size() - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(" \n");
|
||||
}
|
||||
if (exportWaypoints) {
|
||||
for (int i = 0; i <= matchedWaypoints.size() - 1; i++) {
|
||||
String type;
|
||||
if (i == 0) {
|
||||
type = "from";
|
||||
} else if (i == matchedWaypoints.size() - 1) {
|
||||
type = "to";
|
||||
} else {
|
||||
type = "via";
|
||||
}
|
||||
|
||||
MatchedWaypoint wp = matchedWaypoints.get(i);
|
||||
addFeature(sb, type, wp.name, wp.waypoint.ilat, wp.waypoint.ilon);
|
||||
if (i < matchedWaypoints.size() - 1) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(" \n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sb.append(" }\n");
|
||||
}
|
||||
sb.append(" ]\n");
|
||||
sb.append("}\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void addFeature(StringBuilder sb, String type, String name, int ilat, int ilon) {
|
||||
sb.append(" {\n");
|
||||
sb.append(" \"type\": \"Feature\",\n");
|
||||
sb.append(" \"properties\": {\n");
|
||||
sb.append(" \"name\": \"" + StringUtils.escapeJson(name) + "\",\n");
|
||||
sb.append(" \"type\": \"" + type + "\"\n");
|
||||
sb.append(" },\n");
|
||||
sb.append(" \"geometry\": {\n");
|
||||
sb.append(" \"type\": \"Point\",\n");
|
||||
sb.append(" \"coordinates\": [\n");
|
||||
sb.append(" " + formatILon(ilon) + ",\n");
|
||||
sb.append(" " + formatILat(ilat) + "\n");
|
||||
sb.append(" ]\n");
|
||||
sb.append(" }\n");
|
||||
sb.append(" }");
|
||||
}
|
||||
|
||||
private VoiceHint getVoiceHint(int i) {
|
||||
public VoiceHint getVoiceHint(int i) {
|
||||
if (voiceHints == null) return null;
|
||||
for (VoiceHint hint : voiceHints.list) {
|
||||
if (hint.indexInTrack == i) {
|
||||
|
@ -1150,7 +394,7 @@ public final class OsmTrack {
|
|||
return null;
|
||||
}
|
||||
|
||||
private MatchedWaypoint getMatchedWaypoint(int idx) {
|
||||
public MatchedWaypoint getMatchedWaypoint(int idx) {
|
||||
if (matchedWaypoints == null) return null;
|
||||
for (MatchedWaypoint wp : matchedWaypoints) {
|
||||
if (idx == wp.indexInTrack) {
|
||||
|
@ -1168,128 +412,11 @@ public final class OsmTrack {
|
|||
return vnode0 < vnode1 ? vnode0 : vnode1;
|
||||
}
|
||||
|
||||
private int getTotalSeconds() {
|
||||
public int getTotalSeconds() {
|
||||
float s = nodes.size() < 2 ? 0 : nodes.get(nodes.size() - 1).getTime() - nodes.get(0).getTime();
|
||||
return (int) (s + 0.5);
|
||||
}
|
||||
|
||||
public String getFormattedTime() {
|
||||
return format1(getTotalSeconds() / 60.) + "m";
|
||||
}
|
||||
|
||||
public String getFormattedTime2() {
|
||||
int seconds = (int) (getTotalSeconds() + 0.5);
|
||||
int hours = seconds / 3600;
|
||||
int minutes = (seconds - hours * 3600) / 60;
|
||||
seconds = seconds - hours * 3600 - minutes * 60;
|
||||
String time = "";
|
||||
if (hours != 0)
|
||||
time = "" + hours + "h ";
|
||||
if (minutes != 0)
|
||||
time = time + minutes + "m ";
|
||||
if (seconds != 0)
|
||||
time = time + seconds + "s";
|
||||
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";
|
||||
}
|
||||
|
||||
private static String formatILon(int ilon) {
|
||||
return formatPos(ilon - 180000000);
|
||||
}
|
||||
|
||||
private static String formatILat(int ilat) {
|
||||
return formatPos(ilat - 90000000);
|
||||
}
|
||||
|
||||
private static String formatPos(int p) {
|
||||
boolean negative = p < 0;
|
||||
if (negative)
|
||||
p = -p;
|
||||
char[] ac = new char[12];
|
||||
int i = 11;
|
||||
while (p != 0 || i > 3) {
|
||||
ac[i--] = (char) ('0' + (p % 10));
|
||||
p /= 10;
|
||||
if (i == 5)
|
||||
ac[i--] = '.';
|
||||
}
|
||||
if (negative)
|
||||
ac[i--] = '-';
|
||||
return new String(ac, i + 1, 11 - i);
|
||||
}
|
||||
|
||||
private String format1(double n) {
|
||||
String s = "" + (long) (n * 10 + 0.5);
|
||||
int len = s.length();
|
||||
return s.substring(0, len - 1) + "." + s.charAt(len - 1);
|
||||
}
|
||||
|
||||
public void dumpMessages(String filename, RoutingContext rc) throws Exception {
|
||||
BufferedWriter bw = filename == null ? null : new BufferedWriter(new FileWriter(filename));
|
||||
writeMessages(bw, rc);
|
||||
}
|
||||
|
||||
public void writeMessages(BufferedWriter bw, RoutingContext rc) throws Exception {
|
||||
dumpLine(bw, MESSAGES_HEADER);
|
||||
for (String m : aggregateMessages()) {
|
||||
dumpLine(bw, m);
|
||||
}
|
||||
if (bw != null)
|
||||
bw.close();
|
||||
}
|
||||
|
||||
private void dumpLine(BufferedWriter bw, String s) throws Exception {
|
||||
if (bw == null) {
|
||||
System.out.println(s);
|
||||
} else {
|
||||
bw.write(s);
|
||||
bw.write("\n");
|
||||
}
|
||||
}
|
||||
|
||||
public void readGpx(String filename) throws Exception {
|
||||
File f = new File(filename);
|
||||
if (!f.exists())
|
||||
return;
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
|
||||
|
||||
for (; ; ) {
|
||||
String line = br.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
|
||||
int idx0 = line.indexOf("<trkpt lon=\"");
|
||||
if (idx0 >= 0) {
|
||||
idx0 += 12;
|
||||
int idx1 = line.indexOf('"', idx0);
|
||||
int ilon = (int) ((Double.parseDouble(line.substring(idx0, idx1)) + 180.) * 1000000. + 0.5);
|
||||
int idx2 = line.indexOf(" lat=\"");
|
||||
if (idx2 < 0)
|
||||
continue;
|
||||
idx2 += 6;
|
||||
int idx3 = line.indexOf('"', idx2);
|
||||
int ilat = (int) ((Double.parseDouble(line.substring(idx2, idx3)) + 90.) * 1000000. + 0.5);
|
||||
nodes.add(OsmPathElement.create(ilon, ilat, (short) 0, null, false));
|
||||
}
|
||||
}
|
||||
br.close();
|
||||
}
|
||||
|
||||
public boolean equalsTrack(OsmTrack t) {
|
||||
if (nodes.size() != t.nodes.size())
|
||||
return false;
|
||||
|
@ -1398,7 +525,7 @@ public final class OsmTrack {
|
|||
return 2;
|
||||
}
|
||||
|
||||
private float getVoiceHintTime(int i) {
|
||||
public float getVoiceHintTime(int i) {
|
||||
if (voiceHints.list.isEmpty()) {
|
||||
return 0f;
|
||||
}
|
||||
|
|
|
@ -192,34 +192,70 @@ public class RoutingEngine extends Thread {
|
|||
track.message = "track-length = " + track.distance + " filtered ascend = " + track.ascend
|
||||
+ " plain-ascend = " + track.plainAscend + " cost=" + track.cost;
|
||||
if (track.energy != 0) {
|
||||
track.message += " energy=" + track.getFormattedEnergy() + " time=" + track.getFormattedTime2();
|
||||
track.message += " energy=" + Formatter.getFormattedEnergy(track.energy) + " time=" + Formatter.getFormattedTime2(track.getTotalSeconds());
|
||||
}
|
||||
track.name = "brouter_" + routingContext.getProfileName() + "_" + i;
|
||||
|
||||
messageList.add(track.message);
|
||||
track.messageList = messageList;
|
||||
if (outfileBase != null) {
|
||||
String filename = outfileBase + i + ".gpx";
|
||||
OsmTrack oldTrack = new OsmTrack();
|
||||
oldTrack.readGpx(filename);
|
||||
if (track.equalsTrack(oldTrack)) {
|
||||
String filename = outfileBase + i + "." + routingContext.outputFormat;
|
||||
OsmTrack oldTrack = null;
|
||||
switch (routingContext.outputFormat) {
|
||||
case "gpx":
|
||||
oldTrack = new FormatGpx(routingContext).read(filename);
|
||||
break;
|
||||
case "geojson": // read only gpx at the moment
|
||||
case "json":
|
||||
// oldTrack = new FormatJson(routingContext).read(filename);
|
||||
break;
|
||||
case "kml":
|
||||
// oldTrack = new FormatJson(routingContext).read(filename);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (oldTrack != null && track.equalsTrack(oldTrack)) {
|
||||
continue;
|
||||
}
|
||||
oldTrack = null;
|
||||
track.exportWaypoints = routingContext.exportWaypoints;
|
||||
// doesn't work at the moment
|
||||
// use routingContext.outputFormat
|
||||
track.writeGpx(filename);
|
||||
filename = outfileBase + i + "." + routingContext.outputFormat;
|
||||
switch (routingContext.outputFormat) {
|
||||
case "gpx":
|
||||
outputMessage = new FormatGpx(routingContext).format(track);
|
||||
break;
|
||||
case "geojson":
|
||||
case "json":
|
||||
outputMessage = new FormatJson(routingContext).format(track);
|
||||
break;
|
||||
case "kml":
|
||||
outputMessage = new FormatKml(routingContext).format(track);
|
||||
break;
|
||||
case "csv":
|
||||
default:
|
||||
outputMessage = null;
|
||||
break;
|
||||
}
|
||||
if (outputMessage != null) {
|
||||
File out = new File(filename);
|
||||
FileWriter fw = new FileWriter(filename);
|
||||
fw.write(outputMessage);
|
||||
fw.close();
|
||||
outputMessage = null;
|
||||
}
|
||||
|
||||
foundTrack = track;
|
||||
alternativeIndex = i;
|
||||
outfile = filename;
|
||||
} else {
|
||||
if (i == routingContext.getAlternativeIdx(0, 3)) {
|
||||
if ("CSV".equals(System.getProperty("reportFormat"))) {
|
||||
track.dumpMessages(null, routingContext);
|
||||
String filename = outfileBase + i + ".csv";
|
||||
new FormatCsv(routingContext).write(filename, track);
|
||||
} else {
|
||||
if (!quite) {
|
||||
System.out.println(track.formatAsGpx());
|
||||
System.out.println(new FormatGpx(routingContext).format(track));
|
||||
}
|
||||
}
|
||||
foundTrack = track;
|
||||
|
@ -229,7 +265,7 @@ public class RoutingEngine extends Thread {
|
|||
}
|
||||
if (logfileBase != null) {
|
||||
String logfilename = logfileBase + i + ".csv";
|
||||
track.dumpMessages(logfilename, routingContext);
|
||||
new FormatCsv(routingContext).write(logfilename, track);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -308,15 +344,31 @@ public class RoutingEngine extends Thread {
|
|||
OsmNodeNamed n = new OsmNodeNamed(listOne.get(0).crosspoint);
|
||||
n.selev = startNode != null ? startNode.getSElev() : Short.MIN_VALUE;
|
||||
|
||||
// doesn't work at the moment
|
||||
// use routingContext.outputFormat
|
||||
outputMessage = OsmTrack.formatAsGpxWaypoint(n);
|
||||
switch (routingContext.outputFormat) {
|
||||
case "gpx":
|
||||
outputMessage = new FormatGpx(routingContext).formatAsWaypoint(n);
|
||||
break;
|
||||
case "geojson":
|
||||
case "json":
|
||||
outputMessage = new FormatJson(routingContext).formatAsWaypoint(n);
|
||||
break;
|
||||
case "kml":
|
||||
case "csv":
|
||||
default:
|
||||
outputMessage = null;
|
||||
break;
|
||||
}
|
||||
if (outfileBase != null) {
|
||||
String filename = outfileBase + ".gpx";
|
||||
String filename = outfileBase + "." + routingContext.outputFormat;
|
||||
File out = new File(filename);
|
||||
FileWriter fw = new FileWriter(filename);
|
||||
fw.write(outputMessage);
|
||||
fw.close();
|
||||
outputMessage = null;
|
||||
} else {
|
||||
if (!quite && outputMessage != null) {
|
||||
System.out.println(outputMessage);
|
||||
}
|
||||
}
|
||||
long endTime = System.currentTimeMillis();
|
||||
logInfo("execution time = " + (endTime - startTime) / 1000. + " seconds");
|
||||
|
@ -951,7 +1003,7 @@ public class RoutingEngine extends Thread {
|
|||
|
||||
if (track == null) {
|
||||
for (int cfi = 0; cfi < airDistanceCostFactors.length; cfi++) {
|
||||
if (cfi > 0) lastAirDistanceCostFactor = airDistanceCostFactors[cfi-1];
|
||||
if (cfi > 0) lastAirDistanceCostFactor = airDistanceCostFactors[cfi - 1];
|
||||
airDistanceCostFactor = airDistanceCostFactors[cfi];
|
||||
|
||||
if (airDistanceCostFactor < 0.) {
|
||||
|
@ -1447,7 +1499,7 @@ public class RoutingEngine extends Thread {
|
|||
|
||||
boolean inRadius = boundary == null || boundary.isInBoundary(nextNode, bestPath.cost);
|
||||
|
||||
if (inRadius && (isFinalLink || bestPath.cost + bestPath.airdistance <= (lastAirDistanceCostFactor != 0. ? maxTotalCost*lastAirDistanceCostFactor : maxTotalCost) + addDiff)) {
|
||||
if (inRadius && (isFinalLink || bestPath.cost + bestPath.airdistance <= (lastAirDistanceCostFactor != 0. ? maxTotalCost * lastAirDistanceCostFactor : maxTotalCost) + addDiff)) {
|
||||
// add only if this may beat an existing path for that link
|
||||
OsmLinkHolder dominator = link.getFirstLinkHolder(currentNode);
|
||||
while (!trafficSim && dominator != null) {
|
||||
|
@ -1628,7 +1680,7 @@ public class RoutingEngine extends Thread {
|
|||
}
|
||||
|
||||
public String getTime() {
|
||||
return foundTrack.getFormattedTime2();
|
||||
return Formatter.getFormattedTime2(foundTrack.getTotalSeconds());
|
||||
}
|
||||
|
||||
public OsmTrack getFoundTrack() {
|
||||
|
|
|
@ -14,8 +14,9 @@ public class RoutingParamCollector {
|
|||
|
||||
/**
|
||||
* get a list of points and optional extra info for the points
|
||||
* @param lonLats - linked list separated by ';' or '|'
|
||||
* @return - a list
|
||||
*
|
||||
* @param lonLats linked list separated by ';' or '|'
|
||||
* @return a list
|
||||
*/
|
||||
public List<OsmNodeNamed> getWayPointList(String lonLats) {
|
||||
if (lonLats == null) throw new IllegalArgumentException("lonlats parameter not set");
|
||||
|
@ -49,9 +50,10 @@ public class RoutingParamCollector {
|
|||
|
||||
/**
|
||||
* get a list of points (old style, positions only)
|
||||
* @param lons - array with longitudes
|
||||
* @param lats - array with latitudes
|
||||
* @return - a list
|
||||
*
|
||||
* @param lons array with longitudes
|
||||
* @param lats array with latitudes
|
||||
* @return a list
|
||||
*/
|
||||
public List<OsmNodeNamed> readPositions(double[] lons, double[] lats) {
|
||||
List<OsmNodeNamed> wplist = new ArrayList<>();
|
||||
|
@ -93,9 +95,10 @@ public class RoutingParamCollector {
|
|||
|
||||
/**
|
||||
* read a url like parameter list linked with '&'
|
||||
* @param url - parameter list
|
||||
* @return - a hashmap of the parameter
|
||||
* @throws UnsupportedEncodingException
|
||||
*
|
||||
* @param url parameter list
|
||||
* @return a hashmap of the parameter
|
||||
* @throws UnsupportedEncodingException
|
||||
*/
|
||||
public Map<String, String> getUrlParams(String url) throws UnsupportedEncodingException {
|
||||
HashMap<String, String> params = new HashMap<>();
|
||||
|
@ -117,9 +120,10 @@ public class RoutingParamCollector {
|
|||
|
||||
/**
|
||||
* fill a parameter map into the routing context
|
||||
* @param rctx - the context
|
||||
* @param wplist - the list of way points needed for 'straight' parameter
|
||||
* @param params - the list of parameters
|
||||
*
|
||||
* @param rctx the context
|
||||
* @param wplist the list of way points needed for 'straight' parameter
|
||||
* @param params the list of parameters
|
||||
*/
|
||||
public void setParams(RoutingContext rctx, List<OsmNodeNamed> wplist, Map<String, String> params) {
|
||||
if (params != null) {
|
||||
|
@ -129,11 +133,15 @@ public class RoutingParamCollector {
|
|||
if (params.containsKey("profile")) {
|
||||
rctx.localFunction = params.get("profile");
|
||||
}
|
||||
if (params.containsKey("nogoLats")) {
|
||||
if (params.containsKey("nogoLats") && params.get("nogoLats").length() > 0) {
|
||||
List<OsmNodeNamed> nogoList = readNogos(params.get("nogoLons"), params.get("nogoLats"), params.get("nogoRadi"));
|
||||
if (nogoList != null) {
|
||||
RoutingContext.prepareNogoPoints(nogoList);
|
||||
rctx.nogopoints = nogoList;
|
||||
if (rctx.nogopoints == null) {
|
||||
rctx.nogopoints = nogoList;
|
||||
} else {
|
||||
rctx.nogopoints.addAll(nogoList);
|
||||
}
|
||||
}
|
||||
params.remove("nogoLats");
|
||||
params.remove("nogoLons");
|
||||
|
@ -143,7 +151,11 @@ public class RoutingParamCollector {
|
|||
List<OsmNodeNamed> nogoList = readNogoList(params.get("nogos"));
|
||||
if (nogoList != null) {
|
||||
RoutingContext.prepareNogoPoints(nogoList);
|
||||
rctx.nogopoints = nogoList;
|
||||
if (rctx.nogopoints == null) {
|
||||
rctx.nogopoints = nogoList;
|
||||
} else {
|
||||
rctx.nogopoints.addAll(nogoList);
|
||||
}
|
||||
}
|
||||
params.remove("nogos");
|
||||
}
|
||||
|
@ -196,6 +208,12 @@ public class RoutingParamCollector {
|
|||
rctx.turnInstructionMode = Integer.parseInt(value);
|
||||
} else if (key.equals("timode")) {
|
||||
rctx.turnInstructionMode = Integer.parseInt(value);
|
||||
} else if (key.equals("turnInstructionFormat")) {
|
||||
if ("osmand".equalsIgnoreCase(value)) {
|
||||
rctx.turnInstructionMode = 3;
|
||||
} else if ("locus".equalsIgnoreCase(value)) {
|
||||
rctx.turnInstructionMode = 2;
|
||||
}
|
||||
} else if (key.equals("exportWaypoints")) {
|
||||
rctx.exportWaypoints = (Integer.parseInt(value) == 1);
|
||||
} else if (key.equals("format")) {
|
||||
|
@ -213,8 +231,9 @@ public class RoutingParamCollector {
|
|||
|
||||
/**
|
||||
* fill profile parameter list
|
||||
* @param rctx - the routing context
|
||||
* @param params - the list of parameters
|
||||
*
|
||||
* @param rctx the routing context
|
||||
* @param params the list of parameters
|
||||
*/
|
||||
public void setProfileParams(RoutingContext rctx, Map<String, String> params) {
|
||||
if (params != null) {
|
||||
|
|
|
@ -20,8 +20,6 @@ android {
|
|||
minSdkVersion 14
|
||||
targetSdkVersion 33
|
||||
|
||||
resConfigs "en"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
|
|
|
@ -171,7 +171,7 @@ public class BInstallerActivity extends AppCompatActivity {
|
|||
}
|
||||
mButtonDownload.setText(getString(R.string.action_download, getSegmentsPlural(selectedTilesDownload.size())));
|
||||
mButtonDownload.setEnabled(true);
|
||||
mSummaryInfo.setText(getString(R.string.summary_segments, Formatter.formatFileSize(this, tileSize), Formatter.formatFileSize(this, getAvailableSpace(mBaseDir.getAbsolutePath()))));
|
||||
mSummaryInfo.setText(String.format(getString(R.string.summary_segments), Formatter.formatFileSize(this, tileSize), Formatter.formatFileSize(this, getAvailableSpace(mBaseDir.getAbsolutePath()))));
|
||||
} else if (selectedTilesUpdate.size() > 0) {
|
||||
mButtonDownload.setText(getString(R.string.action_update, getSegmentsPlural(selectedTilesUpdate.size())));
|
||||
mButtonDownload.setEnabled(true);
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.io.InputStreamReader;
|
|||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
|
@ -39,10 +40,6 @@ public class BRouterService extends Service {
|
|||
|
||||
BRouterWorker worker = new BRouterWorker();
|
||||
|
||||
for (String key : params.keySet()) {
|
||||
// Log.d("BS", "income " + key + " = " + params.get(key));
|
||||
}
|
||||
|
||||
int engineMode = 0;
|
||||
if (params.containsKey("engineMode")) {
|
||||
engineMode = params.getInt("engineMode", 0);
|
||||
|
@ -103,7 +100,7 @@ public class BRouterService extends Service {
|
|||
boolean canCompress = "true".equals(params.getString("acceptCompressedResult"));
|
||||
try {
|
||||
String gpxMessage = worker.getTrackFromParams(params);
|
||||
if (canCompress && gpxMessage.startsWith("<")) {
|
||||
if (canCompress) {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write("z64".getBytes(Charset.forName("UTF-8"))); // marker prefix
|
||||
|
@ -277,8 +274,13 @@ public class BRouterService extends Service {
|
|||
private void logBundle(Bundle params) {
|
||||
if (AppLogger.isLogging()) {
|
||||
for (String k : params.keySet()) {
|
||||
Object val = "remoteProfile".equals(k) ? "<..cut..>" : params.getString(k);
|
||||
String desc = "key=" + k + (val == null ? "" : " class=" + val.getClass() + " val=" + val.toString());
|
||||
Object val = "remoteProfile".equals(k) ? "<..cut..>" : params.get(k);
|
||||
String desc = "key=" + k + (val == null ? "" : " class=" + val.getClass() + " val=");
|
||||
if (val instanceof double[]) {
|
||||
desc += Arrays.toString(params.getDoubleArray(k));
|
||||
} else {
|
||||
desc += val.toString();
|
||||
}
|
||||
AppLogger.log(desc);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,16 +6,20 @@ import android.os.Bundle;
|
|||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.Map;
|
||||
|
||||
import btools.router.FormatGpx;
|
||||
import btools.router.FormatJson;
|
||||
import btools.router.FormatKml;
|
||||
import btools.router.OsmNodeNamed;
|
||||
import btools.router.OsmNogoPolygon;
|
||||
import btools.router.OsmTrack;
|
||||
import btools.router.RoutingContext;
|
||||
import btools.router.RoutingEngine;
|
||||
import btools.router.RoutingParamCollector;
|
||||
|
||||
public class BRouterWorker {
|
||||
private static final int OUTPUT_FORMAT_GPX = 0;
|
||||
|
@ -39,6 +43,72 @@ public class BRouterWorker {
|
|||
engineMode = params.getInt("engineMode", 0);
|
||||
}
|
||||
|
||||
RoutingContext rc = new RoutingContext();
|
||||
rc.rawTrackPath = rawTrackPath;
|
||||
rc.localFunction = profilePath;
|
||||
|
||||
RoutingParamCollector routingParamCollector = new RoutingParamCollector();
|
||||
|
||||
// parameter pre control
|
||||
if (params.containsKey("lonlats")) {
|
||||
waypoints = routingParamCollector.getWayPointList(params.getString("lonlats"));
|
||||
params.remove("lonlats");
|
||||
}
|
||||
if (params.containsKey("lats")) {
|
||||
double[] lats = params.getDoubleArray("lats");
|
||||
double[] lons = params.getDoubleArray("lons");
|
||||
waypoints = routingParamCollector.readPositions(lons, lats);
|
||||
params.remove("lons");
|
||||
params.remove("lats");
|
||||
}
|
||||
|
||||
if (waypoints == null) {
|
||||
throw new IllegalArgumentException("no points!");
|
||||
}
|
||||
if (engineMode == 0) {
|
||||
if (waypoints.size() < 2) {
|
||||
throw new IllegalArgumentException("we need two lat/lon points at least!");
|
||||
}
|
||||
} else {
|
||||
if (waypoints.size() < 1) {
|
||||
throw new IllegalArgumentException("we need two lat/lon points at least!");
|
||||
}
|
||||
}
|
||||
|
||||
if (nogoList != null && nogoList.size() > 0) {
|
||||
// forward already read nogos from filesystem
|
||||
if (rc.nogopoints == null) {
|
||||
rc.nogopoints = nogoList;
|
||||
} else {
|
||||
rc.nogopoints.addAll(nogoList);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Map<String, String> theParams = new HashMap<>();
|
||||
for (String key : params.keySet()) {
|
||||
Object value = params.get(key);
|
||||
if (value instanceof double[]) {
|
||||
String s = Arrays.toString(params.getDoubleArray(key));
|
||||
s = s.replace("[", "").replace("]", "");
|
||||
theParams.put(key, s);
|
||||
} else {
|
||||
theParams.put(key, value.toString());
|
||||
}
|
||||
}
|
||||
routingParamCollector.setParams(rc, waypoints, theParams);
|
||||
|
||||
if (params.containsKey("extraParams")) {
|
||||
Map<String, String> profileparams = null;
|
||||
try {
|
||||
profileparams = routingParamCollector.getUrlParams(params.getString("extraParams"));
|
||||
routingParamCollector.setProfileParams(rc, profileparams);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String pathToFileResult = params.getString("pathToFileResult");
|
||||
|
||||
if (pathToFileResult != null) {
|
||||
|
@ -52,100 +122,9 @@ public class BRouterWorker {
|
|||
long maxRunningTime = 60000;
|
||||
String sMaxRunningTime = params.getString("maxRunningTime");
|
||||
if (sMaxRunningTime != null) {
|
||||
maxRunningTime = Integer.parseInt(sMaxRunningTime) * 1000;
|
||||
maxRunningTime = Integer.parseInt(sMaxRunningTime) * 1000L;
|
||||
}
|
||||
|
||||
RoutingContext rc = new RoutingContext();
|
||||
rc.rawTrackPath = rawTrackPath;
|
||||
rc.localFunction = profilePath;
|
||||
|
||||
String tiFormat = params.getString("turnInstructionFormat");
|
||||
if (tiFormat != null) {
|
||||
if ("osmand".equalsIgnoreCase(tiFormat)) {
|
||||
rc.turnInstructionMode = 3;
|
||||
} else if ("locus".equalsIgnoreCase(tiFormat)) {
|
||||
rc.turnInstructionMode = 2;
|
||||
}
|
||||
}
|
||||
if (params.containsKey("timode")) {
|
||||
rc.turnInstructionMode = params.getInt("timode");
|
||||
}
|
||||
|
||||
if (params.containsKey("direction")) {
|
||||
rc.startDirection = params.getInt("direction");
|
||||
}
|
||||
if (params.containsKey("heading")) {
|
||||
rc.startDirection = params.getInt("heading");
|
||||
rc.forceUseStartDirection = true;
|
||||
}
|
||||
if (params.containsKey("alternativeidx")) {
|
||||
rc.alternativeIdx = params.getInt("alternativeidx");
|
||||
}
|
||||
|
||||
readNogos(params); // add interface provided nogos
|
||||
if (nogoList != null) {
|
||||
RoutingContext.prepareNogoPoints(nogoList);
|
||||
if (rc.nogopoints == null) {
|
||||
rc.nogopoints = nogoList;
|
||||
} else {
|
||||
rc.nogopoints.addAll(nogoList);
|
||||
}
|
||||
}
|
||||
if (rc.nogopoints == null) {
|
||||
rc.nogopoints = nogoPolygonsList;
|
||||
} else if (nogoPolygonsList != null) {
|
||||
rc.nogopoints.addAll(nogoPolygonsList);
|
||||
}
|
||||
List<OsmNodeNamed> poisList = readPoisList(params);
|
||||
rc.poipoints = poisList;
|
||||
|
||||
if (params.containsKey("lats")) {
|
||||
waypoints = readPositions(params);
|
||||
}
|
||||
if (params.containsKey("lonlats")) {
|
||||
waypoints = readLonlats(params, engineMode);
|
||||
}
|
||||
|
||||
if (waypoints == null) return "no pts ";
|
||||
|
||||
if (params.containsKey("straight")) {
|
||||
try {
|
||||
String straight = params.getString("straight");
|
||||
String[] sa = straight.split(",");
|
||||
for (int i = 0; i < sa.length; i++) {
|
||||
int v = Integer.parseInt(sa[i]);
|
||||
if (waypoints.size() > v) waypoints.get(v).direct = true;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
}
|
||||
|
||||
String extraParams = null;
|
||||
if (params.containsKey("extraParams")) { // add user params
|
||||
extraParams = params.getString("extraParams");
|
||||
}
|
||||
if (extraParams != null && this.profileParams != null) {
|
||||
// don't overwrite incoming values
|
||||
extraParams = this.profileParams + "&" + extraParams;
|
||||
} else if (this.profileParams != null) {
|
||||
extraParams = this.profileParams;
|
||||
}
|
||||
|
||||
if (params.containsKey("extraParams")) { // add user params
|
||||
if (rc.keyValues == null) rc.keyValues = new HashMap<>();
|
||||
StringTokenizer tk = new StringTokenizer(extraParams, "?&");
|
||||
while (tk.hasMoreTokens()) {
|
||||
String t = tk.nextToken();
|
||||
StringTokenizer tk2 = new StringTokenizer(t, "=");
|
||||
if (tk2.hasMoreTokens()) {
|
||||
String key = tk2.nextToken();
|
||||
if (tk2.hasMoreTokens()) {
|
||||
String value = tk2.nextToken();
|
||||
rc.keyValues.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
writeTimeoutData(rc);
|
||||
|
@ -170,50 +149,46 @@ public class BRouterWorker {
|
|||
return cr.getErrorMessage();
|
||||
}
|
||||
|
||||
String format = params.getString("trackFormat");
|
||||
int writeFromat = OUTPUT_FORMAT_GPX;
|
||||
if (format != null) {
|
||||
if ("kml".equals(format)) writeFromat = OUTPUT_FORMAT_KML;
|
||||
if ("json".equals(format)) writeFromat = OUTPUT_FORMAT_JSON;
|
||||
if (rc.outputFormat != null) {
|
||||
if ("kml".equals(rc.outputFormat)) writeFromat = OUTPUT_FORMAT_KML;
|
||||
if ("json".equals(rc.outputFormat)) writeFromat = OUTPUT_FORMAT_JSON;
|
||||
}
|
||||
|
||||
OsmTrack track = cr.getFoundTrack();
|
||||
OsmTrack track = null;
|
||||
track = cr.getFoundTrack();
|
||||
if (track != null) {
|
||||
if (params.containsKey("exportWaypoints")) {
|
||||
track.exportWaypoints = (params.getInt("exportWaypoints", 0) == 1);
|
||||
}
|
||||
track.exportWaypoints = rc.exportWaypoints;
|
||||
if (pathToFileResult == null) {
|
||||
switch (writeFromat) {
|
||||
case OUTPUT_FORMAT_GPX:
|
||||
return track.formatAsGpx();
|
||||
case OUTPUT_FORMAT_KML:
|
||||
return track.formatAsKml();
|
||||
return new FormatKml(rc).format(track);
|
||||
case OUTPUT_FORMAT_JSON:
|
||||
return track.formatAsGeoJson();
|
||||
return new FormatJson(rc).format(track);
|
||||
case OUTPUT_FORMAT_GPX:
|
||||
default:
|
||||
return track.formatAsGpx();
|
||||
return new FormatGpx(rc).format(track);
|
||||
}
|
||||
|
||||
}
|
||||
try {
|
||||
switch (writeFromat) {
|
||||
case OUTPUT_FORMAT_GPX:
|
||||
track.writeGpx(pathToFileResult);
|
||||
break;
|
||||
case OUTPUT_FORMAT_KML:
|
||||
track.writeKml(pathToFileResult);
|
||||
break;
|
||||
case OUTPUT_FORMAT_JSON:
|
||||
track.writeJson(pathToFileResult);
|
||||
break;
|
||||
default:
|
||||
track.writeGpx(pathToFileResult);
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return "error writing file: " + e;
|
||||
}
|
||||
|
||||
}
|
||||
try {
|
||||
switch (writeFromat) {
|
||||
case OUTPUT_FORMAT_KML:
|
||||
new FormatKml(rc).write(pathToFileResult, track);
|
||||
break;
|
||||
case OUTPUT_FORMAT_JSON:
|
||||
new FormatJson(rc).write(pathToFileResult, track);
|
||||
break;
|
||||
case OUTPUT_FORMAT_GPX:
|
||||
default:
|
||||
new FormatGpx(rc).write(pathToFileResult, track);
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return "error writing file: " + e;
|
||||
}
|
||||
|
||||
} else { // get other infos
|
||||
if (cr.getErrorMessage() != null) {
|
||||
return cr.getErrorMessage();
|
||||
|
@ -223,204 +198,6 @@ public class BRouterWorker {
|
|||
return null;
|
||||
}
|
||||
|
||||
private List<OsmNodeNamed> readPositions(Bundle params) {
|
||||
List<OsmNodeNamed> wplist = new ArrayList<>();
|
||||
|
||||
double[] lats = params.getDoubleArray("lats");
|
||||
double[] lons = params.getDoubleArray("lons");
|
||||
|
||||
if (lats == null || lats.length < 2 || lons == null || lons.length < 2) {
|
||||
throw new IllegalArgumentException("we need two lat/lon points at least!");
|
||||
}
|
||||
|
||||
for (int i = 0; i < lats.length && i < lons.length; i++) {
|
||||
OsmNodeNamed n = new OsmNodeNamed();
|
||||
n.name = "via" + i;
|
||||
n.ilon = (int) ((lons[i] + 180.) * 1000000. + 0.5);
|
||||
n.ilat = (int) ((lats[i] + 90.) * 1000000. + 0.5);
|
||||
wplist.add(n);
|
||||
}
|
||||
if (wplist.get(0).name.startsWith("via")) wplist.get(0).name = "from";
|
||||
if (wplist.get(wplist.size() - 1).name.startsWith("via"))
|
||||
wplist.get(wplist.size() - 1).name = "to";
|
||||
|
||||
return wplist;
|
||||
}
|
||||
|
||||
private List<OsmNodeNamed> readLonlats(Bundle params, int mode) {
|
||||
List<OsmNodeNamed> wplist = new ArrayList<>();
|
||||
|
||||
String lonLats = params.getString("lonlats");
|
||||
if (lonLats == null) throw new IllegalArgumentException("lonlats parameter not set");
|
||||
|
||||
String[] coords;
|
||||
if (mode == 0) {
|
||||
coords = lonLats.split("\\|");
|
||||
if (coords.length < 2)
|
||||
throw new IllegalArgumentException("we need two lat/lon points at least!");
|
||||
} else {
|
||||
coords = new String[1];
|
||||
coords[0] = lonLats;
|
||||
}
|
||||
for (int i = 0; i < coords.length; i++) {
|
||||
String[] lonLat = coords[i].split(",");
|
||||
if (lonLat.length < 2)
|
||||
throw new IllegalArgumentException("we need a lat and lon point at least!");
|
||||
wplist.add(readPosition(lonLat[0], lonLat[1], "via" + i));
|
||||
if (lonLat.length > 2) {
|
||||
if (lonLat[2].equals("d")) {
|
||||
wplist.get(wplist.size() - 1).direct = true;
|
||||
} else {
|
||||
wplist.get(wplist.size() - 1).name = lonLat[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wplist.get(0).name.startsWith("via")) wplist.get(0).name = "from";
|
||||
if (wplist.get(wplist.size() - 1).name.startsWith("via"))
|
||||
wplist.get(wplist.size() - 1).name = "to";
|
||||
|
||||
return wplist;
|
||||
}
|
||||
|
||||
private static OsmNodeNamed readPosition(String vlon, String vlat, String name) {
|
||||
if (vlon == null) throw new IllegalArgumentException("lon " + name + " not found in input");
|
||||
if (vlat == null) throw new IllegalArgumentException("lat " + name + " not found in input");
|
||||
|
||||
return readPosition(Double.parseDouble(vlon), Double.parseDouble(vlat), name);
|
||||
}
|
||||
|
||||
private static OsmNodeNamed readPosition(double lon, double lat, String name) {
|
||||
OsmNodeNamed n = new OsmNodeNamed();
|
||||
n.name = name;
|
||||
n.ilon = (int) ((lon + 180.) * 1000000. + 0.5);
|
||||
n.ilat = (int) ((lat + 90.) * 1000000. + 0.5);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
private void readNogos(Bundle params) {
|
||||
if (params.containsKey("nogoLats")) {
|
||||
double[] lats = params.getDoubleArray("nogoLats");
|
||||
double[] lons = params.getDoubleArray("nogoLons");
|
||||
double[] radi = params.getDoubleArray("nogoRadi");
|
||||
|
||||
if (lats == null || lons == null || radi == null) return;
|
||||
|
||||
for (int i = 0; i < lats.length && i < lons.length && i < radi.length; i++) {
|
||||
OsmNodeNamed n = new OsmNodeNamed();
|
||||
n.name = "nogo" + (int) radi[i];
|
||||
n.ilon = (int) ((lons[i] + 180.) * 1000000. + 0.5);
|
||||
n.ilat = (int) ((lats[i] + 90.) * 1000000. + 0.5);
|
||||
n.isNogo = true;
|
||||
n.nogoWeight = Double.NaN;
|
||||
AppLogger.log("added interface provided nogo: " + n);
|
||||
nogoList.add(n);
|
||||
}
|
||||
}
|
||||
if (params.containsKey("nogos")) {
|
||||
nogoList = readNogoList(params);
|
||||
}
|
||||
if (params.containsKey("polylines") ||
|
||||
params.containsKey("polygons")) {
|
||||
nogoPolygonsList = readNogoPolygons(params);
|
||||
}
|
||||
}
|
||||
|
||||
private List<OsmNodeNamed> readNogoList(Bundle params) {
|
||||
// lon,lat,radius|...
|
||||
String nogos = params.getString("nogos");
|
||||
if (nogos == null) return null;
|
||||
|
||||
String[] lonLatRadList = nogos.split("\\|");
|
||||
|
||||
List<OsmNodeNamed> nogoList = new ArrayList<>();
|
||||
for (int i = 0; i < lonLatRadList.length; i++) {
|
||||
String[] lonLatRad = lonLatRadList[i].split(",");
|
||||
String nogoWeight = "NaN";
|
||||
if (lonLatRad.length > 3) {
|
||||
nogoWeight = lonLatRad[3];
|
||||
}
|
||||
nogoList.add(readNogo(lonLatRad[0], lonLatRad[1], lonLatRad[2], nogoWeight));
|
||||
}
|
||||
|
||||
return nogoList;
|
||||
}
|
||||
|
||||
private static OsmNodeNamed readNogo(String lon, String lat, String radius, String nogoWeight) {
|
||||
double weight = "undefined".equals(nogoWeight) ? Double.NaN : Double.parseDouble(nogoWeight);
|
||||
return readNogo(Double.parseDouble(lon), Double.parseDouble(lat), Integer.parseInt(radius), weight);
|
||||
}
|
||||
|
||||
private static OsmNodeNamed readNogo(double lon, double lat, int radius, double nogoWeight) {
|
||||
OsmNodeNamed n = new OsmNodeNamed();
|
||||
n.name = "nogo" + radius;
|
||||
n.ilon = (int) ((lon + 180.) * 1000000. + 0.5);
|
||||
n.ilat = (int) ((lat + 90.) * 1000000. + 0.5);
|
||||
n.isNogo = true;
|
||||
n.nogoWeight = nogoWeight;
|
||||
return n;
|
||||
}
|
||||
|
||||
private List<OsmNodeNamed> readNogoPolygons(Bundle params) {
|
||||
List<OsmNodeNamed> result = new ArrayList<>();
|
||||
parseNogoPolygons(params.getString("polylines"), result, false);
|
||||
parseNogoPolygons(params.getString("polygons"), result, true);
|
||||
return result.size() > 0 ? result : null;
|
||||
}
|
||||
|
||||
private static void parseNogoPolygons(String polygons, List<OsmNodeNamed> result, boolean closed) {
|
||||
if (polygons != null) {
|
||||
String[] polygonList = polygons.split("\\|");
|
||||
for (int i = 0; i < polygonList.length; i++) {
|
||||
String[] lonLatList = polygonList[i].split(",");
|
||||
if (lonLatList.length > 1) {
|
||||
OsmNogoPolygon polygon = new OsmNogoPolygon(closed);
|
||||
polygon.name = "nogo" + i;
|
||||
int j;
|
||||
for (j = 0; j < 2 * (lonLatList.length / 2) - 1; ) {
|
||||
String slon = lonLatList[j++];
|
||||
String slat = lonLatList[j++];
|
||||
int lon = (int) ((Double.parseDouble(slon) + 180.) * 1000000. + 0.5);
|
||||
int lat = (int) ((Double.parseDouble(slat) + 90.) * 1000000. + 0.5);
|
||||
polygon.addVertex(lon, lat);
|
||||
}
|
||||
|
||||
String nogoWeight = "NaN";
|
||||
if (j < lonLatList.length) {
|
||||
nogoWeight = lonLatList[j];
|
||||
}
|
||||
polygon.nogoWeight = Double.parseDouble(nogoWeight);
|
||||
|
||||
if (polygon.points.size() > 0) {
|
||||
polygon.calcBoundingCircle();
|
||||
result.add(polygon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<OsmNodeNamed> readPoisList(Bundle params) {
|
||||
// lon,lat,name|...
|
||||
String pois = params.getString("pois");
|
||||
if (pois == null) return null;
|
||||
|
||||
String[] lonLatNameList = pois.split("\\|");
|
||||
|
||||
List<OsmNodeNamed> poisList = new ArrayList<>();
|
||||
for (int i = 0; i < lonLatNameList.length; i++) {
|
||||
String[] lonLatName = lonLatNameList[i].split(",");
|
||||
|
||||
OsmNodeNamed n = new OsmNodeNamed();
|
||||
n.ilon = (int) ((Double.parseDouble(lonLatName[0]) + 180.) * 1000000. + 0.5);
|
||||
n.ilat = (int) ((Double.parseDouble(lonLatName[1]) + 90.) * 1000000. + 0.5);
|
||||
n.name = lonLatName[2];
|
||||
poisList.add(n);
|
||||
}
|
||||
|
||||
return poisList;
|
||||
}
|
||||
|
||||
private void writeTimeoutData(RoutingContext rc) throws Exception {
|
||||
String timeoutFile = baseDir + "/brouter/modes/timeoutdata.txt";
|
||||
|
@ -431,7 +208,7 @@ public class BRouterWorker {
|
|||
bw.write(rc.rawTrackPath);
|
||||
bw.write("\n");
|
||||
writeWPList(bw, waypoints);
|
||||
writeWPList(bw, nogoList);
|
||||
writeWPList(bw, rc.nogopoints);
|
||||
bw.close();
|
||||
}
|
||||
|
||||
|
|
16
brouter-routing-app/src/main/res/values-ar/strings.xml
Normal file
16
brouter-routing-app/src/main/res/values-ar/strings.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
|
||||
<item quantity="one">%d قطعة</item>
|
||||
<item quantity="other">%d قطع</item>
|
||||
</plurals>
|
||||
<string name="cancel_download">إلغاء التنزيل</string>
|
||||
<string name="import_profile">استيراد ملف تعريف</string>
|
||||
<string name="action_download">تنزيل %s</string>
|
||||
<string name="action_delete">حذف %s</string>
|
||||
<string name="action_update">تحديث %s</string>
|
||||
<string name="action_select">حدد القطع</string>
|
||||
<string name="action_cancel">إيقاف التنزيل</string>
|
||||
<string name="summary_segments">الحجم=%1$s\nالحجم المتوفر=%2$s</string>
|
||||
<string name="notification_title">تحميل القطع</string>
|
||||
</resources>
|
16
brouter-routing-app/src/main/res/values-ca/strings.xml
Normal file
16
brouter-routing-app/src/main/res/values-ca/strings.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
|
||||
<item quantity="one">%d segment</item>
|
||||
<item quantity="other">%d segments</item>
|
||||
</plurals>
|
||||
<string name="cancel_download">Cancel·lar descàrrega</string>
|
||||
<string name="import_profile">Importar perfil</string>
|
||||
<string name="action_download">Descàrrega %s</string>
|
||||
<string name="action_delete">Eliminar %s</string>
|
||||
<string name="action_update">Actualitzar %s</string>
|
||||
<string name="action_select">Seleccionar segments</string>
|
||||
<string name="action_cancel">Aturar Descàrrega</string>
|
||||
<string name="summary_segments">Espai=%1$s\nLliure=%2$s</string>
|
||||
<string name="notification_title">Descarregar segments</string>
|
||||
</resources>
|
16
brouter-routing-app/src/main/res/values-de/strings.xml
Normal file
16
brouter-routing-app/src/main/res/values-de/strings.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
|
||||
<item quantity="one">%d Segment</item>
|
||||
<item quantity="other">%d Segmente</item>
|
||||
</plurals>
|
||||
<string name="cancel_download">Download abbrechen</string>
|
||||
<string name="import_profile">Profil importieren</string>
|
||||
<string name="action_download">%s downloaden</string>
|
||||
<string name="action_delete">%s löschen</string>
|
||||
<string name="action_update">%s aktualisieren</string>
|
||||
<string name="action_select">Segmente auswählen</string>
|
||||
<string name="action_cancel">Download stoppen</string>
|
||||
<string name="summary_segments">Größe=%1$s\nFrei=%2$s</string>
|
||||
<string name="notification_title">Segmente herunterladen</string>
|
||||
</resources>
|
16
brouter-routing-app/src/main/res/values-el/strings.xml
Normal file
16
brouter-routing-app/src/main/res/values-el/strings.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
|
||||
<item quantity="one">%d τμήμα</item>
|
||||
<item quantity="other">%d τμήματα</item>
|
||||
</plurals>
|
||||
<string name="cancel_download">Ακύρωση λήψης</string>
|
||||
<string name="import_profile">Εισαγωγή προφίλ</string>
|
||||
<string name="action_download">Λήψη %s</string>
|
||||
<string name="action_delete">Διαγραφή %s</string>
|
||||
<string name="action_update">Ενημέρωση %s</string>
|
||||
<string name="action_select">Επιλογή τμημάτων</string>
|
||||
<string name="action_cancel">Διακοπή λήψης</string>
|
||||
<string name="summary_segments">Μέγεθος=%1$s\nΕλεύθερο=%2$s</string>
|
||||
<string name="notification_title">Λήψη τμημάτων</string>
|
||||
</resources>
|
16
brouter-routing-app/src/main/res/values-es/strings.xml
Normal file
16
brouter-routing-app/src/main/res/values-es/strings.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
|
||||
<item quantity="one">%d segmento</item>
|
||||
<item quantity="other">%d segmentos</item>
|
||||
</plurals>
|
||||
<string name="cancel_download">Cancelar descarga</string>
|
||||
<string name="import_profile">Importar perfil</string>
|
||||
<string name="action_download">Descargar %s</string>
|
||||
<string name="action_delete">Eliminar %s</string>
|
||||
<string name="action_update">Actualizar %s</string>
|
||||
<string name="action_select">Seleccionar segmentos</string>
|
||||
<string name="action_cancel">Detener descarga</string>
|
||||
<string name="summary_segments">Tamaño=%1$s\nGratis=%2$s</string>
|
||||
<string name="notification_title">Descargar segmentos</string>
|
||||
</resources>
|
16
brouter-routing-app/src/main/res/values-fr/strings.xml
Normal file
16
brouter-routing-app/src/main/res/values-fr/strings.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
|
||||
<item quantity="one">%d segment</item>
|
||||
<item quantity="other">%d segments</item>
|
||||
</plurals>
|
||||
<string name="cancel_download">Annuler le téléchargement</string>
|
||||
<string name="import_profile">Importer le profil</string>
|
||||
<string name="action_download">Télécharger %s</string>
|
||||
<string name="action_delete">Supprimer %s</string>
|
||||
<string name="action_update">Mettre à jour %s</string>
|
||||
<string name="action_select">Sélectionner les segments</string>
|
||||
<string name="action_cancel">Arrêter le téléchargement</string>
|
||||
<string name="summary_segments">Taille=%1$s\nGratuit=%2$s</string>
|
||||
<string name="notification_title">Télécharger les segments</string>
|
||||
</resources>
|
16
brouter-routing-app/src/main/res/values-it/strings.xml
Normal file
16
brouter-routing-app/src/main/res/values-it/strings.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
|
||||
<item quantity="one">%d segmento</item>
|
||||
<item quantity="other">%d segmenti</item>
|
||||
</plurals>
|
||||
<string name="cancel_download">Annulla download</string>
|
||||
<string name="import_profile">Importa profilo</string>
|
||||
<string name="action_download">Scarica %s</string>
|
||||
<string name="action_delete">Elimina %s</string>
|
||||
<string name="action_update">Aggiorna %s</string>
|
||||
<string name="action_select">Seleziona segmenti</string>
|
||||
<string name="action_cancel">Interrompi download</string>
|
||||
<string name="summary_segments">Taglia=%1$s\nGratis=%2$s</string>
|
||||
<string name="notification_title">Scarica segmenti</string>
|
||||
</resources>
|
16
brouter-routing-app/src/main/res/values-nl/strings.xml
Normal file
16
brouter-routing-app/src/main/res/values-nl/strings.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
|
||||
<item quantity="one">%d segment</item>
|
||||
<item quantity="other">%d segmenten</item>
|
||||
</plurals>
|
||||
<string name="cancel_download">Download annuleren</string>
|
||||
<string name="import_profile">Profiel importeren</string>
|
||||
<string name="action_download">Downloaden %s</string>
|
||||
<string name="action_delete">Verwijderen %s</string>
|
||||
<string name="action_update">Bijwerken %s</string>
|
||||
<string name="action_select">Segmenten selecteren</string>
|
||||
<string name="action_cancel">Download stoppen</string>
|
||||
<string name="summary_segments">Grootte=%1$s\nGratis=%2$s</string>
|
||||
<string name="notification_title">Segmenten downloaden</string>
|
||||
</resources>
|
16
brouter-routing-app/src/main/res/values-pl/strings.xml
Normal file
16
brouter-routing-app/src/main/res/values-pl/strings.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
|
||||
<item quantity="one">%d segment</item>
|
||||
<item quantity="other">%d segmenty/ów</item>
|
||||
</plurals>
|
||||
<string name="cancel_download">Anuluj pobieranie</string>
|
||||
<string name="import_profile">Importuj profil</string>
|
||||
<string name="action_download">Pobierz %s</string>
|
||||
<string name="action_delete">Usuń %s</string>
|
||||
<string name="action_update">Zaktualizuj %s</string>
|
||||
<string name="action_select">Wybierz segmenty</string>
|
||||
<string name="action_cancel">Zatrzymaj pobieranie</string>
|
||||
<string name="summary_segments">Rozmiar=%1$s\nDostępne=%2$s</string>
|
||||
<string name="notification_title">Pobieranie segmentów</string>
|
||||
</resources>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">BRouter</string>
|
||||
<string name="profile_filename_example">filename.brf</string>
|
||||
<string name="notification_channel_id">brouter_download</string>
|
||||
<string name="channel_name">Downloads</string>
|
||||
</resources>
|
|
@ -1,37 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2010 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<plurals name="numberOfSegments">
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
|
||||
<item quantity="one">%d segment</item>
|
||||
<item quantity="other">%d segments</item>
|
||||
</plurals>
|
||||
<string name="app_name">BRouter</string>
|
||||
<string name="cancel_download">Cancel Download</string>
|
||||
<string name="import_profile">Import Profile</string>
|
||||
<string name="profile_filename_example">filename.brf</string>
|
||||
<string name="download_info_start">Starting download…</string>
|
||||
<string name="download_info_cancel">Cancelling…</string>
|
||||
<string name="cancel_download">Cancel download</string>
|
||||
<string name="import_profile">Import profile</string>
|
||||
<string name="action_download">Download %s</string>
|
||||
<string name="action_delete">Delete %s</string>
|
||||
<string name="action_update">Update %s</string>
|
||||
<string name="action_select">Select segments</string>
|
||||
<string name="action_cancel">Stop Download</string>
|
||||
<string name="summary_segments">Size=%s\nFree=%s</string>
|
||||
<string name="notification_channel_id">brouter_download</string>
|
||||
<string name="notification_title">Download Segments</string>
|
||||
<string name="channel_name">Downloads</string>
|
||||
<string name="action_cancel">Stop download</string>
|
||||
<string name="summary_segments">Size=%1$s\nFree=%2$s</string>
|
||||
<string name="notification_title">Download segments</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
package btools.server;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -14,11 +10,10 @@ import btools.router.OsmTrack;
|
|||
import btools.router.RoutingContext;
|
||||
import btools.router.RoutingEngine;
|
||||
import btools.router.RoutingParamCollector;
|
||||
import btools.router.SearchBoundary;
|
||||
|
||||
public class BRouter {
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length == 3) { // cgi-input-mode
|
||||
if (args.length == 3 || args.length == 4) { // cgi-input-mode
|
||||
try {
|
||||
System.setProperty("segmentBaseDir", args[0]);
|
||||
System.setProperty("profileBaseDir", args[1]);
|
||||
|
@ -28,19 +23,27 @@ public class BRouter {
|
|||
|
||||
int lonIdx = queryString.indexOf("lonlats=");
|
||||
int sepIdx = queryString.indexOf("&", lonIdx);
|
||||
String lonlats = queryString.substring(lonIdx+8, sepIdx);
|
||||
String lonlats = queryString.substring(lonIdx + 8, sepIdx);
|
||||
|
||||
RoutingContext rc = new RoutingContext();
|
||||
RoutingParamCollector routingParamCollector = new RoutingParamCollector();
|
||||
List<OsmNodeNamed> wplist = routingParamCollector.getWayPointList(lonlats);
|
||||
|
||||
Map<String, String> params = routingParamCollector.getUrlParams(queryString);
|
||||
int engineMode = 0;
|
||||
if (params.containsKey("engineMode")) {
|
||||
engineMode = Integer.parseInt(params.get("engineMode"));
|
||||
}
|
||||
routingParamCollector.setParams(rc, wplist, params);
|
||||
|
||||
// cgi-header
|
||||
System.out.println("Content-type: text/plain");
|
||||
System.out.println();
|
||||
|
||||
String exportName = null;
|
||||
if (args.length == 4) {
|
||||
exportName = args[3];
|
||||
} else {
|
||||
// cgi-header
|
||||
System.out.println("Content-type: text/plain");
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
long maxRunningTime = 60000; // the cgi gets a 1 Minute timeout
|
||||
String sMaxRunningTime = System.getProperty("maxRunningTime");
|
||||
|
@ -48,9 +51,7 @@ public class BRouter {
|
|||
maxRunningTime = Integer.parseInt(sMaxRunningTime) * 1000;
|
||||
}
|
||||
|
||||
|
||||
RoutingEngine re = new RoutingEngine(null, null, new File(args[0]), wplist, rc);
|
||||
|
||||
RoutingEngine re = new RoutingEngine(exportName, null, new File(args[0]), wplist, rc, engineMode);
|
||||
re.doRun(maxRunningTime);
|
||||
if (re.getErrorMessage() != null) {
|
||||
System.out.println(re.getErrorMessage());
|
||||
|
@ -65,96 +66,53 @@ public class BRouter {
|
|||
System.out.println("Find routes in an OSM map");
|
||||
System.out.println("usage: java -jar brouter.jar <segmentdir> <profiledir> <engineMode> <profile> <lonlats-list> [parameter-list] [profile-parameter-list] ");
|
||||
System.out.println(" or: java -cp %CLASSPATH% btools.server.BRouter <segmentdir>> <profiledir> <engineMode> <profile> <lonlats-list> [parameter-list] [profile-parameter-list]");
|
||||
System.out.println(" or: java -jar brouter.jar <segmentdir> <profiledir> <parameter-list> ");
|
||||
System.out.println(" or: java -jar brouter.jar <segmentdir> <profiledir> <parameter-list> [output-filename]");
|
||||
System.exit(0);
|
||||
}
|
||||
RoutingEngine re = null;
|
||||
if ("seed".equals(args[3])) {
|
||||
List<OsmNodeNamed> wplist = new ArrayList<>();
|
||||
wplist.add(readPosition(args, 1, "from"));
|
||||
int searchRadius = Integer.parseInt(args[4]); // if = 0 search a 5x5 square
|
||||
|
||||
String filename = SearchBoundary.getFileName(wplist.get(0));
|
||||
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("traffic/" + filename)));
|
||||
|
||||
for (int direction = 0; direction < 8; direction++) {
|
||||
RoutingContext rc = readRoutingContext(args);
|
||||
SearchBoundary boundary = new SearchBoundary(wplist.get(0), searchRadius, direction / 2);
|
||||
rc.trafficOutputStream = dos;
|
||||
rc.inverseDirection = (direction & 1) != 0;
|
||||
re = new RoutingEngine("mytrack", "mylog", new File(args[0]), wplist, rc);
|
||||
re.boundary = boundary;
|
||||
re.airDistanceCostFactor = rc.trafficDirectionFactor;
|
||||
rc.countTraffic = true;
|
||||
re.doSearch();
|
||||
if (re.getErrorMessage() != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
dos.close();
|
||||
} else {
|
||||
int engineMode = 0;
|
||||
try {
|
||||
engineMode = Integer.parseInt(args[2]);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
|
||||
RoutingParamCollector routingParamCollector = new RoutingParamCollector();
|
||||
List<OsmNodeNamed> wplist = routingParamCollector.getWayPointList(args[4]);
|
||||
|
||||
System.setProperty("segmentBaseDir", args[0]);
|
||||
System.setProperty("profileBaseDir", args[1]);
|
||||
String moreParams = null;
|
||||
String profileParams = null;
|
||||
if (args.length >= 6) {
|
||||
moreParams = args[5];
|
||||
}
|
||||
if (args.length == 7) {
|
||||
profileParams = args[6];
|
||||
}
|
||||
|
||||
RoutingContext rc = new RoutingContext();
|
||||
rc.localFunction = args[3];
|
||||
if (moreParams != null) {
|
||||
Map<String, String> params = routingParamCollector.getUrlParams(moreParams);
|
||||
routingParamCollector.setParams(rc, wplist, params);
|
||||
}
|
||||
if (profileParams != null) {
|
||||
Map<String, String> params = routingParamCollector.getUrlParams(profileParams);
|
||||
routingParamCollector.setProfileParams(rc, params);
|
||||
}
|
||||
try {
|
||||
if (engineMode==RoutingEngine.BROUTER_ENGINEMODE_GETELEV) {
|
||||
re = new RoutingEngine("testinfo", null, new File(args[0]), wplist, rc, engineMode);
|
||||
} else {
|
||||
re = new RoutingEngine("testtrack", null, new File(args[0]), wplist, rc, engineMode);
|
||||
}
|
||||
re.doRun(0);
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
int engineMode = 0;
|
||||
try {
|
||||
engineMode = Integer.parseInt(args[2]);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
}
|
||||
|
||||
RoutingParamCollector routingParamCollector = new RoutingParamCollector();
|
||||
List<OsmNodeNamed> wplist = routingParamCollector.getWayPointList(args[4]);
|
||||
|
||||
private static OsmNodeNamed readPosition(String[] args, int idx, String name) {
|
||||
OsmNodeNamed n = new OsmNodeNamed();
|
||||
n.name = name;
|
||||
n.ilon = (int) ((Double.parseDouble(args[idx]) + 180.) * 1000000. + 0.5);
|
||||
n.ilat = (int) ((Double.parseDouble(args[idx + 1]) + 90.) * 1000000. + 0.5);
|
||||
return n;
|
||||
}
|
||||
|
||||
private static RoutingContext readRoutingContext(String[] args) {
|
||||
RoutingContext c = new RoutingContext();
|
||||
if (args.length > 5) {
|
||||
c.localFunction = args[5];
|
||||
if (args.length > 6) {
|
||||
c.setAlternativeIdx(Integer.parseInt(args[6]));
|
||||
}
|
||||
System.setProperty("segmentBaseDir", args[0]);
|
||||
System.setProperty("profileBaseDir", args[1]);
|
||||
String moreParams = null;
|
||||
String profileParams = null;
|
||||
if (args.length >= 6) {
|
||||
moreParams = args[5];
|
||||
}
|
||||
c.memoryclass = (int) (Runtime.getRuntime().maxMemory() / 1024 / 1024);
|
||||
// c.startDirection= Integer.valueOf( 150 );
|
||||
return c;
|
||||
if (args.length == 7) {
|
||||
profileParams = args[6];
|
||||
}
|
||||
|
||||
RoutingContext rc = new RoutingContext();
|
||||
rc.localFunction = args[3];
|
||||
if (moreParams != null) {
|
||||
Map<String, String> params = routingParamCollector.getUrlParams(moreParams);
|
||||
routingParamCollector.setParams(rc, wplist, params);
|
||||
}
|
||||
if (profileParams != null) {
|
||||
Map<String, String> params = routingParamCollector.getUrlParams(profileParams);
|
||||
routingParamCollector.setProfileParams(rc, params);
|
||||
}
|
||||
try {
|
||||
RoutingEngine re = null;
|
||||
if (engineMode == RoutingEngine.BROUTER_ENGINEMODE_GETELEV) {
|
||||
re = new RoutingEngine("testinfo", null, new File(args[0]), wplist, rc, engineMode);
|
||||
} else {
|
||||
re = new RoutingEngine("testtrack", null, new File(args[0]), wplist, rc, engineMode);
|
||||
}
|
||||
re.doRun(0);
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import btools.router.OsmTrack;
|
|||
import btools.router.ProfileCache;
|
||||
import btools.router.RoutingContext;
|
||||
import btools.router.RoutingEngine;
|
||||
import btools.router.RoutingParamCollector;
|
||||
import btools.server.request.ProfileUploadHandler;
|
||||
import btools.server.request.RequestHandler;
|
||||
import btools.server.request.ServerHandler;
|
||||
|
@ -146,7 +147,9 @@ public class RouteServer extends Thread implements Comparable<RouteServer> {
|
|||
}
|
||||
|
||||
String url = getline.split(" ")[1];
|
||||
Map<String, String> params = getUrlParams(url);
|
||||
|
||||
RoutingParamCollector routingParamCollector = new RoutingParamCollector();
|
||||
Map<String, String> params = routingParamCollector.getUrlParams(url);
|
||||
|
||||
long maxRunningTime = getMaxRunningTime();
|
||||
|
||||
|
@ -186,33 +189,17 @@ public class RouteServer extends Thread implements Comparable<RouteServer> {
|
|||
return;
|
||||
}
|
||||
RoutingContext rc = handler.readRoutingContext();
|
||||
List<OsmNodeNamed> wplist = handler.readWayPointList();
|
||||
List<OsmNodeNamed> wplist = routingParamCollector.getWayPointList(params.get("lonlats"));
|
||||
|
||||
if (wplist.size() < 10) {
|
||||
SuspectManager.nearRecentWps.add(wplist);
|
||||
}
|
||||
int engineMode = 0;
|
||||
for (Map.Entry<String, String> e : params.entrySet()) {
|
||||
if ("engineMode".equals(e.getKey())) {
|
||||
engineMode = Integer.parseInt(e.getValue());
|
||||
} else if ("timode".equals(e.getKey())) {
|
||||
rc.turnInstructionMode = Integer.parseInt(e.getValue());
|
||||
} else if ("heading".equals(e.getKey())) {
|
||||
rc.startDirection = Integer.parseInt(e.getValue());
|
||||
rc.forceUseStartDirection = true;
|
||||
} else if (e.getKey().startsWith("profile:")) {
|
||||
if (rc.keyValues == null) {
|
||||
rc.keyValues = new HashMap<>();
|
||||
}
|
||||
rc.keyValues.put(e.getKey().substring(8), e.getValue());
|
||||
} else if (e.getKey().equals("straight")) {
|
||||
String[] sa = e.getValue().split(",");
|
||||
for (int i = 0; i < sa.length; i++) {
|
||||
int v = Integer.parseInt(sa[i]);
|
||||
if (wplist.size() > v) wplist.get(v).direct = true;
|
||||
}
|
||||
}
|
||||
if (params.containsKey("engineMode")) {
|
||||
engineMode = Integer.parseInt(params.get("engineMode"));
|
||||
}
|
||||
routingParamCollector.setParams(rc, wplist, params);
|
||||
|
||||
cr = new RoutingEngine(null, null, serviceContext.segmentDir, wplist, rc, engineMode);
|
||||
cr.quite = true;
|
||||
cr.doRun(maxRunningTime);
|
||||
|
@ -224,18 +211,29 @@ public class RouteServer extends Thread implements Comparable<RouteServer> {
|
|||
} else {
|
||||
OsmTrack track = cr.getFoundTrack();
|
||||
|
||||
if (engineMode == 2) {
|
||||
// no zip for this engineMode
|
||||
encodings = null;
|
||||
}
|
||||
String headers = encodings == null || encodings.indexOf("gzip") < 0 ? null : "Content-Encoding: gzip\n";
|
||||
writeHttpHeader(bw, handler.getMimeType(), handler.getFileName(), headers, HTTP_STATUS_OK);
|
||||
if (track != null) {
|
||||
if (headers != null) { // compressed
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
Writer w = new OutputStreamWriter(new GZIPOutputStream(baos), "UTF-8");
|
||||
w.write(handler.formatTrack(track));
|
||||
w.close();
|
||||
bw.flush();
|
||||
clientSocket.getOutputStream().write(baos.toByteArray());
|
||||
} else {
|
||||
bw.write(handler.formatTrack(track));
|
||||
if (engineMode == 0) {
|
||||
if (track != null) {
|
||||
if (headers != null) { // compressed
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
Writer w = new OutputStreamWriter(new GZIPOutputStream(baos), "UTF-8");
|
||||
w.write(handler.formatTrack(track));
|
||||
w.close();
|
||||
bw.flush();
|
||||
clientSocket.getOutputStream().write(baos.toByteArray());
|
||||
} else {
|
||||
bw.write(handler.formatTrack(track));
|
||||
}
|
||||
}
|
||||
} else if (engineMode == 2) {
|
||||
String s = cr.getFoundInfo();
|
||||
if (s != null) {
|
||||
bw.write(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package btools.server.request;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import btools.router.OsmNodeNamed;
|
||||
import btools.router.OsmTrack;
|
||||
import btools.router.RoutingContext;
|
||||
import btools.server.ServiceContext;
|
||||
|
@ -19,8 +17,6 @@ public abstract class RequestHandler {
|
|||
|
||||
public abstract RoutingContext readRoutingContext();
|
||||
|
||||
public abstract List<OsmNodeNamed> readWayPointList();
|
||||
|
||||
public abstract String formatTrack(OsmTrack track);
|
||||
|
||||
public abstract String getMimeType();
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
package btools.server.request;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import btools.router.OsmNodeNamed;
|
||||
import btools.router.OsmNogoPolygon;
|
||||
import btools.router.FormatCsv;
|
||||
import btools.router.FormatGpx;
|
||||
import btools.router.FormatJson;
|
||||
import btools.router.FormatKml;
|
||||
import btools.router.OsmTrack;
|
||||
import btools.router.RoutingContext;
|
||||
import btools.server.ServiceContext;
|
||||
|
@ -62,59 +60,9 @@ public class ServerHandler extends RequestHandler {
|
|||
}
|
||||
rc.localFunction = profile;
|
||||
|
||||
rc.setAlternativeIdx(Integer.parseInt(params.get("alternativeidx")));
|
||||
|
||||
List<OsmNodeNamed> poisList = readPoisList();
|
||||
rc.poipoints = poisList;
|
||||
|
||||
List<OsmNodeNamed> nogoList = readNogoList();
|
||||
List<OsmNodeNamed> nogoPolygonsList = readNogoPolygons();
|
||||
|
||||
if (nogoList != null) {
|
||||
RoutingContext.prepareNogoPoints(nogoList);
|
||||
rc.nogopoints = nogoList;
|
||||
}
|
||||
|
||||
if (rc.nogopoints == null) {
|
||||
rc.nogopoints = nogoPolygonsList;
|
||||
} else if (nogoPolygonsList != null) {
|
||||
rc.nogopoints.addAll(nogoPolygonsList);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OsmNodeNamed> readWayPointList() {
|
||||
// lon,lat|...
|
||||
String lonLats = params.get("lonlats");
|
||||
if (lonLats == null) throw new IllegalArgumentException("lonlats parameter not set");
|
||||
|
||||
String[] coords = lonLats.split("\\|");
|
||||
if (coords.length < 2)
|
||||
throw new IllegalArgumentException("we need two lat/lon points at least!");
|
||||
|
||||
List<OsmNodeNamed> wplist = new ArrayList<>();
|
||||
for (int i = 0; i < coords.length; i++) {
|
||||
String[] lonLat = coords[i].split(",");
|
||||
if (lonLat.length < 2)
|
||||
throw new IllegalArgumentException("we need two lat/lon points at least!");
|
||||
wplist.add(readPosition(lonLat[0], lonLat[1], "via" + i));
|
||||
if (lonLat.length > 2) {
|
||||
if (lonLat[2].equals("d")) {
|
||||
wplist.get(wplist.size()-1).direct = true;
|
||||
} else {
|
||||
wplist.get(wplist.size()-1).name = lonLat[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wplist.get(0).name.startsWith("via")) wplist.get(0).name = "from";
|
||||
if (wplist.get(wplist.size() - 1).name.startsWith("via")) wplist.get(wplist.size() - 1).name = "to";
|
||||
|
||||
return wplist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatTrack(OsmTrack track) {
|
||||
String result;
|
||||
|
@ -130,23 +78,17 @@ public class ServerHandler extends RequestHandler {
|
|||
}
|
||||
|
||||
if (format == null || "gpx".equals(format)) {
|
||||
result = track.formatAsGpx();
|
||||
result = new FormatGpx(rc).format(track);
|
||||
} else if ("kml".equals(format)) {
|
||||
result = track.formatAsKml();
|
||||
result = new FormatKml(rc).format(track);
|
||||
} else if ("geojson".equals(format)) {
|
||||
result = track.formatAsGeoJson();
|
||||
result = new FormatJson(rc).format(track);
|
||||
} else if ("csv".equals(format)) {
|
||||
try {
|
||||
StringWriter sw = new StringWriter();
|
||||
BufferedWriter bw = new BufferedWriter(sw);
|
||||
track.writeMessages(bw, rc);
|
||||
return sw.toString();
|
||||
} catch (Exception ex) {
|
||||
return "Error: " + ex.getMessage();
|
||||
}
|
||||
result = new FormatCsv(rc).format(track);
|
||||
} else {
|
||||
System.out.println("unknown track format '" + format + "', using default");
|
||||
result = track.formatAsGpx();
|
||||
//result = track.formatAsGpx();
|
||||
result = new FormatGpx(rc).format(track);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -191,115 +133,4 @@ public class ServerHandler extends RequestHandler {
|
|||
return params.get("trackname") == null ? null : params.get("trackname").replaceAll("[^a-zA-Z0-9 \\._\\-]+", "");
|
||||
}
|
||||
|
||||
private static OsmNodeNamed readPosition(String vlon, String vlat, String name) {
|
||||
if (vlon == null) throw new IllegalArgumentException("lon " + name + " not found in input");
|
||||
if (vlat == null) throw new IllegalArgumentException("lat " + name + " not found in input");
|
||||
|
||||
return readPosition(Double.parseDouble(vlon), Double.parseDouble(vlat), name);
|
||||
}
|
||||
|
||||
private static OsmNodeNamed readPosition(double lon, double lat, String name) {
|
||||
OsmNodeNamed n = new OsmNodeNamed();
|
||||
n.name = name;
|
||||
n.ilon = (int) ((lon + 180.) * 1000000. + 0.5);
|
||||
n.ilat = (int) ((lat + 90.) * 1000000. + 0.5);
|
||||
return n;
|
||||
}
|
||||
|
||||
private List<OsmNodeNamed> readPoisList() {
|
||||
// lon,lat,name|...
|
||||
String pois = params.get("pois");
|
||||
if (pois == null) return null;
|
||||
|
||||
String[] lonLatNameList = pois.split("\\|");
|
||||
|
||||
List<OsmNodeNamed> poisList = new ArrayList<>();
|
||||
for (int i = 0; i < lonLatNameList.length; i++) {
|
||||
String[] lonLatName = lonLatNameList[i].split(",");
|
||||
|
||||
if (lonLatName.length != 3)
|
||||
continue;
|
||||
|
||||
OsmNodeNamed n = new OsmNodeNamed();
|
||||
n.ilon = (int) ((Double.parseDouble(lonLatName[0]) + 180.) * 1000000. + 0.5);
|
||||
n.ilat = (int) ((Double.parseDouble(lonLatName[1]) + 90.) * 1000000. + 0.5);
|
||||
n.name = lonLatName[2];
|
||||
poisList.add(n);
|
||||
}
|
||||
|
||||
return poisList;
|
||||
}
|
||||
|
||||
private List<OsmNodeNamed> readNogoList() {
|
||||
// lon,lat,radius|...
|
||||
String nogos = params.get("nogos");
|
||||
if (nogos == null) return null;
|
||||
|
||||
String[] lonLatRadList = nogos.split("\\|");
|
||||
|
||||
List<OsmNodeNamed> nogoList = new ArrayList<>();
|
||||
for (int i = 0; i < lonLatRadList.length; i++) {
|
||||
String[] lonLatRad = lonLatRadList[i].split(",");
|
||||
String nogoWeight = "NaN";
|
||||
if (lonLatRad.length > 3) {
|
||||
nogoWeight = lonLatRad[3];
|
||||
}
|
||||
nogoList.add(readNogo(lonLatRad[0], lonLatRad[1], lonLatRad[2], nogoWeight));
|
||||
}
|
||||
|
||||
return nogoList;
|
||||
}
|
||||
|
||||
private static OsmNodeNamed readNogo(String lon, String lat, String radius, String nogoWeight) {
|
||||
double weight = "undefined".equals(nogoWeight) ? Double.NaN : Double.parseDouble(nogoWeight);
|
||||
return readNogo(Double.parseDouble(lon), Double.parseDouble(lat), Integer.parseInt(radius), weight);
|
||||
}
|
||||
|
||||
private static OsmNodeNamed readNogo(double lon, double lat, int radius, double nogoWeight) {
|
||||
OsmNodeNamed n = new OsmNodeNamed();
|
||||
n.name = "nogo" + radius;
|
||||
n.ilon = (int) ((lon + 180.) * 1000000. + 0.5);
|
||||
n.ilat = (int) ((lat + 90.) * 1000000. + 0.5);
|
||||
n.isNogo = true;
|
||||
n.nogoWeight = nogoWeight;
|
||||
return n;
|
||||
}
|
||||
|
||||
private List<OsmNodeNamed> readNogoPolygons() {
|
||||
List<OsmNodeNamed> result = new ArrayList<>();
|
||||
parseNogoPolygons(params.get("polylines"), result, false);
|
||||
parseNogoPolygons(params.get("polygons"), result, true);
|
||||
return result.size() > 0 ? result : null;
|
||||
}
|
||||
|
||||
private static void parseNogoPolygons(String polygons, List<OsmNodeNamed> result, boolean closed) {
|
||||
if (polygons != null) {
|
||||
String[] polygonList = polygons.split("\\|");
|
||||
for (int i = 0; i < polygonList.length; i++) {
|
||||
String[] lonLatList = polygonList[i].split(",");
|
||||
if (lonLatList.length > 1) {
|
||||
OsmNogoPolygon polygon = new OsmNogoPolygon(closed);
|
||||
int j;
|
||||
for (j = 0; j < 2 * (lonLatList.length / 2) - 1; ) {
|
||||
String slon = lonLatList[j++];
|
||||
String slat = lonLatList[j++];
|
||||
int lon = (int) ((Double.parseDouble(slon) + 180.) * 1000000. + 0.5);
|
||||
int lat = (int) ((Double.parseDouble(slat) + 90.) * 1000000. + 0.5);
|
||||
polygon.addVertex(lon, lat);
|
||||
}
|
||||
|
||||
String nogoWeight = "NaN";
|
||||
if (j < lonLatList.length) {
|
||||
nogoWeight = lonLatList[j];
|
||||
}
|
||||
polygon.nogoWeight = Double.parseDouble(nogoWeight);
|
||||
|
||||
if (polygon.points.size() > 0) {
|
||||
polygon.calcBoundingCircle();
|
||||
result.add(polygon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,3 +27,5 @@ end
|
|||
# Performance-booster for watching directories on Windows
|
||||
gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
|
||||
|
||||
|
||||
gem "webrick", "~> 1.8"
|
||||
|
|
|
@ -249,6 +249,7 @@ GEM
|
|||
unf_ext
|
||||
unf_ext (0.0.8.2)
|
||||
unicode-display_width (1.8.0)
|
||||
webrick (1.8.1)
|
||||
|
||||
PLATFORMS
|
||||
x86_64-linux
|
||||
|
@ -258,6 +259,7 @@ DEPENDENCIES
|
|||
tzinfo (~> 1.2)
|
||||
tzinfo-data
|
||||
wdm (~> 0.1.1)
|
||||
webrick (~> 1.8)
|
||||
|
||||
BUNDLED WITH
|
||||
2.4.13
|
||||
2.4.20
|
||||
|
|
15
docs/_README.md
Normal file
15
docs/_README.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# BRouter Docs
|
||||
|
||||
This documentation can be used to generate a static website using [jekyll](https://jekyllrb.com/).
|
||||
|
||||
## Dependencies
|
||||
|
||||
jekyll uses ruby and therefore installs dependencies using gem. It is recommended to use bundler to manage those dependencies.
|
||||
|
||||
`bundle install` installs all dependencies. To update dependencies use `bundle update`.
|
||||
|
||||
## Preview
|
||||
|
||||
jekyll provides a built-in webserver which can be used for fast feedback during editing.
|
||||
|
||||
`bundle exec jekyll serve`
|
|
@ -1,87 +1,108 @@
|
|||
Environmental considerations (penalties for traffic, noise, town, no river, no forest) are possible due to the creation of pseudo tags during processing OSM data by spatial SQL queries in https://github.com/abrensch/brouter/blob/master/misc/scripts/mapcreation/brouter.sql. During this processing, roads are extended by a 32 m buffer creating 64 m wide lines. Then it is calculated what percentage of such line is at a specific distance to a noise source or within a forest, for example. The percentage is converted to a factor and the factor is assigned to a class. Ways that pass through different environments and are represented by a single OSM way can be problematic because the class is always based on the average environment along an entire OSM way. For traffic, calculations are on another level of complexity.
|
||||
---
|
||||
parent: Developers
|
||||
---
|
||||
|
||||
# Environmental considerations
|
||||
|
||||
Environmental considerations (penalties for traffic, noise, town, no river, no forest) are possible due to the creation of pseudo tags during processing OSM data by spatial SQL queries in [brouter.sql](https://github.com/abrensch/brouter/blob/master/misc/scripts/mapcreation/brouter.sql). During this processing, roads are extended by a 32 m buffer creating 64 m wide lines. Then it is calculated what percentage of such line is at a specific distance to a noise source or within a forest, for example. The percentage is converted to a factor and the factor is assigned to a class. Ways that pass through different environments and are represented by a single OSM way can be problematic because the class is always based on the average environment along an entire OSM way. For traffic, calculations are on another level of complexity.
|
||||
|
||||
### noise_class
|
||||
|
||||
For proximity of noisy roads (secondary and higher). The noise factor represents the proportion of a road's buffer area that lies within the 64-meter buffer of noisy roads. This proportion is reduced:
|
||||
|
||||
### consider_noise, noise_penalty
|
||||
For proximity of noisy roads (secondary and higher). The noise factor represents the proportion of a road's buffer area that lies within the 64-meter buffer of noisy roads. This proportion is reduced:
|
||||
- for motorways and trunk roads with max speed < 105 by 1.5
|
||||
- for primary roads 2 times
|
||||
- 3 times if maxspeed is 75 - 105 for primary and secondary
|
||||
- other secondary roads 5 times
|
||||
- for primary roads 2 times
|
||||
- 3 times if maxspeed is 75 - 105 for primary and secondary
|
||||
- other secondary roads 5 times
|
||||
|
||||
Noise class is roughly proportional to the noise factor:
|
||||
`noise_class` is roughly proportional to the noise factor:
|
||||
|
||||
noise_factor = noise class
|
||||
- < 0.1 = '1'
|
||||
- < 0.25 = '2'
|
||||
- < 0.4 = '3'
|
||||
- < 0.55 = '4'
|
||||
- < 0.8 = '5'
|
||||
- ELSE = '6'
|
||||
| `noise_factor` | `noise_class` |
|
||||
| -------------- | ------------- |
|
||||
| < 0.1 | 1 |
|
||||
| < 0.25 | 2 |
|
||||
| < 0.4 | 3 |
|
||||
| < 0.55 | 4 |
|
||||
| < 0.8 | 5 |
|
||||
| ELSE | 6 |
|
||||
|
||||
To be classified as noise class 6, a way must be less than 13 m on average from the middle of the carriageway of a motorway with a maximum speed exceeding 105. For a class 5, the distance must be up to 35 meters. (1 - noise factor) * 64 m for a given class determines the distance
|
||||
To be classified as noise class 6, a way must be less than 13 m on average from the middle of the carriageway of a motorway with a maximum speed exceeding 105. For a class 5, the distance must be up to 35 meters. (1 - noise_factor) \* 64 m for a given class determines the distance
|
||||
|
||||
**Max noise class:**
|
||||
| Max speed | Motorway, trunk |Primary|Secondary |
|
||||
|--- |:---: |:---: |:---: |
|
||||
| >105 |6 |4 | 3 |
|
||||
| 105 |5 |4 |3 |
|
||||
| 75 |5 |3 |2 |
|
||||
| highway | maxspeed | max `noise_class` |
|
||||
| -------------- | -------- | ----------------- |
|
||||
| motorway,trunk | > 105 | 6 |
|
||||
| motorway,trunk | 105 | 5 |
|
||||
| motorway,trunk | 75 | 5 |
|
||||
| primary | > 105 | 4 |
|
||||
| primary | 105 | 4 |
|
||||
| primary | 75 | 3 |
|
||||
| secondary | > 105 | 3 |
|
||||
| secondary | 105 | 3 |
|
||||
| secondary | 75 | 2 |
|
||||
|
||||
### river_class
|
||||
|
||||
### consider_river, no_river_penalty
|
||||
OSM data recognized as river:
|
||||
|
||||
- waterway: river, canal
|
||||
- natural: water (except wastewater)
|
||||
|
||||
Waterways have 32 m wide buffers. Water areas have 77 m wide buffers.
|
||||
|
||||
river_see = river class
|
||||
- < 0.17 = '1'
|
||||
- < 0.35 = '2'
|
||||
- < 0.57 = '3'
|
||||
- < 0.80 = '4'
|
||||
- < 0.95 = '5'
|
||||
- ELSE = '6'
|
||||
| `river_see` | `river_class` |
|
||||
| ----------- | ------------- |
|
||||
| < 0.1 | 1 |
|
||||
| < 0.3 | 2 |
|
||||
| < 0.5 | 3 |
|
||||
| < 0.8 | 4 |
|
||||
| < 0.9 | 5 |
|
||||
| ELSE | 6 |
|
||||
|
||||
### forest_class
|
||||
|
||||
OSM data recognized as forest:
|
||||
|
||||
### consider_forest, no_forest_penalty
|
||||
OSM data recognized as forest:
|
||||
- landuse: forest, allotments, flowerbed, orchard, vineyard, recreation_ground, village_green
|
||||
- leisure: garden, park, nature_reserve
|
||||
|
||||
|
||||
No forest buffers are used.
|
||||
|
||||
Imagine you trace the way with a pencil drawing lines 62 meters wide. Then estimated_forest_class=6 corresponds to the case that at least 98% of the line is in the woodland. This number is called a green factor.
|
||||
|
||||
green_factor = forest class
|
||||
- < 0.1 = NULL
|
||||
- < 0.2 = '1'
|
||||
- < 0.4 = '2'
|
||||
- < 0.6 = '3'
|
||||
- < 0.8 = '4'
|
||||
- < 0.98 = '5'
|
||||
- ELSE = '6'
|
||||
| `green_factor` | `forest_class` |
|
||||
| -------------- | -------------- |
|
||||
| < 0.1 | NULL |
|
||||
| < 0.2 | 1 |
|
||||
| < 0.4 | 2 |
|
||||
| < 0.6 | 3 |
|
||||
| < 0.8 | 4 |
|
||||
| < 0.98 | 5 |
|
||||
| ELSE | 6 |
|
||||
|
||||
### town_class
|
||||
|
||||
|
||||
### consider_town, town_penalty
|
||||
Town_class is determined by population data from OSM.
|
||||
|
||||
Class
|
||||
- 1 = 50-80 k people
|
||||
- 2 = 80-150 k people
|
||||
- 3 = 150 - 400 k people
|
||||
- 4 = 400 - 1,000 k people
|
||||
- 5 = 1 - 2 million people
|
||||
- 6 = > 2 million people
|
||||
| population | `town_class` |
|
||||
| ------------------ | ------------ |
|
||||
| < 80 k people | 1 |
|
||||
| < 150 k people | 2 |
|
||||
| < 400 k people | 3 |
|
||||
| < 1 million people | 4 |
|
||||
| < 2 million people | 5 |
|
||||
| > 2 million people | 6 |
|
||||
|
||||
### traffic_class
|
||||
|
||||
### consider_traffic, traffic_penalty
|
||||
(modified copy from the sql file).
|
||||
OSM data used to estimate the traffic:
|
||||
- population of towns (+ distance from position to the towns)
|
||||
- size of industrial areas (landuse=industrial) and distance to them. Not considered: solar & wind farms.
|
||||
- airports international
|
||||
- motorway and trunk road density - traffic on motorways decreases traffic on primary/secondary/tertiary. Calculated on a grid (100 km^2)
|
||||
- density of highways (tertiary and higher) calculated on a grid (100 km^2). Traffic decreases when more such roads are available. Exceptions: near junctions between motorways and other roads the traffic increases on these roads.
|
||||
- mountain-ranges calculated as density of peaks > 400 m traffic is generally on highways in such regions higher as only generated by the local population or industrial areas
|
||||
|
||||
- population of towns (+ distance from position to the towns)
|
||||
- size of industrial areas (landuse=industrial) and distance to them. Not considered: solar & wind farms.
|
||||
- airports international
|
||||
- motorway and trunk road density - traffic on motorways decreases traffic on primary/secondary/tertiary. Calculated on a grid (100 km^2)
|
||||
- density of highways (tertiary and higher) calculated on a grid (100 km^2). Traffic decreases when more such roads are available. Exceptions: near junctions between motorways and other roads the traffic increases on these roads.
|
||||
- mountain-ranges calculated as density of peaks > 400 m traffic is generally on highways in such regions higher as only generated by the local population or industrial areas
|
||||
- calculate traffic from the population (for each segment of type primary secondary tertiary)
|
||||
- SUM of (population of each town < 100 km) / ( town-radius + 2500 + dist(segment-position to the town) ** 2 )
|
||||
- town-radius is calculated as sqrt(population)
|
||||
- SUM of (population of each town < 100 km) / ( town-radius + 2500 + dist(segment-position to the town) \*\* 2 )
|
||||
- town-radius is calculated as sqrt(population)
|
||||
|
|
Loading…
Reference in a new issue