When I try to set a Polygon
on a MultiPolygonField
the following exception is raised:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/mattrowbum/.virtualenvs/my_env/lib/python3.7/site-packages/django/contrib/gis/db/models/proxy.py", line 75, in __set__
instance.__class__.__name__, gtype, type(value)))
TypeError: Cannot set Location SpatialProxy (MULTIPOLYGON) with value of type: <class 'django.contrib.gis.geos.polygon.Polygon'>
That would be understandable, except that a note in the GeoDjango tutorial states:
...a GeoDjango MultiPolygonField will accept a Polygon geometry.
I've had a look at the source of proxy.py, and it is checking whether the value (a Polygon
) is an instance of the relevant geometry class (a MultiPolygon
). I've tried doing this check manually which confirms that a Polygon
does not inherit from MultiPolygon
:
>>> from django.contrib.gis.geos import MultiPolygon, Polygon
>>> ext_coords = ((0, 0), (0, 1), (1, 1), (1, 0), (0, 0))
>>> int_coords = ((0.4, 0.4), (0.4, 0.6), (0.6, 0.6), (0.6, 0.4), (0.4, 0.4))
>>> poly = Polygon(ext_coords, int_coords)
>>> isinstance(poly, Polygon)
True
>>> isinstance(poly, MultiPolygon)
False
This came to my attention when attempting to simplify an existing stored MultiPolygon
value. Below is my model:
from django.contrib.gis.db import models
class Location(models.Model):
name = models.CharField(max_length=180)
mpoly = models.MultiPolygonField(geography=True, blank=True, null=True)
The process I used to simplify the MultiPolygon
is below. The last line causes the exception to be raised:
>>> from my_app.models import Location
>>> location = Location.objects.get(pk=1)
>>> geom = location.mpoly
>>> simplified_geom = geom.simplify(0.0002)
>>> location.mpoly = simplified_geom
If I use the Polygon
to create a MultiPolygon
, it works fine:
>>> multi = MultiPolygon([simplified_geom,])
>>> location.mpoly = multi
Is the note in the tutorial misleading or am I doing something wrong?
EDIT: Further tests on the geometries.
The original MultiPolygon
straight from the model field:
>>> geom = Location.mpoly
>>> type(geom)
<class 'django.contrib.gis.geos.collections.MultiPolygon'>
>>> geom.geom_type
'MultiPolygon'
>>> geom.valid
True
>>> geom.srid
4326
Applying the simplify()
method:
>>> simplified_geom = geom.simplify(0.0002)
>>> type(simplified_geom)
<class 'django.contrib.gis.geos.polygon.Polygon'>
>>> simplified_geom.geom_type
'Polygon'
>>> simplified_geom.valid
True
>>> simplified_geom.srid
4326
Creating a MultiPolygon
from the simplified geometry:
>>> multi = MultiPolygon([simplified_geom,])
>>> type(multi)
<class 'django.contrib.gis.geos.collections.MultiPolygon'>
>>> multi.geom_type
'MultiPolygon'
>>> multi.valid
True
>>> multi.srid
>>> print(multi.srs)
None
Note that the above MultiPolygon
has no SRID. I thought that might have been the reason is was being accepted. I created one with the srid=4326
argument but it too was accepted by the field.
Here's a really basic example of the problem:
>>> # This works
>>> location.mpoly = MultiPolygon()
>>> # This doesn't
>>> location.mpoly = Polygon()
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/mattrowbum/.virtualenvs/musicteacher/lib/python3.7/site-packages/django/contrib/gis/db/models/proxy.py", line 75, in __set__
instance.__class__.__name__, gtype, type(value)))
TypeError: Cannot set Location SpatialProxy (MULTIPOLYGON) with value of type: <class 'django.contrib.gis.geos.polygon.Polygon'>