I'm trying to use MakeValid
to fix (validate) my geometry fields.
I can make it work by getting and updating in single line:
from django.contrib.gis.db.models.functions import MakeValid
MyModel.objects.filter(id=<id>).update(polygon=MakeValid('polygon'))
but for some cases, I have to update polygon
of a single model object already instantiated in a function (meaning I have already done .filter
/.get
) which gives me the following error:
// np is an object of MyModel which has a field 'polygon' which is `MultiPolygon` django model field
np.polygon = MakeValid(np.polygon)
// np.save()
TypeError: Cannot set MyModel SpatialProxy (MULTIPOLYGON) with value of type: <class 'django.contrib.gis.db.models.functions.MakeValid'>
Here, MakeValid(np.polygon)
doesn't return a MultiPolygon
object. Instead, it returns a django.contrib.gis.db.models.functions.MakeValid
wrapper.
Can I get a Geometry object from MakeValid
?
As stated in the linked post MakeValid
is a database function, which means that it can be executed only during querying the database.
It is 1-to-1 similar to the PostGIS
usage of ST_MakeValid
which cannot be executed outside of a table query (cannot exist autonomously).
When you create the np
object, and then you try to do:
np.polygon = MakeValid(np.polygon)
You are essentially trying to apply a database function to an instance of the 'MyModel' class which isn't supposed to work! (as it does not)
What you can do:
You can create a query to update a specific table row:
np = MyModel.objects.filter(id=np.id).update(polygon=MakeValid('polygon'))
Note: The object with id=np.id
's polygon, will be updated in the
database permanently with that method.
You can utilize the GEOSGeometry.buffer()
:
Using polygon.buffer(0)
can tidy up most of the polygon irregularities (it can even solve some types of "bowtie"/self-intersecting polygons)
np.polygon.valid # False
np.polygon.buffer(0) # Make valid with buffer(0)
np.polygon.valid # True
Finally, you can use Shapely
and create a shapely polygon for your calculations which you can make valid with the same method as above (both Shapely's buffer
and GeoDjango's buffer
use GEOS
library):
from shapely.geometry import Polygon
# Initialize the polygon object with one of the following ways:
np_polygon = Polygon([np.polygon.coords])
# or
np_polygon = Polygon(np.polygon.wkt)
np_polygon.is_valid # False
np_polygon = np_polygon.buffer(0) # Make valid with buffer(0)
np_polygon.is_valid # True