#138 WIP Validity
* adds geometry validity checks for SimpleGeomForm is_valid() * shows validity problems on the form if a feature is invalid * optimizes merging of different features into one MultiPolygon * further enhances tests * adds as_feature_collection() method on Geometry model for converting geom MultiPolygon attribute into FeatureCollection json holding each polygon as an own feature -> makes each polygon selectable in new netgis map client
This commit is contained in:
parent
8d34580090
commit
5e65156b54
@ -128,6 +128,7 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
new_identifier = self.create_dummy_string()
|
new_identifier = self.create_dummy_string()
|
||||||
new_comment = self.create_dummy_string()
|
new_comment = self.create_dummy_string()
|
||||||
new_geometry = MultiPolygon(srid=4326) # Create an empty geometry
|
new_geometry = MultiPolygon(srid=4326) # Create an empty geometry
|
||||||
|
geojson = self.create_geojson(new_geometry)
|
||||||
|
|
||||||
check_on_elements = {
|
check_on_elements = {
|
||||||
self.compensation.title: new_title,
|
self.compensation.title: new_title,
|
||||||
@ -142,7 +143,7 @@ class CompensationWorkflowTestCase(BaseWorkflowTestCase):
|
|||||||
"title": new_title,
|
"title": new_title,
|
||||||
"intervention": self.intervention.id, # just keep the intervention as it is
|
"intervention": self.intervention.id, # just keep the intervention as it is
|
||||||
"comment": new_comment,
|
"comment": new_comment,
|
||||||
"geom": new_geometry.geojson,
|
"geom": geojson,
|
||||||
}
|
}
|
||||||
self.client_user.post(url, post_data)
|
self.client_user.post(url, post_data)
|
||||||
self.compensation.refresh_from_db()
|
self.compensation.refresh_from_db()
|
||||||
|
@ -17,8 +17,8 @@ from django.db.models.fields.files import FieldFile
|
|||||||
|
|
||||||
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
|
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
|
||||||
from user.models import User
|
from user.models import User
|
||||||
from django.contrib.gis.forms import OSMWidget, MultiPolygonField
|
from django.contrib.gis.forms import MultiPolygonField
|
||||||
from django.contrib.gis.geos import MultiPolygon, GEOSGeometry, Polygon
|
from django.contrib.gis.geos import MultiPolygon, Polygon
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.http import HttpRequest, HttpResponseRedirect
|
from django.http import HttpRequest, HttpResponseRedirect
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
@ -291,11 +291,13 @@ class SimpleGeomForm(BaseForm):
|
|||||||
# Initialize geometry
|
# Initialize geometry
|
||||||
try:
|
try:
|
||||||
geom = self.instance.geometry.geom
|
geom = self.instance.geometry.geom
|
||||||
geom.transform(ct=DEFAULT_SRID_RLP)
|
|
||||||
self.empty = geom.empty
|
self.empty = geom.empty
|
||||||
|
|
||||||
if self.empty:
|
if self.empty:
|
||||||
raise AttributeError
|
raise AttributeError
|
||||||
geom = geom.geojson
|
|
||||||
|
geojson = self.instance.geometry.as_feature_collection(srid=DEFAULT_SRID_RLP)
|
||||||
|
geom = json.dumps(geojson)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# If no geometry exists for this form, we simply set the value to None and zoom to the maximum level
|
# If no geometry exists for this form, we simply set the value to None and zoom to the maximum level
|
||||||
geom = ""
|
geom = ""
|
||||||
@ -326,19 +328,26 @@ class SimpleGeomForm(BaseForm):
|
|||||||
self.add_error("geom", _("Only surfaces allowed. Points or lines must be buffered."))
|
self.add_error("geom", _("Only surfaces allowed. Points or lines must be buffered."))
|
||||||
is_valid = False
|
is_valid = False
|
||||||
return is_valid
|
return is_valid
|
||||||
features.append(g)
|
|
||||||
if len(features) > 0:
|
|
||||||
form_geom = features[0]
|
|
||||||
for g in features[1:]:
|
|
||||||
form_geom = form_geom.union(g)
|
|
||||||
form_geom.transform(coord_trans=DEFAULT_SRID)
|
|
||||||
if form_geom.geom_type != "MultiPolygon":
|
|
||||||
form_geom = MultiPolygon(MultiPolygon.from_ewkt(form_geom.ewkt))
|
|
||||||
|
|
||||||
# Write unioned Multipolygon into cleaned data
|
polygon = Polygon.from_ewkt(g.ewkt)
|
||||||
if self.cleaned_data is None:
|
is_valid = polygon.valid
|
||||||
self.cleaned_data = {}
|
if not is_valid:
|
||||||
self.cleaned_data["geom"] = form_geom.wkt
|
self.add_error("geom", polygon.valid_reason)
|
||||||
|
return is_valid
|
||||||
|
|
||||||
|
features.append(polygon)
|
||||||
|
form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP)
|
||||||
|
for feature in features:
|
||||||
|
form_geom = form_geom.union(feature)
|
||||||
|
|
||||||
|
# Make sure to convert into a MultiPolygon. Relevant if a single Polygon is provided.
|
||||||
|
if form_geom.geom_type != "MultiPolygon":
|
||||||
|
form_geom = MultiPolygon(form_geom, srid=DEFAULT_SRID_RLP)
|
||||||
|
|
||||||
|
# Write unioned Multipolygon into cleaned data
|
||||||
|
if self.cleaned_data is None:
|
||||||
|
self.cleaned_data = {}
|
||||||
|
self.cleaned_data["geom"] = form_geom.ewkt
|
||||||
|
|
||||||
return is_valid
|
return is_valid
|
||||||
|
|
||||||
|
@ -5,11 +5,15 @@ Contact: michel.peltriaux@sgdnord.rlp.de
|
|||||||
Created on: 15.11.21
|
Created on: 15.11.21
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import json
|
||||||
|
|
||||||
from django.contrib.gis.db.models import MultiPolygonField
|
from django.contrib.gis.db.models import MultiPolygonField
|
||||||
|
from django.contrib.gis.geos import Polygon
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from konova.models import BaseResource, UuidModel
|
from konova.models import BaseResource, UuidModel
|
||||||
|
from konova.sub_settings.lanis_settings import DEFAULT_SRID_RLP
|
||||||
from konova.utils.wfs.spatial import ParcelWFSFetcher
|
from konova.utils.wfs.spatial import ParcelWFSFetcher
|
||||||
|
|
||||||
|
|
||||||
@ -179,6 +183,34 @@ class Geometry(BaseResource):
|
|||||||
|
|
||||||
return parcels
|
return parcels
|
||||||
|
|
||||||
|
def as_feature_collection(self, srid=DEFAULT_SRID_RLP):
|
||||||
|
""" Returns a FeatureCollection structure holding all polygons of the MultiPolygon as single features
|
||||||
|
|
||||||
|
This method is used to convert a single MultiPolygon into multiple Polygons, which can be used as separated
|
||||||
|
features in the NETGIS map client.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
srid (int): The spatial reference system identifier to be transformed to
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
geojson (dict): The FeatureCollection json (as dict)
|
||||||
|
"""
|
||||||
|
geom = self.geom
|
||||||
|
geom.transform(ct=srid)
|
||||||
|
|
||||||
|
polygons = []
|
||||||
|
for coords in geom.coords:
|
||||||
|
p = Polygon(coords[0], srid=geom.srid)
|
||||||
|
polygons.append(p)
|
||||||
|
geojson = {
|
||||||
|
"type": "FeatureCollection",
|
||||||
|
"features": [
|
||||||
|
json.loads(x.geojson) for x in polygons
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return geojson
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class GeometryConflict(UuidModel):
|
class GeometryConflict(UuidModel):
|
||||||
"""
|
"""
|
||||||
|
@ -432,11 +432,12 @@ class BaseTestCase(TestCase):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if geom1.srid != geom2.srid:
|
if geom1.srid != geom2.srid:
|
||||||
|
tolerance = 0.001
|
||||||
# Due to prior possible transformation of any of these geometries, we need to make sure there exists a
|
# Due to prior possible transformation of any of these geometries, we need to make sure there exists a
|
||||||
# transformation from one coordinate system into the other, which is valid
|
# transformation from one coordinate system into the other, which is valid
|
||||||
geom1_t = geom1.transform(geom2.srid, clone=True)
|
geom1_t = geom1.transform(geom2.srid, clone=True)
|
||||||
geom2_t = geom2.transform(geom1.srid, clone=True)
|
geom2_t = geom2.transform(geom1.srid, clone=True)
|
||||||
self.assertTrue(geom1_t.equals(geom2) or geom2_t.equals(geom1))
|
self.assertTrue(geom1_t.equals_exact(geom2, tolerance) or geom2_t.equals_exact(geom1, tolerance))
|
||||||
else:
|
else:
|
||||||
self.assertTrue(geom1.equals(geom2))
|
self.assertTrue(geom1.equals(geom2))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user