first stuff for graph building – collecting points
This commit is contained in:
parent
ffd5c3fa70
commit
211ef767db
7 changed files with 164 additions and 0 deletions
|
@ -90,6 +90,10 @@ class LevelGeometries():
|
|||
def holes(self):
|
||||
return cascaded_union([holes.geometry for holes in self.level.holes.all()]).intersection(self.areas)
|
||||
|
||||
@cached_property
|
||||
def accessible(self):
|
||||
return self.areas.difference(self.holes).difference(self.obstacles)
|
||||
|
||||
@cached_property
|
||||
def buildings_with_holes(self):
|
||||
return self.buildings.difference(self.holes)
|
||||
|
|
0
src/c3nav/routing/__init__.py
Normal file
0
src/c3nav/routing/__init__.py
Normal file
147
src/c3nav/routing/graph.py
Normal file
147
src/c3nav/routing/graph.py
Normal file
|
@ -0,0 +1,147 @@
|
|||
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 c3nav.mapdata.models import Level
|
||||
|
||||
|
||||
class GraphLevel():
|
||||
def __init__(self, graph, level):
|
||||
self.graph = graph
|
||||
self.level = level
|
||||
self.rooms = []
|
||||
|
||||
def build(self):
|
||||
self.collect_rooms()
|
||||
self.create_points()
|
||||
|
||||
def collect_rooms(self):
|
||||
accessibles = self.level.geometries.accessible
|
||||
accessibles = [accessibles] if isinstance(accessibles, Polygon) else accessibles.geoms
|
||||
for geometry in accessibles:
|
||||
self.rooms.append(GraphRoom(self, geometry))
|
||||
|
||||
def create_points(self):
|
||||
for room in self.rooms:
|
||||
room.create_points()
|
||||
|
||||
def _ellipse_bbox(self, x, y, height):
|
||||
x *= settings.RENDER_SCALE
|
||||
y *= settings.RENDER_SCALE
|
||||
y = height-y
|
||||
return ((x - 2, y - 2), (x + 2, y + 2))
|
||||
|
||||
def draw_png(self):
|
||||
filename = os.path.join(settings.RENDER_ROOT, 'level-%s.png' % self.level.name)
|
||||
graph_filename = os.path.join(settings.RENDER_ROOT, 'level-%s-graph.png' % self.level.name)
|
||||
|
||||
im = Image.open(filename)
|
||||
height = im.size[1]
|
||||
draw = ImageDraw.Draw(im)
|
||||
i = 0
|
||||
for room in self.rooms:
|
||||
for point in room.points:
|
||||
i += 1
|
||||
draw.ellipse(self._ellipse_bbox(point.x, point.y, height), (255, 0, 0))
|
||||
print(i, 'points')
|
||||
|
||||
im.save(graph_filename)
|
||||
|
||||
|
||||
class GraphRoom():
|
||||
def __init__(self, level, geometry):
|
||||
self.level = level
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def create_points(self):
|
||||
original_geometry = self.geometry
|
||||
geometry = original_geometry.buffer(-0.6, join_style=JOIN_STYLE.mitre)
|
||||
|
||||
if geometry.is_empty:
|
||||
return
|
||||
|
||||
if isinstance(geometry, Polygon):
|
||||
polygons = [geometry]
|
||||
else:
|
||||
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))
|
||||
|
||||
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))
|
||||
|
||||
|
||||
class GraphPoint():
|
||||
def __init__(self, room, x, y):
|
||||
self.room = room
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
@cached_property
|
||||
def ellipse_bbox(self):
|
||||
x = self.x * settings.RENDER_SCALE
|
||||
y = self.y * settings.RENDER_SCALE
|
||||
return ((x-5, y-5), (x+5, y+5))
|
||||
|
||||
|
||||
class Graph():
|
||||
def __init__(self):
|
||||
self.levels = {}
|
||||
|
||||
def build(self):
|
||||
for level in Level.objects.all():
|
||||
self.levels[level.name] = GraphLevel(self, level)
|
||||
|
||||
for level in self.levels.values():
|
||||
level.build()
|
||||
level.draw_png()
|
||||
|
||||
|
0
src/c3nav/routing/management/__init__.py
Normal file
0
src/c3nav/routing/management/__init__.py
Normal file
0
src/c3nav/routing/management/commands/__init__.py
Normal file
0
src/c3nav/routing/management/commands/__init__.py
Normal file
12
src/c3nav/routing/management/commands/buildgraph.py
Normal file
12
src/c3nav/routing/management/commands/buildgraph.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
|
||||
from c3nav.mapdata.render import render_all_levels
|
||||
from c3nav.routing.graph import Graph
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'build the routing graph'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
graphbuilder = Graph()
|
||||
graphbuilder.build()
|
|
@ -145,6 +145,7 @@ INSTALLED_APPS = [
|
|||
'c3nav.api',
|
||||
'rest_framework',
|
||||
'c3nav.mapdata',
|
||||
'c3nav.routing',
|
||||
'c3nav.editor',
|
||||
'c3nav.control',
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue