5

I am manipulating GIS data w/ geopandas and storing it in various Django models. geopandas uses shapely under-the-hood while Django does not.

Here is some code:

import geopandas as gpd
from django.contrib.gis.db import models

class MyModel(models.Model):
  geometry = models.PolygonField()
  name = models.CharField(max_length=255, null=False, unique=True)
  some_property = models.IntegerField()

gdf = gpd.read_file("some_data.geojson")
# ...do some stuff w/ gdf...

for data in gdf.to_dict("records"):
  name = data.pop("name")
  MyModel.objects.create_or_update(
    name=name,
    defaults=data,
  )

The above will fail w/ errors like:

TypeError: Cannot set MyModel SpatialProxy (POLYGON) with value of type: <class 'shapely.geometry.polygon.Polygon'>

Unless I add some icky code like:

from django.contrib.gis.geos import fromstr, Polygon 
data["geometry"] = Polygon(fromstr(str(data["geometry"])))

Is there any way to avoid this and directly map from shapely to Django?


edit:

Here are some values:

>> data["geometry"]
<shapely.geometry.polygon.Polygon object at 0x7fb374f41908>
>> str(data["geometry"])
'POLYGON ((-4.337076919429241 53.41842814531255, -4.336698521348041 53.4182242737367, ....))'
>> fromstr(str(data["geometry"]))
<Polygon object at 0x7fb3733d158e>
Georgy
  • 12,464
  • 7
  • 65
  • 73
trubliphone
  • 4,132
  • 3
  • 42
  • 66

1 Answers1

2

Your solution doesn't seem as icky as you might think.

Since your data['geometry'] field returns a WKT string representation ('POLYGON ((-4.337076919429241 53.41842814531255, ... ))) you can avoid the fromstr step and pass it directly to a GEOSGeometry:

from django.contrib.gis.geos import GEOSGeometry

polygon = GEOSGeometry('POLYGON ((-4.337076919429241 53.41842814531255, ... ))')

You can also add some error handling and stop worrying about you solution falling apart :) :

for data in gdf.to_dict("records"):
    name = data.pop("name")
    geometry_str = data.pop('geometry')        
    try:
        geometry = GEOSGeometry(geometry_str)
    except (TypeError, ValueError) as exc:
        # If the geometry_str is not a valid WKT, EWKT or HEXEWKB string
        # or is None then either continue, break or do something else.
        # I will go with continue here.
        continue

    if geometry.geom_type != 'Polygon':
        # If the created geometry is not a Polygon don't pass it on MyModel
        continue 

    MyModel.objects.update_or_create(
        name=name, geometry=geometry,
        defaults=data,
    )
tgrandje
  • 2,332
  • 11
  • 33
John Moutafis
  • 22,254
  • 11
  • 68
  • 112