Merge branch 'abrensch:master' into rework-voicehint

This commit is contained in:
afischerdev 2023-12-10 14:19:49 +01:00 committed by GitHub
commit 4c310bf4b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 1600 additions and 1650 deletions

View 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");
}
}
}

View 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;
}
}

View 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);
}
}
}

View 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");
}
}

View 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);
}
}

View file

@ -7,33 +7,20 @@ package btools.router;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.EOFException; import java.io.EOFException;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; 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.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.TimeZone;
import btools.mapaccess.MatchedWaypoint; import btools.mapaccess.MatchedWaypoint;
import btools.mapaccess.OsmPos; import btools.mapaccess.OsmPos;
import btools.util.CompactLongMap; import btools.util.CompactLongMap;
import btools.util.FrozenLongMap; import btools.util.FrozenLongMap;
import btools.util.StringUtils;
public final class OsmTrack { public final class OsmTrack {
final public static String version = "1.7.3"; final public static String version = "1.7.3";
@ -66,7 +53,7 @@ public final class OsmTrack {
private CompactLongMap<OsmPathElementHolder> detourMap; private CompactLongMap<OsmPathElementHolder> detourMap;
private VoiceHintList voiceHints; public VoiceHintList voiceHints;
public String message = null; public String message = null;
public List<String> messageList = null; public List<String> messageList = null;
@ -178,7 +165,7 @@ public final class OsmTrack {
nodesMap = new FrozenLongMap<>(nodesMap); nodesMap = new FrozenLongMap<>(nodesMap);
} }
private List<String> aggregateMessages() { public List<String> aggregateMessages() {
ArrayList<String> res = new ArrayList<>(); ArrayList<String> res = new ArrayList<>();
MessageData current = null; MessageData current = null;
for (OsmPathElement n : nodes) { for (OsmPathElement n : nodes) {
@ -200,7 +187,7 @@ public final class OsmTrack {
return res; return res;
} }
private List<String> aggregateSpeedProfile() { public List<String> aggregateSpeedProfile() {
ArrayList<String> res = new ArrayList<>(); ArrayList<String> res = new ArrayList<>();
int vmax = -1; int vmax = -1;
int vmaxe = -1; int vmaxe = -1;
@ -395,752 +382,9 @@ public final class OsmTrack {
public int plainAscend; public int plainAscend;
public int cost; public int cost;
public int energy; 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 List<String> iternity;
public void writeJson(String filename) throws Exception { public VoiceHint getVoiceHint(int i) {
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) {
if (voiceHints == null) return null; if (voiceHints == null) return null;
for (VoiceHint hint : voiceHints.list) { for (VoiceHint hint : voiceHints.list) {
if (hint.indexInTrack == i) { if (hint.indexInTrack == i) {
@ -1150,7 +394,7 @@ public final class OsmTrack {
return null; return null;
} }
private MatchedWaypoint getMatchedWaypoint(int idx) { public MatchedWaypoint getMatchedWaypoint(int idx) {
if (matchedWaypoints == null) return null; if (matchedWaypoints == null) return null;
for (MatchedWaypoint wp : matchedWaypoints) { for (MatchedWaypoint wp : matchedWaypoints) {
if (idx == wp.indexInTrack) { if (idx == wp.indexInTrack) {
@ -1168,128 +412,11 @@ public final class OsmTrack {
return vnode0 < vnode1 ? vnode0 : vnode1; 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(); float s = nodes.size() < 2 ? 0 : nodes.get(nodes.size() - 1).getTime() - nodes.get(0).getTime();
return (int) (s + 0.5); 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) { public boolean equalsTrack(OsmTrack t) {
if (nodes.size() != t.nodes.size()) if (nodes.size() != t.nodes.size())
return false; return false;
@ -1398,7 +525,7 @@ public final class OsmTrack {
return 2; return 2;
} }
private float getVoiceHintTime(int i) { public float getVoiceHintTime(int i) {
if (voiceHints.list.isEmpty()) { if (voiceHints.list.isEmpty()) {
return 0f; return 0f;
} }

View file

@ -192,34 +192,70 @@ public class RoutingEngine extends Thread {
track.message = "track-length = " + track.distance + " filtered ascend = " + track.ascend track.message = "track-length = " + track.distance + " filtered ascend = " + track.ascend
+ " plain-ascend = " + track.plainAscend + " cost=" + track.cost; + " plain-ascend = " + track.plainAscend + " cost=" + track.cost;
if (track.energy != 0) { 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; track.name = "brouter_" + routingContext.getProfileName() + "_" + i;
messageList.add(track.message); messageList.add(track.message);
track.messageList = messageList; track.messageList = messageList;
if (outfileBase != null) { if (outfileBase != null) {
String filename = outfileBase + i + ".gpx"; String filename = outfileBase + i + "." + routingContext.outputFormat;
OsmTrack oldTrack = new OsmTrack(); OsmTrack oldTrack = null;
oldTrack.readGpx(filename); switch (routingContext.outputFormat) {
if (track.equalsTrack(oldTrack)) { 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; continue;
} }
oldTrack = null; oldTrack = null;
track.exportWaypoints = routingContext.exportWaypoints; track.exportWaypoints = routingContext.exportWaypoints;
// doesn't work at the moment filename = outfileBase + i + "." + routingContext.outputFormat;
// use routingContext.outputFormat switch (routingContext.outputFormat) {
track.writeGpx(filename); 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; foundTrack = track;
alternativeIndex = i; alternativeIndex = i;
outfile = filename; outfile = filename;
} else { } else {
if (i == routingContext.getAlternativeIdx(0, 3)) { if (i == routingContext.getAlternativeIdx(0, 3)) {
if ("CSV".equals(System.getProperty("reportFormat"))) { if ("CSV".equals(System.getProperty("reportFormat"))) {
track.dumpMessages(null, routingContext); String filename = outfileBase + i + ".csv";
new FormatCsv(routingContext).write(filename, track);
} else { } else {
if (!quite) { if (!quite) {
System.out.println(track.formatAsGpx()); System.out.println(new FormatGpx(routingContext).format(track));
} }
} }
foundTrack = track; foundTrack = track;
@ -229,7 +265,7 @@ public class RoutingEngine extends Thread {
} }
if (logfileBase != null) { if (logfileBase != null) {
String logfilename = logfileBase + i + ".csv"; String logfilename = logfileBase + i + ".csv";
track.dumpMessages(logfilename, routingContext); new FormatCsv(routingContext).write(logfilename, track);
} }
break; break;
} }
@ -308,15 +344,31 @@ public class RoutingEngine extends Thread {
OsmNodeNamed n = new OsmNodeNamed(listOne.get(0).crosspoint); OsmNodeNamed n = new OsmNodeNamed(listOne.get(0).crosspoint);
n.selev = startNode != null ? startNode.getSElev() : Short.MIN_VALUE; n.selev = startNode != null ? startNode.getSElev() : Short.MIN_VALUE;
// doesn't work at the moment switch (routingContext.outputFormat) {
// use routingContext.outputFormat case "gpx":
outputMessage = OsmTrack.formatAsGpxWaypoint(n); 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) { if (outfileBase != null) {
String filename = outfileBase + ".gpx"; String filename = outfileBase + "." + routingContext.outputFormat;
File out = new File(filename); File out = new File(filename);
FileWriter fw = new FileWriter(filename); FileWriter fw = new FileWriter(filename);
fw.write(outputMessage); fw.write(outputMessage);
fw.close(); fw.close();
outputMessage = null;
} else {
if (!quite && outputMessage != null) {
System.out.println(outputMessage);
}
} }
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
logInfo("execution time = " + (endTime - startTime) / 1000. + " seconds"); logInfo("execution time = " + (endTime - startTime) / 1000. + " seconds");
@ -951,7 +1003,7 @@ public class RoutingEngine extends Thread {
if (track == null) { if (track == null) {
for (int cfi = 0; cfi < airDistanceCostFactors.length; cfi++) { 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]; airDistanceCostFactor = airDistanceCostFactors[cfi];
if (airDistanceCostFactor < 0.) { if (airDistanceCostFactor < 0.) {
@ -1447,7 +1499,7 @@ public class RoutingEngine extends Thread {
boolean inRadius = boundary == null || boundary.isInBoundary(nextNode, bestPath.cost); 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 // add only if this may beat an existing path for that link
OsmLinkHolder dominator = link.getFirstLinkHolder(currentNode); OsmLinkHolder dominator = link.getFirstLinkHolder(currentNode);
while (!trafficSim && dominator != null) { while (!trafficSim && dominator != null) {
@ -1628,7 +1680,7 @@ public class RoutingEngine extends Thread {
} }
public String getTime() { public String getTime() {
return foundTrack.getFormattedTime2(); return Formatter.getFormattedTime2(foundTrack.getTotalSeconds());
} }
public OsmTrack getFoundTrack() { public OsmTrack getFoundTrack() {

View file

@ -14,8 +14,9 @@ public class RoutingParamCollector {
/** /**
* get a list of points and optional extra info for the points * 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) { public List<OsmNodeNamed> getWayPointList(String lonLats) {
if (lonLats == null) throw new IllegalArgumentException("lonlats parameter not set"); 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) * get a list of points (old style, positions only)
* @param lons - array with longitudes *
* @param lats - array with latitudes * @param lons array with longitudes
* @return - a list * @param lats array with latitudes
* @return a list
*/ */
public List<OsmNodeNamed> readPositions(double[] lons, double[] lats) { public List<OsmNodeNamed> readPositions(double[] lons, double[] lats) {
List<OsmNodeNamed> wplist = new ArrayList<>(); List<OsmNodeNamed> wplist = new ArrayList<>();
@ -93,9 +95,10 @@ public class RoutingParamCollector {
/** /**
* read a url like parameter list linked with '&' * read a url like parameter list linked with '&'
* @param url - parameter list *
* @return - a hashmap of the parameter * @param url parameter list
* @throws UnsupportedEncodingException * @return a hashmap of the parameter
* @throws UnsupportedEncodingException
*/ */
public Map<String, String> getUrlParams(String url) throws UnsupportedEncodingException { public Map<String, String> getUrlParams(String url) throws UnsupportedEncodingException {
HashMap<String, String> params = new HashMap<>(); HashMap<String, String> params = new HashMap<>();
@ -117,9 +120,10 @@ public class RoutingParamCollector {
/** /**
* fill a parameter map into the routing context * fill a parameter map into the routing context
* @param rctx - the context *
* @param wplist - the list of way points needed for 'straight' parameter * @param rctx the context
* @param params - the list of parameters * @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) { public void setParams(RoutingContext rctx, List<OsmNodeNamed> wplist, Map<String, String> params) {
if (params != null) { if (params != null) {
@ -129,11 +133,15 @@ public class RoutingParamCollector {
if (params.containsKey("profile")) { if (params.containsKey("profile")) {
rctx.localFunction = params.get("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")); List<OsmNodeNamed> nogoList = readNogos(params.get("nogoLons"), params.get("nogoLats"), params.get("nogoRadi"));
if (nogoList != null) { if (nogoList != null) {
RoutingContext.prepareNogoPoints(nogoList); RoutingContext.prepareNogoPoints(nogoList);
rctx.nogopoints = nogoList; if (rctx.nogopoints == null) {
rctx.nogopoints = nogoList;
} else {
rctx.nogopoints.addAll(nogoList);
}
} }
params.remove("nogoLats"); params.remove("nogoLats");
params.remove("nogoLons"); params.remove("nogoLons");
@ -143,7 +151,11 @@ public class RoutingParamCollector {
List<OsmNodeNamed> nogoList = readNogoList(params.get("nogos")); List<OsmNodeNamed> nogoList = readNogoList(params.get("nogos"));
if (nogoList != null) { if (nogoList != null) {
RoutingContext.prepareNogoPoints(nogoList); RoutingContext.prepareNogoPoints(nogoList);
rctx.nogopoints = nogoList; if (rctx.nogopoints == null) {
rctx.nogopoints = nogoList;
} else {
rctx.nogopoints.addAll(nogoList);
}
} }
params.remove("nogos"); params.remove("nogos");
} }
@ -196,6 +208,12 @@ public class RoutingParamCollector {
rctx.turnInstructionMode = Integer.parseInt(value); rctx.turnInstructionMode = Integer.parseInt(value);
} else if (key.equals("timode")) { } else if (key.equals("timode")) {
rctx.turnInstructionMode = Integer.parseInt(value); 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")) { } else if (key.equals("exportWaypoints")) {
rctx.exportWaypoints = (Integer.parseInt(value) == 1); rctx.exportWaypoints = (Integer.parseInt(value) == 1);
} else if (key.equals("format")) { } else if (key.equals("format")) {
@ -213,8 +231,9 @@ public class RoutingParamCollector {
/** /**
* fill profile parameter list * 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) { public void setProfileParams(RoutingContext rctx, Map<String, String> params) {
if (params != null) { if (params != null) {

View file

@ -20,8 +20,6 @@ android {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 33 targetSdkVersion 33
resConfigs "en"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }

View file

@ -171,7 +171,7 @@ public class BInstallerActivity extends AppCompatActivity {
} }
mButtonDownload.setText(getString(R.string.action_download, getSegmentsPlural(selectedTilesDownload.size()))); mButtonDownload.setText(getString(R.string.action_download, getSegmentsPlural(selectedTilesDownload.size())));
mButtonDownload.setEnabled(true); 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) { } else if (selectedTilesUpdate.size() > 0) {
mButtonDownload.setText(getString(R.string.action_update, getSegmentsPlural(selectedTilesUpdate.size()))); mButtonDownload.setText(getString(R.string.action_update, getSegmentsPlural(selectedTilesUpdate.size())));
mButtonDownload.setEnabled(true); mButtonDownload.setEnabled(true);

View file

@ -19,6 +19,7 @@ import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
@ -39,10 +40,6 @@ public class BRouterService extends Service {
BRouterWorker worker = new BRouterWorker(); BRouterWorker worker = new BRouterWorker();
for (String key : params.keySet()) {
// Log.d("BS", "income " + key + " = " + params.get(key));
}
int engineMode = 0; int engineMode = 0;
if (params.containsKey("engineMode")) { if (params.containsKey("engineMode")) {
engineMode = params.getInt("engineMode", 0); engineMode = params.getInt("engineMode", 0);
@ -103,7 +100,7 @@ public class BRouterService extends Service {
boolean canCompress = "true".equals(params.getString("acceptCompressedResult")); boolean canCompress = "true".equals(params.getString("acceptCompressedResult"));
try { try {
String gpxMessage = worker.getTrackFromParams(params); String gpxMessage = worker.getTrackFromParams(params);
if (canCompress && gpxMessage.startsWith("<")) { if (canCompress) {
try { try {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write("z64".getBytes(Charset.forName("UTF-8"))); // marker prefix baos.write("z64".getBytes(Charset.forName("UTF-8"))); // marker prefix
@ -277,8 +274,13 @@ public class BRouterService extends Service {
private void logBundle(Bundle params) { private void logBundle(Bundle params) {
if (AppLogger.isLogging()) { if (AppLogger.isLogging()) {
for (String k : params.keySet()) { for (String k : params.keySet()) {
Object val = "remoteProfile".equals(k) ? "<..cut..>" : params.getString(k); Object val = "remoteProfile".equals(k) ? "<..cut..>" : params.get(k);
String desc = "key=" + k + (val == null ? "" : " class=" + val.getClass() + " val=" + val.toString()); 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); AppLogger.log(desc);
} }
} }

View file

@ -6,16 +6,20 @@ import android.os.Bundle;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.util.ArrayList; import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; 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.OsmNodeNamed;
import btools.router.OsmNogoPolygon;
import btools.router.OsmTrack; import btools.router.OsmTrack;
import btools.router.RoutingContext; import btools.router.RoutingContext;
import btools.router.RoutingEngine; import btools.router.RoutingEngine;
import btools.router.RoutingParamCollector;
public class BRouterWorker { public class BRouterWorker {
private static final int OUTPUT_FORMAT_GPX = 0; private static final int OUTPUT_FORMAT_GPX = 0;
@ -39,6 +43,72 @@ public class BRouterWorker {
engineMode = params.getInt("engineMode", 0); 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"); String pathToFileResult = params.getString("pathToFileResult");
if (pathToFileResult != null) { if (pathToFileResult != null) {
@ -52,100 +122,9 @@ public class BRouterWorker {
long maxRunningTime = 60000; long maxRunningTime = 60000;
String sMaxRunningTime = params.getString("maxRunningTime"); String sMaxRunningTime = params.getString("maxRunningTime");
if (sMaxRunningTime != null) { 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 { try {
writeTimeoutData(rc); writeTimeoutData(rc);
@ -170,50 +149,46 @@ public class BRouterWorker {
return cr.getErrorMessage(); return cr.getErrorMessage();
} }
String format = params.getString("trackFormat");
int writeFromat = OUTPUT_FORMAT_GPX; int writeFromat = OUTPUT_FORMAT_GPX;
if (format != null) { if (rc.outputFormat != null) {
if ("kml".equals(format)) writeFromat = OUTPUT_FORMAT_KML; if ("kml".equals(rc.outputFormat)) writeFromat = OUTPUT_FORMAT_KML;
if ("json".equals(format)) writeFromat = OUTPUT_FORMAT_JSON; if ("json".equals(rc.outputFormat)) writeFromat = OUTPUT_FORMAT_JSON;
} }
OsmTrack track = null;
OsmTrack track = cr.getFoundTrack(); track = cr.getFoundTrack();
if (track != null) { if (track != null) {
if (params.containsKey("exportWaypoints")) { track.exportWaypoints = rc.exportWaypoints;
track.exportWaypoints = (params.getInt("exportWaypoints", 0) == 1);
}
if (pathToFileResult == null) { if (pathToFileResult == null) {
switch (writeFromat) { switch (writeFromat) {
case OUTPUT_FORMAT_GPX:
return track.formatAsGpx();
case OUTPUT_FORMAT_KML: case OUTPUT_FORMAT_KML:
return track.formatAsKml(); return new FormatKml(rc).format(track);
case OUTPUT_FORMAT_JSON: case OUTPUT_FORMAT_JSON:
return track.formatAsGeoJson(); return new FormatJson(rc).format(track);
case OUTPUT_FORMAT_GPX:
default: 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 } else { // get other infos
if (cr.getErrorMessage() != null) { if (cr.getErrorMessage() != null) {
return cr.getErrorMessage(); return cr.getErrorMessage();
@ -223,204 +198,6 @@ public class BRouterWorker {
return null; 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 { private void writeTimeoutData(RoutingContext rc) throws Exception {
String timeoutFile = baseDir + "/brouter/modes/timeoutdata.txt"; String timeoutFile = baseDir + "/brouter/modes/timeoutdata.txt";
@ -431,7 +208,7 @@ public class BRouterWorker {
bw.write(rc.rawTrackPath); bw.write(rc.rawTrackPath);
bw.write("\n"); bw.write("\n");
writeWPList(bw, waypoints); writeWPList(bw, waypoints);
writeWPList(bw, nogoList); writeWPList(bw, rc.nogopoints);
bw.close(); bw.close();
} }

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View file

@ -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>

View file

@ -1,37 +1,16 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project <resources xmlns:tools="http://schemas.android.com/tools">
<plurals name="numberOfSegments" tools:ignore="MissingQuantity">
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">
<item quantity="one">%d segment</item> <item quantity="one">%d segment</item>
<item quantity="other">%d segments</item> <item quantity="other">%d segments</item>
</plurals> </plurals>
<string name="app_name">BRouter</string> <string name="cancel_download">Cancel download</string>
<string name="cancel_download">Cancel Download</string> <string name="import_profile">Import profile</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="action_download">Download %s</string> <string name="action_download">Download %s</string>
<string name="action_delete">Delete %s</string> <string name="action_delete">Delete %s</string>
<string name="action_update">Update %s</string> <string name="action_update">Update %s</string>
<string name="action_select">Select segments</string> <string name="action_select">Select segments</string>
<string name="action_cancel">Stop Download</string> <string name="action_cancel">Stop download</string>
<string name="summary_segments">Size=%s\nFree=%s</string> <string name="summary_segments">Size=%1$s\nFree=%2$s</string>
<string name="notification_channel_id">brouter_download</string> <string name="notification_title">Download segments</string>
<string name="notification_title">Download Segments</string>
<string name="channel_name">Downloads</string>
</resources> </resources>

View file

@ -1,11 +1,7 @@
package btools.server; package btools.server;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -14,11 +10,10 @@ import btools.router.OsmTrack;
import btools.router.RoutingContext; import btools.router.RoutingContext;
import btools.router.RoutingEngine; import btools.router.RoutingEngine;
import btools.router.RoutingParamCollector; import btools.router.RoutingParamCollector;
import btools.router.SearchBoundary;
public class BRouter { public class BRouter {
public static void main(String[] args) throws Exception { 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 { try {
System.setProperty("segmentBaseDir", args[0]); System.setProperty("segmentBaseDir", args[0]);
System.setProperty("profileBaseDir", args[1]); System.setProperty("profileBaseDir", args[1]);
@ -28,19 +23,27 @@ public class BRouter {
int lonIdx = queryString.indexOf("lonlats="); int lonIdx = queryString.indexOf("lonlats=");
int sepIdx = queryString.indexOf("&", lonIdx); int sepIdx = queryString.indexOf("&", lonIdx);
String lonlats = queryString.substring(lonIdx+8, sepIdx); String lonlats = queryString.substring(lonIdx + 8, sepIdx);
RoutingContext rc = new RoutingContext(); RoutingContext rc = new RoutingContext();
RoutingParamCollector routingParamCollector = new RoutingParamCollector(); RoutingParamCollector routingParamCollector = new RoutingParamCollector();
List<OsmNodeNamed> wplist = routingParamCollector.getWayPointList(lonlats); List<OsmNodeNamed> wplist = routingParamCollector.getWayPointList(lonlats);
Map<String, String> params = routingParamCollector.getUrlParams(queryString); 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); routingParamCollector.setParams(rc, wplist, params);
// cgi-header String exportName = null;
System.out.println("Content-type: text/plain"); if (args.length == 4) {
System.out.println(); 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 long maxRunningTime = 60000; // the cgi gets a 1 Minute timeout
String sMaxRunningTime = System.getProperty("maxRunningTime"); String sMaxRunningTime = System.getProperty("maxRunningTime");
@ -48,9 +51,7 @@ public class BRouter {
maxRunningTime = Integer.parseInt(sMaxRunningTime) * 1000; maxRunningTime = Integer.parseInt(sMaxRunningTime) * 1000;
} }
RoutingEngine re = new RoutingEngine(exportName, null, new File(args[0]), wplist, rc, engineMode);
RoutingEngine re = new RoutingEngine(null, null, new File(args[0]), wplist, rc);
re.doRun(maxRunningTime); re.doRun(maxRunningTime);
if (re.getErrorMessage() != null) { if (re.getErrorMessage() != null) {
System.out.println(re.getErrorMessage()); System.out.println(re.getErrorMessage());
@ -65,96 +66,53 @@ public class BRouter {
System.out.println("Find routes in an OSM map"); 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("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 -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); 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)); int engineMode = 0;
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("traffic/" + filename))); try {
engineMode = Integer.parseInt(args[2]);
for (int direction = 0; direction < 8; direction++) { } catch (NumberFormatException e) {
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());
}
} }
}
RoutingParamCollector routingParamCollector = new RoutingParamCollector();
List<OsmNodeNamed> wplist = routingParamCollector.getWayPointList(args[4]);
private static OsmNodeNamed readPosition(String[] args, int idx, String name) { System.setProperty("segmentBaseDir", args[0]);
OsmNodeNamed n = new OsmNodeNamed(); System.setProperty("profileBaseDir", args[1]);
n.name = name; String moreParams = null;
n.ilon = (int) ((Double.parseDouble(args[idx]) + 180.) * 1000000. + 0.5); String profileParams = null;
n.ilat = (int) ((Double.parseDouble(args[idx + 1]) + 90.) * 1000000. + 0.5); if (args.length >= 6) {
return n; moreParams = args[5];
}
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]));
}
} }
c.memoryclass = (int) (Runtime.getRuntime().maxMemory() / 1024 / 1024); if (args.length == 7) {
// c.startDirection= Integer.valueOf( 150 ); profileParams = args[6];
return c; }
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());
}
} }
} }

View file

@ -29,6 +29,7 @@ import btools.router.OsmTrack;
import btools.router.ProfileCache; import btools.router.ProfileCache;
import btools.router.RoutingContext; import btools.router.RoutingContext;
import btools.router.RoutingEngine; import btools.router.RoutingEngine;
import btools.router.RoutingParamCollector;
import btools.server.request.ProfileUploadHandler; import btools.server.request.ProfileUploadHandler;
import btools.server.request.RequestHandler; import btools.server.request.RequestHandler;
import btools.server.request.ServerHandler; import btools.server.request.ServerHandler;
@ -146,7 +147,9 @@ public class RouteServer extends Thread implements Comparable<RouteServer> {
} }
String url = getline.split(" ")[1]; 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(); long maxRunningTime = getMaxRunningTime();
@ -186,33 +189,17 @@ public class RouteServer extends Thread implements Comparable<RouteServer> {
return; return;
} }
RoutingContext rc = handler.readRoutingContext(); RoutingContext rc = handler.readRoutingContext();
List<OsmNodeNamed> wplist = handler.readWayPointList(); List<OsmNodeNamed> wplist = routingParamCollector.getWayPointList(params.get("lonlats"));
if (wplist.size() < 10) { if (wplist.size() < 10) {
SuspectManager.nearRecentWps.add(wplist); SuspectManager.nearRecentWps.add(wplist);
} }
int engineMode = 0; int engineMode = 0;
for (Map.Entry<String, String> e : params.entrySet()) { if (params.containsKey("engineMode")) {
if ("engineMode".equals(e.getKey())) { engineMode = Integer.parseInt(params.get("engineMode"));
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;
}
}
} }
routingParamCollector.setParams(rc, wplist, params);
cr = new RoutingEngine(null, null, serviceContext.segmentDir, wplist, rc, engineMode); cr = new RoutingEngine(null, null, serviceContext.segmentDir, wplist, rc, engineMode);
cr.quite = true; cr.quite = true;
cr.doRun(maxRunningTime); cr.doRun(maxRunningTime);
@ -224,18 +211,29 @@ public class RouteServer extends Thread implements Comparable<RouteServer> {
} else { } else {
OsmTrack track = cr.getFoundTrack(); 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"; String headers = encodings == null || encodings.indexOf("gzip") < 0 ? null : "Content-Encoding: gzip\n";
writeHttpHeader(bw, handler.getMimeType(), handler.getFileName(), headers, HTTP_STATUS_OK); writeHttpHeader(bw, handler.getMimeType(), handler.getFileName(), headers, HTTP_STATUS_OK);
if (track != null) { if (engineMode == 0) {
if (headers != null) { // compressed if (track != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); if (headers != null) { // compressed
Writer w = new OutputStreamWriter(new GZIPOutputStream(baos), "UTF-8"); ByteArrayOutputStream baos = new ByteArrayOutputStream();
w.write(handler.formatTrack(track)); Writer w = new OutputStreamWriter(new GZIPOutputStream(baos), "UTF-8");
w.close(); w.write(handler.formatTrack(track));
bw.flush(); w.close();
clientSocket.getOutputStream().write(baos.toByteArray()); bw.flush();
} else { clientSocket.getOutputStream().write(baos.toByteArray());
bw.write(handler.formatTrack(track)); } else {
bw.write(handler.formatTrack(track));
}
}
} else if (engineMode == 2) {
String s = cr.getFoundInfo();
if (s != null) {
bw.write(s);
} }
} }
} }

View file

@ -1,9 +1,7 @@
package btools.server.request; package btools.server.request;
import java.util.List;
import java.util.Map; import java.util.Map;
import btools.router.OsmNodeNamed;
import btools.router.OsmTrack; import btools.router.OsmTrack;
import btools.router.RoutingContext; import btools.router.RoutingContext;
import btools.server.ServiceContext; import btools.server.ServiceContext;
@ -19,8 +17,6 @@ public abstract class RequestHandler {
public abstract RoutingContext readRoutingContext(); public abstract RoutingContext readRoutingContext();
public abstract List<OsmNodeNamed> readWayPointList();
public abstract String formatTrack(OsmTrack track); public abstract String formatTrack(OsmTrack track);
public abstract String getMimeType(); public abstract String getMimeType();

View file

@ -1,14 +1,12 @@
package btools.server.request; package btools.server.request;
import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import btools.router.OsmNodeNamed; import btools.router.FormatCsv;
import btools.router.OsmNogoPolygon; import btools.router.FormatGpx;
import btools.router.FormatJson;
import btools.router.FormatKml;
import btools.router.OsmTrack; import btools.router.OsmTrack;
import btools.router.RoutingContext; import btools.router.RoutingContext;
import btools.server.ServiceContext; import btools.server.ServiceContext;
@ -62,59 +60,9 @@ public class ServerHandler extends RequestHandler {
} }
rc.localFunction = profile; 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; 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 @Override
public String formatTrack(OsmTrack track) { public String formatTrack(OsmTrack track) {
String result; String result;
@ -130,23 +78,17 @@ public class ServerHandler extends RequestHandler {
} }
if (format == null || "gpx".equals(format)) { if (format == null || "gpx".equals(format)) {
result = track.formatAsGpx(); result = new FormatGpx(rc).format(track);
} else if ("kml".equals(format)) { } else if ("kml".equals(format)) {
result = track.formatAsKml(); result = new FormatKml(rc).format(track);
} else if ("geojson".equals(format)) { } else if ("geojson".equals(format)) {
result = track.formatAsGeoJson(); result = new FormatJson(rc).format(track);
} else if ("csv".equals(format)) { } else if ("csv".equals(format)) {
try { result = new FormatCsv(rc).format(track);
StringWriter sw = new StringWriter();
BufferedWriter bw = new BufferedWriter(sw);
track.writeMessages(bw, rc);
return sw.toString();
} catch (Exception ex) {
return "Error: " + ex.getMessage();
}
} else { } else {
System.out.println("unknown track format '" + format + "', using default"); System.out.println("unknown track format '" + format + "', using default");
result = track.formatAsGpx(); //result = track.formatAsGpx();
result = new FormatGpx(rc).format(track);
} }
return result; 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 \\._\\-]+", ""); 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);
}
}
}
}
}
} }

View file

@ -27,3 +27,5 @@ end
# Performance-booster for watching directories on Windows # Performance-booster for watching directories on Windows
gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin] gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
gem "webrick", "~> 1.8"

View file

@ -249,6 +249,7 @@ GEM
unf_ext unf_ext
unf_ext (0.0.8.2) unf_ext (0.0.8.2)
unicode-display_width (1.8.0) unicode-display_width (1.8.0)
webrick (1.8.1)
PLATFORMS PLATFORMS
x86_64-linux x86_64-linux
@ -258,6 +259,7 @@ DEPENDENCIES
tzinfo (~> 1.2) tzinfo (~> 1.2)
tzinfo-data tzinfo-data
wdm (~> 0.1.1) wdm (~> 0.1.1)
webrick (~> 1.8)
BUNDLED WITH BUNDLED WITH
2.4.13 2.4.20

15
docs/_README.md Normal file
View 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`

View file

@ -1,49 +1,67 @@
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
### 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 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 motorways and trunk roads with max speed < 105 by 1.5
- for primary roads 2 times - for primary roads 2 times
- 3 times if maxspeed is 75 - 105 for primary and secondary - 3 times if maxspeed is 75 - 105 for primary and secondary
- other secondary roads 5 times - 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 | `noise_factor` | `noise_class` |
- < 0.1 = '1' | -------------- | ------------- |
- < 0.25 = '2' | < 0.1 | 1 |
- < 0.4 = '3' | < 0.25 | 2 |
- < 0.55 = '4' | < 0.4 | 3 |
- < 0.8 = '5' | < 0.55 | 4 |
- ELSE = '6' | < 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:** | highway | maxspeed | max `noise_class` |
| Max speed | Motorway, trunk |Primary|Secondary | | -------------- | -------- | ----------------- |
|--- |:---: |:---: |:---: | | motorway,trunk | > 105 | 6 |
| >105 |6 |4 | 3 | | motorway,trunk | 105 | 5 |
| 105 |5 |4 |3 | | motorway,trunk | 75 | 5 |
| 75 |5 |3 |2 | | 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: OSM data recognized as river:
- waterway: river, canal - waterway: river, canal
- natural: water (except wastewater) - natural: water (except wastewater)
Waterways have 32 m wide buffers. Water areas have 77 m wide buffers. Waterways have 32 m wide buffers. Water areas have 77 m wide buffers.
river_see = river class | `river_see` | `river_class` |
- < 0.17 = '1' | ----------- | ------------- |
- < 0.35 = '2' | < 0.1 | 1 |
- < 0.57 = '3' | < 0.3 | 2 |
- < 0.80 = '4' | < 0.5 | 3 |
- < 0.95 = '5' | < 0.8 | 4 |
- ELSE = '6' | < 0.9 | 5 |
| ELSE | 6 |
### forest_class
### consider_forest, no_forest_penalty
OSM data recognized as forest: OSM data recognized as forest:
- landuse: forest, allotments, flowerbed, orchard, vineyard, recreation_ground, village_green - landuse: forest, allotments, flowerbed, orchard, vineyard, recreation_ground, village_green
- leisure: garden, park, nature_reserve - leisure: garden, park, nature_reserve
@ -51,37 +69,40 @@ 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. 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 | `green_factor` | `forest_class` |
- < 0.1 = NULL | -------------- | -------------- |
- < 0.2 = '1' | < 0.1 | NULL |
- < 0.4 = '2' | < 0.2 | 1 |
- < 0.6 = '3' | < 0.4 | 2 |
- < 0.8 = '4' | < 0.6 | 3 |
- < 0.98 = '5' | < 0.8 | 4 |
- ELSE = '6' | < 0.98 | 5 |
| ELSE | 6 |
### town_class
### consider_town, town_penalty
Town_class is determined by population data from OSM. Town_class is determined by population data from OSM.
Class | population | `town_class` |
- 1 = 50-80 k people | ------------------ | ------------ |
- 2 = 80-150 k people | < 80 k people | 1 |
- 3 = 150 - 400 k people | < 150 k people | 2 |
- 4 = 400 - 1,000 k people | < 400 k people | 3 |
- 5 = 1 - 2 million people | < 1 million people | 4 |
- 6 = > 2 million people | < 2 million people | 5 |
| > 2 million people | 6 |
### traffic_class
### consider_traffic, traffic_penalty
(modified copy from the sql file). (modified copy from the sql file).
OSM data used to estimate the traffic: 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. - population of towns (+ distance from position to the towns)
- airports international - size of industrial areas (landuse=industrial) and distance to them. Not considered: solar & wind farms.
- motorway and trunk road density - traffic on motorways decreases traffic on primary/secondary/tertiary. Calculated on a grid (100 km^2) - airports international
- 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. - motorway and trunk road density - traffic on motorways decreases traffic on primary/secondary/tertiary. Calculated on a grid (100 km^2)
- 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 - 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) - 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 ) - 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) - town-radius is calculated as sqrt(population)