diff --git a/brouter-core/src/main/java/btools/router/FormatCsv.java b/brouter-core/src/main/java/btools/router/FormatCsv.java
new file mode 100644
index 0000000..37b7408
--- /dev/null
+++ b/brouter-core/src/main/java/btools/router/FormatCsv.java
@@ -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");
+ }
+ }
+
+}
diff --git a/brouter-core/src/main/java/btools/router/FormatGpx.java b/brouter-core/src/main/java/btools/router/FormatGpx.java
new file mode 100644
index 0000000..e162292
--- /dev/null
+++ b/brouter-core/src/main/java/btools/router/FormatGpx.java
@@ -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("\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("\n");
+ }
+ }
+
+ if (turnInstructionMode == 4) { // comment style
+ sb.append("\n");
+ sb.append("\n");
+ sb.append("\n");
+ }
+ sb.append("\n");
+ } else {
+ sb.append(" creator=\"BRouter-" + t.version + "\" version=\"1.1\">\n");
+ }
+ if (turnInstructionMode == 9) {
+ sb.append(" \n");
+ sb.append(" ").append(t.name).append("\n");
+ sb.append(" \n");
+ sb.append(" ").append(t.messageList.get(0)).append("\n");
+ if (t.params != null && t.params.size() > 0) {
+ sb.append(" e : t.params.entrySet()) {
+ if (i++ != 0) sb.append("&");
+ sb.append(e.getKey()).append("=").append(e.getValue());
+ }
+ sb.append("]]>\n");
+ }
+ sb.append(" \n");
+ sb.append(" \n");
+ }
+ if (turnInstructionMode == 3 || turnInstructionMode == 8) { // osmand style, cruiser
+ float lastRteTime = 0;
+
+ sb.append(" \n");
+
+ float rteTime = t.getVoiceHintTime(0);
+ StringBuffer first = new StringBuffer();
+ // define start point
+ {
+ first.append(" \n")
+ .append(" start\n \n");
+ if (rteTime != lastRteTime) { // add timing only if available
+ double ti = rteTime - lastRteTime;
+ first.append(" \n");
+ lastRteTime = rteTime;
+ }
+ first.append(" 0\n \n \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(" \n")
+ .append(" ")
+ .append(turnInstructionMode == 3 ? hint.getMessageString() : hint.getCruiserMessageString())
+ .append("\n \n");
+
+ rteTime = t.getVoiceHintTime(i + 1);
+
+ if (rteTime != lastRteTime) { // add timing only if available
+ double ti = rteTime - lastRteTime;
+ sb.append(" \n");
+ lastRteTime = rteTime;
+ }
+ sb.append(" ")
+ .append(turnInstructionMode == 3 ? hint.getCommandString() : hint.getCruiserCommandString())
+ .append("\n ").append("" + (int) hint.angle)
+ .append("\n ").append("" + hint.indexInTrack).append("\n \n \n");
+ }
+ sb.append(" \n")
+ .append(" destination\n \n");
+ sb.append(" \n");
+ sb.append(" ").append("" + (t.nodes.size() - 1)).append("\n \n \n");
+
+ sb.append("\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(" ")
+ .append(hint.selev == Short.MIN_VALUE ? "" : "" + (hint.selev / 4.) + "")
+ .append("").append(hint.getMessageString()).append("")
+ .append("").append("" + hint.distanceToNext).append("");
+ 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("").append("" + ti).append("")
+ .append("").append("" + speed).append("");
+ lastRteTime = rteTime;
+ }
+ sb.append("").append("" + hint.getLocusAction()).append("")
+ .append("\n");
+ }
+ }
+ if (turnInstructionMode == 5) { // gpsies style
+ for (VoiceHint hint : t.voiceHints.list) {
+ sb.append(" ")
+ .append("").append(hint.getMessageString()).append("")
+ .append("").append(hint.getSymbolString().toLowerCase()).append("")
+ .append("").append(hint.getSymbolString()).append("")
+ .append("\n");
+ }
+ }
+
+ if (turnInstructionMode == 6) { // orux style
+ for (VoiceHint hint : t.voiceHints.list) {
+ sb.append(" ")
+ .append(hint.selev == Short.MIN_VALUE ? "" : "" + (hint.selev / 4.) + "")
+ .append("\n" +
+ " \n" +
+ " ").append("" + hint.getOruxAction())
+ .append("\n" +
+ " \n" +
+ " \n" +
+ " \n");
+ }
+ }
+
+ for (int i = 0; i <= t.pois.size() - 1; i++) {
+ OsmNodeNamed poi = t.pois.get(i);
+ sb.append(" \n")
+ .append(" ").append(StringUtils.escapeXml10(poi.name)).append("\n")
+ .append(" \n");
+ }
+
+ if (t.exportWaypoints) {
+ for (int i = 0; i <= t.matchedWaypoints.size() - 1; i++) {
+ MatchedWaypoint wt = t.matchedWaypoints.get(i);
+ sb.append(" \n")
+ .append(" ").append(StringUtils.escapeXml10(wt.name)).append("\n");
+ if (i == 0) {
+ sb.append(" from\n");
+ } else if (i == t.matchedWaypoints.size() - 1) {
+ sb.append(" to\n");
+ } else {
+ sb.append(" via\n");
+ }
+ sb.append(" \n");
+ }
+ }
+ sb.append(" \n");
+ if (turnInstructionMode == 9
+ || turnInstructionMode == 2
+ || turnInstructionMode == 8
+ || turnInstructionMode == 4) { // Locus, comment, cruise, brouter style
+ sb.append(" ").append(t.name).append("\n");
+ sb.append(" ").append(t.voiceHints.getTransportMode()).append("\n");
+ } else {
+ sb.append(" ").append(t.name).append("\n");
+ }
+
+ if (turnInstructionMode == 7) {
+ sb.append(" \n");
+ sb.append(" ").append("" + t.voiceHints.getLocusRouteType()).append("\n");
+ sb.append(" 1\n");
+ sb.append(" \n");
+ }
+
+
+ // all points
+ sb.append(" \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 ? "" : "" + n.getElev() + "";
+ VoiceHint hint = t.getVoiceHint(idx);
+ MatchedWaypoint mwpt = t.getMatchedWaypoint(idx);
+
+ if (t.showTime) {
+ sele += "";
+ }
+ if (turnInstructionMode == 8) {
+ if (mwpt != null &&
+ !mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
+ sele += "" + mwpt.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 += "" + mwpt.name + "";
+ }
+ sele += "" + hint.getCruiserMessageString() + "";
+ sele += "" + hint.getCommandString(hint.cmd) + "";
+ if (mwpt != null) {
+ sele += "Via";
+ }
+ sele += "";
+ 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 += "" + (((int) (speed * 10)) / 10.f) + "";
+ }
+
+ sele += "" + hint.getCommandString() + ";" + (int) (hint.distanceToNext) + "," + hint.formatGeometry() + "";
+ if (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway)) {
+ sele += "" + n.message.wayKeyValues + "";
+ lastway = n.message.wayKeyValues;
+ }
+ if (n.message != null && n.message.nodeKeyValues != null) {
+ sele += "" + n.message.nodeKeyValues + "";
+ }
+ sele += "";
+
+ }
+ if (idx == 0 && hint == null) {
+ if (mwpt != null && mwpt.direct) {
+ sele += "beeline";
+ } else {
+ sele += "start";
+ }
+ sele += "Via";
+
+ } else if (idx == t.nodes.size() - 1 && hint == null) {
+
+ sele += "end";
+ sele += "Via";
+
+ } else {
+ if (mwpt != null && hint == null) {
+ if (mwpt.direct) {
+ // bNextDirect = true;
+ sele += "beeline";
+ } else {
+ sele += "" + mwpt.name + "";
+ }
+ sele += "Via";
+ 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 += "";
+ 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 += "" + (((int) (speed * 10)) / 10.f) + "";
+ }
+ if (n.message != null && n.message.wayKeyValues != null && !n.message.wayKeyValues.equals(lastway)) {
+ sele += "" + n.message.wayKeyValues + "";
+ lastway = n.message.wayKeyValues;
+ }
+ if (n.message != null && n.message.nodeKeyValues != null) {
+ sele += "" + n.message.nodeKeyValues + "";
+ }
+ sele += "";
+ }
+ }
+ }
+
+ 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 += "" + mwpt.name + "";
+ }
+ if (mwpt.direct && bNextDirect) {
+ sele += "" + hint.getLocusSymbolString() + "pass_placeShaping";
+ // bNextDirect = false;
+ } else if (mwpt.direct) {
+ if (idx == 0)
+ sele += "pass_placeVia";
+ else
+ sele += "pass_placeShaping";
+ bNextDirect = true;
+ } else if (bNextDirect) {
+ sele += "beeline" + hint.getLocusSymbolString() + "Shaping";
+ bNextDirect = false;
+ } else {
+ sele += "" + hint.getLocusSymbolString() + "Via";
+ }
+ } else {
+ sele += "" + hint.getLocusSymbolString() + "";
+ }
+ } else {
+ if (idx == 0 && hint == null) {
+
+ int pos = sele.indexOf("" + mwpt.name + "";
+ if (mwpt != null && mwpt.direct) {
+ bNextDirect = true;
+ }
+ sele += "pass_place";
+ sele += "Via";
+
+ } else if (idx == t.nodes.size() - 1 && hint == null) {
+
+ int pos = sele.indexOf("" + mwpt.name + "";
+ if (bNextDirect) {
+ sele += "beeline";
+ }
+ sele += "pass_place";
+ sele += "Via";
+
+ } else {
+ if (mwpt != null) {
+ if (!mwpt.name.startsWith("via") && !mwpt.name.startsWith("from") && !mwpt.name.startsWith("to")) {
+ sele += "" + mwpt.name + "";
+ }
+ if (mwpt.direct && bNextDirect) {
+ sele += "beelinepass_placeShaping";
+ } else if (mwpt.direct) {
+ if (idx == 0)
+ sele += "pass_placeVia";
+ else
+ sele += "pass_placeShaping";
+ bNextDirect = true;
+ } else if (bNextDirect) {
+ sele += "beelinepass_placeShaping";
+ bNextDirect = false;
+ } else if (mwpt.name.startsWith("via") ||
+ mwpt.name.startsWith("from") ||
+ mwpt.name.startsWith("to")) {
+ if (bNextDirect) {
+ sele += "beelinepass_placeShaping";
+ } else {
+ sele += "pass_placeVia";
+ }
+ bNextDirect = false;
+ } else {
+ sele += "" + mwpt.name + "";
+ sele += "pass_placeVia";
+ }
+ }
+ }
+ }
+ }
+ sb.append(" ").append(sele).append("\n");
+
+ nn = n;
+ }
+
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append("\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("\n");
+ sb.append("\n");
+ }
+
+ public void formatGpxFooter(BufferedWriter sb) throws IOException {
+ sb.append("\n");
+ }
+
+ public void formatWaypointGpx(BufferedWriter sb, OsmNodeNamed n) throws IOException {
+ sb.append(" ");
+ if (n.getSElev() != Short.MIN_VALUE) {
+ sb.append("").append("" + n.getElev()).append("");
+ }
+ if (n.name != null) {
+ sb.append("").append(StringUtils.escapeXml10(n.name)).append("");
+ }
+ if (n.nodeDescription != null && rc != null) {
+ sb.append("").append(rc.expctxWay.getKeyValueDescription(false, n.nodeDescription)).append("");
+ }
+ sb.append("\n");
+ }
+
+ public static String getWaypoint(int ilon, int ilat, String name, String desc) {
+ return "" + name + "" + (desc != null ? "" + desc + "" : "") + "";
+ }
+
+ 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("= 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;
+ }
+
+}
diff --git a/brouter-core/src/main/java/btools/router/FormatJson.java b/brouter-core/src/main/java/btools/router/FormatJson.java
new file mode 100644
index 0000000..a2c5b7a
--- /dev/null
+++ b/brouter-core/src/main/java/btools/router/FormatJson.java
@@ -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 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);
+ }
+ }
+
+}
diff --git a/brouter-core/src/main/java/btools/router/FormatKml.java b/brouter-core/src/main/java/btools/router/FormatKml.java
new file mode 100644
index 0000000..5798c5c
--- /dev/null
+++ b/brouter-core/src/main/java/btools/router/FormatKml.java
@@ -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("\n");
+
+ sb.append("\n");
+ sb.append(" \n");
+ sb.append(" KML Samples\n");
+ sb.append(" 1\n");
+ sb.append(" 3.497064\n");
+ sb.append(" 872\n");
+ sb.append(" To enable simple instructions add: 'instructions=1' as parameter to the URL\n");
+ sb.append(" \n");
+ sb.append(" Paths\n");
+ sb.append(" 0\n");
+ sb.append(" Examples of paths.\n");
+ sb.append(" \n");
+ sb.append(" Tessellated\n");
+ sb.append(" 0\n");
+ sb.append(" tag has a value of 1, the line will contour to the underlying terrain]]>\n");
+ sb.append(" \n");
+ sb.append(" 1\n");
+ sb.append(" ");
+
+ for (OsmPathElement n : t.nodes) {
+ sb.append(formatILon(n.getILon())).append(",").append(formatILat(n.getILat())).append("\n");
+ }
+
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ sb.append(" \n");
+ if (t.exportWaypoints || !t.pois.isEmpty()) {
+ if (!t.pois.isEmpty()) {
+ sb.append(" \n");
+ sb.append(" poi\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(" \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(" \n");
+ sb.append("\n");
+
+ return sb.toString();
+ }
+
+ private void createFolder(StringBuilder sb, String type, List waypoints) {
+ sb.append(" \n");
+ sb.append(" " + type + "\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(" \n");
+ }
+
+ private void createPlaceMark(StringBuilder sb, String name, int ilat, int ilon) {
+ sb.append(" \n");
+ sb.append(" " + StringUtils.escapeXml10(name) + "\n");
+ sb.append(" \n");
+ sb.append(" " + formatILon(ilon) + "," + formatILat(ilat) + "\n");
+ sb.append(" \n");
+ sb.append(" \n");
+ }
+
+}
diff --git a/brouter-core/src/main/java/btools/router/Formatter.java b/brouter-core/src/main/java/btools/router/Formatter.java
new file mode 100644
index 0000000..190d279
--- /dev/null
+++ b/brouter-core/src/main/java/btools/router/Formatter.java
@@ -0,0 +1,73 @@
+package btools.router;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+
+public abstract class Formatter {
+ private static final int OUTPUT_FORMAT_GPX = 0;
+ private static final int OUTPUT_FORMAT_KML = 1;
+ private static final int OUTPUT_FORMAT_JSON = 2;
+ private static final int OUTPUT_FORMAT_CSV = 3;
+
+ 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);
+ }
+
+}