com.graphhopper.gpx.GpxConversions Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of graphhopper-web-bundle Show documentation
Show all versions of graphhopper-web-bundle Show documentation
Use the GraphHopper routing engine as a web-service
/*
* Licensed to GraphHopper GmbH under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*
* GraphHopper GmbH licenses this file to you 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.
*/
package com.graphhopper.gpx;
import com.graphhopper.jackson.Gpx;
import com.graphhopper.matching.Observation;
import com.graphhopper.util.*;
import com.graphhopper.util.shapes.GHPoint;
import com.graphhopper.util.shapes.GHPoint3D;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.regex.Pattern;
public class GpxConversions {
private static final AngleCalc AC = AngleCalc.ANGLE_CALC;
private static final Pattern XML_ESCAPE_PATTERN = Pattern.compile("[\\<\\>]");
static String simpleXMLEscape(String str) {
// We could even use the 'more flexible' CDATA section but for now do the following:
// The 'and' could be important sometimes but remove others
return XML_ESCAPE_PATTERN.matcher(str.replace("&", "&")).replaceAll("_");
}
public static List createGPXList(InstructionList instructions) {
List gpxList = new ArrayList<>();
long timeOffset = 0;
for (Instruction instruction : instructions) {
int i = 0;
for (GHPoint3D point : instruction.getPoints()) {
GPXEntry gpxEntry;
if (i == 0) {
gpxEntry = new GPXEntry(point, timeOffset);
} else {
// We don't have timestamps for pillar nodes
gpxEntry = new GPXEntry(point);
}
gpxList.add(gpxEntry);
i++;
}
timeOffset = timeOffset + instruction.getTime();
}
return gpxList;
}
private static void createWayPointBlock(StringBuilder output, Instruction instruction, DecimalFormat decimalFormat, Translation tr) {
output.append("\n");
String name;
if (instruction.getName().isEmpty())
name = instruction.getTurnDescription(tr);
else
name = instruction.getName();
output.append(" ").append(simpleXMLEscape(name)).append(" ");
output.append(" ");
}
public static String createGPX(InstructionList instructions, String trackName, long startTimeMillis, boolean includeElevation, boolean withRoute, boolean withTrack, boolean withWayPoints, String version, Translation tr) {
DateFormat formatter = Helper.createFormatter();
DecimalFormat decimalFormat = new DecimalFormat("#", DecimalFormatSymbols.getInstance(Locale.ROOT));
decimalFormat.setMinimumFractionDigits(1);
decimalFormat.setMaximumFractionDigits(6);
decimalFormat.setMinimumIntegerDigits(1);
String header = ""
+ ""
+ "\n"
+ " "
+ ""
+ "GraphHopper GPX "
+ ""
+ ""
+ " ";
StringBuilder gpxOutput = new StringBuilder(header);
if (!instructions.isEmpty()) {
if (withWayPoints) {
createWayPointBlock(gpxOutput, instructions.get(0), decimalFormat, tr); // Start
for (Instruction currInstr : instructions) {
if ((currInstr.getSign() == Instruction.REACHED_VIA) // Via
|| (currInstr.getSign() == Instruction.FINISH)) // End
{
createWayPointBlock(gpxOutput, currInstr, decimalFormat, tr);
}
}
}
if (withRoute) {
gpxOutput.append("\n");
Instruction nextInstr = null;
for (Instruction currInstr : instructions) {
if (null != nextInstr)
createRteptBlock(gpxOutput, nextInstr, currInstr, decimalFormat, tr);
nextInstr = currInstr;
}
createRteptBlock(gpxOutput, nextInstr, null, decimalFormat, tr);
gpxOutput.append("\n ");
}
}
if (withTrack) {
gpxOutput.append("\n").append(trackName).append(" ");
gpxOutput.append("");
for (GPXEntry entry : createGPXList(instructions)) {
gpxOutput.append("\n");
if (includeElevation)
gpxOutput.append("").append(Helper.round2(((GHPoint3D) entry.getPoint()).getEle())).append(" ");
if (entry.getTime() != null)
gpxOutput.append("");
gpxOutput.append(" ");
}
gpxOutput.append("\n ");
gpxOutput.append("\n ");
}
// we could now use 'wpt' for via points
gpxOutput.append("\n ");
return gpxOutput.toString();
}
private static void createRteptBlock(StringBuilder output, Instruction instruction, Instruction nextI, DecimalFormat decimalFormat, Translation tr) {
output.append("\n");
if (!instruction.getName().isEmpty())
output.append("").append(simpleXMLEscape(instruction.getTurnDescription(tr))).append(" ");
output.append("");
output.append("").append(Helper.round(instruction.getDistance(), 1)).append(" ");
output.append("").append(instruction.getTime()).append(" ");
String direction = calcDirection(instruction, nextI);
if (!direction.isEmpty())
output.append("").append(direction).append(" ");
double azimuth = calcAzimuth(instruction, nextI);
if (!Double.isNaN(azimuth))
output.append("").append(Helper.round2(azimuth)).append(" ");
if (instruction instanceof RoundaboutInstruction) {
RoundaboutInstruction ri = (RoundaboutInstruction) instruction;
output.append("").append(ri.getExitNumber()).append(" ");
}
output.append("").append(instruction.getSign()).append(" ");
output.append(" ");
output.append(" ");
}
/**
* Return the direction like 'NE' based on the first tracksegment of the instruction. If
* Instruction does not contain enough coordinate points, an empty string will be returned.
*/
public static String calcDirection(Instruction instruction, Instruction nextI) {
double azimuth = calcAzimuth(instruction, nextI);
if (Double.isNaN(azimuth))
return "";
return AC.azimuth2compassPoint(azimuth);
}
/**
* Return the azimuth in degree based on the first tracksegment of this instruction. If this
* instruction contains less than 2 points then NaN will be returned or the specified
* instruction will be used if that is the finish instruction.
*/
public static double calcAzimuth(Instruction instruction, Instruction nextI) {
double nextLat;
double nextLon;
if (instruction.getPoints().size() >= 2) {
nextLat = instruction.getPoints().getLat(1);
nextLon = instruction.getPoints().getLon(1);
} else if (nextI != null && instruction.getPoints().size() == 1) {
nextLat = nextI.getPoints().getLat(0);
nextLon = nextI.getPoints().getLon(0);
} else {
return Double.NaN;
}
double lat = instruction.getPoints().getLat(0);
double lon = instruction.getPoints().getLon(0);
return AC.calcAzimuth(lat, lon, nextLat, nextLon);
}
public static List getEntries(Gpx.Trk trk) {
ArrayList gpxEntries = new ArrayList<>();
for (Gpx.Trkseg t : trk.trkseg) {
for (Gpx.Trkpt trkpt : t.trkpt) {
gpxEntries.add(new Observation(new GHPoint3D(trkpt.lat, trkpt.lon, trkpt.ele)));
}
}
return gpxEntries;
}
/**
* @author Peter Karich
*/
public static class GPXEntry {
private GHPoint point;
private Long time;
public GPXEntry(GHPoint p) {
this.point = p;
}
public GPXEntry(GHPoint p, long time) {
this.point = p;
this.time = time;
}
public Long getTime() {
return time;
}
public GHPoint getPoint() {
return point;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GPXEntry gpxEntry = (GPXEntry) o;
return Objects.equals(point, gpxEntry.point) &&
Objects.equals(time, gpxEntry.time);
}
@Override
public int hashCode() {
return Objects.hash(point, time);
}
@Override
public String toString() {
return "GPXEntry{" +
"point=" + point +
", time=" + time +
'}';
}
}
}