def from_handmade(cls, start, middlePoints, destination): """Convert hand-made route data to a way """ if start and destination: # route points & message points are generated at once # * empty string as message => no message point, just route point routePoints = [(start[0], start[1], None)] messagePoints = [] mLength = 0 # in meters lastLat, lastLon = start[0], start[1] for point in middlePoints: lat, lon, elevation, message = point mLength += geo.distance(lastLat, lastLon, lat, lon) * 1000 routePoints.append((lat, lon, elevation)) if message != "": # is it a message point ? point = TurnByTurnPoint(lat, lon, elevation, message) point.distanceFromStart = mLength messagePoints.append(point) lastLat, lastLon = lat, lon routePoints.append((destination[0], destination[1], None)) way = cls(routePoints) way.add_message_points(messagePoints) # huge guestimation (avg speed 60 km/h = 16.7 m/s) seconds = mLength / 16.7 way.duration = seconds way._setLength(mLength) # done, return the result return way else: return None
def basic_test(self): """Test basic TurnByTurn point functionality""" point = TurnByTurnPoint(1.0, 2.0, elevation=123.45, message="foo message", ssml_message="ssml message", icon="some_icon_id") # distances should be None by default self.assertIsNone(point.current_distance) self.assertIsNone(point.distance_from_start) # visited should be False as well self.assertFalse(point.visited) self.assertEqual(point.ssml_message, "ssml message") self.assertEqual(point.icon, "some_icon_id") # check if the LLEMI tuple is correct as well self.assertEqual(point.llemi, (1.0, 2.0, 123.45, "foo message", "some_icon_id")) # try setting the distances point.current_distance = 200 point.distance_from_start = 300 self.assertEqual(point.current_distance, 200) self.assertEqual(point.distance_from_start, 300)
def from_valhalla(cls, result): """Convert Valhalla routing result json to a way""" if result: route = [] turns = [] for leg in result['trip']['legs']: poly = decode_valhalla(leg['shape']) maneuvers = [] for m in leg['maneuvers']: icon_id = VALHALLA_TYPE_ICON_MAP.get(m["type"], constants.DEFAULT_NAVIGATION_STEP_ICON) maneuvers.append(TurnByTurnPoint(poly[m['begin_shape_index']][0], poly[m['begin_shape_index']][1], message=m['instruction'], icon=icon_id) ) route.extend(poly) turns.extend(maneuvers) way = cls(route) way.duration = result['trip']['summary']['time'] way._setLength(result['trip']["summary"]["length"]) way.add_message_points(turns) return way else: return None
def from_osm_scout_json(cls, result): """Convert OSM Scout Server routing result JSON to a way. :param result: OSM Scout Server processed JSON routing result """ if result: routePoints = [] # get route as (lat, lon, None) route_lle_tuples = list( itertools.zip_longest(result["lat"], result["lng"], [None])) # create a way from the lle tuples way = cls(route_lle_tuples) way._set_duration(result["summary"]["time"]) way._set_length(result["summary"]["length"]) # get turns from the json result turns = [] for maneuver in result.get("maneuvers", []): lat = maneuver["lat"] lon = maneuver["lng"] description = maneuver.get("verbal_pre_transition_instruction", "") turns.append(TurnByTurnPoint(lat, lon, message=description)) way.add_message_points(turns) return way else: return None
def from_google_directions_result(cls, gd_result): """Convert Google Directions result to a way object. :param gd_result: Google Directions result """ leg = gd_result['routes'][0]['legs'][0] steps = leg['steps'] points = decode_polyline( gd_result['routes'][0]['overview_polyline']['points']) # length of the route can computed from its metadata if 'distance' in leg: # this field might not be present m_length = leg['distance']['value'] else: m_length = None # the route also contains the expected duration in seconds if 'duration' in leg: # this field might not be present s_duration = leg['duration']['value'] else: s_duration = None way = cls(points) way._set_length(m_length) way._set_duration(s_duration) message_points = [] m_distance_from_start = 0 for step in steps: # TODO: abbreviation filtering message = step['html_instructions'] # as you can see, for some reason, # the coordinates in Google Directions steps are reversed: lat = step['start_location']['lat'] lon = step['start_location']['lng'] #TODO: end location ? point = TurnByTurnPoint(lat, lon, message=message) point.distance_from_start = m_distance_from_start # store point to temporary list message_points.append(point) # update distance for next point mDistanceFromLast = step["distance"]['value'] m_distance_from_start = m_distance_from_start + mDistanceFromLast way.add_message_points(message_points) return way
def process_and_save_directions(self, route): """process and save directions""" # apply filters route = self.filter_directions(route) # add a fake destination step, so there is a "destination reached" message if route.point_count > 0: (lat, lon) = route.get_point_by_index(-1).getLL() dest_step = TurnByTurnPoint(lat, lon) dest_step.ssml_message = '<p xml:lang="en">you <b>should</b> be near the destination</p>' dest_step.description = 'you <b>should</b> be near the destination' dest_step.distance_from_start = route.length # TODO: make this multilingual # add it to the end of the message point list route.add_message_point(dest_step) # save self._directions = route
def from_google_directions_result(cls, gResult): """Convert Google Directions result to a way object""" leg = gResult['routes'][0]['legs'][0] steps = leg['steps'] points = decode_polyline(gResult['routes'][0]['overview_polyline']['points']) # length of the route can computed from its metadata if 'distance' in leg: # this field might not be present mLength = leg['distance']['value'] else: mLength = None # the route also contains the expected duration in seconds if 'duration' in leg: # this field might not be present sDuration = leg['duration']['value'] else: sDuration = None way = cls(points) way._setLength(mLength) way.duration = sDuration messagePoints = [] mDistanceFromStart = 0 for step in steps: # TODO: abbreviation filtering message = step['html_instructions'] # as you can see, for some reason, # the coordinates in Google Directions steps are reversed: lat = step['start_location']['lat'] lon = step['start_location']['lng'] #TODO: end location ? point = TurnByTurnPoint(lat, lon, message=message) point.distanceFromStart = mDistanceFromStart # store point to temporary list messagePoints.append(point) # update distance for next point mDistanceFromLast = step["distance"]['value'] mDistanceFromStart = mDistanceFromStart + mDistanceFromLast way.add_message_points(messagePoints) return way
def detect_monav_turns(result): """Go through the edges and try to detect turns, return a list of RoutingPoints for the turns. :param result: a Monav offline routing result :type result: Monav offline routing result object :returns: list of RoutingPoints for turns :rtype: list of RoutingPoint instances """ # How to get the corresponding nodes for edges # -> edges are ordered and contain the n_segments property # -> n_segments describes from how many segments the given edge consists # -> by counting n_segments for subsequent edges, we get the node id # EXAMPLE: # a route with 2 edges, 3 segments each # -> firs point has id 0 # -> the last point od the first edge has id 3 # -> the last point of the route has id 6 turns = [] edges = result.edges nodes = result.nodes names = result.edge_names # need at least two edges for any meaningful routing if len(edges) >= 2: lastEdge = edges[0] nodeId = 0 maxNodeId = len(nodes) - 1 for edge in edges[1:]: nodeId += lastEdge.n_segments edgeId = edge.type_id nameId = edge.name_id name = names[nameId] # if lastEdge.branching_possible: if True: # looks like branching possible is not needed # turn directions are actually needed only # when there are some other roads to to turn to lastName = names[lastEdge.name_id] if lastEdge.type_id != edgeId or lastName != name: # if route type or name changes, it might be a turn node = nodes[nodeId] if nodeId <= maxNodeId: prevNode = nodes[nodeId - 1] nextNode = nodes[nodeId + 1] # NOTE: if the turn consists # from many segments, taking more points into # account might be needed first = prevNode.latitude, prevNode.longitude middle = node.latitude, node.longitude last = nextNode.latitude, nextNode.longitude angle = geo.turnAngle(first, middle, last) turnDescription = _get_turn_description(angle, name=name) # get the corresponding node turns.append( TurnByTurnPoint(node.latitude, node.longitude, message=turnDescription)) # DEBUG # turns.append(TurnByTurnPoint(prevNode.latitude, prevNode.longitude, message="prev")) # turns.append(TurnByTurnPoint(nextNode.latitude, nextNode.longitude, message="next")) lastEdge = edge return turns