Skip to content

Commit

Permalink
Merge pull request #11 from amauryval/dev_misc
Browse files Browse the repository at this point in the history
Dev misc
  • Loading branch information
amauryval authored Aug 31, 2020
2 parents 936c77f + b939eb0 commit c0e2bb3
Show file tree
Hide file tree
Showing 10 changed files with 528 additions and 132 deletions.
66 changes: 66 additions & 0 deletions example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,72 @@
")\n",
"show(my_shortest_path_map.figure)"
]
},
{
"cell_type": "markdown",
"source": [
"# Compute an isochrone based on a distance and display it"
],
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%% md\n"
}
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"%%time\n",
"source_node = poi_from_location_gdf[poi_from_location_gdf['topo_uuid'] == start_node_topo_uuid]\n",
"\n",
"trip_speed = 3 # km/h\n",
"\n",
"location_point = source_node.iloc[0][\"geometry\"]\n",
"isochrones_polygons_from_location, isochrones_lines_from_location = OsmGt.isochrone_distance_from_coordinates(\n",
" location_point,\n",
" [1000], # meters\n",
" trip_speed,\n",
" mode=\"pedestrian\"\n",
")\n",
"\n",
"layers_to_add = [\n",
" {\n",
" \"input_gdf\": isochrones_polygons_from_location,\n",
" \"legend\": \"isochrone from distance\",\n",
" \"fill_color\": \"red\",\n",
" \"line_color\": \"red\",\n",
" \"fill_alpha\": 0.7\n",
" },\n",
" {\n",
" \"input_gdf\": isochrones_lines_from_location,\n",
" \"legend\": \"path\",\n",
" \"color\": \"black\",\n",
" \"line_width\": 2\n",
" },\n",
" {\n",
" \"input_gdf\": source_node,\n",
" \"legend\": \"Source node\",\n",
" \"style\": \"circle\",\n",
" \"color\": \"orange\",\n",
" \"size\": 15\n",
" },\n",
"]\n",
"\n",
"my_shortest_path_map = EasyMapBokeh(\n",
" \"isochrone\",\n",
" layers=layers_to_add\n",
")\n",
"show(my_shortest_path_map.figure)\n"
],
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%%\n"
}
}
}
],
"metadata": {
Expand Down
364 changes: 266 additions & 98 deletions index.html

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions osmgt/compoments/roads.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def from_location(
location_name: str,
additionnal_nodes: Optional[gpd.GeoDataFrame],
mode: str,
interpolate_lines: bool = False
) -> None:
self._check_transport_mode(mode)
super().from_location(location_name)
Expand All @@ -56,14 +57,15 @@ def from_location(
request = self._from_location_name_query_builder(self._location_id, query)
raw_data = self._query_on_overpass_api(request)
self._output_data = self.__build_network_topology(
raw_data, additionnal_nodes, mode
raw_data, additionnal_nodes, mode, interpolate_lines
)

def from_bbox(
self,
bbox_value: Tuple[float, float, float, float],
additionnal_nodes: Optional[gpd.GeoDataFrame],
mode: str,
interpolate_lines: bool = False
) -> None:
self._check_transport_mode(mode)
super().from_bbox(bbox_value)
Expand All @@ -73,7 +75,7 @@ def from_bbox(
request = self._from_bbox_query_builder(self._bbox_value, query)
raw_data = self._query_on_overpass_api(request)
self._output_data = self.__build_network_topology(
raw_data, additionnal_nodes, mode
raw_data, additionnal_nodes, mode, interpolate_lines
)

def get_graph(self) -> GraphHelpers:
Expand Down Expand Up @@ -103,6 +105,7 @@ def __build_network_topology(
raw_data: List[Dict],
additionnal_nodes: Optional[gpd.GeoDataFrame],
mode: str,
interpolate_lines: bool
) -> List[Dict]:
if additionnal_nodes is not None:
additionnal_nodes = self._check_topology_field(additionnal_nodes)
Expand Down Expand Up @@ -135,6 +138,7 @@ def __build_network_topology(
self._TOPO_FIELD,
self._ID_OSM_FIELD,
mode,
interpolate_lines
).run()

return raw_data_topology_rebuild
Expand Down
22 changes: 13 additions & 9 deletions osmgt/geometry/geom_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def compute_wg84_line_length(input_geom: LineString) -> float:
return total_length


class Concave_hull:
class ConcaveHull:
# source: http://blog.thehumangeo.com/2014/05/12/drawing-boundaries-in-python/
__TOLERANCE_VALUE: float = 1.87

Expand Down Expand Up @@ -111,14 +111,18 @@ def _compute(self) -> None:
s = (a + b + c) / self.__SEMIPERIMETER_DIVISOR

# Area of triangle by Heron's formula
area = math.sqrt(s * (s - a) * (s - b) * (s - c))
circum_r = a * b * c / (self.__HERON_FORMULA_DIVISOR * area)

# Here's the radius filter.
if circum_r < 1.0 / self.__TOLERANCE_VALUE:
self.add_edge(coords, ia, ib)
self.add_edge(coords, ib, ic)
self.add_edge(coords, ic, ia)
delta = (s * (s - a) * (s - b) * (s - c))
if delta > 0:
area = math.sqrt(delta)
if area > 0:
circum_r = a * b * c / (self.__HERON_FORMULA_DIVISOR * area)

# Here's the radius filter.
if circum_r < 1.0 / self.__TOLERANCE_VALUE:
self.add_edge(coords, ia, ib)
self.add_edge(coords, ib, ic)
self.add_edge(coords, ic, ia)

multilinestring_built = MultiLineString(self._edge_points)
self._triangles: List = list(polygonize(multilinestring_built))

Expand Down
59 changes: 47 additions & 12 deletions osmgt/geometry/network_topology.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def __init__(
uuid_field: str,
original_field_id: str,
mode_post_processing: str,
improve_line_output: bool = False
) -> None:
"""
Expand All @@ -81,6 +82,7 @@ def __init__(

self._network_data = self._check_inputs(network_data)
self._mode_post_processing = mode_post_processing
self._improve_line_output = improve_line_output

self._additionnal_nodes = additionnal_nodes
if self._additionnal_nodes is None:
Expand Down Expand Up @@ -214,43 +216,76 @@ def mode_processing(self, input_feature):
if self._mode_post_processing == "vehicle":
# by default
new_forward_feature = self._direction_processing(input_feature, "forward")
new_elements.append(new_forward_feature)
new_elements.extend(new_forward_feature)
if input_feature.get("junction", None) in ["roundabout", "jughandle"]:
return new_elements

if input_feature.get(self.__ONEWAY_FIELD, None) != "yes":
new_backward_feature = self._direction_processing(
input_feature, "backward"
)
new_elements.append(new_backward_feature)
new_elements.extend(new_backward_feature)

elif self._mode_post_processing == "pedestrian":
# it's the default behavior in fact
# it's the default behavior

feature = self._direction_processing(input_feature)
new_elements.append(feature)
new_elements.extend(feature)

return new_elements

def _direction_processing(self, input_feature, direction=None):
def _direction_processing(self, input_feature: Dict, direction: Optional[str] = None):
new_features = []
input_feature_copy = dict(input_feature)

if self._improve_line_output:
new_coords = list(self._split_line(input_feature_copy, 3))
new_lines_coords = list(zip(new_coords, new_coords[1:]))
del input_feature_copy[self.__COORDINATES_FIELD]

for idx, sub_line_coords in enumerate(new_lines_coords):
new_features.append(
self.__proceed_direction_geom(direction, input_feature_copy, sub_line_coords, idx)
)
else:
new_coords = list(self._split_line(input_feature_copy, 1))
del input_feature_copy[self.__COORDINATES_FIELD]
new_features.append(
self.__proceed_direction_geom(direction, input_feature_copy, new_coords)
)

return new_features

def __proceed_direction_geom(self, direction, input_feature, sub_line_coords, idx=None):
feature = dict(input_feature)

if idx is not None:
idx = f"_{idx}"
else:
idx = ""

if direction == "backward":
new_linestring = LineString(feature[self.__COORDINATES_FIELD][::-1])
new_linestring = LineString(sub_line_coords[::-1])
elif direction in ["forward", None]:
new_linestring = LineString(feature[self.__COORDINATES_FIELD])
new_linestring = LineString(sub_line_coords)
else:
raise NetworkTopologyError(f"Direction issue: value '{direction}' found")
feature[self.__GEOMETRY_FIELD] = new_linestring

if direction is not None:
feature[self.__FIELD_ID] = f"{feature[self.__FIELD_ID]}_{direction}"
feature[self.__FIELD_ID] = f"{feature[self.__FIELD_ID]}{idx}_{direction}"
else:
feature[self.__FIELD_ID] = f"{feature[self.__FIELD_ID]}"
feature[self.__FIELD_ID] = f"{feature[self.__FIELD_ID]}{idx}"

del feature[self.__COORDINATES_FIELD]
return feature

def _split_line(self, feature, interpolation_level):
new_line_coords = interpolate_curve_based_on_original_points(
np.array(feature[self.__COORDINATES_FIELD]),
interpolation_level,
)
return new_line_coords

def _prepare_data(self):

self._network_data = {
Expand Down Expand Up @@ -365,7 +400,7 @@ def proceed_nodes_on_network(self, nearest_line_content):
self.__GEOMETRY_FIELD: connection,
self.__CLEANING_FILED_STATUS: self.__TOPOLOGY_TAG_ADDED,
self.__FIELD_ID: f"{self.__TOPOLOGY_TAG_ADDED}_{node_key}",
self._original_field_id: f"{self.__TOPOLOGY_TAG_ADDED}_{self.__TOPOLOGY_TAG_ADDED}_{node_key}",
self._original_field_id: f"{self.__TOPOLOGY_TAG_ADDED}_{node_key}",
}

return {
Expand Down Expand Up @@ -394,7 +429,7 @@ def _topology_builder(
middle_coordinates_values = self._insert_value(
middle_coordinates_values,
point_intersection,
tuple([point_intersection]), # TODO check type
tuple([point_intersection]),
)

middle_coordinates_values = self._insert_value(
Expand Down
28 changes: 27 additions & 1 deletion osmgt/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,33 @@ def isochrone_from_coordinates(
"""

isochrone_polygons_gdf, isochrone_lines_gdf = OsmGtIsochrone(
isochrones_times, trip_speed
trip_speed, isochrones_times
).from_location_point(coordinates, mode)

return isochrone_polygons_gdf, isochrone_lines_gdf

@staticmethod
def isochrone_distance_from_coordinates(
coordinates: Point,
distances: List,
trip_speed: float,
mode: str = "pedestrian",
) -> Tuple[gpd.GeoDataFrame, gpd.GeoDataFrame]:
"""
:param coordinates: location points
:type coordinates: shapely.geometry.Point
:param distances: distances (meters)
:type distances: list of int
:param trip_speed: trip speed in km/h
:type trip_speed: int
:param mode: the transport mode
:type mode: str, default 'pedestrian', one of : pedestrian, vehicle
:return: 2 geodataframe : isochrones polygons and isochrones lines (roads)
:rtype: tuple(geopandas.GeoDataFrame)
"""

isochrone_polygons_gdf, isochrone_lines_gdf = OsmGtIsochrone(
trip_speed, None, distances
).from_location_point(coordinates, mode)

return isochrone_polygons_gdf, isochrone_lines_gdf
Expand Down
Loading

0 comments on commit c0e2bb3

Please sign in to comment.