def from_gtfs(feed, route_id, service_id, shape_id=None): """ A way to build a trip from GTFS data. Args: feed: the GTFS feed that contains the route route_id (int): the route_id for the route service_id (int): the service_id for the trip shape_id (int): the shape_id for the route Returns: dpd.driving.Trip: a Trip """ trips = feed.trips[(feed.trips["route_id"] == route_id) & (feed.trips["service_id"] == service_id)]["trip_id"] if not shape_id: shape_id = feed.trips[feed.trips["trip_id"] == trips.iloc[0]].shape_id.iloc[0] line = feed.build_geometry_by_shape([shape_id])[shape_id] aea_line = epsg4326_to_aea(line) @lru_cache( maxsize=128 ) # adds a little complexity, but reduces runtime by half :) def stop_id_to_distance_cached(stop_id): return trip._stop_id_to_distance(feed, aea_line, stop_id) trip = Trip() for trip_id in trips: d = feed.stop_times[feed.stop_times["trip_id"] == trip_id].copy() d["arrival_time_object"] = d.arrival_time.map( timestring_to_timeobject) d["departure_time_object"] = d.departure_time.map( timestring_to_timeobject) d["distance"] = d.stop_id.map(stop_id_to_distance_cached) for index, stop in d.iterrows(): trip.add_stop(name="", geometry=Point( feed.stops[feed.stops.stop_id == stop["stop_id"]]["stop_lon"], feed.stops[feed.stops.stop_id == stop["stop_id"]]["stop_lat"]), distance=stop["distance"], arrival_time=stop["arrival_time_object"], departure_time=stop["departure_time_object"]) return trip
def plot_schedule(feed, route_id, service_id, shape_id=None): trips = feed.trips[ (feed.trips["route_id"] == route_id) & (feed.trips["service_id"] == service_id) ]["trip_id"] if not shape_id: shape_id = feed.trips[feed.trips["trip_id"] == trips.iloc[0]].shape_id.iloc[0] line = feed.build_geometry_by_shape([shape_id])[shape_id] aea_line = epsg4326_to_aea(line) @lru_cache(maxsize=128) # adds a little complexity, but reduces runtime by half :) def stop_id_to_distance_cached(stop_id): return _stop_id_to_distance(feed, aea_line, stop_id) for trip_id in trips: d = feed.stop_times[feed.stop_times["trip_id"] == trip_id].copy() d["arrival_time_object"] = d.arrival_time.map(timestring_to_timeobject) d["departure_time_object"] = d.departure_time.map(timestring_to_timeobject) d["distance"] = d.stop_id.map(stop_id_to_distance_cached) plt.plot(d.arrival_time_object, d.distance / 1000) plt.xlabel("Time of Day") plt.ylabel("Distance on Route") plt.show()
def __init__( self, way, stops=[], tolerance=None, max_cant=0.1524, max_cant_deficiency=0.075, gague=1.435, ): """ Args: way (shapely.geometry.LineString): a LineString that contains the route the vehicle follows in EPSG:4326 stops ([{"geo": shapely.geometry.Point, "name": str}]): a list of "stops" that have a name and a geometry tolerance (int): the minimum distance (in meters) for which to keep neighboring stops so they do not create unrealistic curves max_cant: (float): the maximum allowable cant (in meters) max_cant_deficiency (float): the maximum allowable cant deficiency (in meters) gague (float): the track gague (in meters) Returns: dpd.driving.Route: a route table """ aea_way = epsg4326_to_aea(way) index = list(map(str, zip(way.xy[0], way.xy[1]))) geometry = list(map(Point, zip(aea_way.xy[0], aea_way.xy[1]))) super().__init__(geometry, columns=["geometry"], index=index) self.crs = "North America Albers Equal Area Conic" self["stop_name"] = "" for stop in stops: self.at[stop["geo"], "stop_name"] = stop["name"] self["after_geometry"] = self["geometry"].shift(-1) self["distance_to_next_point"] = self.apply( lambda row: None if not row["after_geometry"] else LineString( [row["geometry"], row["after_geometry"]]).length, axis=1, ) if ( tolerance ): # not perfect (e.g. it is possible we will compeltely remove a segment where there are many close points and we really wanted to keep one or two) self.drop( self[(self["distance_to_next_point"] < tolerance) & (self["stop_name"] == "")].index, inplace=True, ) self["after_geometry"] = self["geometry"].shift(-1) self["distance_to_next_point"] = self.apply( lambda row: None if not row["after_geometry"] else LineString( [row["geometry"], row["after_geometry"]]).length, axis=1, ) self["total_distance_to_this_point"] = ( self["distance_to_next_point"].shift(1).cumsum().fillna(0)) self["before_geometry"] = self["geometry"].shift(1) self["radius_of_curvature"] = self.apply( lambda row: 5000 if row["before_geometry"] is None or row[ "after_geometry"] is None else radius_of_curvature( (row["before_geometry"].x, row["before_geometry"].y), (row["geometry"].x, row["geometry"].y), (row["after_geometry"].x, row["after_geometry"].y), ), axis=1, ) self["speed_limit"] = self["radius_of_curvature"].apply( lambda radius_of_curvature: np.sqrt(9.8 * ( max_cant + max_cant_deficiency) * radius_of_curvature / gague)) self.max_cant: float = max_cant self.max_cant_deficiency: float = max_cant_deficiency self.gague: float = gague
def _stop_id_to_distance(feed, aea_line, stop_id): stop = feed.stops[feed.stops.stop_id == stop_id] stop_point = Point(stop.stop_lon, stop.stop_lat) stop_point_aea = epsg4326_to_aea(stop_point) distance = aea_line.project(stop_point_aea) return distance
def calculate_aea_geometry(self): """ Calculate an aea_geometry column. """ self["aea_geometry"] = self.geometry.map( lambda geometry: epsg4326_to_aea(geometry))
def test_epsg4326_to_aea(self): point = Point(-118.481389, 34.021944) aea_point = epsg4326_to_aea(point) self.assertEqual(aea_point.x, -1955052.147737653) self.assertEqual(aea_point.y, -468997.4237058063)