简体   繁体   中英

GeoDjango inteprets MultiPolygon as LinearRing

I'm importing some OSM data from Trimble into a PostGIS database to process it as part of a Django app. This works fine for points and lines but I'm struggling with the polygons.

The import appears to work fine:

shp2pgsql -d -I aeroway_polygon_polygon.shp aeroway_polygon | psql

Django InspectDB interprets the data in a sensible manner:

./manage.py inspectdb > models.py

models.py contents:

class AerowayPolygon(models.Model):
    gid = models.AutoField(primary_key=True)
    id = models.FloatField(blank=True, null=True)
    osm_id = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True)
    z_order = models.FloatField(blank=True, null=True)
    aeroway = models.CharField(max_length=80, blank=True, null=True)
    name = models.CharField(max_length=80, blank=True, null=True)
    name_en = models.CharField(db_column='name:en', max_length=80, blank=True, null=True)  # Field renamed to remove unsuitable characters.
    operator = models.CharField(max_length=80, blank=True, null=True)
    ref = models.CharField(max_length=80, blank=True, null=True)
    faa = models.CharField(max_length=80, blank=True, null=True)
    iata = models.CharField(max_length=80, blank=True, null=True)
    icao = models.CharField(max_length=80, blank=True, null=True)
    website = models.CharField(max_length=80, blank=True, null=True)
    contact_we = models.CharField(db_column='contact:we', max_length=80, blank=True, null=True)  # Field renamed to remove unsuitable characters.
    phone = models.CharField(max_length=80, blank=True, null=True)
    contact_ph = models.CharField(db_column='contact:ph', max_length=80, blank=True, null=True)  # Field renamed to remove unsuitable characters.
    ele = models.CharField(max_length=80, blank=True, null=True)
    tower_type = models.CharField(db_column='tower:type', max_length=80, blank=True, null=True)  # Field renamed to remove unsuitable characters.
    geom = models.MultiPolygonField(srid=0, dim=4, blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'aeroway_polygon'

Any attempt to access objects from the database cause GEOS to complain about a LinearRing.

>>> from data.models import AerowayPolygon
>>> AerowayPolygon.objects.all()[0]
GEOS_ERROR: IllegalArgumentException: Points of LinearRing do not form a closed linestring

The error is not wrong, the points don't close the LineString. But I'm confused as I think the type should be a MultiPolygon and should, therefore, work fine. What gives?


I dug a little deeper by manually trying to take geometries from PostGIS.

As a well-known binary hex string, I get the same behaviour:

>>> from django.contrib.gis.geos import GEOSGeometry
>>> wkb ='01060000C00100000001030000C0020000008E0000000064931E4F47DDBF4020B11AB5BC49400000000000000000FFFFFFFFFFFFEFFF006493B23347DDBF442075F9B7BC494 ...     003C9368871FDDBF4020B193B4BC49400000000000000000FFFFFFFFFFFFEFFF'
>>> GEOSGeometry(wkb)
GEOS_ERROR: IllegalArgumentException: Points of LinearRing do not form a closed linestring

However, if I pre-convert to well known text using ST_AsEWKT, all appears well:

>>> wkt = 'MULTIPOLYGON(((-0.45747735963738 51.4742768635629 0 -1.79769313486232e+308,-0.457470821752906 51.474364454451 0 -1.79769313486232e+308, ... ,-0.455049373745112 51.4742607703088 0 -1.79769313486232e+308)))'
>>> GEOSGeometry(wkt)
<MultiPolygon object at 0x7f0948769098>

Each polygon of a MultiPolygon should still form a closed linestring. Your data is probably malformed or corrupted.

You can try to fix this by using ST_MakeValid .

UPDATE aeroway_polygon
SET geom = ST_Multi(ST_CollectionExtract(ST_Makevalid(geom), 3))
WHERE ST_IsValid(geom) = false;

Note that I didn't test this query, I found it here on gis.stackexchange .

Polygons by definition are closed geometries, which means that the first and last Point must be the same exact Point .
For Multipolygons the same principle exists, hence every Polygon of a Multipolygon must be closed and there must not be "free" edges inside the geometry.

If you check your dataset you will find that some of those LineStrings are not closing.

@AntoinePinsard gives a good PostGIS solution.
In GeoDjango version >= 1.10, MakeValid exists as a database function and we can use it on queries:

AerowayPolygon.objects.all().update(geom=MakeValid('geom'))

If your Django version < 1.10, I have a set of answers on how to create a custom database function and use it as in the example above:

 from django.contrib.gis.db.models.functions import GeoFunc class MyMakeValid(GeoFunc): function='ST_MakeValid' AerowayPolygon.objects.all().update(geom=MyMakeValid('geom')) 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM