refactor and improve room point generator
This commit is contained in:
parent
211ef767db
commit
87eb8dc380
3 changed files with 113 additions and 49 deletions
|
@ -1,17 +1,12 @@
|
|||
import os
|
||||
from math import atan2, pi, degrees
|
||||
|
||||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
from django.conf import settings
|
||||
from kombu.utils import cached_property
|
||||
from shapely.geometry import JOIN_STYLE
|
||||
from shapely.geometry import LineString
|
||||
from shapely.geometry import MultiPolygon
|
||||
from shapely.geometry import Point
|
||||
from shapely.geometry import Polygon
|
||||
from django.utils.functional import cached_property
|
||||
from PIL import Image, ImageDraw
|
||||
from shapely.geometry import JOIN_STYLE, LineString, Polygon
|
||||
|
||||
from c3nav.mapdata.models import Level
|
||||
from c3nav.routing.utils import get_coords_angles, polygon_to_mpl_path
|
||||
|
||||
|
||||
class GraphLevel():
|
||||
|
@ -63,38 +58,9 @@ class GraphRoom():
|
|||
self.geometry = geometry
|
||||
self.points = []
|
||||
|
||||
def cleanup_coords(self, coords):
|
||||
result = []
|
||||
last_coord = coords[-1]
|
||||
for coord in coords:
|
||||
if ((coord[0] - last_coord[0]) ** 2 + (coord[1] - last_coord[1]) ** 2) ** 0.5 >= 0.01:
|
||||
result.append(coord)
|
||||
last_coord = coord
|
||||
return result
|
||||
self.clear_geometry = geometry.buffer(-0.3, join_style=JOIN_STYLE.mitre)
|
||||
|
||||
def coord_angle(self, coord1, coord2):
|
||||
return degrees(atan2(-(coord2[1] - coord1[1]), coord2[0] - coord1[0])) % 360
|
||||
|
||||
def split_coords_by_angle(self, geom):
|
||||
coords = list(self.cleanup_coords(geom.coords))
|
||||
last_coords = coords[-2:]
|
||||
last_angle = self.coord_angle(last_coords[-2], last_coords[-1])
|
||||
left = []
|
||||
right = []
|
||||
for coord in coords:
|
||||
angle = self.coord_angle(last_coords[-1], coord)
|
||||
angle_diff = (last_angle-angle) % 360
|
||||
if angle_diff < 180:
|
||||
left.append(last_coords[-1])
|
||||
else:
|
||||
right.append(last_coords[-1])
|
||||
last_coords.append(coord)
|
||||
last_angle = angle
|
||||
|
||||
if not geom.is_ccw:
|
||||
left, right = right, left
|
||||
|
||||
return left, right
|
||||
self.mpl_path = polygon_to_mpl_path(geometry)
|
||||
|
||||
def create_points(self):
|
||||
original_geometry = self.geometry
|
||||
|
@ -109,14 +75,41 @@ class GraphRoom():
|
|||
polygons = geometry.geoms
|
||||
|
||||
for polygon in polygons:
|
||||
left, right = self.split_coords_by_angle(polygon.exterior)
|
||||
for x, y in right:
|
||||
self.points.append(GraphPoint(self, x, y))
|
||||
self._add_ring(polygon.exterior, want_left=False)
|
||||
|
||||
for interior in polygon.interiors:
|
||||
left, right = self.split_coords_by_angle(interior)
|
||||
for x, y in left:
|
||||
self.points.append(GraphPoint(self, x, y))
|
||||
self._add_ring(interior, want_left=True)
|
||||
|
||||
def _add_ring(self, geom, want_left):
|
||||
"""
|
||||
add the points of a ring, but only those that have a specific direction change.
|
||||
additionally removes unneeded points if the neighbors can be connected in self.clear_geometry
|
||||
:param geom: LinearRing
|
||||
:param want_left: True if the direction has to be left, False if it has to be right
|
||||
"""
|
||||
coords = []
|
||||
skipped = False
|
||||
can_delete_last = False
|
||||
for coord, is_left in get_coords_angles(geom):
|
||||
if is_left != want_left:
|
||||
skipped = True
|
||||
continue
|
||||
|
||||
if not skipped and can_delete_last and len(coords) >= 2:
|
||||
if LineString((coords[-2], coord)).within(self.clear_geometry):
|
||||
coords[-1] = coord
|
||||
continue
|
||||
|
||||
coords.append(coord)
|
||||
can_delete_last = not skipped
|
||||
skipped = False
|
||||
|
||||
if not skipped and can_delete_last and len(coords) >= 3:
|
||||
if LineString((coords[-2], coords[0])).within(self.clear_geometry):
|
||||
coords.pop()
|
||||
|
||||
for coord in coords:
|
||||
self.points.append(GraphPoint(self, *coord))
|
||||
|
||||
|
||||
class GraphPoint():
|
||||
|
@ -143,5 +136,3 @@ class Graph():
|
|||
for level in self.levels.values():
|
||||
level.build()
|
||||
level.draw_png()
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
|
||||
from c3nav.mapdata.render import render_all_levels
|
||||
from c3nav.routing.graph import Graph
|
||||
|
||||
|
||||
|
|
74
src/c3nav/routing/utils.py
Normal file
74
src/c3nav/routing/utils.py
Normal file
|
@ -0,0 +1,74 @@
|
|||
from math import atan2, degrees
|
||||
|
||||
import numpy as np
|
||||
from matplotlib.path import Path
|
||||
|
||||
|
||||
def cleanup_coords(coords):
|
||||
"""
|
||||
remove coordinates that are closer than 0.01 (1cm)
|
||||
:param coords: list of (x, y) coordinates
|
||||
:return: list of (x, y) coordinates
|
||||
"""
|
||||
result = []
|
||||
last_coord = coords[-1]
|
||||
for coord in coords:
|
||||
if ((coord[0] - last_coord[0]) ** 2 + (coord[1] - last_coord[1]) ** 2) ** 0.5 >= 0.01:
|
||||
result.append(coord)
|
||||
last_coord = coord
|
||||
return result
|
||||
|
||||
|
||||
def coord_angle(coord1, coord2):
|
||||
"""
|
||||
calculate angle in degrees from coord1 to coord2
|
||||
:param coord1: (x, y) coordinate
|
||||
:param coord2: (x, y) coordinate
|
||||
:return: angle in degrees
|
||||
"""
|
||||
return degrees(atan2(-(coord2[1] - coord1[1]), coord2[0] - coord1[0])) % 360
|
||||
|
||||
|
||||
def get_coords_angles(geom):
|
||||
"""
|
||||
inspects all coordinates of a LinearRing counterclockwise and checks if they are a left or a right turn.
|
||||
:param geom: LinearRing
|
||||
:rtype: a list of ((x, y), is_left) tuples
|
||||
"""
|
||||
coords = list(cleanup_coords(geom.coords))
|
||||
last_coords = coords[-2:]
|
||||
last_angle = coord_angle(last_coords[-2], last_coords[-1])
|
||||
result = []
|
||||
|
||||
invert = not geom.is_ccw
|
||||
|
||||
for coord in coords:
|
||||
angle = coord_angle(last_coords[-1], coord)
|
||||
angle_diff = (last_angle-angle) % 360
|
||||
result.append((last_coords[-1], (angle_diff < 180) ^ invert))
|
||||
last_coords.append(coord)
|
||||
last_angle = angle
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def polygon_to_mpl_path(polygon):
|
||||
"""
|
||||
convert a shapely Polygon to a matplotlib Path
|
||||
:param polygon: shapely Polygon
|
||||
:return: matplotlib Path
|
||||
"""
|
||||
vertices = []
|
||||
codes = []
|
||||
_mpl_add_linearring(polygon.exterior, vertices, codes)
|
||||
for interior in polygon.interiors:
|
||||
_mpl_add_linearring(interior, vertices, codes)
|
||||
return Path(np.array(vertices), codes=codes)
|
||||
|
||||
|
||||
def _mpl_add_linearring(linearring, vertices, codes):
|
||||
coords = list(linearring.coords)
|
||||
vertices.extend(coords)
|
||||
vertices.append(coords[0])
|
||||
codes.append(Path.MOVETO)
|
||||
codes.extend([Path.LINETO] * len(coords))
|
Loading…
Add table
Add a link
Reference in a new issue