From 9e79ca74ae76856dce8f06de520ad2eaffd2611a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Mon, 8 May 2017 16:05:44 +0200 Subject: [PATCH] add geomtype to GeometryField and remove it from GeometryFeature --- src/c3nav/mapdata/fields.py | 18 +++++- .../migrations/0063_auto_20170508_1404.py | 61 +++++++++++++++++++ src/c3nav/mapdata/models/geometry/base.py | 5 -- src/c3nav/mapdata/models/geometry/section.py | 9 +-- src/c3nav/mapdata/models/geometry/space.py | 10 +-- src/c3nav/mapdata/models/locations.py | 5 +- 6 files changed, 90 insertions(+), 18 deletions(-) create mode 100644 src/c3nav/mapdata/migrations/0063_auto_20170508_1404.py diff --git a/src/c3nav/mapdata/fields.py b/src/c3nav/mapdata/fields.py index d64b9903..059f655b 100644 --- a/src/c3nav/mapdata/fields.py +++ b/src/c3nav/mapdata/fields.py @@ -5,7 +5,7 @@ from django.core.validators import RegexValidator from django.db import models from django.utils.translation import ugettext_lazy as _ from shapely import validation -from shapely.geometry import mapping, shape +from shapely.geometry import mapping, shape, Polygon, LineString from shapely.geometry.base import BaseGeometry from c3nav.mapdata.utils.geometry import clean_geometry @@ -26,6 +26,18 @@ def validate_geometry(geometry): class GeometryField(models.TextField): default_validators = [validate_geometry] + def __init__(self, geomtype=None, *args, **kwargs): + self.geomtype = geomtype + if geomtype not in (None, 'polygon', 'polyline'): + raise ValueError(_('GeometryField.geomtype has to be None, "polygon" or "polyline"')) + super().__init__(*args, **kwargs) + + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + if self.geomtype is not None: + kwargs['geomtype'] = self.geomtype + return name, path, args, kwargs + def from_db_value(self, value, expression, connection, context): if value is None: return value @@ -35,6 +47,10 @@ class GeometryField(models.TextField): return clean_geometry(shape(json.loads(value))) def get_prep_value(self, value): + if self.geomtype == 'polygon' and not isinstance(value, Polygon): + raise TypeError(_('Expected Polygon instance, got %s instead.') % repr(value)) + elif self.geomtype == 'polyline' and not isinstance(value, LineString): + raise TypeError(_('Expected LineString instance, got %s instead.') % repr(value)) return json.dumps(format_geojson(mapping(value))) diff --git a/src/c3nav/mapdata/migrations/0063_auto_20170508_1404.py b/src/c3nav/mapdata/migrations/0063_auto_20170508_1404.py new file mode 100644 index 00000000..aac59a8c --- /dev/null +++ b/src/c3nav/mapdata/migrations/0063_auto_20170508_1404.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2017-05-08 14:04 +from __future__ import unicode_literals + +import c3nav.mapdata.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('mapdata', '0062_auto_20170508_1400'), + ] + + operations = [ + migrations.AlterField( + model_name='arealocation', + name='geometry', + field=c3nav.mapdata.fields.GeometryField(geomtype='polygon'), + ), + migrations.AlterField( + model_name='building', + name='geometry', + field=c3nav.mapdata.fields.GeometryField(geomtype='polygon'), + ), + migrations.AlterField( + model_name='door', + name='geometry', + field=c3nav.mapdata.fields.GeometryField(geomtype='polygon'), + ), + migrations.AlterField( + model_name='hole', + name='geometry', + field=c3nav.mapdata.fields.GeometryField(geomtype='polygon'), + ), + migrations.AlterField( + model_name='lineobstacle', + name='geometry', + field=c3nav.mapdata.fields.GeometryField(geomtype='polyline'), + ), + migrations.AlterField( + model_name='obstacle', + name='geometry', + field=c3nav.mapdata.fields.GeometryField(geomtype='polygon'), + ), + migrations.AlterField( + model_name='space', + name='geometry', + field=c3nav.mapdata.fields.GeometryField(geomtype='polygon'), + ), + migrations.AlterField( + model_name='stair', + name='geometry', + field=c3nav.mapdata.fields.GeometryField(geomtype='polyline'), + ), + migrations.AlterField( + model_name='stuffedarea', + name='geometry', + field=c3nav.mapdata.fields.GeometryField(geomtype='polygon'), + ), + ] diff --git a/src/c3nav/mapdata/models/geometry/base.py b/src/c3nav/mapdata/models/geometry/base.py index f33545f9..c604ebe7 100644 --- a/src/c3nav/mapdata/models/geometry/base.py +++ b/src/c3nav/mapdata/models/geometry/base.py @@ -1,8 +1,6 @@ from collections import OrderedDict - from shapely.geometry import Point, mapping -from c3nav.mapdata.fields import GeometryField from c3nav.mapdata.models.base import Feature, FeatureBase from c3nav.mapdata.utils.json import format_geojson @@ -21,9 +19,6 @@ class GeometryFeature(Feature, metaclass=GeometryFeatureBase): """ A map feature with a geometry """ - geometry = GeometryField() - - geomtype = None class Meta: abstract = True diff --git a/src/c3nav/mapdata/models/geometry/section.py b/src/c3nav/mapdata/models/geometry/section.py index 3c3a3ace..bc11c8e0 100644 --- a/src/c3nav/mapdata/models/geometry/section.py +++ b/src/c3nav/mapdata/models/geometry/section.py @@ -3,6 +3,7 @@ from collections import OrderedDict from django.db import models from django.utils.translation import ugettext_lazy as _ +from c3nav.mapdata.fields import GeometryField from c3nav.mapdata.models.geometry.base import GeometryFeature, GeometryFeatureBase LEVEL_FEATURE_TYPES = OrderedDict() @@ -35,7 +36,7 @@ class Building(SectionFeature): """ The outline of a building on a specific level """ - geomtype = 'polygon' + geometry = GeometryField('polygon') class Meta: verbose_name = _('Building') @@ -47,7 +48,7 @@ class Space(SectionFeature): """ An accessible space. Shouldn't overlap. """ - geomtype = 'polygon' + geometry = GeometryField('polygon') CATEGORIES = ( ('', _('normal')), @@ -82,7 +83,7 @@ class Door(SectionFeature): """ A connection between two rooms """ - geomtype = 'polygon' + geometry = GeometryField('polygon') class Meta: verbose_name = _('Door') @@ -94,7 +95,7 @@ class Hole(SectionFeature): """ A hole in the ground of a room, e.g. for stairs. """ - geomtype = 'polygon' + geometry = GeometryField('polygon') class Meta: verbose_name = _('Hole') diff --git a/src/c3nav/mapdata/models/geometry/space.py b/src/c3nav/mapdata/models/geometry/space.py index 8408294c..f8efce4a 100644 --- a/src/c3nav/mapdata/models/geometry/space.py +++ b/src/c3nav/mapdata/models/geometry/space.py @@ -4,6 +4,7 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ from shapely.geometry import CAP_STYLE, JOIN_STYLE, mapping +from c3nav.mapdata.fields import GeometryField from c3nav.mapdata.models.geometry.base import GeometryFeature, GeometryFeatureBase from c3nav.mapdata.utils.json import format_geojson @@ -37,7 +38,7 @@ class StuffedArea(SpaceFeature): """ A slow area with many tables or similar. Avoid it from routing by slowing it a bit down """ - geomtype = 'polygon' + geometry = GeometryField('polygon') class Meta: verbose_name = _('Stuffed Area') @@ -49,7 +50,7 @@ class Stair(SpaceFeature): """ A stair """ - geomtype = 'polyline' + geometry = GeometryField('polyline') class Meta: verbose_name = _('Stair') @@ -83,7 +84,7 @@ class Obstacle(SpaceFeature): """ An obstacle """ - geomtype = 'polygon' + geometry = GeometryField('polygon') class Meta: verbose_name = _('Obstacle') @@ -95,10 +96,9 @@ class LineObstacle(SpaceFeature): """ An obstacle that is a line with a specific width """ + geometry = GeometryField('polyline') width = models.DecimalField(_('obstacle width'), max_digits=4, decimal_places=2, default=0.15) - geomtype = 'polyline' - class Meta: verbose_name = _('Line Obstacle') verbose_name_plural = _('Line Obstacles') diff --git a/src/c3nav/mapdata/models/locations.py b/src/c3nav/mapdata/models/locations.py index 8851b796..6353cd29 100644 --- a/src/c3nav/mapdata/models/locations.py +++ b/src/c3nav/mapdata/models/locations.py @@ -7,7 +7,7 @@ from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext_lazy -from c3nav.mapdata.fields import JSONField, validate_bssid_lines +from c3nav.mapdata.fields import JSONField, validate_bssid_lines, GeometryField from c3nav.mapdata.lastupdate import get_last_mapdata_update from c3nav.mapdata.models.base import Feature from c3nav.mapdata.models.geometry.section import SectionFeature @@ -118,6 +118,7 @@ class AreaLocation(LocationModelMixin, SectionFeature): ('needs_permission', _('Excluded, needs permission to include')), ) + geometry = GeometryField('polygon') slug = models.SlugField(_('Name'), unique=True, max_length=50) location_type = models.CharField(max_length=20, choices=LOCATION_TYPES, verbose_name=_('Location Type')) titles = JSONField() @@ -132,8 +133,6 @@ class AreaLocation(LocationModelMixin, SectionFeature): verbose_name=_('Routing Inclusion')) bssids = models.TextField(blank=True, validators=[validate_bssid_lines], verbose_name=_('BSSIDs')) - geomtype = 'polygon' - class Meta: verbose_name = _('Area Location') verbose_name_plural = _('Area Locations')