From 9f532914f2b19a02acfb732d83b65eb7ef2d68b0 Mon Sep 17 00:00:00 2001 From: Ilya Zverev Date: Wed, 1 Nov 2017 14:49:36 +0300 Subject: [PATCH] tmp --- mapsme_subways.py | 13 ++++++-- subway_structure.py | 78 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/mapsme_subways.py b/mapsme_subways.py index 8fef75a..6c01422 100755 --- a/mapsme_subways.py +++ b/mapsme_subways.py @@ -10,6 +10,7 @@ import urllib.request from subway_structure import ( distance, + distance_on_line, download_cities, find_transfers, get_unused_entrances_geojson, @@ -306,15 +307,21 @@ def prepare_mapsme_data(transfers, cities): 'route_id': uid(route.id, 'r'), 'itineraries': [] } - for variant in route: + for i, variant in enumerate(route): + logging.warn('%s[%s]', route.name, i) itin = [] time = 0 + vertex = 0 for i, stop in enumerate(variant): stops[stop.stoparea.id] = stop.stoparea itin.append([uid(stop.stoparea.id), time]) if i+1 < len(variant): - # TODO: estimate segment length and calculate proper time - d = distance(stop.stop, variant[i+1].stop) + d = distance_on_line(stop.stop, variant[i+1].stop, variant.tracks, vertex) + if not d: + d = distance(stop.stop, variant[i+1].stop) + else: + vertex = d[1] + d = d[0] time += round(d*3.6/SPEED_ON_LINE) + 20 routes['itineraries'].append({'stops': itin, 'interval': DEFAULT_INTERVAL}) network['routes'].append(routes) diff --git a/subway_structure.py b/subway_structure.py index 1352d6f..69b3f4d 100644 --- a/subway_structure.py +++ b/subway_structure.py @@ -61,20 +61,28 @@ def project_on_segment(p, p1, p2): res = (p1[0] + u*dp[0], p1[1] + u*dp[1]) if res[0] < min(p1[0], p2[0]) or res[0] > max(p1[0], p2[0]): return None - return res + return res, u -def project_on_line(p, line): +def project_on_line_relative(p, line, start_vertex=0): + """Projects p on a list of coordinates and returns a tuple of (i, u, p): + index of vertex, a relative position [0; 1] to vertex i+1 + and a projected point itself.""" result = None + if len(line) < 2: + return result d_min = MAX_DISTANCE_STOP_TO_LINE * 5 # First, check vertices in the line - for vertex in line: - d = distance(p, vertex) + for i in range(start_vertex, len(line)): + d = distance(p, line[i]) if d < d_min: - result = vertex + if i == len(line) - 1: + result = i-1, 1, line[i] + else: + result = i, 0, line[i] d_min = d # And then calculate distances to each segment - for seg in range(len(line)-1): + for seg in range(start_vertex, len(line)-1): # Check bbox for speed if not ((min(line[seg][0], line[seg+1][0]) - MAX_DISTANCE_STOP_TO_LINE <= p[0] <= max(line[seg][0], line[seg+1][0]) + MAX_DISTANCE_STOP_TO_LINE) and @@ -83,11 +91,57 @@ def project_on_line(p, line): continue proj = project_on_segment(p, line[seg], line[seg+1]) if proj: - d = distance(p, proj) + d = distance(p, proj[0]) if d < d_min: - result = proj + result = seg, proj[1], proj[0] d_min = d - return NOWHERE_STOP if not result else result + return result + + +def project_on_line(p, line, start_vertex=0): + pos = project_on_line_relative(p, line, start_vertex) + if not pos: + return NOWHERE_STOP + return pos[2] + + +def distance_on_line(p1, p2, line, start_vertex=0): + """Calculates distance via line between projections + of points p1 and p2. Returns a TUPLE of (d, vertex): + d is the distance and vertex is the number of the second + vertex, to continue calculations for the next point.""" + pos1 = project_on_line_relative(p1, line, start_vertex) + if not pos1: + logging.warn('p1 %s is not projected', p1) + return None + pos2 = project_on_line_relative(p2, line, pos1[0]) + if not pos2: + if line[0] == line[-1]: + logging.warn('Trying circular') + pos2 = project_on_line_relative(p2, line, 0) + if not pos2: + logging.warn('p2 %s is not projected, line %s to %s', p2, line[0], line[-1]) + return None + if pos2[0] < pos1[0] or (pos2[0] == pos1[0] and pos2[1] < pos1[1]): + logging.warn('Pos1 %s is after pos2 %s', pos1, pos2) + if line[0] == line[-1]: + # If a line is circular, calculate distance properly + pos2 += len(line) + line = line + line + else: + logging.warn('Line is not circular') + pos1, pos2 = pos2, pos1 + if pos1[0] == pos2[0]: + return distance(line[pos1[0]], line[pos1[0]+1]) * ( + pos2[1]-pos1[1]), pos1[0] + d = 0 + if pos1[1] < 1: + d += distance(line[pos1[0]], line[pos1[0]+1]) * (1-pos1[1]) + for i in range(pos1[0]+1, pos2[0]): + d += distance(line[i], line[i+1]) + if pos2[1] > 0: + d += distance(line[pos2[0]], line[pos2[0]+1]) * pos2[1] + return d, pos2[0] def angle_between(p1, c, p2): @@ -601,6 +655,12 @@ class Route: msg = 'Angle between stops around "{}" is too narrow, {} degrees'.format( self.stops[si+1].stoparea.name, angle) city.error_if(angle < 20, msg, relation) + proj1 = project_on_line_relative(self.stops[1].stop, self.tracks) + proj2 = project_on_line_relative(self.stops[min(len(self.stops)-1, 3)].stop, + self.tracks) + if proj1[0] > proj2[0] or (proj1[0] == proj2[0] and proj1[1] > proj2[1]): + city.warn('Tracks seem to go in the opposite direction to stops', relation) + self.tracks.reverse() def __len__(self): return len(self.stops)