#34406: Add support for curved geometries in GeoDjango
-------------------------------------+-------------------------------------
Reporter: Fabien Le Frapper | Owner: Fabien Le
| Frapper
Type: New feature | Status: assigned
Component: GIS | Version: 4.1
Severity: Normal | Resolution:
Keywords: geodjango gdal | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by Fabien Le Frapper:
Old 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 full snippet
>
> {{{#!python
> 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)
> }}}
New 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 :
- 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 full snippet
{{{#!python
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)
}}}
--
--
Ticket URL: <https://code.djangoproject.com/ticket/34406#comment:6>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/01070186d0bd2bd4-731ae7e5-9eae-4423-a824-1af5b68b7331-000000%40eu-central-1.amazonses.com.