Opened 21 months ago
Last modified 4 weeks ago
#34406 closed New feature
Add support for curved geometries in GeoDjango — at Initial Version
Reported by: | Fabien Le Frapper | Owned by: | nobody |
---|---|---|---|
Component: | GIS | Version: | dev |
Severity: | Normal | Keywords: | geodjango gdal |
Cc: | Anthony Ricaud, Claude Paroz | Triage Stage: | Accepted |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
I tried ingesting curved geometries in a GeometryField
in GeoDjango.
At first I encountered some errors as these are not officially supported in GeoDjango, but I noticed that gdal
is able to go from a geometry to another using converters :
- Currently supported geometries https://github.com/django/django/blob/main/django/contrib/gis/gdal/geometries.py#L727
- Corresponding geometries in
gdal
https://gdal.org/doxygen/ogr__core_8h.html#a800236a0d460ef66e687b7b65610f12a
I successfully ingested the following geometries for a project :
- 9: "CompoundCurve"
- 10: "CurvePolygon"
- 11: "MultiCurve"
- 12: "MultiSurface"
Below is a code snippet I used, subclassing OGRGeometry
and OGRGeomType
in order to bypass existing GeoDjango validation to add support for more geometries :
- Link to the snippet with syntax highlighting https://gist.github.com/fabienheureux/2dd4dad8f7c4fedef708154eea1470f9
- It mainly adds keys for new geometries and classes inheriting for
OGRGeometry
to handle these in GeoDjango - It shows how we could make these geometries backward compatible (from curved geometries to more standard geometries) using a simple call to existing gdal methods
Is there a reason why it is not supported at the moment in GeoDjango ?
Would you consider a pull request adding support for these geometries ?
I could suggest a patch based on the snippet above, but I am not sure how to treat the transform
part of it.
Should we keep this part ?
It seems that Postgis can handle these polygons https://postgis.net/docs/using_postgis_dbmanagement.html#CircularString
Here is the same snippet but without syntax highlighting
from django.contrib.gis.gdal.geometries import GEO_CLASSES, OGRGeometry from django.contrib.gis.gdal.geomtype import OGRGeomType from django.contrib.gis.gdal.libgdal import lgdal from django.contrib.gis.gdal.prototypes import ds as capi from django.contrib.gis.gdal.prototypes import geom as geom_api class ExtendedOGRGeometry(OGRGeometry): def __init__(self, geom_input, srs=None): try: super().__init__(geom_input, srs) except KeyError: if ( not isinstance(geom_input, self.ptr_type) and self.geom_type.num not in gdal_transform.keys() ): raise self.__class__ = EXTENDED_GEO_CLASSES[self.geom_type.num] @property def geom_type(self): "Return the Type for this Geometry." return ExtendedOGRGeomType(geom_api.get_geom_type(self.ptr)) class CurvePolygon(ExtendedOGRGeometry): pass class CompoundCurve(ExtendedOGRGeometry): pass class MultiSurface(ExtendedOGRGeometry): pass class MultiCurve(ExtendedOGRGeometry): pass EXTENDED_GEO_CLASSES = { **GEO_CLASSES, 9: CompoundCurve, 10: CurvePolygon, 11: MultiCurve, 12: MultiSurface, } class ExtendedOGRGeomType(OGRGeomType): # Copy paste of original types dictionnary from GeoDjango implementation # https://github.com/django/django/blob/main/django/contrib/gis/gdal/geomtype.py#L9 _types = { 0: "Unknown", 1: "Point", 2: "LineString", 3: "Polygon", 4: "MultiPoint", 5: "MultiLineString", 6: "MultiPolygon", 7: "GeometryCollection", 100: "None", 101: "LinearRing", 102: "PointZ", 1 + OGRGeomType.wkb25bit: "Point25D", 2 + OGRGeomType.wkb25bit: "LineString25D", 3 + OGRGeomType.wkb25bit: "Polygon25D", 4 + OGRGeomType.wkb25bit: "MultiPoint25D", 5 + OGRGeomType.wkb25bit: "MultiLineString25D", 6 + OGRGeomType.wkb25bit: "MultiPolygon25D", 7 + OGRGeomType.wkb25bit: "GeometryCollection25D", # Extended geometry types 9: "CompoundCurve", 10: "CurvePolygon", 11: "MultiCurve", 12: "MultiSurface", } # New bindings to existing GDAL methods force_to_polygon = geom_output(lgdal.OGR_G_ForceToPolygon, [c_void_p]) force_to_multi_polygon = geom_output(lgdal.OGR_G_ForceToMultiPolygon, [c_void_p]) force_to_line = geom_output(lgdal.OGR_G_ForceToLineString, [c_void_p]) force_to_multi_line = geom_output(lgdal.OGR_G_ForceToMultiLineString, [c_void_p]) # The functions below need to be called on the corresponding geometry type # before saving it in the database to prevent errors in geodjango. gdal_transform = { 9: force_to_line, 10: force_to_polygon, 11: force_to_multi_line, 12: force_to_multi_polygon, } def ingest_curved_geometry(geom_ptr): transform = gdal_transform[geom.geom_type.num] geom = ExtendedOGRGeometry(transform(geom_api.clone_geom(geom_ptr))) # FIXME: for a yet unknown reason, the initial SRID is not kept when using ExtendedOGRGeometry geom.srid = 3857 geom.transform(4326)