Skip to content

Commit dafadcc

Browse files
author
Peter
committed
Merge pull request graphhopper#177 from JohannesPelzer/master
Added azimuth and direction to GPX <rtept> extension
2 parents 493a38b + a793373 commit dafadcc

File tree

6 files changed

+218
-37
lines changed

6 files changed

+218
-37
lines changed

core/src/main/java/com/graphhopper/util/AngleCalc2D.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,28 @@ public class AngleCalc2D
2727
{
2828

2929
/**
30-
* Return orientation of line relative to north. (North by coordinates, not magnetic north)
30+
* Return orientation of line relative to east.
3131
* <p>
32-
* @return Orientation in interval -pi to +pi where 0 is north and pi is south
32+
* @return Orientation in interval -pi to +pi where 0 is east
33+
* <p>
34+
* @Deprecated because it seems to be nicer to align to north so try to use calcOrientationNorth
35+
* instaead
3336
*/
37+
@Deprecated
3438
public double calcOrientation( double lat1, double lon1, double lat2, double lon2 )
3539
{
3640
return Math.atan2(lat2 - lat1, lon2 - lon1);
41+
//return Math.atan2(lon2 - lon1, lat2 - lat1);
42+
}
43+
44+
/**
45+
* Return orientation of line relative to north. (North by coordinates, not magnetic north)
46+
* <p>
47+
* @return Orientation in interval -pi to +pi where 0 is north and pi is south
48+
*/
49+
public double calcOrientationNorth( double lat1, double lon1, double lat2, double lon2 )
50+
{
51+
return Math.atan2(lon2 - lon1, lat2 - lat1);
3752
}
3853

3954
/**
@@ -61,6 +76,25 @@ public double alignOrientation( double baseOrientation, double orientation )
6176
return resultOrientation;
6277
}
6378

79+
/**
80+
* Calculate Azimuth for a line given by two coordinates. Direction in Degree where 0 is North,
81+
* 90 is East, and 270 is West
82+
* <p>
83+
* @param lat1
84+
* @param lon1
85+
* @param lat2
86+
* @param lon2
87+
* @return
88+
*/
89+
double calcAzimuth( double lat1, double lon1, double lat2, double lon2 )
90+
{
91+
double orientation = calcOrientationNorth(lat1, lon1, lat2, lon2);
92+
double baseOrientation = calcOrientationNorth(2, 0, 1, 0); // south
93+
double alignedOrientation = alignOrientation(baseOrientation, orientation);
94+
95+
return Math.toDegrees(alignedOrientation);
96+
}
97+
6498
String azimuth2compassPoint( double azimuth )
6599
{
66100

core/src/main/java/com/graphhopper/util/Instruction.java

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
package com.graphhopper.util;
1919

2020
import java.util.List;
21-
import java.util.Map;
22-
import java.util.HashMap;
2321
import java.text.DecimalFormat;
2422

2523
public class Instruction
@@ -172,31 +170,68 @@ public String toString()
172170
return sb.toString();
173171
}
174172

175-
public Map<String, String> calcExtensions()
173+
/**
174+
* Return Direction/Compass point based on the first tracksegment of the instruction. If
175+
* Instruction does not contain enough coordinate points, NULL will be returned.
176+
* <p>
177+
* @return
178+
*/
179+
String getDirection( Instruction nextI )
176180
{
177181
AngleCalc2D ac = new AngleCalc2D();
182+
double azimuth = calcAzimuth(nextI);
183+
if (Double.compare(Double.NaN, azimuth) == 0)
184+
{
185+
return null;
186+
}
187+
String dir = ac.azimuth2compassPoint(azimuth);
188+
return dir;
189+
}
190+
191+
/**
192+
* Return Azimuth based on the first tracksegment of the instruction. If Instruction does not
193+
* contain enough coordinate points, NULL will be returned.
194+
* <p>
195+
* @return
196+
*/
197+
String getAzimuth( Instruction nextI )
198+
{
199+
double az = calcAzimuth(nextI);
200+
if (Double.compare(Double.NaN, az) == 0)
201+
{
202+
return null;
203+
}
204+
178205
DecimalFormat angleFormatter = new DecimalFormat("#");
206+
return angleFormatter.format(az);
207+
208+
}
209+
210+
private double calcAzimuth( Instruction nextI )
211+
{
212+
double nextLat;
213+
double nextLon;
214+
215+
if (points.getSize() >= 2)
216+
{
217+
nextLat = points.getLatitude(1);
218+
nextLon = points.getLongitude(1);
219+
} else if (points.getSize() == 1 && null != nextI)
220+
{
221+
nextLat = nextI.points.getLatitude(0);
222+
nextLon = nextI.points.getLongitude(0);
223+
} else
224+
{
225+
return Double.NaN;
226+
}
227+
228+
double lat = points.getLatitude(0);
229+
double lon = points.getLongitude(0);
230+
231+
AngleCalc2D ac = new AngleCalc2D();
179232

180-
// Add info for extensions
181-
Map<String, String> extensions = new HashMap<String, String>();
182-
183-
// TODO instead of indication use angle which can be used here
184-
// double distanceToNext = distanceCalc.calcDist(nextLat, nextLon, lat, lon);
185-
// extensions.put("distance", angleFormatter.format(distanceToNext));
186-
//
187-
// if (!(firstInstr && first))
188-
// {
189-
// // impossible to calculate an angle for first point of first instruction
190-
// double prevLat = first ? prevInstr.getLastLat() : points.getLatitude(i - 1);
191-
// double prevLon = first ? prevInstr.getLastLon() : points.getLongitude(i - 1);
192-
// double turnAngle = ac.calcTurnAngleDeg(prevLat, prevLon, lat, lon, nextLat, nextLon);
193-
// extensions.put("turn-angle", angleFormatter.format(turnAngle));
194-
// }
195-
//
196-
// double azimuth = ac.calcAzimuthDeg(lat, lon, nextLat, nextLon);
197-
// extensions.put("azimuth", angleFormatter.format(azimuth));
198-
// extensions.put("direction", ac.azimuth2compassPoint(azimuth));
199-
return extensions;
233+
double azimuth = ac.calcAzimuth(lat, lon, nextLat, nextLon);
234+
return azimuth;
200235
}
201236

202237
void checkOne()

core/src/main/java/com/graphhopper/util/InstructionList.java

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,20 @@ public String createGPX( String trackName, long startTimeMillis, String timeZone
253253
if (!isEmpty())
254254
{
255255
track.append("<rte>");
256+
//Instruction prevI = null, middleI = null, nextI = null;
257+
Instruction thisI = null, nextI;
258+
256259
for (Instruction i : instructions)
257260
{
258-
createExtensionsBlock(track, i);
261+
nextI = i;
262+
263+
if (null != thisI)
264+
{
265+
createRteptBlock(track, thisI, nextI);
266+
}
267+
thisI = nextI;
259268
}
269+
createRteptBlock(track, thisI, null);
260270
track.append("</rte>");
261271
}
262272

@@ -308,24 +318,31 @@ public String getLanguage()
308318
}
309319
};
310320

311-
private String createExtensionsBlock( StringBuilder sbEx, Instruction instruction )
321+
private void createRteptBlock( StringBuilder output, Instruction instruction, Instruction nextI )
312322
{
313-
sbEx.append("<rtept lat=\"").append(InstructionList.round(instruction.getFirstLat(), 6)).
323+
output.append("<rtept lat=\"").append(InstructionList.round(instruction.getFirstLat(), 6)).
314324
append("\" lon=\"").append(InstructionList.round(instruction.getFirstLon(), 6)).append("\">");
315325

316326
if (!instruction.getName().isEmpty())
317-
sbEx.append("<desc>").append(getTurnDescription(instruction, NO_TRANSLATE)).append("</desc>");
327+
output.append("<desc>").append(getTurnDescription(instruction, NO_TRANSLATE)).append("</desc>");
318328

319-
sbEx.append("<extensions>");
329+
output.append("<extensions>");
320330

321-
sbEx.append("<distance>").append((int) instruction.getDistance()).append("</distance>");
322-
sbEx.append("<time>").append(instruction.getTime()).append("</time>");
331+
output.append("<distance>").append((int) instruction.getDistance()).append("</distance>");
332+
output.append("<time>").append(instruction.getTime()).append("</time>");
323333

324-
// sbEx.append("<direction>").append(instruction.getDirection()).append("</direction>");
325-
// sbEx.append("<azimuth>").append(instruction.getAzimutz()).append("</azimuth>");
326-
sbEx.append("</extensions>");
327-
sbEx.append("</rtept>");
328-
return sbEx.toString();
334+
String direction = instruction.getDirection(nextI);
335+
if (null != direction)
336+
{
337+
output.append("<direction>").append(direction).append("</direction>");
338+
}
339+
String azimuth = instruction.getAzimuth(nextI);
340+
if (null != azimuth)
341+
{
342+
output.append("<azimuth>").append(azimuth).append("</azimuth>");
343+
}
344+
output.append("</extensions>");
345+
output.append("</rtept>");
329346
}
330347

331348
public static String getWayName( String name, int paveType, int wayType, TranslationMap.Translation tr )

core/src/test/java/com/graphhopper/util/AngleCalcTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ public void testOrientation()
3737
assertEquals(-135.0, Math.toDegrees(ac.calcOrientation(0, 0, -10, -10)), 0.0001);
3838
}
3939

40+
@Test
41+
public void testOrientationNorth()
42+
{
43+
assertEquals(0.0, Math.toDegrees(ac.calcOrientationNorth(0, 0, 10, 0)), 0.0001);
44+
assertEquals(45.0, Math.toDegrees(ac.calcOrientationNorth(0, 0, 10, 10)), 0.0001);
45+
assertEquals(90.0, Math.toDegrees(ac.calcOrientationNorth(0, 0, 0, 10)), 0.0001);
46+
assertEquals(-135.0, Math.toDegrees(ac.calcOrientationNorth(0, 0, -10, -10)), 0.0001);
47+
}
48+
4049
@Test
4150
public void testAlignOrientation()
4251
{
@@ -53,6 +62,14 @@ public void testCombined()
5362
assertEquals(146.458, Math.toDegrees(ac.alignOrientation(0, orientation)), 0.001);
5463
}
5564

65+
@Test
66+
public void testCalcAzimuth()
67+
{
68+
assertEquals(90.0, ac.calcAzimuth(0, 0, 0, 10), 0.0001);
69+
assertEquals(180.0, ac.calcAzimuth(0, 0, -10, 0), 0.0001);
70+
assertEquals(270.0, ac.calcAzimuth(0, 0, 0, -10), 0.0001);
71+
}
72+
5673
@Test
5774
public void testAzimuthCompassPoint()
5875
{

core/src/test/java/com/graphhopper/util/InstructionListTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ public void testInstructionsWithTimeAndPlace()
310310
assertEquals(9.9, wayList.get(3).getFirstLon(), 1e-3);
311311

312312
String gpxStr = wayList.createGPX("test", 0, "GMT+1");
313+
313314
assertTrue(gpxStr, gpxStr.contains("<trkpt lat=\"15.0\" lon=\"10.0\"><time>1970-01-01T01:00:00+01:00</time>"));
314315
assertTrue(gpxStr, gpxStr.contains("<extensions>") && gpxStr.contains("</extensions>"));
315316
assertTrue(gpxStr, gpxStr.contains("<rtept lat=\"15.1\" lon=\"10.0\">"));
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Licensed to GraphHopper and Peter Karich under one or more contributor
3+
* license agreements. See the NOTICE file distributed with this work for
4+
* additional information regarding copyright ownership.
5+
*
6+
* GraphHopper licenses this file to you under the Apache License,
7+
* Version 2.0 (the "License"); you may not use this file except in
8+
* compliance with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package com.graphhopper.util;
19+
20+
import org.junit.Test;
21+
import static org.junit.Assert.*;
22+
23+
/**
24+
*
25+
* @author Johannes Pelzer
26+
*/
27+
public class InstructionTest
28+
{
29+
@Test
30+
public void testGetAzimuthAndGetDirection() {
31+
PointList pl = new PointList();
32+
pl.add(49.942, 11.584);
33+
pl.add(49.942, 11.582);
34+
Instruction i1 = new Instruction(Instruction.CONTINUE_ON_STREET, "temp", 0, 0, pl).setDistance(240).setTime(15000);
35+
36+
assertEquals("270", i1.getAzimuth(null));
37+
assertEquals("W", i1.getDirection(null));
38+
39+
40+
PointList p2 = new PointList();
41+
p2.add(49.942, 11.580);
42+
p2.add(49.944, 11.582);
43+
Instruction i2 = new Instruction(Instruction.CONTINUE_ON_STREET, "temp", 0, 0, p2).setDistance(240).setTime(15000);
44+
45+
assertEquals("45", i2.getAzimuth(null));
46+
assertEquals("NE", i2.getDirection(null));
47+
48+
49+
PointList p3 = new PointList();
50+
p3.add(49.942, 11.580);
51+
p3.add(49.944, 11.580);
52+
Instruction i3 = new Instruction(Instruction.CONTINUE_ON_STREET, "temp", 0, 0, p3).setDistance(240).setTime(15000);
53+
54+
55+
assertEquals("0", i3.getAzimuth(null));
56+
assertEquals("N", i3.getDirection(null));
57+
58+
PointList p4 = new PointList();
59+
p4.add(49.940, 11.580);
60+
p4.add(49.920, 11.586);
61+
Instruction i4 = new Instruction(Instruction.CONTINUE_ON_STREET, "temp", 0, 0, p4).setDistance(240).setTime(15000);
62+
63+
64+
65+
assertEquals("S", i4.getDirection(null));
66+
67+
PointList p5 = new PointList();
68+
p5.add(49.940, 11.580);
69+
Instruction i5 = new Instruction(Instruction.CONTINUE_ON_STREET, "temp", 0, 0, p5).setDistance(240).setTime(15000);
70+
71+
assertEquals(null, i5.getAzimuth(null));
72+
assertEquals(null, i5.getDirection(null));
73+
}
74+
75+
76+
77+
}

0 commit comments

Comments
 (0)