[GIS] Geodjango – User’s uploaded shapefiles and model creation

geodjango

I have a big interrogation about how it could be possible to have a GeoDjango app that can store in database the uploaded shapefiles (or other geospatial files) from users. When you load shapefiles in GeoDjango, you have to create a new model that matches the fields of the shapefile. How could it be possible to create a new model automatically each time a user uploads a files? You can't append the new model definition to your models.py each time? For me, that does not make any sense.

Does it means the best way is to use a generic model definition and only allow files upload that match this definition,and then to create a new instance of this generic model? Can someone explain me what is the best practice to create an app that can store users uploaded spatial files?

Best Answer

You are right, adding a new model to models.py each time is impossible. And, if it where, you would end up with a lot of unmanageable tables.

One option is to use a schema-less datastore (i.e. a NoSQL database) but then you'd loose the spaial capabilities of PostGIS (or have to use both PostGIS and a NoSQL db, no fun!). And then there's the concept of "hstore" in PostgreSQL which allows you to store a set of key/value pairs in a single column. Unfortunately, the django-hstore lib requires its own db.backend definition, this would result in a conflict with the geodjango-backend.

This means that the solution I would recommend is one described in the book "Python Geospatial Development". I'll just outline the approach here, either you get the idea or you could get yourself a copy of the book for a more thourough explaination.

In brief:

Model your app like this:

class Shapefile(models.Model):
    filename = models.CharField(max_length=255)
    srs = models.CharField(max_length=255)
    geom_type = models.CharField(max_length=50)
    encoding = models.CharField(max_length=20)

class Attribute(models.Model):
    shapefile = models.ForeignKey(Shapefile)
    name = models.CharField(max_length=255)
    type = models.IntegerField()
    width = models.IntegerField()
    precision = models.IntegerField()

class Feature(models.Model):
    shapefile = models.ForeignKey(Shapefile)
    geom_point = models.PointField(srid=4326, blank=True, null=true)
    geom_multipoint = models.MultiPointField(srid=4326, blank=True, null=true)
    geom_multilinestring = models.MultiLineStringField(srid=4326, blank=True, null=true)
    geom_multipolygon = models.MultiPolygonField(srid=4326, blank=True, null=true)
    geom_geometrycollection = models.GeometryCollectionField(srid=4326, blank=True, null=true)

    objects = models.GeoManager()

class AttributeValue(models.Model):
    feature = models.ForeignKey(Feature)
    attribute = models.ForeignKey(Attribute)
    value = models.CharField(max_length=255, blank=True, null=true)

In this way the shapefile schema is modeled in a set of relations

Related Question