~ubuntu-branches/ubuntu/oneiric/python-django/oneiric-201108291626

« back to all changes in this revision

Viewing changes to docs/ref/contrib/gis/tutorial.txt

  • Committer: Bazaar Package Importer
  • Author(s): Jamie Strandboge
  • Date: 2010-10-12 11:34:35 UTC
  • mfrom: (1.1.12 upstream) (29.1.1 maverick-security)
  • Revision ID: james.westby@ubuntu.com-20101012113435-yy57c8tx6g9anf3e
Tags: 1.2.3-1ubuntu0.1
* SECURITY UPDATE: XSS in CSRF protections. New upstream release
  - CVE-2010-3082
* debian/patches/01_disable_url_verify_regression_tests.diff:
  - updated to disable another test that fails without internet connection
  - patch based on work by Kai Kasurinen and Krzysztof Klimonda
* debian/control: don't Build-Depends on locales-all, which doesn't exist
  in maverick

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
  data formats.
16
16
* Editing of geometry fields inside the admin.
17
17
 
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.
21
21
 
22
22
.. note::
27
27
 
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. [#]_
32
32
 
33
33
.. note::
34
34
 
35
 
    Proceed through the tutorial sections sequentially for step-by-step 
 
35
    Proceed through the tutorial sections sequentially for step-by-step
36
36
    instructions.
37
37
 
38
38
.. _OGC: http://www.opengeospatial.org/
51
51
    are already built into the database.
52
52
 
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>`::
56
56
 
57
57
    $ createdb -T template_postgis geodjango
58
 
    
 
58
 
59
59
.. note::
60
60
 
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
67
67
        $ exit
68
 
    
 
68
 
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.
72
72
 
73
73
Users of SQLite and SpatiaLite should consult the instructions on how
92
92
The ``geodjango`` project settings are stored in the ``settings.py`` file.  Edit
93
93
the database connection settings appropriately::
94
94
 
95
 
    DATABASES = { 
 
95
    DATABASES = {
96
96
        'default': {
97
97
             'ENGINE': 'django.contrib.gis.db.backends.postgis',
98
98
             'NAME': 'geodjango',
104
104
 
105
105
    These database settings are for Django 1.2 and above.
106
106
 
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)::
110
110
 
142
142
 
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.
149
149
 
153
153
Use ``ogrinfo`` to examine spatial data
154
154
---------------------------------------
155
155
 
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)::
158
158
 
159
159
    $ ogrinfo world/data/TM_WORLD_BORDERS-0.3.shp
192
192
    LAT: Real (7.3)
193
193
 
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
202
202
provided.
203
203
 
204
204
Geographic Models
213
213
    from django.contrib.gis.db import models
214
214
 
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()
229
229
 
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.
236
236
        class Meta:
237
237
            verbose_name_plural = "World Borders"
238
 
 
239
 
        # Returns the string representation of the model.       
 
238
 
 
239
        # Returns the string representation of the model.
240
240
        def __unicode__(self):
241
241
            return self.name
242
242
 
243
243
Two important things to note:
244
244
 
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.
249
249
 
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.
255
255
 
256
256
__ http://en.wikipedia.org/wiki/SRID
257
257
 
259
259
--------------
260
260
 
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``
263
263
model::
264
264
 
265
265
    $ python manage.py sqlall world
295
295
    Installing custom SQL for world.WorldBorders model
296
296
 
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).
300
300
 
301
301
Importing Spatial Data
303
303
 
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:
309
309
 
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'))
341
341
 
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::
344
344
 
345
345
    >>> from django.contrib.gis.gdal import *
347
347
    >>> print ds
348
348
    / ... /geodjango/world/data/TM_WORLD_BORDERS-0.3.shp (ESRI Shapefile)
349
349
 
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::
352
352
 
353
353
    >>> print len(ds)
367
367
.. note::
368
368
 
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.
376
376
 
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.
393
393
 
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:
396
396
 
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']
405
405
 
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()``
409
409
method)::
410
410
 
427
427
    >>> print feat.get('NAME')
428
428
    San Marino
429
429
 
430
 
Here the boundary geometry for San Marino is extracted and looking 
 
430
Here the boundary geometry for San Marino is extracted and looking
431
431
exported to WKT and GeoJSON::
432
432
 
433
433
    >>> geom = feat.geom
465
465
    world_shp = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data/TM_WORLD_BORDERS-0.3.shp'))
466
466
 
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')
470
470
 
471
471
        lm.save(strict=True, verbose=verbose)
473
473
A few notes about what's going on:
474
474
 
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
503
503
 
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.
511
511
 
512
512
The general usage of the command goes as follows::
525
525
A few notes about the command-line options given above:
526
526
 
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`.
533
533
 
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::
536
536
 
537
537
    # This is an auto-generated Django model module created by ogrinspect.
584
584
    >>> pnt_wkt = 'POINT(-95.3385 29.7245)'
585
585
 
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::
611
611
 
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>]
643
643
 
644
644
__ http://spatialreference.org/ref/epsg/32140/
645
645
 
646
646
Lazy Geometries
647
647
---------------
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::
652
652
 
653
653
    >>> sm = WorldBorders.objects.get(name='San Marino')
654
654
    >>> sm.mpoly
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>
682
682
Geographic Admin
683
683
----------------
684
684
 
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.
687
687
 
688
688
Basics
689
689
^^^^^^
690
690
 
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`_).
693
693
 
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::
696
696
 
697
697
    from django.contrib.gis import admin