From 83ca6abd00b4890e0bf5569409c02d8f4a7bd051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Sat, 6 May 2017 17:24:09 +0200 Subject: [PATCH] convert mapdata.models.geometry into python package --- src/c3nav/mapdata/api.py | 5 +- src/c3nav/mapdata/models/__init__.py | 2 - src/c3nav/mapdata/models/base.py | 43 -------- src/c3nav/mapdata/models/geometry/__init__.py | 3 + .../models/{geometry.py => geometry/area.py} | 101 +++--------------- src/c3nav/mapdata/models/geometry/base.py | 47 ++++++++ src/c3nav/mapdata/models/geometry/level.py | 101 ++++++++++++++++++ src/c3nav/mapdata/models/locations.py | 2 +- 8 files changed, 167 insertions(+), 137 deletions(-) create mode 100644 src/c3nav/mapdata/models/geometry/__init__.py rename src/c3nav/mapdata/models/{geometry.py => geometry/area.py} (58%) create mode 100644 src/c3nav/mapdata/models/geometry/base.py create mode 100644 src/c3nav/mapdata/models/geometry/level.py diff --git a/src/c3nav/mapdata/api.py b/src/c3nav/mapdata/api.py index 9286f1b8..83893cd4 100644 --- a/src/c3nav/mapdata/api.py +++ b/src/c3nav/mapdata/api.py @@ -11,8 +11,9 @@ from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet from c3nav.access.apply import filter_arealocations_by_access, filter_queryset_by_access from c3nav.mapdata.lastupdate import get_last_mapdata_update -from c3nav.mapdata.models import GEOMETRY_FEATURE_TYPES, AreaLocation, Level, LocationGroup, Source -from c3nav.mapdata.models.geometry import Stair +from c3nav.mapdata.models import AreaLocation, Level, LocationGroup, Source +from c3nav.mapdata.models.geometry.area import Stair +from c3nav.mapdata.models.geometry.base import GEOMETRY_FEATURE_TYPES from c3nav.mapdata.search import get_location from c3nav.mapdata.serializers.main import LevelSerializer, SourceSerializer from c3nav.mapdata.utils.cache import CachedReadOnlyViewSetMixin, cache_mapdata_api_response, get_bssid_areas_cached diff --git a/src/c3nav/mapdata/models/__init__.py b/src/c3nav/mapdata/models/__init__.py index 85b8f14d..071ea822 100644 --- a/src/c3nav/mapdata/models/__init__.py +++ b/src/c3nav/mapdata/models/__init__.py @@ -1,5 +1,3 @@ from .level import Level # noqa from .source import Source # noqa -from .geometry import LevelFeature # noqa -from c3nav.mapdata.models.base import GEOMETRY_FEATURE_TYPES # noqa from .locations import AreaLocation, LocationGroup # noqa diff --git a/src/c3nav/mapdata/models/base.py b/src/c3nav/mapdata/models/base.py index f41b8062..0ebc1a6a 100644 --- a/src/c3nav/mapdata/models/base.py +++ b/src/c3nav/mapdata/models/base.py @@ -1,18 +1,11 @@ from collections import OrderedDict - from django.db import models from django.db.models.base import ModelBase from django.utils.translation import get_language -from shapely.geometry import Point, mapping -from c3nav.mapdata.fields import GeometryField from c3nav.mapdata.lastupdate import set_last_mapdata_update -from c3nav.mapdata.utils.json import format_geojson FEATURE_TYPES = OrderedDict() -GEOMETRY_FEATURE_TYPES = OrderedDict() -LEVEL_FEATURE_TYPES = OrderedDict() -AREA_FEATURE_TYPES = OrderedDict() class FeatureBase(ModelBase): @@ -20,13 +13,6 @@ class FeatureBase(ModelBase): cls = super().__new__(mcs, name, bases, attrs) if not cls._meta.abstract and name != 'Source': FEATURE_TYPES[name.lower()] = cls - if hasattr(cls, 'geometry'): - GEOMETRY_FEATURE_TYPES[name.lower()] = cls - if hasattr(cls, 'level'): - LEVEL_FEATURE_TYPES[name.lower()] = cls - if hasattr(cls, 'area'): - AREA_FEATURE_TYPES[name.lower()] = cls - return cls @@ -54,32 +40,3 @@ class Feature(models.Model, metaclass=FeatureBase): abstract = True -class GeometryFeature(Feature): - """ - A map feature with a geometry - """ - geometry = GeometryField() - - geomtype = None - - class Meta: - abstract = True - - def get_geojson_properties(self): - return OrderedDict(( - ('type', self.__class__.__name__.lower()), - ('id', self.id), - )) - - def to_geojson(self): - return OrderedDict(( - ('type', 'Feature'), - ('properties', self.get_geojson_properties()), - ('geometry', format_geojson(mapping(self.geometry), round=False)), - )) - - def get_shadow_geojson(self): - return None - - def contains(self, x, y): - return self.geometry.contains(Point(x, y)) diff --git a/src/c3nav/mapdata/models/geometry/__init__.py b/src/c3nav/mapdata/models/geometry/__init__.py new file mode 100644 index 00000000..b28b04f6 --- /dev/null +++ b/src/c3nav/mapdata/models/geometry/__init__.py @@ -0,0 +1,3 @@ + + + diff --git a/src/c3nav/mapdata/models/geometry.py b/src/c3nav/mapdata/models/geometry/area.py similarity index 58% rename from src/c3nav/mapdata/models/geometry.py rename to src/c3nav/mapdata/models/geometry/area.py index 58932771..e946a6cd 100644 --- a/src/c3nav/mapdata/models/geometry.py +++ b/src/c3nav/mapdata/models/geometry/area.py @@ -1,30 +1,24 @@ from collections import OrderedDict - from django.db import models from django.utils.translation import ugettext_lazy as _ -from shapely.geometry import CAP_STYLE, JOIN_STYLE -from shapely.geometry.geo import mapping +from shapely.geometry import JOIN_STYLE, CAP_STYLE, mapping -from c3nav.mapdata.models.base import GeometryFeature +from c3nav.mapdata.models.geometry.base import GeometryFeature, GeometryFeatureBase from c3nav.mapdata.utils.json import format_geojson -class LevelFeature(GeometryFeature): - """ - a map feature that has a geometry and belongs to a level - """ - level = models.ForeignKey('mapdata.Level', on_delete=models.CASCADE, verbose_name=_('level')) - - class Meta: - abstract = True - - def get_geojson_properties(self): - result = super().get_geojson_properties() - result['level'] = self.level.id - return result +AREA_FEATURE_TYPES = OrderedDict() -class AreaFeature(GeometryFeature): +class AreaFeatureBase(GeometryFeatureBase): + def __new__(mcs, name, bases, attrs): + cls = super().__new__(mcs, name, bases, attrs) + if not cls._meta.abstract: + AREA_FEATURE_TYPES[name.lower()] = cls + return cls + + +class AreaFeature(GeometryFeature, metaclass=AreaFeatureBase): """ a map feature that has a geometry and belongs to an area """ @@ -39,53 +33,6 @@ class AreaFeature(GeometryFeature): return result -class Building(LevelFeature): - """ - The outline of a building on a specific level - """ - geomtype = 'polygon' - - class Meta: - verbose_name = _('Building') - verbose_name_plural = _('Buildings') - default_related_name = 'buildings' - - -class Area(LevelFeature): - """ - An accessible area. Shouldn't overlap. - """ - geomtype = 'polygon' - - CATEGORIES = ( - ('', _('normal')), - ('stairs', _('stairs')), - ('escalator', _('escalator')), - ('elevator', _('elevator')), - ) - LAYERS = ( - ('', _('normal')), - ('upper', _('upper')), - ('lowerr', _('lower')), - ) - - public = models.BooleanField(verbose_name=_('public')) - category = models.CharField(verbose_name=_('category'), choices=CATEGORIES, max_length=16) - layer = models.CharField(verbose_name=_('layer'), choices=LAYERS, max_length=16) - - class Meta: - verbose_name = _('Area') - verbose_name_plural = _('Areas') - default_related_name = 'areas' - - def get_geojson_properties(self): - result = super().get_geojson_properties() - result['category'] = self.category - result['layer'] = self.layer - result['public'] = self.public - return result - - class StuffedArea(AreaFeature): """ A slow area with many tables or similar. Avoid it from routing by slowing it a bit down @@ -172,27 +119,3 @@ class LineObstacle(AreaFeature): result = super().get_geojson_properties() result['width'] = float(self.width) return result - - -class Door(LevelFeature): - """ - A connection between two rooms - """ - geomtype = 'polygon' - - class Meta: - verbose_name = _('Door') - verbose_name_plural = _('Doors') - default_related_name = 'doors' - - -class Hole(LevelFeature): - """ - A hole in the ground of a room, e.g. for stairs. - """ - geomtype = 'polygon' - - class Meta: - verbose_name = _('Hole') - verbose_name_plural = _('Holes') - default_related_name = 'holes' diff --git a/src/c3nav/mapdata/models/geometry/base.py b/src/c3nav/mapdata/models/geometry/base.py new file mode 100644 index 00000000..e858dc52 --- /dev/null +++ b/src/c3nav/mapdata/models/geometry/base.py @@ -0,0 +1,47 @@ +from collections import OrderedDict +from shapely.geometry import mapping, Point + +from c3nav.mapdata.fields import GeometryField +from c3nav.mapdata.models.base import Feature, FeatureBase +from c3nav.mapdata.utils.json import format_geojson + +GEOMETRY_FEATURE_TYPES = OrderedDict() + + +class GeometryFeatureBase(FeatureBase): + def __new__(mcs, name, bases, attrs): + cls = super().__new__(mcs, name, bases, attrs) + if not cls._meta.abstract: + GEOMETRY_FEATURE_TYPES[name.lower()] = cls + return cls + + +class GeometryFeature(Feature, metaclass=GeometryFeatureBase): + """ + A map feature with a geometry + """ + geometry = GeometryField() + + geomtype = None + + class Meta: + abstract = True + + def get_geojson_properties(self): + return OrderedDict(( + ('type', self.__class__.__name__.lower()), + ('id', self.id), + )) + + def to_geojson(self): + return OrderedDict(( + ('type', 'Feature'), + ('properties', self.get_geojson_properties()), + ('geometry', format_geojson(mapping(self.geometry), round=False)), + )) + + def get_shadow_geojson(self): + return None + + def contains(self, x, y): + return self.geometry.contains(Point(x, y)) diff --git a/src/c3nav/mapdata/models/geometry/level.py b/src/c3nav/mapdata/models/geometry/level.py new file mode 100644 index 00000000..52d9d8d0 --- /dev/null +++ b/src/c3nav/mapdata/models/geometry/level.py @@ -0,0 +1,101 @@ +from collections import OrderedDict +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from c3nav.mapdata.models.geometry.base import GeometryFeature, GeometryFeatureBase + +LEVEL_FEATURE_TYPES = OrderedDict() + + +class LevelFeatureBase(GeometryFeatureBase): + def __new__(mcs, name, bases, attrs): + cls = super().__new__(mcs, name, bases, attrs) + if not cls._meta.abstract: + LEVEL_FEATURE_TYPES[name.lower()] = cls + return cls + + +class LevelFeature(GeometryFeature, metaclass=LevelFeatureBase): + """ + a map feature that has a geometry and belongs to a level + """ + level = models.ForeignKey('mapdata.Level', on_delete=models.CASCADE, verbose_name=_('level')) + + class Meta: + abstract = True + + def get_geojson_properties(self): + result = super().get_geojson_properties() + result['level'] = self.level.id + return result + + +class Building(LevelFeature): + """ + The outline of a building on a specific level + """ + geomtype = 'polygon' + + class Meta: + verbose_name = _('Building') + verbose_name_plural = _('Buildings') + default_related_name = 'buildings' + + +class Area(LevelFeature): + """ + An accessible area. Shouldn't overlap. + """ + geomtype = 'polygon' + + CATEGORIES = ( + ('', _('normal')), + ('stairs', _('stairs')), + ('escalator', _('escalator')), + ('elevator', _('elevator')), + ) + LAYERS = ( + ('', _('normal')), + ('upper', _('upper')), + ('lowerr', _('lower')), + ) + + public = models.BooleanField(verbose_name=_('public')) + category = models.CharField(verbose_name=_('category'), choices=CATEGORIES, max_length=16) + layer = models.CharField(verbose_name=_('layer'), choices=LAYERS, max_length=16) + + class Meta: + verbose_name = _('Area') + verbose_name_plural = _('Areas') + default_related_name = 'areas' + + def get_geojson_properties(self): + result = super().get_geojson_properties() + result['category'] = self.category + result['layer'] = self.layer + result['public'] = self.public + return result + + +class Door(LevelFeature): + """ + A connection between two rooms + """ + geomtype = 'polygon' + + class Meta: + verbose_name = _('Door') + verbose_name_plural = _('Doors') + default_related_name = 'doors' + + +class Hole(LevelFeature): + """ + A hole in the ground of a room, e.g. for stairs. + """ + geomtype = 'polygon' + + class Meta: + verbose_name = _('Hole') + verbose_name_plural = _('Holes') + default_related_name = 'holes' diff --git a/src/c3nav/mapdata/models/locations.py b/src/c3nav/mapdata/models/locations.py index 9575ad43..0038ee2e 100644 --- a/src/c3nav/mapdata/models/locations.py +++ b/src/c3nav/mapdata/models/locations.py @@ -11,7 +11,7 @@ from c3nav.mapdata.fields import JSONField, validate_bssid_lines from c3nav.mapdata.lastupdate import get_last_mapdata_update from c3nav.mapdata.models import Level from c3nav.mapdata.models.base import Feature -from c3nav.mapdata.models.geometry import LevelFeature +from c3nav.mapdata.models.geometry.level import LevelFeature class Location: