16
16
* Editing of geometry fields inside the admin.
18
This tutorial assumes a familiarity with Django; thus, if you're brand new to
19
Django please read through the :ref:`regular tutorial <intro-tutorial01>` to introduce
18
This tutorial assumes a familiarity with Django; thus, if you're brand new to
19
Django please read through the :doc:`regular tutorial </intro/tutorial01>` to introduce
20
20
yourself with basic Django concepts.
28
28
This tutorial is going to guide you through guide the user through the creation
29
29
of a geographic web application for viewing the `world borders`_. [#]_ Some of
30
the code used in this tutorial is taken from and/or inspired by the
30
the code used in this tutorial is taken from and/or inspired by the
31
31
`GeoDjango basic apps`_ project. [#]_
35
Proceed through the tutorial sections sequentially for step-by-step
35
Proceed through the tutorial sections sequentially for step-by-step
38
38
.. _OGC: http://www.opengeospatial.org/
51
51
are already built into the database.
53
53
First, a spatial database needs to be created for our project. If using
54
PostgreSQL and PostGIS, then the following commands will
54
PostgreSQL and PostGIS, then the following commands will
55
55
create the database from a :ref:`spatial database template <spatialdb_template>`::
57
57
$ createdb -T template_postgis geodjango
61
61
This command must be issued by a database user that has permissions to
65
65
$ sudo su - postgres
66
66
$ createuser --createdb geo
69
69
Replace ``geo`` to correspond to the system login user name will be
70
connecting to the database. For example, ``johndoe`` if that is the
70
connecting to the database. For example, ``johndoe`` if that is the
71
71
system user that will be running GeoDjango.
73
73
Users of SQLite and SpatiaLite should consult the instructions on how
105
105
These database settings are for Django 1.2 and above.
107
In addition, modify the :setting:`INSTALLED_APPS` setting to include
107
In addition, modify the :setting:`INSTALLED_APPS` setting to include
108
108
:mod:`django.contrib.admin`, :mod:`django.contrib.gis`,
109
109
and ``world`` (our newly created application)::
143
143
* ``.shp``: Holds the vector data for the world borders geometries.
144
144
* ``.shx``: Spatial index file for geometries stored in the ``.shp``.
145
* ``.dbf``: Database file for holding non-geometric attribute data
146
(e.g., integer and character fields).
145
* ``.dbf``: Database file for holding non-geometric attribute data
146
(e.g., integer and character fields).
147
147
* ``.prj``: Contains the spatial reference information for the geographic
148
148
data stored in the shapefile.
153
153
Use ``ogrinfo`` to examine spatial data
154
154
---------------------------------------
156
The GDAL ``ogrinfo`` utility is excellent for examining metadata about
156
The GDAL ``ogrinfo`` utility is excellent for examining metadata about
157
157
shapefiles (or other vector data sources)::
159
159
$ ogrinfo world/data/TM_WORLD_BORDERS-0.3.shp
194
194
This detailed summary information tells us the number of features in the layer
195
(246), the geographical extent, the spatial reference system ("SRS WKT"),
195
(246), the geographical extent, the spatial reference system ("SRS WKT"),
196
196
as well as detailed information for each attribute field. For example,
197
197
``FIPS: String (2.0)`` indicates that there's a ``FIPS`` character field
198
198
with a maximum length of 2; similarly, ``LON: Real (8.3)`` is a floating-point
199
199
field that holds a maximum of 8 digits up to three decimal places. Although
200
200
this information may be found right on the `world borders`_ website, this shows
201
you how to determine this information yourself when such metadata is not
201
you how to determine this information yourself when such metadata is not
204
204
Geographic Models
213
213
from django.contrib.gis.db import models
215
215
class WorldBorders(models.Model):
216
# Regular Django fields corresponding to the attributes in the
216
# Regular Django fields corresponding to the attributes in the
217
217
# world borders shapefile.
218
218
name = models.CharField(max_length=50)
219
219
area = models.IntegerField()
227
227
lon = models.FloatField()
228
228
lat = models.FloatField()
230
# GeoDjango-specific: a geometry field (MultiPolygonField), and
230
# GeoDjango-specific: a geometry field (MultiPolygonField), and
231
231
# overriding the default manager with a GeoManager instance.
232
232
mpoly = models.MultiPolygonField()
233
233
objects = models.GeoManager()
235
235
# So the model is pluralized correctly in the admin.
237
237
verbose_name_plural = "World Borders"
239
# Returns the string representation of the model.
239
# Returns the string representation of the model.
240
240
def __unicode__(self):
243
243
Two important things to note:
245
245
1. The ``models`` module is imported from :mod:`django.contrib.gis.db`.
246
2. The model overrides its default manager with
246
2. The model overrides its default manager with
247
247
:class:`~django.contrib.gis.db.models.GeoManager`; this is *required*
248
to perform spatial queries.
248
to perform spatial queries.
250
250
When declaring a geometry field on your model the default spatial reference system
251
251
is WGS84 (meaning the `SRID`__ is 4326) -- in other words, the field coordinates are in
252
252
longitude/latitude pairs in units of degrees. If you want the coordinate system to be
253
253
different, then SRID of the geometry field may be customized by setting the ``srid``
254
with an integer corresponding to the coordinate system of your choice.
254
with an integer corresponding to the coordinate system of your choice.
256
256
__ http://en.wikipedia.org/wiki/SRID
261
261
After you've defined your model, it needs to be synced with the spatial database.
262
First, let's look at the SQL that will generate the table for the ``WorldBorders``
262
First, let's look at the SQL that will generate the table for the ``WorldBorders``
265
265
$ python manage.py sqlall world
295
295
Installing custom SQL for world.WorldBorders model
297
297
The ``syncdb`` command may also prompt you to create an admin user; go ahead and
298
do so (not required now, may be done at any point in the future using the
298
do so (not required now, may be done at any point in the future using the
299
299
``createsuperuser`` management command).
301
301
Importing Spatial Data
304
304
This section will show you how to take the data from the world borders
305
305
shapefile and import it into GeoDjango models using the :ref:`ref-layermapping`.
306
There are many different different ways to import data in to a
306
There are many different different ways to import data in to a
307
307
spatial database -- besides the tools included within GeoDjango, you
308
308
may also use the following to populate your spatial database:
310
* `ogr2ogr`_: Command-line utility, included with GDAL, that
310
* `ogr2ogr`_: Command-line utility, included with GDAL, that
311
311
supports loading a multitude of vector data formats into
312
312
the PostGIS, MySQL, and Oracle spatial databases.
313
313
* `shp2pgsql`_: This utility is included with PostGIS and only supports
339
339
>>> world_shp = os.path.abspath(os.path.join(os.path.dirname(world.__file__),
340
340
... 'data/TM_WORLD_BORDERS-0.3.shp'))
342
Now, the world borders shapefile may be opened using GeoDjango's
342
Now, the world borders shapefile may be opened using GeoDjango's
343
343
:class:`~django.contrib.gis.gdal.DataSource` interface::
345
345
>>> from django.contrib.gis.gdal import *
348
348
/ ... /geodjango/world/data/TM_WORLD_BORDERS-0.3.shp (ESRI Shapefile)
350
Data source objects can have different layers of geospatial features; however,
350
Data source objects can have different layers of geospatial features; however,
351
351
shapefiles are only allowed to have one layer::
353
353
>>> print len(ds)
369
369
Unfortunately the shapefile data format does not allow for greater
370
specificity with regards to geometry types. This shapefile, like
370
specificity with regards to geometry types. This shapefile, like
371
371
many others, actually includes ``MultiPolygon`` geometries in its
372
372
features. You need to watch out for this when creating your models
373
as a GeoDjango ``PolygonField`` will not accept a ``MultiPolygon``
373
as a GeoDjango ``PolygonField`` will not accept a ``MultiPolygon``
374
374
type geometry -- thus a ``MultiPolygonField`` is used in our model's
375
375
definition instead.
391
391
Here we've noticed that the shapefile is in the popular WGS84 spatial reference
392
392
system -- in other words, the data uses units of degrees longitude and latitude.
394
In addition, shapefiles also support attribute fields that may contain
394
In addition, shapefiles also support attribute fields that may contain
395
395
additional data. Here are the fields on the World Borders layer:
397
397
>>> print lyr.fields
403
403
>>> [fld.__name__ for fld in lyr.field_types]
404
404
['OFTString', 'OFTString', 'OFTString', 'OFTInteger', 'OFTString', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTReal', 'OFTReal']
406
You can iterate over each feature in the layer and extract information from both
407
the feature's geometry (accessed via the ``geom`` attribute) as well as the
406
You can iterate over each feature in the layer and extract information from both
407
the feature's geometry (accessed via the ``geom`` attribute) as well as the
408
408
feature's attribute fields (whose **values** are accessed via ``get()``
465
465
world_shp = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data/TM_WORLD_BORDERS-0.3.shp'))
467
467
def run(verbose=True):
468
lm = LayerMapping(WorldBorders, world_shp, world_mapping,
468
lm = LayerMapping(WorldBorders, world_shp, world_mapping,
469
469
transform=False, encoding='iso-8859-1')
471
471
lm.save(strict=True, verbose=verbose)
473
473
A few notes about what's going on:
475
475
* Each key in the ``world_mapping`` dictionary corresponds to a field in the
476
``WorldBorders`` model, and the value is the name of the shapefile field
477
that data will be loaded from.
476
``WorldBorders`` model, and the value is the name of the shapefile field
477
that data will be loaded from.
478
478
* The key ``mpoly`` for the geometry field is ``MULTIPOLYGON``, the
479
479
geometry type we wish to import as. Even if simple polygons are encountered
480
480
in the shapefile they will automatically be converted into collections prior
504
504
Try ``ogrinspect``
505
505
------------------
506
Now that you've seen how to define geographic models and import data with the
506
Now that you've seen how to define geographic models and import data with the
507
507
:ref:`ref-layermapping`, it's possible to further automate this process with
508
508
use of the :djadmin:`ogrinspect` management command. The :djadmin:`ogrinspect`
509
command introspects a GDAL-supported vector data source (e.g., a shapefile) and
509
command introspects a GDAL-supported vector data source (e.g., a shapefile) and
510
510
generates a model definition and ``LayerMapping`` dictionary automatically.
512
512
The general usage of the command goes as follows::
525
525
A few notes about the command-line options given above:
527
527
* The ``--srid=4326`` option sets the SRID for the geographic field.
528
* The ``--mapping`` option tells ``ogrinspect`` to also generate a
528
* The ``--mapping`` option tells ``ogrinspect`` to also generate a
529
529
mapping dictionary for use with :class:`~django.contrib.gis.utils.LayerMapping`.
530
530
* The ``--multi`` option is specified so that the geographic field is a
531
531
:class:`~django.contrib.gis.db.models.MultiPolygonField` instead of just a
532
532
:class:`~django.contrib.gis.db.models.PolygonField`.
534
The command produces the following output, which may be copied
534
The command produces the following output, which may be copied
535
535
directly into the ``models.py`` of a GeoDjango application::
537
537
# This is an auto-generated Django model module created by ogrinspect.
584
584
>>> pnt_wkt = 'POINT(-95.3385 29.7245)'
586
586
The ``pnt_wkt`` string represents the point at -95.3385 degrees longitude,
587
and 29.7245 degrees latitude. The geometry is in a format known as
587
and 29.7245 degrees latitude. The geometry is in a format known as
588
588
Well Known Text (WKT), an open standard issued by the Open Geospatial
589
589
Consortium (OGC). [#]_ Import the ``WorldBorders`` model, and perform
590
590
a ``contains`` lookup using the ``pnt_wkt`` as the parameter::
612
612
Automatic Spatial Transformations
613
613
---------------------------------
614
When querying the spatial database GeoDjango automatically transforms
614
When querying the spatial database GeoDjango automatically transforms
615
615
geometries if they're in a different coordinate system. In the following
616
616
example, the coordinate will be expressed in terms of `EPSG SRID 32140`__,
617
617
a coordinate system specific to south Texas **only** and in units of
634
634
('SELECT "world_worldborders"."id", "world_worldborders"."name", "world_worldborders"."area",
635
635
"world_worldborders"."pop2005", "world_worldborders"."fips", "world_worldborders"."iso2",
636
636
"world_worldborders"."iso3", "world_worldborders"."un", "world_worldborders"."region",
637
"world_worldborders"."subregion", "world_worldborders"."lon", "world_worldborders"."lat",
638
"world_worldborders"."mpoly" FROM "world_worldborders"
637
"world_worldborders"."subregion", "world_worldborders"."lon", "world_worldborders"."lat",
638
"world_worldborders"."mpoly" FROM "world_worldborders"
639
639
WHERE ST_Intersects("world_worldborders"."mpoly", ST_Transform(%s, 4326))',
640
640
(<django.contrib.gis.db.backend.postgis.adaptor.PostGISAdaptor object at 0x25641b0>,))
641
641
>>> qs # printing evaluates the queryset
642
[<WorldBorders: United States>]
642
[<WorldBorders: United States>]
644
644
__ http://spatialreference.org/ref/epsg/32140/
648
648
Geometries come to GeoDjango in a standardized textual representation. Upon
649
access of the geometry field, GeoDjango creates a `GEOS geometry object <ref-geos>`,
650
exposing powerful functionality, such as serialization properties for
649
access of the geometry field, GeoDjango creates a `GEOS geometry object <ref-geos>`,
650
exposing powerful functionality, such as serialization properties for
651
651
popular geospatial formats::
653
653
>>> sm = WorldBorders.objects.get(name='San Marino')
655
655
<MultiPolygon object at 0x24c6798>
656
>>> sm.mpoly.wkt # WKT
656
>>> sm.mpoly.wkt # WKT
657
657
MULTIPOLYGON (((12.4157980000000006 43.9579540000000009, 12.4505540000000003 43.9797209999999978, ...
658
658
>>> sm.mpoly.wkb # WKB (as Python binary buffer)
659
659
<read-only buffer for 0x1fe2c70, size -1, offset 0 at 0x2564c40>
685
GeoDjango extends :ref:`Django's admin application <ref-contrib-admin>` to
686
enable support for editing geometry fields.
685
GeoDjango extends :doc:`Django's admin application </ref/contrib/admin/index>`
686
to enable support for editing geometry fields.
691
GeoDjango also supplements the Django admin by allowing users to create
691
GeoDjango also supplements the Django admin by allowing users to create
692
692
and modify geometries on a JavaScript slippy map (powered by `OpenLayers`_).
694
Let's dive in again -- create a file called ``admin.py`` inside the
694
Let's dive in again -- create a file called ``admin.py`` inside the
695
695
``world`` application, and insert the following::
697
697
from django.contrib.gis import admin