diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2022-02-14 14:44:10 +0100 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2022-12-12 12:21:00 +0100 |
commit | 5ecc278a4beded9d64b02744c8b80f39c8c04e19 (patch) | |
tree | 3787c9f7d0ea6e05ffcce26552bd17f55b78d6de /ishtar_common | |
parent | 39551b72a41b5ad00f8d200db2b49e4f3e02515f (diff) | |
download | Ishtar-5ecc278a4beded9d64b02744c8b80f39c8c04e19.tar.bz2 Ishtar-5ecc278a4beded9d64b02744c8b80f39c8c04e19.zip |
Geodata redesign: model adaptation - admin
Diffstat (limited to 'ishtar_common')
-rw-r--r-- | ishtar_common/admin.py | 41 | ||||
-rw-r--r-- | ishtar_common/migrations/0220_auto_20220211_1630.py | 114 | ||||
-rw-r--r-- | ishtar_common/models_common.py | 119 |
3 files changed, 256 insertions, 18 deletions
diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py index e1f823d75..0ef72991f 100644 --- a/ishtar_common/admin.py +++ b/ishtar_common/admin.py @@ -1145,6 +1145,9 @@ general_models = [ models.LicenseType, models.Language, models.PersonType, + models_common.GeoProviderType, + models_common.GeoDataType, + models_common.GeoOriginType, ] for model in general_models: admin_site.register(model, GeneralTypeAdmin) @@ -2407,3 +2410,41 @@ class ApiSheetFilterAdmin(admin.ModelAdmin): admin_site.register(models_rest.ApiSheetFilter, ApiSheetFilterAdmin) + + +class GeoVectorDataForm(forms.ModelForm): + class Meta: + model = models_common.GeoVectorData + exclude = [] + + def clean(self): + fields = [ + "x", + "point_2d", + "point_3d", + "multi_points", + "multi_line", + "multi_polygon", + ] + non_empty = [f for f in fields if self.cleaned_data.get(f, None)] + if len(non_empty) > 1: + raise forms.ValidationError( + _( + "Only one type of geographic data is accepted. " + "Create different objects if you have many." + ) + ) + if bool(self.cleaned_data.get("x", None)) != bool( + self.cleaned_data.get("y", None) + ): + raise forms.ValidationError( + _("You cannot set only x or only y coordinate.") + ) + return self.cleaned_data + + +class GeoVectorDataAdmin(admin.ModelAdmin): + model = models_common.GeoVectorData + + +admin_site.register(models_common.GeoVectorData, GeoVectorDataAdmin)
\ No newline at end of file diff --git a/ishtar_common/migrations/0220_auto_20220211_1630.py b/ishtar_common/migrations/0220_auto_20220211_1630.py new file mode 100644 index 000000000..159b65fcd --- /dev/null +++ b/ishtar_common/migrations/0220_auto_20220211_1630.py @@ -0,0 +1,114 @@ +# Generated by Django 2.2.24 on 2022-02-11 16:30 + +import django.contrib.gis.db.models.fields +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import ishtar_common.models_common +import re + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('ishtar_common', '0219_auto_20220120_1552'), + ] + + operations = [ + migrations.CreateModel( + name='GeoDataType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('label', models.TextField(verbose_name='Label')), + ('txt_idx', models.TextField(help_text='The slug is the standardized version of the name. It contains only lowercase letters, numbers and hyphens. Each slug must be unique.', unique=True, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.", 'invalid')], verbose_name='Textual ID')), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('available', models.BooleanField(default=True, verbose_name='Available')), + ('order', models.IntegerField(default=10, verbose_name='Order')), + ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ishtar_common.GeoDataType', verbose_name='Parent')), + ], + options={ + 'verbose_name': 'Geographic - Data type', + 'verbose_name_plural': 'Geographic - Data types', + 'ordering': ('order', 'label'), + }, + bases=(ishtar_common.models_common.Cached, models.Model), + ), + migrations.CreateModel( + name='GeoOriginType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('label', models.TextField(verbose_name='Label')), + ('txt_idx', models.TextField(help_text='The slug is the standardized version of the name. It contains only lowercase letters, numbers and hyphens. Each slug must be unique.', unique=True, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.", 'invalid')], verbose_name='Textual ID')), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('available', models.BooleanField(default=True, verbose_name='Available')), + ('order', models.IntegerField(default=10, verbose_name='Order')), + ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ishtar_common.GeoOriginType', verbose_name='Parent')), + ], + options={ + 'verbose_name': 'Geographic - Origin type', + 'verbose_name_plural': 'Geographic - Origin types', + 'ordering': ('order', 'label'), + }, + bases=(ishtar_common.models_common.Cached, models.Model), + ), + migrations.CreateModel( + name='GeoProviderType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('label', models.TextField(verbose_name='Label')), + ('txt_idx', models.TextField(help_text='The slug is the standardized version of the name. It contains only lowercase letters, numbers and hyphens. Each slug must be unique.', unique=True, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.", 'invalid')], verbose_name='Textual ID')), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('available', models.BooleanField(default=True, verbose_name='Available')), + ('order', models.IntegerField(default=10, verbose_name='Order')), + ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='ishtar_common.GeoProviderType', verbose_name='Parent')), + ], + options={ + 'verbose_name': 'Geographic - Provider type', + 'verbose_name_plural': 'Geographic - Provider types', + 'ordering': ('order', 'label'), + }, + bases=(ishtar_common.models_common.Cached, models.Model), + ), + migrations.AlterModelOptions( + name='person', + options={'ordering': ['name', 'surname'], 'permissions': (('view_own_person', 'Can view own Person'), ('add_own_person', 'Can add own Person'), ('change_own_person', 'Can change own Person'), ('delete_own_person', 'Can delete own Person')), 'verbose_name': 'Person', 'verbose_name_plural': 'Persons'}, + ), + migrations.AlterModelOptions( + name='spatialreferencesystem', + options={'ordering': ('order', 'label'), 'verbose_name': 'Spatial reference system', 'verbose_name_plural': 'Spatial reference systems'}, + ), + migrations.CreateModel( + name='GeoVectorData', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(default='Default', max_length=200, verbose_name='Name')), + ('source_id', models.PositiveIntegerField()), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('x', models.FloatField(blank=True, help_text='User input', null=True, verbose_name='X')), + ('y', models.FloatField(blank=True, help_text='User input', null=True, verbose_name='Y')), + ('z', models.FloatField(blank=True, help_text='User input', null=True, verbose_name='Z')), + ('cached_x', models.FloatField(blank=True, null=True, verbose_name='X (cached)')), + ('cached_y', models.FloatField(blank=True, null=True, verbose_name='Y (cached)')), + ('cached_z', models.FloatField(blank=True, null=True, verbose_name='Z (cached)')), + ('estimated_error_x', models.FloatField(blank=True, null=True, verbose_name='Estimated error for X')), + ('estimated_error_y', models.FloatField(blank=True, null=True, verbose_name='Estimated error for Y')), + ('estimated_error_z', models.FloatField(blank=True, null=True, verbose_name='Estimated error for Z')), + ('point_2d', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326, verbose_name='Point (2D)')), + ('point_3d', django.contrib.gis.db.models.fields.PointField(blank=True, dim=3, null=True, srid=4326, verbose_name='Point (3D)')), + ('multi_points', django.contrib.gis.db.models.fields.MultiPointField(blank=True, null=True, srid=4326, verbose_name='Multi points')), + ('multi_line', django.contrib.gis.db.models.fields.MultiLineStringField(blank=True, null=True, srid=4326, verbose_name='Multi line')), + ('multi_polygon', django.contrib.gis.db.models.fields.MultiPolygonField(blank=True, null=True, srid=4326, verbose_name='Multi polygon')), + ('need_update', models.BooleanField(default=False, verbose_name='Need update')), + ('data_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='ishtar_common.GeoDataType', verbose_name='Data type')), + ('origin', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='ishtar_common.GeoOriginType', verbose_name='Origin')), + ('provider', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='ishtar_common.GeoProviderType', verbose_name='Provider')), + ('source_content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='content_type_geovectordata', to='contenttypes.ContentType')), + ('spatial_reference_system', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='ishtar_common.SpatialReferenceSystem', verbose_name='Spatial Reference System')), + ], + options={ + 'verbose_name': 'Geographic - Vector data', + 'verbose_name_plural': 'Geographic - Vector data', + }, + ), + ] diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py index 5cbb5da1e..ebf480ff0 100644 --- a/ishtar_common/models_common.py +++ b/ishtar_common/models_common.py @@ -2876,7 +2876,10 @@ class SpatialReferenceSystem(GeneralType): class Meta: verbose_name = _("Spatial reference system") verbose_name_plural = _("Spatial reference systems") - ordering = ("label",) + ordering = ( + "order", + "label", + ) @classmethod def get_documentation_string(cls): @@ -2894,18 +2897,87 @@ post_save.connect(post_save_cache, sender=SpatialReferenceSystem) post_delete.connect(post_save_cache, sender=SpatialReferenceSystem) +class GeoOriginType(HierarchicalType): + """ + ex: topographical surveys, georeferencing, ... + """ + + order = models.IntegerField(_("Order"), default=10) + + class Meta: + verbose_name = _("Geographic - Origin type") + verbose_name_plural = _("Geographic - Origin types") + ordering = ( + "order", + "label", + ) + + +class GeoDataType(HierarchicalType): + """ + ex: outline, z-sup, ... + """ + + order = models.IntegerField(_("Order"), default=10) + + class Meta: + verbose_name = _("Geographic - Data type") + verbose_name_plural = _("Geographic - Data types") + ordering = ( + "order", + "label", + ) + + +class GeoProviderType(HierarchicalType): + """ + ex: GeoNames, IGN, ... + """ + + order = models.IntegerField(_("Order"), default=10) + + class Meta: + verbose_name = _("Geographic - Provider type") + verbose_name_plural = _("Geographic - Provider types") + ordering = ( + "order", + "label", + ) + + class GeoVectorData(models.Model): name = models.CharField(_("Name"), default=_("Default"), max_length=200) source_content_type = models.ForeignKey( - ContentType, related_name="content_type_geovectordata", - on_delete=models.CASCADE) + ContentType, related_name="content_type_geovectordata", on_delete=models.CASCADE + ) source_id = models.PositiveIntegerField() - source = GenericForeignKey('content_type', 'object_id') - + source = GenericForeignKey("source_content_type", "source_id") + origin = models.ForeignKey( + GeoOriginType, + blank=True, + null=True, + on_delete=models.PROTECT, + verbose_name=_("Origin"), + ) + data_type = models.ForeignKey( + GeoDataType, + blank=True, + null=True, + on_delete=models.PROTECT, + verbose_name=_("Data type"), + ) + provider = models.ForeignKey( + GeoProviderType, + blank=True, + null=True, + on_delete=models.PROTECT, + verbose_name=_("Provider"), + ) + comment = models.TextField(_("Comment"), default="", blank=True) x = models.FloatField(_("X"), blank=True, null=True, help_text=_("User input")) y = models.FloatField(_("Y"), blank=True, null=True, help_text=_("User input")) z = models.FloatField(_("Z"), blank=True, null=True, help_text=_("User input")) - # x == cached_x if user input else get it from point_2d + # x == cached_x if user input else get it from other sources # cached is converted to the display SRID cached_x = models.FloatField(_("X (cached)"), blank=True, null=True) cached_y = models.FloatField(_("Y (cached)"), blank=True, null=True) @@ -2926,14 +2998,16 @@ class GeoVectorData(models.Model): null=True, on_delete=models.PROTECT, ) - point = models.PointField(_("Point"), blank=True, null=True, dim=3) point_2d = models.PointField(_("Point (2D)"), blank=True, null=True) + point_3d = models.PointField(_("Point (3D)"), blank=True, null=True, dim=3) + multi_points = models.MultiPointField(_("Multi points"), blank=True, null=True) + multi_line = models.MultiLineStringField(_("Multi line"), blank=True, null=True) multi_polygon = models.MultiPolygonField(_("Multi polygon"), blank=True, null=True) need_update = models.BooleanField(_("Need update"), default=False) class Meta: - verbose_name = _("Geographic vector data") - verbose_name_plural = _("Geographic vector data") + verbose_name = _("Geographic - Vector data") + verbose_name_plural = _("Geographic - Vector data") def display_coordinates(self, rounded=5, dim=2, cache=True): srid = None @@ -2956,8 +3030,11 @@ class GeoVectorData(models.Model): if dim == 3: coordinates.append(self.z) else: - args = {"x": self.x, "y": self.y, - "srid": self.spatial_reference_system.srid} + args = { + "x": self.x, + "y": self.y, + "srid": self.spatial_reference_system.srid, + } if dim == 3: args["z"] = self.z point = Point(**args).transform(srid, clone=True) @@ -2967,8 +3044,8 @@ class GeoVectorData(models.Model): elif self.point_2d and dim == 2: point = self.point_2d.transform(srid, clone=True) coordinates = [point.x, point.y] - elif self.point and dim == 3: - point = self.point.transform(srid, clone=True) + elif self.point_3d and dim == 3: + point = self.point_3d.transform(srid, clone=True) coordinates = [point.x, point.y, point.z] else: return @@ -2978,8 +3055,9 @@ class GeoVectorData(models.Model): def get_coordinates_from_polygon(self, rounded=5, srid: int = None): if self.multi_polygon: - return self.convert_coordinates(self.multi_polygon.centroid, - rounded=rounded, srid=srid) + return self.convert_coordinates( + self.multi_polygon.centroid, rounded=rounded, srid=srid + ) def get_x(self, srid: int = None) -> float: coord = self.get_coordinates(srid) @@ -3097,16 +3175,21 @@ class GeoVectorData(models.Model): return self._geojson_serialize("multi_polygon") + + post_save.connect(post_save_geodata, sender=GeoVectorData) class GeoItem(models.Model): main_geodata = models.ForeignKey( - GeoVectorData, on_delete=models.SET_NULL, blank=True, null=True, - related_name="main_related_items_%(app_label)s_%(class)s" + GeoVectorData, + on_delete=models.SET_NULL, + blank=True, + null=True, + related_name="main_related_items_%(app_label)s_%(class)s", ) geodata = models.ManyToManyField( - GeoVectorData, null=True, related_name="related_items_%(app_label)s_%(class)s" + GeoVectorData, blank=True, related_name="related_items_%(app_label)s_%(class)s" ) GEO_SOURCE = (("T", _("Town")), ("P", _("Precise")), ("M", _("Polygon"))) |