4

I'm working in Django 1.9 and Postgres 9.4 with PostGIS extensions. I have a KML file, and I want to use it to update my geo fields. This is my model:

class PCT(models.Model):
    code = models.CharField(max_length=3, primary_key=True)
    boundary = models.GeometryField(null=True, blank=True)
    objects = models.GeoManager()

This is my current code:

    from django.contrib.gis.gdal import DataSource
    from django.contrib.gis.utils import LayerMapping
    ds = DataSource(options['filename'])
    layer_mapping = {
        'code': 'Name',
        'boundary': 'Unknown'
    }
    lm = LayerMapping(PCT, options['filename'],
                      layer_mapping, transform=False) # , unique='code')
    lm.save(strict=True)

It works OK, but if I run it a second time, it won't update any existing PCT fields - instead it creates new ones. This is a problem when I want to update the boundaries. (I can't just delete and re-create the fields, because there are foreign key dependencies.)

How can I use my KML file to update existing files?

@alex suggested trying this, but I'm getting an error:

    ds = DataSource(options['filename'])
    for layer in ds:
        for i in layer:
            code = str(i['Name'])
            geom = i.geom
            print geom

            geos_geom = geom.geos
            print geos_geom

            boundary = GEOSGeometry(geom)
            print boundary

            pct = PCT.objects.get(code=code)
            if pct:
                pct.boundary = geos_geom
                pct.save()

This throws an error at the boundary = GEOSGeometry(geom) line: TypeError: Improper geometry input type: <class 'django.contrib.gis.gdal.geometries.Polygon'>.

The output from the print statements up to that point is as follows:

POLYGON ((-1.407325 54.593612 0,-1.423288 54.603131 0,-1.431455 54.595297 0 ...
SRID=4326;POLYGON Z ((-1.4073249999999999 54.5936120000000003 0.0000000000000000

What am I doing wrong?

I tried simply passing a string to GEOSGeometry:

boundary = GEOSGeometry(str(i.geom))
pct = PCT.objects.get(code=code)
if pct:
  pct.boundary = boundary
  pct.save()

Perhaps this solves the issue? But now I get a different error on pct.save():

django.db.utils.DataError: Geometry has Z dimension but column does not

I can see by looking at the print statements that there is indeed a z column, but it's always zero, is it possible to get rid of this somehow?

1

1 Answer 1

3

There is indeed no need to use LayerMapping here: I can't test this right now so it may be bugged, but the idea is to test if your object exists first, and if it does, update the geometry.

from django.contrib.gis.gdal import DataSource
from django.contrib.gis.geos import GEOSGeometry


def update_pct():
     source = DataSource('some/path/to/the/file.kml') 
     for layer in source:
         for feat in layer:   
             code = feat['code'].value
             geom = feat.get_geoms()  # Get the feature geometry.
             geos_geom = geom.geos   # Get the geos representation of the geometry.
             boundary = GEOSGeometry(geom)  # Make a GeosGeometry object using the string representation.

             pct = PCT.objects.get(code=code)  # Note that .get() raises a DoesNotExist error if the object doesn't exist, so you will have to catch this.
             pct.boundary = boundary
             pct.save()

You probably also want to store details about when PCTs are updated somewhere.

1
  • Thanks! I had to do for layer in source: for feat in layer: to get it to work, but this solved the problem. I just used .get() to get the PCT.
    – Richard
    Feb 26, 2016 at 17:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.