-8

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?

John Moutafis
  • 22,254
  • 11
  • 68
  • 112
randomuser
  • 1,201
  • 2
  • 10
  • 19
  • is `np.polygon` a multipolygon? I remember reading somewhere that makeValid does not support multipolygons – N. Ivanov Aug 16 '17 at 08:48
  • yes. updated in question as well. And it works for MultiPolygons also. First query works fine. – randomuser Aug 16 '17 at 08:52
  • 15
    FYI, this question is the [subject of a meta question](https://meta.stackoverflow.com/questions/355484/am-i-doing-something-wrong-or-is-the-user-a-help-vampire). – Christian Dean Aug 22 '17 at 20:03

1 Answers1

16

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:

  1. 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.

  2. 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
    
  3. 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
    
John Moutafis
  • 22,254
  • 11
  • 68
  • 112
  • I already have object `np` in my function. Getting it again using `filter` and then updating it will make it make it inefficient, right? – randomuser Aug 17 '17 at 10:11
  • @ajaysingh Yes it will be a bit inefficient, so you need to decide were to use the `MakeValid` function. Another solution would be to create a `MakeValid` for model objects and not as a database function but that may be overcomplicated. – John Moutafis Aug 17 '17 at 10:28
  • Can you give me some guidance on how to create `MakeValid` for model objects? – randomuser Aug 17 '17 at 10:38
  • @ajaysingh I think I found some solutions without much complication. Have a look at my edited answer!! – John Moutafis Aug 17 '17 at 12:56
  • Hey, I tried it today only. It sort of works for me. But it sometimes returns `Polygon` object and sometimes `MultiPolygon` object. Any ideas on that? Also, is there any difference between `MakeValid` and `buffer(0)`? – randomuser Aug 22 '17 at 11:52
  • @ajaysingh As I write in the answer, `MakeValid` is a database function and `buffer(0)` is an object method. I do not know about the polygon and multi-polygon though, seems strange. – John Moutafis Aug 22 '17 at 11:55
  • any useful link to find the difference between `MakeValid` and `buffer(0)`? Also, what's the difference between `buffer(0)` and `buffer(1)` – randomuser Aug 22 '17 at 12:48