Skip to content

Commit 3602547

Browse files
authored
Merge pull request #70 from cevi/dev
- fix wrong elevation data - fix waypoint misaligment
2 parents d3a0710 + cb891f9 commit 3602547

File tree

9 files changed

+188
-125
lines changed

9 files changed

+188
-125
lines changed

python_program/automatic_walk_time_tables/generator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from automatic_walk_time_tables.geo_processing.find_walk_table_points import select_waypoints
77
from automatic_walk_time_tables.geo_processing.map_numbers import find_map_numbers
88
from automatic_walk_time_tables.map_downloader.create_map import MapCreator
9+
from automatic_walk_time_tables.utils.path import Path_LV03
910
from automatic_walk_time_tables.walk_time_table.walk_table import plot_elevation_profile, create_walk_table
1011
from automatic_walk_time_tables.utils.file_parser import parse_gpx_file, parse_kml_file
1112
from automatic_walk_time_tables.utils import path
@@ -28,7 +29,7 @@ def __init__(self, args: argparse.Namespace, uuid: str = ''):
2829
# get the extension of the file
2930
extension = pathlib.Path(self.args.file_name).suffix
3031

31-
self.path_ = path.Path_LV03()
32+
self.path_: Path_LV03 = path.Path_LV03()
3233
self.path_.clear()
3334

3435
if extension == '.gpx':

python_program/automatic_walk_time_tables/geo_processing/coord_transformation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ def WGS84toLV03(self, latitude, longitude, ellHeight):
168168
d = []
169169
d.append(self.WGStoCHy(latitude, longitude))
170170
d.append(self.WGStoCHx(latitude, longitude))
171-
d.append(self.WGStoCHh(latitude, longitude, ellHeight))
171+
d.append(ellHeight)
172172
return d
173173

174174

python_program/automatic_walk_time_tables/geo_processing/find_swisstopo_name.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import os
44
from math import sqrt
55

6+
from automatic_walk_time_tables.utils.point import Point_LV95
7+
68
logger = logging.getLogger(__name__)
79

810

@@ -24,7 +26,7 @@ def add_to_database(file, db, typeIndex, name, x, y):
2426
logger.debug("Database has " + str(len(db)) + " entries.")
2527

2628

27-
def find_name(coord, dist):
29+
def find_name(point: Point_LV95, dist):
2830
"""
2931
See also https://api3.geo.admin.ch/api/faq/index.html#which-layers-have-a-tooltip
3032
fair use limit 20 Request per minute
@@ -36,8 +38,8 @@ def find_name(coord, dist):
3638

3739
flurname_list = [swiss_name
3840
for swiss_name in database if
39-
abs(swiss_name.x - coord[0]) < dist * 4 and
40-
abs(swiss_name.y - coord[1]) < dist * 4]
41+
abs(swiss_name.x - point.lat) < dist * 4 and
42+
abs(swiss_name.y - point.lon) < dist * 4]
4143

4244
# print(list(map(lambda x: x.name, flurname_list)))
4345

@@ -50,7 +52,7 @@ def find_name(coord, dist):
5052
# Flurname swisstopo: 0.9
5153

5254
flurname_list.sort(key=lambda swiss_name:
53-
sqrt((abs(swiss_name.x - coord[0]) ** 2 + abs(swiss_name.y - coord[1]) ** 2)) / (
55+
sqrt((abs(swiss_name.x - point.lat) ** 2 + abs(swiss_name.y - point.lon) ** 2)) / (
5456
2 if swiss_name.object_type in ['Haltestelle Bahn', 'Huegel', 'Pass', 'Strassenpass', 'Alpiner Gipfel',
5557
'Gipfel',
5658
] else
@@ -67,7 +69,7 @@ def find_name(coord, dist):
6769
return ''
6870

6971
return ('bei/m ' if sqrt(
70-
abs(flurname_list[0].x - coord[0]) ** 2 + abs(flurname_list[0].y - coord[1]) ** 2) > dist else '') + \
72+
abs(flurname_list[0].x - point.lat) ** 2 + abs(flurname_list[0].y - point.lon) ** 2) > dist else '') + \
7173
flurname_list[0].name
7274

7375

python_program/automatic_walk_time_tables/geo_processing/find_walk_table_points.py

Lines changed: 73 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
from copy import copy
33
from typing import List, Tuple
44

5-
import geopy.distance
65
from automatic_walk_time_tables.utils import path, point
6+
from automatic_walk_time_tables.utils.point import Point_LV03
7+
from automatic_walk_time_tables.utils.way_point import WayPoint
78

89
logger = logging.getLogger(__name__)
910

1011

11-
def select_waypoints(path_ : path.Path, walk_point_limit=21):
12+
def select_waypoints(path_: path.Path_LV03, walk_point_limit=21) -> Tuple[float, List[WayPoint], List[WayPoint]]:
1213
"""
1314
Algorithm that selects suitable points for the Marschzeittabelle.
1415
Some parts are inspired by the Ramer–Douglas–Peucker algorithm. Especially
@@ -37,7 +38,7 @@ def select_waypoints(path_ : path.Path, walk_point_limit=21):
3738
return total_distance, pts_step_2, pts_step_3
3839

3940

40-
def reduce_number_of_points(pts_step_2: List[Tuple[float, point.Point]], walk_point_limit: int):
41+
def reduce_number_of_points(pts_step_2: List[WayPoint], walk_point_limit: int):
4142
"""
4243
Final selection: Iteratively reduce the number of points to the maximum specified in walk_point_limit. To
4344
achieve this we iteratively increase a maximum derivation (drv_limit) value until we have dropped enough points.
@@ -53,10 +54,10 @@ def reduce_number_of_points(pts_step_2: List[Tuple[float, point.Point]], walk_po
5354
through the selected points, we increase drv_limit by 2 meters and try again.
5455
"""
5556

56-
pts_step_3: List[Tuple[float, point.Point]] = copy(pts_step_2)
57+
pts_step_3: List[WayPoint] = copy(pts_step_2)
5758

58-
pt_A: Tuple[float, point.Point] = None
59-
pt_B: Tuple[float, point.Point] = None
59+
pt_A: WayPoint = None
60+
pt_B: WayPoint = None
6061

6162
drv_limit = 0
6263

@@ -81,19 +82,22 @@ def reduce_number_of_points(pts_step_2: List[Tuple[float, point.Point]], walk_po
8182
m, b = calc_secant_line(pt_A, pt_C)
8283
secant_elev = calc_secant_elevation(m, b, pt_B)
8384

84-
if abs(secant_elev - pt_B[1].h) < drv_limit:
85+
if abs(secant_elev - pt_B.point.h) < drv_limit:
8586

8687
# Check if B must be replaced by a previously dropped point D
8788
pt_D = None
88-
for pt in list(filter(lambda p: pt_A[0] < p[0] < pt_C[0], pts_dropped)):
89+
for pt in list(
90+
filter(lambda p: pt_A.accumulated_distance < p[0] < pt_C.accumulated_distance,
91+
pts_dropped)):
8992
secant_elev = calc_secant_elevation(m, b, pt)
90-
if abs(secant_elev - pt[1].h) >= drv_limit:
93+
if abs(secant_elev - pt.point.h) >= drv_limit:
9194
pt_D = pt
9295
break
9396

9497
if pt_D is not None: # Replace B with point D
9598
pts_step_3.remove(pt_B)
96-
index = next(x for x, val in enumerate(pts_step_3) if val[0] > pt_D[0])
99+
index = next(x for x, val in enumerate(pts_step_3) if
100+
val.accumulated_distance > pt_D.accumulated_distance)
97101
pts_step_3.insert(index, pt_D)
98102
pts_dropped.append(pt_B)
99103

@@ -113,7 +117,7 @@ def reduce_number_of_points(pts_step_2: List[Tuple[float, point.Point]], walk_po
113117
return pts_step_3
114118

115119

116-
def remove_unnecessary_points(pts_step_1: List[Tuple[int, point.Point]]):
120+
def remove_unnecessary_points(pts_step_1: List[WayPoint]):
117121
"""
118122
Now we loop through the preselected points and tighten the selection criteria.
119123
Into the list pts_step_2 we include points according to the following rules:
@@ -132,26 +136,27 @@ def remove_unnecessary_points(pts_step_1: List[Tuple[int, point.Point]]):
132136

133137
drv_limit = 20
134138

135-
last_pt: Tuple[float, point.Point] = pts_step_1[0]
136-
pts_step_2: List[Tuple[float, point.Point]] = [last_pt]
139+
last_pt: WayPoint = pts_step_1[0]
140+
pts_step_2: List[WayPoint] = [last_pt]
137141

138142
for pt in pts_step_1[1:]:
139143

140144
if last_pt is not None:
141145

142-
last_coord = get_coordinates(last_pt[1].to_WGS84())
143-
coord = get_coordinates(pt[1].to_WGS84())
146+
last_coord = get_coordinates(last_pt.point)
147+
coord = get_coordinates(pt.point)
144148

145-
if (abs(last_pt[1].h - pt[1].h) > 20
146-
and geopy.distance.distance(last_coord, coord).m > 250) \
147-
or geopy.distance.distance(last_coord, coord).m > 1500: # TODO: this line seems obsole, bug?
149+
if abs(last_pt.point.h - pt.point.h) > 20 and coord[0] - last_coord[0] > 250:
148150

149151
m, b = calc_secant_line(last_pt, pt)
150152

151153
# add point with max derivation
152-
for intermediate_point in list(filter(lambda p: last_pt[0] < p[0] < pt[0], pts_step_1)):
154+
for intermediate_point in \
155+
list(filter(
156+
lambda p: last_pt.accumulated_distance < p.accumulated_distance < pt.accumulated_distance,
157+
pts_step_1)):
153158

154-
derivation = abs(calc_secant_elevation(m, b, intermediate_point) - intermediate_point[1].h)
159+
derivation = abs(calc_secant_elevation(m, b, intermediate_point) - intermediate_point.point.h)
155160
if drv_limit <= derivation:
156161
pts_step_2.append(intermediate_point)
157162
drv_limit = derivation
@@ -173,7 +178,7 @@ def get_coordinates(pt: point.Point):
173178
return pt.lat, pt.lon
174179

175180

176-
def preselection_step(path_ : path.Path):
181+
def preselection_step(path_: path.Path):
177182
"""
178183
Preselection: Select all points from the tracking file which satisfy one of the following criteria.
179184
This guarantees that all important points are considered. We call the set of selected points pts_step_1.
@@ -184,68 +189,66 @@ def preselection_step(path_ : path.Path):
184189
- distance to last selected point bigger than 250 meters
185190
"""
186191

187-
cumulated_distance = 0.0
188-
pts_step_1: List[Tuple[float, point.Point]] = []
192+
accumulated_distance = 0.0
193+
way_points: List[WayPoint] = []
189194

190-
lastSlope = None
191-
lastCoord = None
195+
last_slope = 0
196+
last_point = path_.points[0]
192197

193-
lastPoint = None
198+
# Insert first point of path
199+
way_points.append(WayPoint(0, last_point))
194200

195201
# (1) Preselection
196-
for i,pt in enumerate(path_.points):
197-
newCoord = get_coordinates(pt.to_WGS84())
198-
199-
if i == 0: # First point
200-
pts_step_1.append((0, pt))
201-
lastCoord = get_coordinates(pt.to_WGS84())
202-
lastSlope = 0.
203-
elif i == len(path_.points) - 1: # Last point
204-
distDelta = geopy.distance.distance(lastCoord, newCoord).km
205-
cumulated_distance += distDelta
206-
pts_step_1.append((cumulated_distance, pt))
207-
else: # Any other point
208-
distDelta = geopy.distance.distance(lastCoord, newCoord).km
209-
cumulated_distance += distDelta
210-
211-
if distDelta > 0.:
212-
newSlope = (pt.h - pts_step_1[-1][1].h) / distDelta
213-
if abs(newSlope) > 0.0:
214-
if abs(newSlope - lastSlope) > 0.5: # Slope change
215-
pts_step_1.append((cumulated_distance, pt))
216-
lastSlope = newSlope
217-
lastCoord = newCoord
218-
continue
219-
220-
if geopy.distance.distance(lastCoord, newCoord).m > 250:
221-
pts_step_1.append((cumulated_distance, pt))
222-
lastCoord = newCoord
223-
lastSlope = newSlope
224-
continue
225-
226-
return cumulated_distance, pts_step_1
227-
228-
229-
def calc_secant_elevation(m: float, b: float, pt_B: Tuple[float, point.Point]):
202+
for i in range(len(path_.points) - 2):
203+
204+
last_coord: Point_LV03 = path_.points[i].to_LV03()
205+
coord: Point_LV03 = path_.points[i + 1].to_LV03()
206+
207+
delta_dist = last_coord.distance(coord)
208+
accumulated_distance += delta_dist
209+
210+
# skip duplicated points in GPX file, less than 1cm in distance
211+
if delta_dist <= 0.01:
212+
continue
213+
214+
slope = (coord.h - way_points[-1].point.h) / delta_dist
215+
if abs(slope) > 0. and abs(slope - last_slope) > 0.5: # significant slope change
216+
way_points.append(WayPoint(accumulated_distance, coord))
217+
218+
elif accumulated_distance - way_points[-1].accumulated_distance > 250:
219+
way_points.append(WayPoint(accumulated_distance, coord))
220+
221+
# TODO: add significant local extremum in track elevation
222+
223+
# Insert last point of path
224+
way_points.append(WayPoint(accumulated_distance, path_.points[-1]))
225+
226+
logger.info(""
227+
"Total route length = {}m".format(accumulated_distance))
228+
229+
return accumulated_distance, way_points
230+
231+
232+
def calc_secant_elevation(m: float, b: float, pt_B: WayPoint):
230233
"""
231234
Calculates the elevation of a point on the secant line defined by m and b. The point on the
232235
secant line is chosen such that location matches the location of pkr_B.
233236
elevation = m * loc_of_pt_B + b
234237
"""
235238

236-
# pt_B[0] returns the location of point B in km
237-
return m * (pt_B[0] * 1000) + b
239+
# pt_B[0] returns the location of point B in meter
240+
return m * pt_B.accumulated_distance + b
238241

239242

240-
def calc_secant_line(pt_A: Tuple[float, point.Point], pt_C: Tuple[float, point.Point]):
243+
def calc_secant_line(pt_A: WayPoint, pt_C: WayPoint):
241244
"""
242245
Constructs a secant line through points A and C, i.g. a linear function passing through point A and C.
243246
Returns the slope and the intersect of a linear function through A and C.
244247
"""
245248

246-
# the locations of pt_A and pt_C given in km.
247-
x1, y1 = pt_A[0] * 1000, pt_A[1].h
248-
x2, y2 = pt_C[0] * 1000, pt_C[1].h
249+
# the locations of pt_A and pt_C given is given in m.
250+
x1, y1 = pt_A.accumulated_distance, pt_A.point.h
251+
x2, y2 = pt_C.accumulated_distance, pt_C.point.h
249252

250253
# if the location of A and C is identical, the slope m is defined as 0
251254
m: float = (y1 - y2) / (x1 - x2) if (x1 - x2) != 0 else 0.0
@@ -256,30 +259,27 @@ def calc_secant_line(pt_A: Tuple[float, point.Point], pt_C: Tuple[float, point.P
256259
return m, b
257260

258261

259-
def prepare_for_plot(path_ : path.Path):
262+
def prepare_for_plot(path_: path.Path_LV03):
260263
"""
261264
Prepares a gpx file for plotting.
262265
Returns two list, one with the elevation of all points in the gpx file and one with the associated,
263266
accumulated distance form the starting point.
264267
"""
265268

266-
coord: Tuple[float, float] = None
269+
coord: Point_LV03 = None
267270
accumulated_distance: float = 0.0
268271

269272
distances: List[float] = []
270273
heights: List[float] = []
271274

272275
for pt in path_.points:
273276

274-
newCoord = get_coordinates(pt.to_WGS84())
275-
276277
if coord is not None:
277-
distDelta = geopy.distance.distance(coord, newCoord).km
278-
accumulated_distance += distDelta
278+
accumulated_distance += coord.distance(pt)
279279

280280
distances.append(accumulated_distance)
281281
heights.append(pt.h)
282282

283-
coord = newCoord
283+
coord = pt
284284

285285
return distances, heights

0 commit comments

Comments
 (0)