Opened 3 years ago
Last modified 14 months 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
gdalhttps://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
OGRGeometryto 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)