fix more bugs caused by updates, especially using unwrap_geom
This commit is contained in:
parent
1837c49ab8
commit
9618d7304f
9 changed files with 59 additions and 38 deletions
|
@ -22,6 +22,7 @@ from c3nav.editor.views.base import etag_func
|
|||
from c3nav.mapdata.api import api_etag
|
||||
from c3nav.mapdata.models import Area, MapUpdate, Source
|
||||
from c3nav.mapdata.models.geometry.space import POI
|
||||
from c3nav.mapdata.utils.geometry import unwrap_geom
|
||||
from c3nav.mapdata.utils.user import can_access_editor
|
||||
|
||||
|
||||
|
@ -69,7 +70,7 @@ class EditorViewSet(EditorViewSetMixin, ViewSet):
|
|||
@staticmethod
|
||||
def _get_level_geometries(level):
|
||||
buildings = level.buildings.all()
|
||||
buildings_geom = unary_union([building.geometry.wrapped_geom for building in buildings])
|
||||
buildings_geom = unary_union([unwrap_geom(building.geometry) for building in buildings])
|
||||
spaces = {space.pk: space for space in level.spaces.all()}
|
||||
holes_geom = []
|
||||
for space in spaces.values():
|
||||
|
@ -77,9 +78,9 @@ class EditorViewSet(EditorViewSetMixin, ViewSet):
|
|||
space.geometry = space.geometry.difference(buildings_geom)
|
||||
columns = [column.geometry for column in space.columns.all()]
|
||||
if columns:
|
||||
columns_geom = unary_union([column.geometry.wrapped_geom for column in space.columns.all()])
|
||||
columns_geom = unary_union([unwrap_geom(column.geometry) for column in space.columns.all()])
|
||||
space.geometry = space.geometry.difference(columns_geom)
|
||||
holes = [hole.geometry.wrapped_geom for hole in space.holes.all()]
|
||||
holes = [unwrap_geom(hole.geometry) for hole in space.holes.all()]
|
||||
if holes:
|
||||
space_holes_geom = unary_union(holes)
|
||||
holes_geom.append(space_holes_geom.intersection(space.geometry))
|
||||
|
@ -221,7 +222,10 @@ class EditorViewSet(EditorViewSetMixin, ViewSet):
|
|||
if request.user_permissions.can_access_base_mapdata:
|
||||
doors = [door for door in level.doors.filter(Door.q_for_request(request)).all()
|
||||
if door.geometry.intersects(space.geometry)]
|
||||
doors_space_geom = unary_union([door.geometry for door in doors]+[space.geometry])
|
||||
doors_space_geom = unary_union(
|
||||
[unwrap_geom(door.geometry) for door in doors] +
|
||||
[unwrap_geom(space.geometry)]
|
||||
)
|
||||
|
||||
levels, levels_on_top, levels_under = self._get_levels_pk(request, level.primary_level)
|
||||
if level.on_top_of_id is not None:
|
||||
|
|
|
@ -41,7 +41,7 @@ class EditorFormBase(I18nModelFormMixin, ModelForm):
|
|||
# hide geometry widget
|
||||
self.fields['geometry'].widget = HiddenInput()
|
||||
if not creating:
|
||||
self.initial['geometry'] = json.dumps(mapping(self.instance.geometry), separators=(',', ':'))
|
||||
self.initial['geometry'] = mapping(self.instance.geometry)
|
||||
|
||||
if self._meta.model.__name__ == 'Source' and self.request.user.is_superuser:
|
||||
Source = self.request.changeset.wrap_model('Source')
|
||||
|
|
|
@ -55,6 +55,9 @@ class GeometryField(models.JSONField):
|
|||
def to_python(self, value):
|
||||
if value is None or value == '':
|
||||
return None
|
||||
if isinstance(value, str):
|
||||
# todo: this is all too complex, why do we need this?
|
||||
value = json.loads(value)
|
||||
try:
|
||||
geometry = shape(value)
|
||||
except Exception:
|
||||
|
|
|
@ -9,7 +9,7 @@ from shapely.geometry.base import BaseGeometry
|
|||
from shapely.ops import unary_union
|
||||
|
||||
from c3nav.mapdata.models.base import SerializableMixin
|
||||
from c3nav.mapdata.utils.geometry import assert_multipolygon, good_representative_point, smart_mapping
|
||||
from c3nav.mapdata.utils.geometry import assert_multipolygon, good_representative_point, smart_mapping, unwrap_geom
|
||||
from c3nav.mapdata.utils.json import format_geojson
|
||||
|
||||
geometry_affecting_fields = ('height', 'width', 'access_restriction')
|
||||
|
@ -119,11 +119,11 @@ class GeometryMixin(SerializableMixin):
|
|||
return True
|
||||
if self.geometry is self.orig_geometry:
|
||||
return False
|
||||
if not self.geometry.equals_exact(self.orig_geometry, 0.05):
|
||||
if not self.geometry.equals_exact(unwrap_geom(self.orig_geometry), 0.05):
|
||||
return True
|
||||
field = self._meta.get_field('geometry')
|
||||
rounded = field.to_python(field.get_prep_value(self.geometry))
|
||||
if not rounded.equals_exact(self.orig_geometry, 0.005):
|
||||
if not rounded.equals_exact(unwrap_geom(self.orig_geometry), 0.005):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -132,7 +132,7 @@ class GeometryMixin(SerializableMixin):
|
|||
new_geometry = field.get_final_value(self.geometry)
|
||||
if self.orig_geometry is None:
|
||||
return new_geometry
|
||||
difference = new_geometry.symmetric_difference(self.orig_geometry)
|
||||
difference = new_geometry.symmetric_difference(unwrap_geom(self.orig_geometry))
|
||||
if self._meta.get_field('geometry').geomtype in ('polygon', 'multipolygon'):
|
||||
difference = unary_union(assert_multipolygon(difference))
|
||||
return difference
|
||||
|
|
|
@ -26,7 +26,7 @@ from c3nav.mapdata.models.geometry.base import GeometryMixin
|
|||
from c3nav.mapdata.models.locations import SpecificLocation
|
||||
from c3nav.mapdata.utils.cache.changes import changed_geometries
|
||||
from c3nav.mapdata.utils.geometry import (assert_multilinestring, assert_multipolygon, clean_cut_polygon,
|
||||
cut_polygon_with_line)
|
||||
cut_polygon_with_line, unwrap_geom)
|
||||
|
||||
|
||||
class LevelGeometryMixin(GeometryMixin):
|
||||
|
@ -222,7 +222,7 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
|
|||
stairs = []
|
||||
|
||||
# collect all accessible areas on this level
|
||||
buildings_geom = unary_union(tuple(building.geometry.wrapped_geom for building in level.buildings.all()))
|
||||
buildings_geom = unary_union(tuple(unwrap_geom(building.geometry) for building in level.buildings.all()))
|
||||
for space in level.spaces.all():
|
||||
spaces[space.pk] = space
|
||||
space.orig_geometry = space.geometry
|
||||
|
@ -271,9 +271,9 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
|
|||
space_areas.update({space.pk: [] for space in level.spaces.all()})
|
||||
for area in areas:
|
||||
area.spaces = set()
|
||||
area.geometry_prep = prepared.prep(area.geometry)
|
||||
area.geometry_prep = prepared.prep(unwrap_geom(area.geometry))
|
||||
for space in level.spaces.all():
|
||||
if area.geometry_prep.intersects(space.geometry):
|
||||
if area.geometry_prep.intersects(unwrap_geom(space.geometry)):
|
||||
area.spaces.add(space.pk)
|
||||
space_areas[space.pk].append(area)
|
||||
|
||||
|
@ -470,9 +470,11 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
|
|||
for space in level.spaces.all():
|
||||
space.geometry = space.orig_geometry
|
||||
|
||||
buildings_geom = unary_union(tuple(b.geometry.wrapped_geom for b in level.buildings.all()))
|
||||
buildings_geom = unary_union(tuple(unwrap_geom(b.geometry) for b in level.buildings.all()))
|
||||
doors_geom = unary_union(tuple(d.geometry for d in level.doors.all()))
|
||||
space_geom = unary_union(tuple((s.geometry if not s.outside else s.geometry.difference(buildings_geom))
|
||||
space_geom = unary_union(tuple((unwrap_geom(s.geometry)
|
||||
if not s.outside
|
||||
else s.geometry.difference(buildings_geom))
|
||||
for s in level.spaces.all()))
|
||||
|
||||
# accessible area on this level is doors + spaces - holes
|
||||
|
@ -494,7 +496,7 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
|
|||
space_geom = space.geometry
|
||||
if space.outside:
|
||||
space_geom = space_geom.difference(buildings_geom)
|
||||
space_geom_prep = prepared.prep(space_geom)
|
||||
space_geom_prep = prepared.prep(unwrap_geom(space_geom))
|
||||
holes_geom = unary_union(tuple(h.geometry for h in space.holes.all()))
|
||||
|
||||
# remaining_space means remaining space (=obstacles) that still needs to be added to altitude areas
|
||||
|
@ -572,7 +574,7 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
|
|||
all_candidates = AltitudeArea.objects.select_related('level')
|
||||
for candidate in all_candidates:
|
||||
candidate.area = candidate.geometry.area
|
||||
candidate.geometry_prep = prepared.prep(candidate.geometry)
|
||||
candidate.geometry_prep = prepared.prep(unwrap_geom(candidate.geometry))
|
||||
all_candidates = sorted(all_candidates, key=attrgetter('area'), reverse=True)
|
||||
|
||||
num_modified = 0
|
||||
|
@ -606,7 +608,7 @@ class AltitudeArea(LevelGeometryMixin, models.Model):
|
|||
num_deleted += 1
|
||||
continue
|
||||
|
||||
if not field.get_final_value(new_area.geometry).equals_exact(candidate.geometry, 0.00001):
|
||||
if not field.get_final_value(new_area.geometry).equals_exact(unwrap_geom(candidate.geometry), 0.00001):
|
||||
num_modified += 1
|
||||
|
||||
candidate.geometry = new_area.geometry
|
||||
|
|
|
@ -13,7 +13,7 @@ from c3nav.mapdata.render.geometry.altitudearea import AltitudeAreaGeometries
|
|||
from c3nav.mapdata.render.geometry.hybrid import HybridGeometry
|
||||
from c3nav.mapdata.render.geometry.mesh import Mesh
|
||||
from c3nav.mapdata.utils.cache import AccessRestrictionAffected
|
||||
from c3nav.mapdata.utils.geometry import get_rings
|
||||
from c3nav.mapdata.utils.geometry import get_rings, unwrap_geom
|
||||
from c3nav.mapdata.utils.mesh import triangulate_rings
|
||||
|
||||
empty_geometry_collection = GeometryCollection()
|
||||
|
@ -65,7 +65,7 @@ class LevelGeometries:
|
|||
@classmethod
|
||||
def build_for_level(cls, level, altitudeareas_above):
|
||||
geoms = LevelGeometries()
|
||||
buildings_geom = unary_union([b.geometry.wrapped_geom for b in level.buildings.all()])
|
||||
buildings_geom = unary_union([unwrap_geom(b.geometry) for b in level.buildings.all()])
|
||||
geoms.buildings = buildings_geom
|
||||
buildings_geom_prep = prepared.prep(buildings_geom)
|
||||
|
||||
|
@ -89,10 +89,10 @@ class LevelGeometries:
|
|||
space.holes_geom = empty_geometry_collection
|
||||
space.walkable_geom = space.geometry
|
||||
|
||||
spaces_geom = unary_union([s.geometry for s in level.spaces.all()])
|
||||
doors_geom = unary_union([d.geometry for d in level.doors.all()])
|
||||
spaces_geom = unary_union([unwrap_geom(s.geometry) for s in level.spaces.all()])
|
||||
doors_geom = unary_union([unwrap_geom(d.geometry) for d in level.doors.all()])
|
||||
doors_geom = doors_geom.intersection(buildings_geom)
|
||||
walkable_spaces_geom = unary_union([s.walkable_geom for s in level.spaces.all()])
|
||||
walkable_spaces_geom = unary_union([unwrap_geom(s.walkable_geom) for s in level.spaces.all()])
|
||||
geoms.doors = doors_geom.difference(walkable_spaces_geom)
|
||||
if level.on_top_of_id is None:
|
||||
geoms.holes = unary_union([s.holes_geom for s in level.spaces.all()])
|
||||
|
@ -127,7 +127,9 @@ class LevelGeometries:
|
|||
buffered.difference(buildings_geom)
|
||||
)
|
||||
|
||||
colors.setdefault(space.get_color_sorted(), {}).setdefault(access_restriction, []).append(space.geometry)
|
||||
colors.setdefault(space.get_color_sorted(), {}).setdefault(access_restriction, []).append(
|
||||
unwrap_geom(space.geometry)
|
||||
)
|
||||
|
||||
for area in space.areas.all():
|
||||
access_restriction = area.access_restriction_id or space.access_restriction_id
|
||||
|
@ -166,7 +168,9 @@ class LevelGeometries:
|
|||
|
||||
geoms.ramps.extend(ramp.geometry for ramp in space.ramps.all())
|
||||
|
||||
heightareas.setdefault(int((space.height or level.default_height)*1000), []).append(space.geometry)
|
||||
heightareas.setdefault(int((space.height or level.default_height)*1000), []).append(
|
||||
unwrap_geom(space.geometry)
|
||||
)
|
||||
colors.pop(None, None)
|
||||
|
||||
# merge ground colors
|
||||
|
@ -178,8 +182,8 @@ class LevelGeometries:
|
|||
|
||||
# add altitudegroup geometries and split ground colors into them
|
||||
for altitudearea in level.altitudeareas.all():
|
||||
altitudearea_prep = prepared.prep(altitudearea.geometry)
|
||||
altitudearea_colors = {color: {access_restriction: area.intersection(altitudearea.geometry)
|
||||
altitudearea_prep = prepared.prep(unwrap_geom(altitudearea.geometry))
|
||||
altitudearea_colors = {color: {access_restriction: area.intersection(unwrap_geom(altitudearea.geometry))
|
||||
for access_restriction, area in areas.items()
|
||||
if altitudearea_prep.intersects(area)}
|
||||
for color, areas in colors.items()}
|
||||
|
|
|
@ -16,7 +16,7 @@ from c3nav.mapdata.models import Level, MapUpdate, Source
|
|||
from c3nav.mapdata.render.geometry import AltitudeAreaGeometries, LevelGeometries
|
||||
from c3nav.mapdata.utils.cache import AccessRestrictionAffected, MapHistory
|
||||
from c3nav.mapdata.utils.cache.package import CachePackage
|
||||
from c3nav.mapdata.utils.geometry import get_rings
|
||||
from c3nav.mapdata.utils.geometry import get_rings, unwrap_geom
|
||||
|
||||
empty_geometry_collection = GeometryCollection()
|
||||
|
||||
|
@ -24,7 +24,7 @@ empty_geometry_collection = GeometryCollection()
|
|||
class Cropper:
|
||||
def __init__(self, geometry=None):
|
||||
self.geometry = geometry
|
||||
self.geometry_prep = None if geometry is None else prepared.prep(geometry)
|
||||
self.geometry_prep = None if geometry is None else prepared.prep(unwrap_geom(geometry))
|
||||
|
||||
def intersection(self, other):
|
||||
if self.geometry is None:
|
||||
|
@ -179,7 +179,7 @@ class LevelRenderData:
|
|||
) if not geom.is_empty)
|
||||
|
||||
for altitudearea in old_geoms.altitudeareas:
|
||||
new_geometry = crop_to.intersection(altitudearea.geometry)
|
||||
new_geometry = crop_to.intersection(unwrap_geom(altitudearea.geometry))
|
||||
if new_geometry.is_empty:
|
||||
continue
|
||||
new_geometry_prep = prepared.prep(new_geometry)
|
||||
|
|
|
@ -40,10 +40,18 @@ class WrappedGeometry():
|
|||
|
||||
@cached_property
|
||||
def wrapped_geom(self):
|
||||
if not self.wrapped_geojson['coordinates']:
|
||||
if not self.wrapped_geojson or not self.wrapped_geojson['coordinates']:
|
||||
return GeometryCollection()
|
||||
return shapely_shape(self.wrapped_geojson)
|
||||
|
||||
def __getstate__(self):
|
||||
self.picklable = True
|
||||
# make sure geometry is cached
|
||||
if self.wrapped_geojson:
|
||||
# noinspection PyStatementEffect
|
||||
self.wrapped_geom
|
||||
super().__getstate__()
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in ('__getstate__'):
|
||||
self.picklable = True
|
||||
|
@ -64,8 +72,8 @@ class WrappedGeometry():
|
|||
return result
|
||||
|
||||
|
||||
def unwrap_geometry(geometry):
|
||||
return getattr(geometry, 'geom', geometry)
|
||||
def unwrap_geom(geometry):
|
||||
return geometry.wrapped_geom if isinstance(geometry, WrappedGeometry) else geometry
|
||||
|
||||
|
||||
def smart_mapping(geometry):
|
||||
|
|
|
@ -20,7 +20,7 @@ from shapely.ops import unary_union
|
|||
from c3nav.mapdata.models import AltitudeArea, Area, GraphEdge, Level, LocationGroup, MapUpdate, Space, WayType
|
||||
from c3nav.mapdata.models.geometry.space import POI, CrossDescription, LeaveDescription
|
||||
from c3nav.mapdata.models.locations import CustomLocationProxyMixin
|
||||
from c3nav.mapdata.utils.geometry import assert_multipolygon, get_rings, good_representative_point
|
||||
from c3nav.mapdata.utils.geometry import assert_multipolygon, get_rings, good_representative_point, unwrap_geom
|
||||
from c3nav.mapdata.utils.locations import CustomLocation
|
||||
from c3nav.routing.exceptions import LocationUnreachable, NoRouteFound, NotYetRoutable
|
||||
from c3nav.routing.route import Route
|
||||
|
@ -63,7 +63,7 @@ class Router:
|
|||
restrictions = {}
|
||||
nodes = deque()
|
||||
for level in levels_query:
|
||||
buildings_geom = unary_union(tuple(building.geometry.wrapped_geom for building in level.buildings.all()))
|
||||
buildings_geom = unary_union(tuple(unwrap_geom(building.geometry) for building in level.buildings.all()))
|
||||
|
||||
nodes_before_count = len(nodes)
|
||||
|
||||
|
@ -122,9 +122,9 @@ class Router:
|
|||
space.areas.add(area.pk)
|
||||
|
||||
for area in level.altitudeareas.all():
|
||||
if not space.geometry_prep.intersects(area.geometry):
|
||||
if not space.geometry_prep.intersects(unwrap_geom(area.geometry)):
|
||||
continue
|
||||
for subgeom in assert_multipolygon(accessible_geom.intersection(area.geometry)):
|
||||
for subgeom in assert_multipolygon(accessible_geom.intersection(unwrap_geom(area.geometry))):
|
||||
if subgeom.is_empty:
|
||||
continue
|
||||
area_clear_geom = unary_union(tuple(get_rings(subgeom.difference(obstacles_geom))))
|
||||
|
@ -511,7 +511,7 @@ class BaseRouterProxy:
|
|||
|
||||
@cached_property
|
||||
def geometry_prep(self):
|
||||
return prepared.prep(self.src.geometry)
|
||||
return prepared.prep(unwrap_geom(self.src.geometry))
|
||||
|
||||
def __getstate__(self):
|
||||
result = self.__dict__.copy()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue