Source code for plaza_preprocessing.optimizer.graphprocessor.spiderwebgraph

from math import ceil
from typing import List
from shapely.geometry import Point, LineString, Polygon
from plaza_preprocessing.optimizer import utils
from plaza_preprocessing.optimizer.graphprocessor.graphprocessor import GraphProcessor


[docs]class SpiderWebGraphProcessor(GraphProcessor): """ Process a plaza with a spider web graph """ def __init__(self, spacing_m, visibility_delta_m): self.spacing_m = spacing_m self.visibility_delta_m = visibility_delta_m
[docs] def create_graph_edges(self, plaza_geometry: Polygon, entry_points: List[Point]) -> List[LineString]: """ create a spiderwebgraph and connect edges to entry points """ if not plaza_geometry: raise ValueError("Plaza geometry not defined for spiderwebgraph processor") if not entry_points: raise ValueError("No entry points defined for spiderwebgraph processor") graph_edges = self._calc_spiderwebgraph(plaza_geometry) if not graph_edges: # no graph edges could be constructed return [] return self._connect_entry_points_with_graph(entry_points, graph_edges)
[docs] def optimize_lines(self, plaza_geometry: Polygon, lines: List[LineString], tolerance_m: float) -> List[LineString]: """ simplify lines to reduce amount of line points. Optimizations that fall outside of the plaza will be discarded """ tolerance = utils.meters_to_degrees(tolerance_m) return list(map(lambda line: self._get_simplified_visible_line(plaza_geometry, line, tolerance), lines))
def _get_simplified_visible_line(self, plaza_geometry: Polygon, line: LineString, tolerance): """ returns the simplified line if it's inside the plaza, the original line otherwise""" simplified_line = line.simplify(tolerance, preserve_topology=False) return simplified_line if utils.line_visible(plaza_geometry, simplified_line, self.visibility_delta_m) else line def _calc_spiderwebgraph(self, plaza_geometry): """ calculate spider web graph edges""" spacing = utils.meters_to_degrees(self.spacing_m) x_left, y_bottom, x_right, y_top = plaza_geometry.bounds # based on https://github.com/michaelminn/mmqgis rows = int(ceil((y_top - y_bottom) / spacing)) columns = int(ceil((x_right - x_left) / spacing)) graph_edges = [] for column in range(0, columns + 1): for row in range(0, rows + 1): x_1 = x_left + (column * spacing) x_2 = x_left + ((column + 1) * spacing) y_1 = y_bottom + (row * spacing) y_2 = y_bottom + ((row + 1) * spacing) top_left = (x_1, y_1) top_right = (x_2, y_1) bottom_left = (x_1, y_2) bottom_right = (x_2, y_2) # horizontal line if column < columns: h_line = self._get_spiderweb_intersection_line(plaza_geometry, top_left, top_right) if h_line: graph_edges.append(h_line) # vertical line if row < rows: v_line = self._get_spiderweb_intersection_line(plaza_geometry, top_left, bottom_left) if v_line: graph_edges.append(v_line) # diagonal line if row < rows and column < columns: d1_line = self._get_spiderweb_intersection_line(plaza_geometry, top_left, bottom_right) if d1_line: graph_edges.append(d1_line) d2_line = self._get_spiderweb_intersection_line(plaza_geometry, bottom_left, top_right) if d2_line: graph_edges.append(d2_line) return graph_edges def _get_spiderweb_intersection_line(self, plaza_geometry, start, end): """ returns a line that is completely inside the plaza, if possible """ line = LineString([start, end]) if not utils.line_visible(plaza_geometry, line, self.visibility_delta_m): return None return line def _connect_entry_points_with_graph(self, entry_points, graph_edges): connection_lines = [] for entry_point in entry_points: neighbor_line = utils.find_nearest_geometry(entry_point, graph_edges) target_point = min( neighbor_line.coords, key=lambda c: Point(c).distance(entry_point)) connection_line = (LineString([(entry_point.x, entry_point.y), target_point])) connection_lines.append(connection_line) graph_edges.extend(connection_lines) return graph_edges