parts/django/docs/ref/contrib/gis/tutorial.txt
changeset 307 c6bca38c1cbf
equal deleted inserted replaced
306:5ff1fc726848 307:c6bca38c1cbf
       
     1 ==================
       
     2 GeoDjango Tutorial
       
     3 ==================
       
     4 
       
     5 Introduction
       
     6 ============
       
     7 
       
     8 GeoDjango is an add-on for Django that turns it into a world-class geographic
       
     9 Web framework.  GeoDjango strives to make at as simple as possible to create
       
    10 geographic Web applications, like location-based services.  Some features include:
       
    11 
       
    12 * Django model fields for `OGC`_ geometries.
       
    13 * Extensions to Django's ORM for the querying and manipulation of spatial data.
       
    14 * Loosely-coupled, high-level Python interfaces for GIS geometry operations and
       
    15   data formats.
       
    16 * Editing of geometry fields inside the admin.
       
    17 
       
    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 yourself with basic Django concepts.
       
    21 
       
    22 .. note::
       
    23 
       
    24     GeoDjango has special prerequisites overwhat is required by Django --
       
    25     please consult the :ref:`installation documentation <ref-gis-install>`
       
    26     for more details.
       
    27 
       
    28 This tutorial will guide you through the creation of a geographic Web
       
    29 application for viewing the `world borders`_. [#]_ Some of the code
       
    30 used in this tutorial is taken from and/or inspired by the `GeoDjango
       
    31 basic apps`_ project. [#]_
       
    32 
       
    33 .. note::
       
    34 
       
    35     Proceed through the tutorial sections sequentially for step-by-step
       
    36     instructions.
       
    37 
       
    38 .. _OGC: http://www.opengeospatial.org/
       
    39 .. _world borders: http://thematicmapping.org/downloads/world_borders.php
       
    40 .. _GeoDjango basic apps: http://code.google.com/p/geodjango-basic-apps/
       
    41 
       
    42 Setting Up
       
    43 ==========
       
    44 
       
    45 Create a Spatial Database
       
    46 -------------------------
       
    47 
       
    48 .. note::
       
    49 
       
    50     MySQL and Oracle users can skip this section because spatial types
       
    51     are already built into the database.
       
    52 
       
    53 First, a spatial database needs to be created for our project.  If using
       
    54 PostgreSQL and PostGIS, then the following commands will
       
    55 create the database from a :ref:`spatial database template <spatialdb_template>`::
       
    56 
       
    57     $ createdb -T template_postgis geodjango
       
    58 
       
    59 .. note::
       
    60 
       
    61     This command must be issued by a database user that has permissions to
       
    62     create a database.  Here is an example set of commands to create such
       
    63     a user::
       
    64 
       
    65         $ sudo su - postgres
       
    66         $ createuser --createdb geo
       
    67         $ exit
       
    68 
       
    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
       
    71     system user that will be running GeoDjango.
       
    72 
       
    73 Users of SQLite and SpatiaLite should consult the instructions on how
       
    74 to create a :ref:`SpatiaLite database <create_spatialite_db>`.
       
    75 
       
    76 Create GeoDjango Project
       
    77 ------------------------
       
    78 
       
    79 Use the ``django-admin.py`` script like normal to create a ``geodjango`` project::
       
    80 
       
    81     $ django-admin.py startproject geodjango
       
    82 
       
    83 With the project initialized, now create a ``world`` Django application within
       
    84 the ``geodjango`` project::
       
    85 
       
    86     $ cd geodjango
       
    87     $ python manage.py startapp world
       
    88 
       
    89 Configure ``settings.py``
       
    90 -------------------------
       
    91 
       
    92 The ``geodjango`` project settings are stored in the ``settings.py`` file.  Edit
       
    93 the database connection settings appropriately::
       
    94 
       
    95     DATABASES = {
       
    96         'default': {
       
    97              'ENGINE': 'django.contrib.gis.db.backends.postgis',
       
    98              'NAME': 'geodjango',
       
    99              'USER': 'geo',
       
   100          }
       
   101     }
       
   102 
       
   103 .. note::
       
   104 
       
   105     These database settings are for Django 1.2 and above.
       
   106 
       
   107 In addition, modify the :setting:`INSTALLED_APPS` setting to include
       
   108 :mod:`django.contrib.admin`, :mod:`django.contrib.gis`,
       
   109 and ``world`` (our newly created application)::
       
   110 
       
   111     INSTALLED_APPS = (
       
   112         'django.contrib.auth',
       
   113         'django.contrib.contenttypes',
       
   114         'django.contrib.sessions',
       
   115         'django.contrib.sites',
       
   116         'django.contrib.admin',
       
   117         'django.contrib.gis',
       
   118         'world'
       
   119     )
       
   120 
       
   121 Geographic Data
       
   122 ===============
       
   123 
       
   124 .. _worldborders:
       
   125 
       
   126 World Borders
       
   127 -------------
       
   128 
       
   129 The world borders data is available in this `zip file`__.  Create a data directory
       
   130 in the ``world`` application, download the world borders data, and unzip.
       
   131 On GNU/Linux platforms the following commands should do it::
       
   132 
       
   133     $ mkdir world/data
       
   134     $ cd world/data
       
   135     $ wget http://thematicmapping.org/downloads/TM_WORLD_BORDERS-0.3.zip
       
   136     $ unzip TM_WORLD_BORDERS-0.3.zip
       
   137     $ cd ../..
       
   138 
       
   139 The world borders ZIP file contains a set of data files collectively known as
       
   140 an `ESRI Shapefile`__, one of the most popular geospatial data formats.  When
       
   141 unzipped the world borders data set includes files with the following extensions:
       
   142 
       
   143 * ``.shp``: Holds the vector data for the world borders geometries.
       
   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).
       
   147 * ``.prj``: Contains the spatial reference information for the geographic
       
   148   data stored in the shapefile.
       
   149 
       
   150 __ http://thematicmapping.org/downloads/TM_WORLD_BORDERS-0.3.zip
       
   151 __ http://en.wikipedia.org/wiki/Shapefile
       
   152 
       
   153 Use ``ogrinfo`` to examine spatial data
       
   154 ---------------------------------------
       
   155 
       
   156 The GDAL ``ogrinfo`` utility is excellent for examining metadata about
       
   157 shapefiles (or other vector data sources)::
       
   158 
       
   159     $ ogrinfo world/data/TM_WORLD_BORDERS-0.3.shp
       
   160     INFO: Open of `world/data/TM_WORLD_BORDERS-0.3.shp'
       
   161           using driver `ESRI Shapefile' successful.
       
   162     1: TM_WORLD_BORDERS-0.3 (Polygon)
       
   163 
       
   164 Here ``ogrinfo`` is telling us that the shapefile has one layer, and that
       
   165 layer contains polygon data.  To find out more we'll specify the layer name
       
   166 and use the ``-so`` option to get only important summary information::
       
   167 
       
   168     $ ogrinfo -so world/data/TM_WORLD_BORDERS-0.3.shp TM_WORLD_BORDERS-0.3
       
   169     INFO: Open of `world/data/TM_WORLD_BORDERS-0.3.shp'
       
   170           using driver `ESRI Shapefile' successful.
       
   171 
       
   172     Layer name: TM_WORLD_BORDERS-0.3
       
   173     Geometry: Polygon
       
   174     Feature Count: 246
       
   175     Extent: (-180.000000, -90.000000) - (180.000000, 83.623596)
       
   176     Layer SRS WKT:
       
   177     GEOGCS["GCS_WGS_1984",
       
   178         DATUM["WGS_1984",
       
   179             SPHEROID["WGS_1984",6378137.0,298.257223563]],
       
   180         PRIMEM["Greenwich",0.0],
       
   181         UNIT["Degree",0.0174532925199433]]
       
   182     FIPS: String (2.0)
       
   183     ISO2: String (2.0)
       
   184     ISO3: String (3.0)
       
   185     UN: Integer (3.0)
       
   186     NAME: String (50.0)
       
   187     AREA: Integer (7.0)
       
   188     POP2005: Integer (10.0)
       
   189     REGION: Integer (3.0)
       
   190     SUBREGION: Integer (3.0)
       
   191     LON: Real (8.3)
       
   192     LAT: Real (7.3)
       
   193 
       
   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"),
       
   196 as well as detailed information for each attribute field.  For example,
       
   197 ``FIPS: String (2.0)`` indicates that there's a ``FIPS`` character field
       
   198 with a maximum length of 2; similarly, ``LON: Real (8.3)`` is a floating-point
       
   199 field that holds a maximum of 8 digits up to three decimal places.  Although
       
   200 this information may be found right on the `world borders`_ Web site, this shows
       
   201 you how to determine this information yourself when such metadata is not
       
   202 provided.
       
   203 
       
   204 Geographic Models
       
   205 =================
       
   206 
       
   207 Defining a Geographic Model
       
   208 ---------------------------
       
   209 
       
   210 Now that we've examined our world borders data set using ``ogrinfo``, we can
       
   211 create a GeoDjango model to represent this data::
       
   212 
       
   213     from django.contrib.gis.db import models
       
   214 
       
   215     class WorldBorders(models.Model):
       
   216         # Regular Django fields corresponding to the attributes in the
       
   217 	# world borders shapefile.
       
   218         name = models.CharField(max_length=50)
       
   219         area = models.IntegerField()
       
   220         pop2005 = models.IntegerField('Population 2005')
       
   221         fips = models.CharField('FIPS Code', max_length=2)
       
   222         iso2 = models.CharField('2 Digit ISO', max_length=2)
       
   223         iso3 = models.CharField('3 Digit ISO', max_length=3)
       
   224         un = models.IntegerField('United Nations Code')
       
   225         region = models.IntegerField('Region Code')
       
   226         subregion = models.IntegerField('Sub-Region Code')
       
   227     	lon = models.FloatField()
       
   228     	lat = models.FloatField()
       
   229 
       
   230 	# GeoDjango-specific: a geometry field (MultiPolygonField), and
       
   231         # overriding the default manager with a GeoManager instance.
       
   232 	mpoly = models.MultiPolygonField()
       
   233 	objects = models.GeoManager()
       
   234 
       
   235         # So the model is pluralized correctly in the admin.
       
   236         class Meta:
       
   237             verbose_name_plural = "World Borders"
       
   238 
       
   239         # Returns the string representation of the model.
       
   240         def __unicode__(self):
       
   241             return self.name
       
   242 
       
   243 Two important things to note:
       
   244 
       
   245 1. The ``models`` module is imported from :mod:`django.contrib.gis.db`.
       
   246 2. The model overrides its default manager with
       
   247    :class:`~django.contrib.gis.db.models.GeoManager`; this is *required*
       
   248    to perform spatial queries.
       
   249 
       
   250 When declaring a geometry field on your model the default spatial reference system
       
   251 is WGS84 (meaning the `SRID`__ is 4326) -- in other words, the field coordinates are in
       
   252 longitude/latitude pairs in units of degrees.  If you want the coordinate system to be
       
   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.
       
   255 
       
   256 __ http://en.wikipedia.org/wiki/SRID
       
   257 
       
   258 Run ``syncdb``
       
   259 --------------
       
   260 
       
   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``
       
   263 model::
       
   264 
       
   265     $ python manage.py sqlall world
       
   266 
       
   267 This management command should produce the following output::
       
   268 
       
   269     BEGIN;
       
   270     CREATE TABLE "world_worldborders" (
       
   271         "id" serial NOT NULL PRIMARY KEY,
       
   272         "name" varchar(50) NOT NULL,
       
   273         "area" integer NOT NULL,
       
   274         "pop2005" integer NOT NULL,
       
   275         "fips" varchar(2) NOT NULL,
       
   276         "iso2" varchar(2) NOT NULL,
       
   277         "iso3" varchar(3) NOT NULL,
       
   278         "un" integer NOT NULL,
       
   279         "region" integer NOT NULL,
       
   280         "subregion" integer NOT NULL,
       
   281         "lon" double precision NOT NULL,
       
   282         "lat" double precision NOT NULL
       
   283     )
       
   284     ;
       
   285     SELECT AddGeometryColumn('world_worldborders', 'mpoly', 4326, 'MULTIPOLYGON', 2);
       
   286     ALTER TABLE "world_worldborders" ALTER "mpoly" SET NOT NULL;
       
   287     CREATE INDEX "world_worldborders_mpoly_id" ON "world_worldborders" USING GIST ( "mpoly" GIST_GEOMETRY_OPS );
       
   288     COMMIT;
       
   289 
       
   290 If satisfied, you may then create this table in the database by running the
       
   291 ``syncdb`` management command::
       
   292 
       
   293     $ python manage.py syncdb
       
   294     Creating table world_worldborders
       
   295     Installing custom SQL for world.WorldBorders model
       
   296 
       
   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
       
   299 ``createsuperuser`` management command).
       
   300 
       
   301 Importing Spatial Data
       
   302 ======================
       
   303 
       
   304 This section will show you how to take the data from the world borders
       
   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
       
   307 spatial database -- besides the tools included within GeoDjango, you
       
   308 may also use the following to populate your spatial database:
       
   309 
       
   310 * `ogr2ogr`_: Command-line utility, included with GDAL, that
       
   311   supports loading a multitude of vector data formats into
       
   312   the PostGIS, MySQL, and Oracle spatial databases.
       
   313 * `shp2pgsql`_: This utility is included with PostGIS and only supports
       
   314   ESRI shapefiles.
       
   315 
       
   316 .. _ogr2ogr: http://www.gdal.org/ogr2ogr.html
       
   317 .. _shp2pgsql: http://postgis.refractions.net/documentation/manual-1.5/ch04.html#shp2pgsql_usage
       
   318 
       
   319 .. _gdalinterface:
       
   320 
       
   321 GDAL Interface
       
   322 --------------
       
   323 
       
   324 Earlier we used the the ``ogrinfo`` to explore the contents of the world borders
       
   325 shapefile.  Included within GeoDjango is an interface to GDAL's powerful OGR
       
   326 library -- in other words, you'll be able explore all the vector data sources
       
   327 that OGR supports via a Pythonic API.
       
   328 
       
   329 First, invoke the Django shell::
       
   330 
       
   331     $ python manage.py shell
       
   332 
       
   333 If the :ref:`worldborders` data was downloaded like earlier in the
       
   334 tutorial, then we can determine the path using Python's built-in
       
   335 ``os`` module::
       
   336 
       
   337     >>> import os
       
   338     >>> from geodjango import world
       
   339     >>> world_shp = os.path.abspath(os.path.join(os.path.dirname(world.__file__),
       
   340     ...                             'data/TM_WORLD_BORDERS-0.3.shp'))
       
   341 
       
   342 Now, the world borders shapefile may be opened using GeoDjango's
       
   343 :class:`~django.contrib.gis.gdal.DataSource` interface::
       
   344 
       
   345     >>> from django.contrib.gis.gdal import *
       
   346     >>> ds = DataSource(world_shp)
       
   347     >>> print ds
       
   348     / ... /geodjango/world/data/TM_WORLD_BORDERS-0.3.shp (ESRI Shapefile)
       
   349 
       
   350 Data source objects can have different layers of geospatial features; however,
       
   351 shapefiles are only allowed to have one layer::
       
   352 
       
   353     >>> print len(ds)
       
   354     1
       
   355     >>> lyr = ds[0]
       
   356     >>> print lyr
       
   357     TM_WORLD_BORDERS-0.3
       
   358 
       
   359 You can see what the geometry type of the layer is and how many features it
       
   360 contains::
       
   361 
       
   362     >>> print lyr.geom_type
       
   363     Polygon
       
   364     >>> print len(lyr)
       
   365     246
       
   366 
       
   367 .. note::
       
   368 
       
   369     Unfortunately the shapefile data format does not allow for greater
       
   370     specificity with regards to geometry types.  This shapefile, like
       
   371     many others, actually includes ``MultiPolygon`` geometries in its
       
   372     features.  You need to watch out for this when creating your models
       
   373     as a GeoDjango ``PolygonField`` will not accept a ``MultiPolygon``
       
   374     type geometry -- thus a ``MultiPolygonField`` is used in our model's
       
   375     definition instead.
       
   376 
       
   377 The :class:`~django.contrib.gis.gdal.Layer` may also have a spatial reference
       
   378 system associated with it -- if it does, the ``srs`` attribute will return a
       
   379 :class:`~django.contrib.gis.gdal.SpatialReference` object::
       
   380 
       
   381     >>> srs = lyr.srs
       
   382     >>> print srs
       
   383     GEOGCS["GCS_WGS_1984",
       
   384         DATUM["WGS_1984",
       
   385             SPHEROID["WGS_1984",6378137.0,298.257223563]],
       
   386         PRIMEM["Greenwich",0.0],
       
   387         UNIT["Degree",0.0174532925199433]]
       
   388     >>> srs.proj4 # PROJ.4 representation
       
   389     '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs '
       
   390 
       
   391 Here we've noticed that the shapefile is in the popular WGS84 spatial reference
       
   392 system -- in other words, the data uses units of degrees longitude and latitude.
       
   393 
       
   394 In addition, shapefiles also support attribute fields that may contain
       
   395 additional data.  Here are the fields on the World Borders layer:
       
   396 
       
   397     >>> print lyr.fields
       
   398     ['FIPS', 'ISO2', 'ISO3', 'UN', 'NAME', 'AREA', 'POP2005', 'REGION', 'SUBREGION', 'LON', 'LAT']
       
   399 
       
   400 Here we are examining the OGR types (e.g., whether a field is an integer or
       
   401 a string) associated with each of the fields:
       
   402 
       
   403     >>> [fld.__name__ for fld in lyr.field_types]
       
   404     ['OFTString', 'OFTString', 'OFTString', 'OFTInteger', 'OFTString', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTReal', 'OFTReal']
       
   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
       
   408 feature's attribute fields (whose **values** are accessed via ``get()``
       
   409 method)::
       
   410 
       
   411     >>> for feat in lyr:
       
   412     ...    print feat.get('NAME'), feat.geom.num_points
       
   413     ...
       
   414     Guernsey 18
       
   415     Jersey 26
       
   416     South Georgia South Sandwich Islands 338
       
   417     Taiwan 363
       
   418 
       
   419 :class:`~django.contrib.gis.gdal.Layer` objects may be sliced::
       
   420 
       
   421     >>> lyr[0:2]
       
   422     [<django.contrib.gis.gdal.feature.Feature object at 0x2f47690>, <django.contrib.gis.gdal.feature.Feature object at 0x2f47650>]
       
   423 
       
   424 And individual features may be retrieved by their feature ID::
       
   425 
       
   426     >>> feat = lyr[234]
       
   427     >>> print feat.get('NAME')
       
   428     San Marino
       
   429 
       
   430 Here the boundary geometry for San Marino is extracted and looking
       
   431 exported to WKT and GeoJSON::
       
   432 
       
   433     >>> geom = feat.geom
       
   434     >>> print geom.wkt
       
   435     POLYGON ((12.415798 43.957954,12.450554 ...
       
   436     >>> print geom.json
       
   437     { "type": "Polygon", "coordinates": [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ...
       
   438 
       
   439 
       
   440 ``LayerMapping``
       
   441 ----------------
       
   442 
       
   443 We're going to dive right in -- create a file called ``load.py`` inside the
       
   444 ``world`` application, and insert the following::
       
   445 
       
   446     import os
       
   447     from django.contrib.gis.utils import LayerMapping
       
   448     from models import WorldBorders
       
   449 
       
   450     world_mapping = {
       
   451         'fips' : 'FIPS',
       
   452         'iso2' : 'ISO2',
       
   453         'iso3' : 'ISO3',
       
   454         'un' : 'UN',
       
   455         'name' : 'NAME',
       
   456         'area' : 'AREA',
       
   457         'pop2005' : 'POP2005',
       
   458         'region' : 'REGION',
       
   459         'subregion' : 'SUBREGION',
       
   460         'lon' : 'LON',
       
   461         'lat' : 'LAT',
       
   462         'mpoly' : 'MULTIPOLYGON',
       
   463     }
       
   464 
       
   465     world_shp = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data/TM_WORLD_BORDERS-0.3.shp'))
       
   466 
       
   467     def run(verbose=True):
       
   468         lm = LayerMapping(WorldBorders, world_shp, world_mapping,
       
   469                           transform=False, encoding='iso-8859-1')
       
   470 
       
   471         lm.save(strict=True, verbose=verbose)
       
   472 
       
   473 A few notes about what's going on:
       
   474 
       
   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.
       
   478 * The key ``mpoly`` for the geometry field is ``MULTIPOLYGON``, the
       
   479   geometry type we wish to import as.  Even if simple polygons are encountered
       
   480   in the shapefile they will automatically be converted into collections prior
       
   481   to insertion into the database.
       
   482 * The path to the shapefile is not absolute -- in other words, if you move the
       
   483   ``world`` application (with ``data`` subdirectory) to a different location,
       
   484   then the script will still work.
       
   485 * The ``transform`` keyword is set to ``False`` because the data in the
       
   486   shapefile does not need to be converted -- it's already in WGS84 (SRID=4326).
       
   487 * The ``encoding`` keyword is set to the character encoding of string values in
       
   488   the shapefile. This ensures that string values are read and saved correctly
       
   489   from their original encoding system.
       
   490 
       
   491 Afterwards, invoke the Django shell from the ``geodjango`` project directory::
       
   492 
       
   493    $ python manage.py shell
       
   494 
       
   495 Next, import the ``load`` module, call the ``run`` routine, and watch ``LayerMapping``
       
   496 do the work::
       
   497 
       
   498    >>> from world import load
       
   499    >>> load.run()
       
   500 
       
   501 
       
   502 .. _ogrinspect-intro:
       
   503 
       
   504 Try ``ogrinspect``
       
   505 ------------------
       
   506 Now that you've seen how to define geographic models and import data with the
       
   507 :ref:`ref-layermapping`, it's possible to further automate this process with
       
   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
       
   510 generates a model definition and ``LayerMapping`` dictionary automatically.
       
   511 
       
   512 The general usage of the command goes as follows::
       
   513 
       
   514     $ python manage.py ogrinspect [options] <data_source> <model_name> [options]
       
   515 
       
   516 Where ``data_source`` is the path to the GDAL-supported data source and
       
   517 ``model_name`` is the name to use for the model.  Command-line options may
       
   518 be used to further define how the model is generated.
       
   519 
       
   520 For example, the following command nearly reproduces the ``WorldBorders`` model
       
   521 and mapping dictionary created above, automatically::
       
   522 
       
   523     $ python manage.py ogrinspect world/data/TM_WORLD_BORDERS-0.3.shp WorldBorders --srid=4326 --mapping --multi
       
   524 
       
   525 A few notes about the command-line options given above:
       
   526 
       
   527 * The ``--srid=4326`` option sets the SRID for the geographic field.
       
   528 * The ``--mapping`` option tells ``ogrinspect`` to also generate a
       
   529   mapping dictionary for use with :class:`~django.contrib.gis.utils.LayerMapping`.
       
   530 * The ``--multi`` option is specified so that the geographic field is a
       
   531   :class:`~django.contrib.gis.db.models.MultiPolygonField` instead of just a
       
   532   :class:`~django.contrib.gis.db.models.PolygonField`.
       
   533 
       
   534 The command produces the following output, which may be copied
       
   535 directly into the ``models.py`` of a GeoDjango application::
       
   536 
       
   537     # This is an auto-generated Django model module created by ogrinspect.
       
   538     from django.contrib.gis.db import models
       
   539 
       
   540     class WorldBorders(models.Model):
       
   541         fips = models.CharField(max_length=2)
       
   542         iso2 = models.CharField(max_length=2)
       
   543         iso3 = models.CharField(max_length=3)
       
   544         un = models.IntegerField()
       
   545         name = models.CharField(max_length=50)
       
   546         area = models.IntegerField()
       
   547         pop2005 = models.IntegerField()
       
   548         region = models.IntegerField()
       
   549         subregion = models.IntegerField()
       
   550         lon = models.FloatField()
       
   551         lat = models.FloatField()
       
   552         geom = models.MultiPolygonField(srid=4326)
       
   553         objects = models.GeoManager()
       
   554 
       
   555     # Auto-generated `LayerMapping` dictionary for WorldBorders model
       
   556     worldborders_mapping = {
       
   557         'fips' : 'FIPS',
       
   558         'iso2' : 'ISO2',
       
   559         'iso3' : 'ISO3',
       
   560         'un' : 'UN',
       
   561         'name' : 'NAME',
       
   562         'area' : 'AREA',
       
   563         'pop2005' : 'POP2005',
       
   564         'region' : 'REGION',
       
   565         'subregion' : 'SUBREGION',
       
   566         'lon' : 'LON',
       
   567         'lat' : 'LAT',
       
   568         'geom' : 'MULTIPOLYGON',
       
   569     }
       
   570 
       
   571 Spatial Queries
       
   572 ===============
       
   573 
       
   574 Spatial Lookups
       
   575 ---------------
       
   576 GeoDjango extends the Django ORM and allows the use of spatial lookups.
       
   577 Let's do an example where we find the ``WorldBorder`` model that contains
       
   578 a point.  First, fire up the management shell::
       
   579 
       
   580     $ python manage.py shell
       
   581 
       
   582 Now, define a point of interest [#]_::
       
   583 
       
   584     >>> pnt_wkt = 'POINT(-95.3385 29.7245)'
       
   585 
       
   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
       
   588 Well Known Text (WKT), an open standard issued by the Open Geospatial
       
   589 Consortium (OGC). [#]_  Import the ``WorldBorders`` model, and perform
       
   590 a ``contains`` lookup using the ``pnt_wkt`` as the parameter::
       
   591 
       
   592     >>> from world.models import WorldBorders
       
   593     >>> qs = WorldBorders.objects.filter(mpoly__contains=pnt_wkt)
       
   594     >>> qs
       
   595     [<WorldBorders: United States>]
       
   596 
       
   597 Here we retrieved a ``GeoQuerySet`` that has only one model: the one
       
   598 for the United States (which is what we would expect).  Similarly,
       
   599 a :ref:`GEOS geometry object <ref-geos>` may also be used -- here the ``intersects``
       
   600 spatial lookup is combined with the ``get`` method to retrieve
       
   601 only the ``WorldBorders`` instance for San Marino instead of a queryset::
       
   602 
       
   603     >>> from django.contrib.gis.geos import Point
       
   604     >>> pnt = Point(12.4604, 43.9420)
       
   605     >>> sm = WorldBorders.objects.get(mpoly__intersects=pnt)
       
   606     >>> sm
       
   607     <WorldBorders: San Marino>
       
   608 
       
   609 The ``contains`` and ``intersects`` lookups are just a subset of what's
       
   610 available -- the :ref:`ref-gis-db-api` documentation has more.
       
   611 
       
   612 Automatic Spatial Transformations
       
   613 ---------------------------------
       
   614 When querying the spatial database GeoDjango automatically transforms
       
   615 geometries if they're in a different coordinate system.  In the following
       
   616 example, the coordinate will be expressed in terms of `EPSG SRID 32140`__,
       
   617 a coordinate system specific to south Texas **only** and in units of
       
   618 **meters** and not degrees::
       
   619 
       
   620     >>> from django.contrib.gis.geos import *
       
   621     >>> pnt = Point(954158.1, 4215137.1, srid=32140)
       
   622 
       
   623 Note that ``pnt`` may also constructed with EWKT, an "extended" form of
       
   624 WKT that includes the SRID::
       
   625 
       
   626     >>> pnt = GEOSGeometry('SRID=32140;POINT(954158.1 4215137.1)')
       
   627 
       
   628 When using GeoDjango's ORM, it will automatically wrap geometry values
       
   629 in transformation SQL, allowing the developer to work at a higher level
       
   630 of abstraction::
       
   631 
       
   632     >>> qs = WorldBorders.objects.filter(mpoly__intersects=pnt)
       
   633     >>> qs.query.as_sql() # Generating the SQL
       
   634     ('SELECT "world_worldborders"."id", "world_worldborders"."name", "world_worldborders"."area",
       
   635     "world_worldborders"."pop2005", "world_worldborders"."fips", "world_worldborders"."iso2",
       
   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"
       
   639     WHERE ST_Intersects("world_worldborders"."mpoly", ST_Transform(%s, 4326))',
       
   640     (<django.contrib.gis.db.backend.postgis.adaptor.PostGISAdaptor object at 0x25641b0>,))
       
   641     >>> qs # printing evaluates the queryset
       
   642     [<WorldBorders: United States>]
       
   643 
       
   644 __ http://spatialreference.org/ref/epsg/32140/
       
   645 
       
   646 Lazy Geometries
       
   647 ---------------
       
   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
       
   651 popular geospatial formats::
       
   652 
       
   653     >>> sm = WorldBorders.objects.get(name='San Marino')
       
   654     >>> sm.mpoly
       
   655     <MultiPolygon object at 0x24c6798>
       
   656     >>> sm.mpoly.wkt # WKT
       
   657     MULTIPOLYGON (((12.4157980000000006 43.9579540000000009, 12.4505540000000003 43.9797209999999978, ...
       
   658     >>> sm.mpoly.wkb # WKB (as Python binary buffer)
       
   659     <read-only buffer for 0x1fe2c70, size -1, offset 0 at 0x2564c40>
       
   660     >>> sm.mpoly.geojson # GeoJSON (requires GDAL)
       
   661     '{ "type": "MultiPolygon", "coordinates": [ [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ...
       
   662 
       
   663 This includes access to all of the advanced geometric operations provided by
       
   664 the GEOS library::
       
   665 
       
   666     >>> pnt = Point(12.4604, 43.9420)
       
   667     >>> sm.mpoly.contains(pnt)
       
   668     True
       
   669     >>> pnt.contains(sm.mpoly)
       
   670     False
       
   671 
       
   672 ``GeoQuerySet`` Methods
       
   673 -----------------------
       
   674 
       
   675 
       
   676 Putting your data on the map
       
   677 ============================
       
   678 
       
   679 Google
       
   680 ------
       
   681 
       
   682 Geographic Admin
       
   683 ----------------
       
   684 
       
   685 GeoDjango extends :doc:`Django's admin application </ref/contrib/admin/index>`
       
   686 to enable support for editing geometry fields.
       
   687 
       
   688 Basics
       
   689 ^^^^^^
       
   690 
       
   691 GeoDjango also supplements the Django admin by allowing users to create
       
   692 and modify geometries on a JavaScript slippy map (powered by `OpenLayers`_).
       
   693 
       
   694 Let's dive in again -- create a file called ``admin.py`` inside the
       
   695 ``world`` application, and insert the following::
       
   696 
       
   697     from django.contrib.gis import admin
       
   698     from models import WorldBorders
       
   699 
       
   700     admin.site.register(WorldBorders, admin.GeoModelAdmin)
       
   701 
       
   702 Next, edit your ``urls.py`` in the ``geodjango`` project folder to look
       
   703 as follows::
       
   704 
       
   705     from django.conf.urls.defaults import *
       
   706     from django.contrib.gis import admin
       
   707 
       
   708     admin.autodiscover()
       
   709 
       
   710     urlpatterns = patterns('',
       
   711         (r'^admin/', include(admin.site.urls)),
       
   712     )
       
   713 
       
   714 Start up the Django development server::
       
   715 
       
   716     $ python manage.py runserver
       
   717 
       
   718 Finally, browse to ``http://localhost:8000/admin/``, and log in with the admin
       
   719 user created after running ``syncdb``.  Browse to any of the ``WorldBorders``
       
   720 entries -- the borders may be edited by clicking on a polygon and dragging
       
   721 the vertexes to the desired position.
       
   722 
       
   723 .. _OpenLayers: http://openlayers.org/
       
   724 .. _Open Street Map: http://openstreetmap.org/
       
   725 .. _Vector Map Level 0: http://earth-info.nga.mil/publications/vmap0.html
       
   726 .. _Metacarta: http://metacarta.com
       
   727 
       
   728 .. _osmgeoadmin-intro:
       
   729 
       
   730 ``OSMGeoAdmin``
       
   731 ^^^^^^^^^^^^^^^
       
   732 
       
   733 With the :class:`~django.contrib.gis.admin.OSMGeoAdmin`, GeoDjango uses
       
   734 a `Open Street Map`_ layer in the admin.
       
   735 This provides more context (including street and thoroughfare details) than
       
   736 available with the :class:`~django.contrib.gis.admin.GeoModelAdmin`
       
   737 (which uses the `Vector Map Level 0`_ WMS data set hosted at `Metacarta`_).
       
   738 
       
   739 First, there are some important requirements and limitations:
       
   740 
       
   741 * :class:`~django.contrib.gis.admin.OSMGeoAdmin` requires that the
       
   742   :ref:`spherical mercator projection be added <addgoogleprojection>`
       
   743   to the to be added to the ``spatial_ref_sys`` table (PostGIS 1.3 and
       
   744   below, only).
       
   745 * The PROJ.4 datum shifting files must be installed (see the
       
   746   :ref:`PROJ.4 installation instructions <proj4>` for more details).
       
   747 
       
   748 If you meet these requirements, then just substitute in the ``OSMGeoAdmin``
       
   749 option class in your ``admin.py`` file::
       
   750 
       
   751     admin.site.register(WorldBorders, admin.OSMGeoAdmin)
       
   752 
       
   753 .. rubric:: Footnotes
       
   754 
       
   755 .. [#] Special thanks to Bjørn Sandvik of `thematicmapping.org <http://thematicmapping.org>`_ for providing and maintaining this data set.
       
   756 .. [#] GeoDjango basic apps was written by Dane Springmeyer, Josh Livni, and Christopher Schmidt.
       
   757 .. [#] Here the point is for the `University of Houston Law Center <http://www.law.uh.edu/>`_ .
       
   758 .. [#] Open Geospatial Consortium, Inc., `OpenGIS Simple Feature Specification For SQL <http://www.opengis.org/docs/99-049.pdf>`_, Document 99-049.