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'>