PageRenderTime 30ms CodeModel.GetById 17ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 0ms

/docs/ref/contrib/gis/tutorial.txt

https://code.google.com/p/mango-py/
Plain Text | 757 lines | 578 code | 179 blank | 0 comment | 0 complexity | 0d0b528fc509f942a56e49ed00a10b0f MD5 | raw file
  1==================
  2GeoDjango Tutorial
  3==================
  4
  5Introduction
  6============
  7
  8GeoDjango is an add-on for Django that turns it into a world-class geographic
  9Web framework.  GeoDjango strives to make at as simple as possible to create
 10geographic 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
 18This tutorial assumes a familiarity with Django; thus, if you're brand new to
 19Django please read through the :doc:`regular tutorial </intro/tutorial01>` to introduce
 20yourself 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
 28This tutorial will guide you through the creation of a geographic Web
 29application for viewing the `world borders`_. [#]_ Some of the code
 30used in this tutorial is taken from and/or inspired by the `GeoDjango
 31basic 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
 42Setting Up
 43==========
 44
 45Create 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
 53First, a spatial database needs to be created for our project.  If using
 54PostgreSQL and PostGIS, then the following commands will
 55create 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
 73Users of SQLite and SpatiaLite should consult the instructions on how
 74to create a :ref:`SpatiaLite database <create_spatialite_db>`.
 75
 76Create GeoDjango Project
 77------------------------
 78
 79Use the ``django-admin.py`` script like normal to create a ``geodjango`` project::
 80
 81    $ django-admin.py startproject geodjango
 82
 83With the project initialized, now create a ``world`` Django application within
 84the ``geodjango`` project::
 85
 86    $ cd geodjango
 87    $ python manage.py startapp world
 88
 89Configure ``settings.py``
 90-------------------------
 91
 92The ``geodjango`` project settings are stored in the ``settings.py`` file.  Edit
 93the 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
107In addition, modify the :setting:`INSTALLED_APPS` setting to include
108:mod:`django.contrib.admin`, :mod:`django.contrib.gis`,
109and ``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
121Geographic Data
122===============
123
124.. _worldborders:
125
126World Borders
127-------------
128
129The world borders data is available in this `zip file`__.  Create a data directory
130in the ``world`` application, download the world borders data, and unzip.
131On 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
139The world borders ZIP file contains a set of data files collectively known as
140an `ESRI Shapefile`__, one of the most popular geospatial data formats.  When
141unzipped 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
153Use ``ogrinfo`` to examine spatial data
154---------------------------------------
155
156The GDAL ``ogrinfo`` utility is excellent for examining metadata about
157shapefiles (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
164Here ``ogrinfo`` is telling us that the shapefile has one layer, and that
165layer contains polygon data.  To find out more we'll specify the layer name
166and 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
194This detailed summary information tells us the number of features in the layer
195(246), the geographical extent, the spatial reference system ("SRS WKT"),
196as well as detailed information for each attribute field.  For example,
197``FIPS: String (2.0)`` indicates that there's a ``FIPS`` character field
198with a maximum length of 2; similarly, ``LON: Real (8.3)`` is a floating-point
199field that holds a maximum of 8 digits up to three decimal places.  Although
200this information may be found right on the `world borders`_ Web site, this shows
201you how to determine this information yourself when such metadata is not
202provided.
203
204Geographic Models
205=================
206
207Defining a Geographic Model
208---------------------------
209
210Now that we've examined our world borders data set using ``ogrinfo``, we can
211create 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
243Two important things to note:
244
2451. The ``models`` module is imported from :mod:`django.contrib.gis.db`.
2462. The model overrides its default manager with
247   :class:`~django.contrib.gis.db.models.GeoManager`; this is *required*
248   to perform spatial queries.
249
250When declaring a geometry field on your model the default spatial reference system
251is WGS84 (meaning the `SRID`__ is 4326) -- in other words, the field coordinates are in
252longitude/latitude pairs in units of degrees.  If you want the coordinate system to be
253different, then SRID of the geometry field may be customized by setting the ``srid``
254with an integer corresponding to the coordinate system of your choice.
255
256__ http://en.wikipedia.org/wiki/SRID
257
258Run ``syncdb``
259--------------
260
261After you've defined your model, it needs to be synced with the spatial database.
262First, let's look at the SQL that will generate the table for the ``WorldBorders``
263model::
264
265    $ python manage.py sqlall world
266
267This 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
290If 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
297The ``syncdb`` command may also prompt you to create an admin user; go ahead and
298do so (not required now, may be done at any point in the future using the
299``createsuperuser`` management command).
300
301Importing Spatial Data
302======================
303
304This section will show you how to take the data from the world borders
305shapefile and import it into GeoDjango models using the :ref:`ref-layermapping`.
306There are many different different ways to import data in to a
307spatial database -- besides the tools included within GeoDjango, you
308may 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
321GDAL Interface
322--------------
323
324Earlier we used the ``ogrinfo`` to explore the contents of the world borders
325shapefile.  Included within GeoDjango is an interface to GDAL's powerful OGR
326library -- in other words, you'll be able explore all the vector data sources
327that OGR supports via a Pythonic API.
328
329First, invoke the Django shell::
330
331    $ python manage.py shell
332
333If the :ref:`worldborders` data was downloaded like earlier in the
334tutorial, 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
342Now, 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
350Data source objects can have different layers of geospatial features; however,
351shapefiles 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
359You can see what the geometry type of the layer is and how many features it
360contains::
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
377The :class:`~django.contrib.gis.gdal.Layer` may also have a spatial reference
378system 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
391Here we've noticed that the shapefile is in the popular WGS84 spatial reference
392system -- in other words, the data uses units of degrees longitude and latitude.
393
394In addition, shapefiles also support attribute fields that may contain
395additional 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
400Here we are examining the OGR types (e.g., whether a field is an integer or
401a 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
406You can iterate over each feature in the layer and extract information from both
407the feature's geometry (accessed via the ``geom`` attribute) as well as the
408feature's attribute fields (whose **values** are accessed via ``get()``
409method)::
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
424And individual features may be retrieved by their feature ID::
425
426    >>> feat = lyr[234]
427    >>> print feat.get('NAME')
428    San Marino
429
430Here the boundary geometry for San Marino is extracted and looking
431exported 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
443We'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
473A 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
491Afterwards, invoke the Django shell from the ``geodjango`` project directory::
492
493   $ python manage.py shell
494
495Next, import the ``load`` module, call the ``run`` routine, and watch ``LayerMapping``
496do the work::
497
498   >>> from world import load
499   >>> load.run()
500
501
502.. _ogrinspect-intro:
503
504Try ``ogrinspect``
505------------------
506Now 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
508use of the :djadmin:`ogrinspect` management command.  The :djadmin:`ogrinspect`
509command  introspects a GDAL-supported vector data source (e.g., a shapefile) and
510generates a model definition and ``LayerMapping`` dictionary automatically.
511
512The general usage of the command goes as follows::
513
514    $ python manage.py ogrinspect [options] <data_source> <model_name> [options]
515
516Where ``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
518be used to further define how the model is generated.
519
520For example, the following command nearly reproduces the ``WorldBorders`` model
521and 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
525A 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
534The command produces the following output, which may be copied
535directly 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
571Spatial Queries
572===============
573
574Spatial Lookups
575---------------
576GeoDjango extends the Django ORM and allows the use of spatial lookups.
577Let's do an example where we find the ``WorldBorder`` model that contains
578a point.  First, fire up the management shell::
579
580    $ python manage.py shell
581
582Now, define a point of interest [#]_::
583
584    >>> pnt_wkt = 'POINT(-95.3385 29.7245)'
585
586The ``pnt_wkt`` string represents the point at -95.3385 degrees longitude,
587and 29.7245 degrees latitude.  The geometry is in a format known as
588Well Known Text (WKT), an open standard issued by the Open Geospatial
589Consortium (OGC). [#]_  Import the ``WorldBorders`` model, and perform
590a ``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
597Here we retrieved a ``GeoQuerySet`` that has only one model: the one
598for the United States (which is what we would expect).  Similarly,
599a :ref:`GEOS geometry object <ref-geos>` may also be used -- here the ``intersects``
600spatial lookup is combined with the ``get`` method to retrieve
601only 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
609The ``contains`` and ``intersects`` lookups are just a subset of what's
610available -- the :ref:`ref-gis-db-api` documentation has more.
611
612Automatic Spatial Transformations
613---------------------------------
614When querying the spatial database GeoDjango automatically transforms
615geometries if they're in a different coordinate system.  In the following
616example, the coordinate will be expressed in terms of `EPSG SRID 32140`__,
617a 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
623Note that ``pnt`` may also constructed with EWKT, an "extended" form of
624WKT that includes the SRID::
625
626    >>> pnt = GEOSGeometry('SRID=32140;POINT(954158.1 4215137.1)')
627
628When using GeoDjango's ORM, it will automatically wrap geometry values
629in transformation SQL, allowing the developer to work at a higher level
630of abstraction::
631
632    >>> qs = WorldBorders.objects.filter(mpoly__intersects=pnt)
633    >>> print qs.query # 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    >>> qs # printing evaluates the queryset
641    [<WorldBorders: United States>]
642
643__ http://spatialreference.org/ref/epsg/32140/
644
645Lazy Geometries
646---------------
647Geometries come to GeoDjango in a standardized textual representation.  Upon
648access of the geometry field, GeoDjango creates a `GEOS geometry object <ref-geos>`,
649exposing powerful functionality, such as serialization properties for
650popular geospatial formats::
651
652    >>> sm = WorldBorders.objects.get(name='San Marino')
653    >>> sm.mpoly
654    <MultiPolygon object at 0x24c6798>
655    >>> sm.mpoly.wkt # WKT
656    MULTIPOLYGON (((12.4157980000000006 43.9579540000000009, 12.4505540000000003 43.9797209999999978, ...
657    >>> sm.mpoly.wkb # WKB (as Python binary buffer)
658    <read-only buffer for 0x1fe2c70, size -1, offset 0 at 0x2564c40>
659    >>> sm.mpoly.geojson # GeoJSON (requires GDAL)
660    '{ "type": "MultiPolygon", "coordinates": [ [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ...
661
662This includes access to all of the advanced geometric operations provided by
663the GEOS library::
664
665    >>> pnt = Point(12.4604, 43.9420)
666    >>> sm.mpoly.contains(pnt)
667    True
668    >>> pnt.contains(sm.mpoly)
669    False
670
671``GeoQuerySet`` Methods
672-----------------------
673
674
675Putting your data on the map
676============================
677
678Google
679------
680
681Geographic Admin
682----------------
683
684GeoDjango extends :doc:`Django's admin application </ref/contrib/admin/index>`
685to enable support for editing geometry fields.
686
687Basics
688^^^^^^
689
690GeoDjango also supplements the Django admin by allowing users to create
691and modify geometries on a JavaScript slippy map (powered by `OpenLayers`_).
692
693Let's dive in again -- create a file called ``admin.py`` inside the
694``world`` application, and insert the following::
695
696    from django.contrib.gis import admin
697    from models import WorldBorders
698
699    admin.site.register(WorldBorders, admin.GeoModelAdmin)
700
701Next, edit your ``urls.py`` in the ``geodjango`` project folder to look
702as follows::
703
704    from django.conf.urls.defaults import *
705    from django.contrib.gis import admin
706
707    admin.autodiscover()
708
709    urlpatterns = patterns('',
710        (r'^admin/', include(admin.site.urls)),
711    )
712
713Start up the Django development server::
714
715    $ python manage.py runserver
716
717Finally, browse to ``http://localhost:8000/admin/``, and log in with the admin
718user created after running ``syncdb``.  Browse to any of the ``WorldBorders``
719entries -- the borders may be edited by clicking on a polygon and dragging
720the vertexes to the desired position.
721
722.. _OpenLayers: http://openlayers.org/
723.. _Open Street Map: http://openstreetmap.org/
724.. _Vector Map Level 0: http://earth-info.nga.mil/publications/vmap0.html
725.. _Metacarta: http://metacarta.com
726
727.. _osmgeoadmin-intro:
728
729``OSMGeoAdmin``
730^^^^^^^^^^^^^^^
731
732With the :class:`~django.contrib.gis.admin.OSMGeoAdmin`, GeoDjango uses
733a `Open Street Map`_ layer in the admin.
734This provides more context (including street and thoroughfare details) than
735available with the :class:`~django.contrib.gis.admin.GeoModelAdmin`
736(which uses the `Vector Map Level 0`_ WMS data set hosted at `Metacarta`_).
737
738First, there are some important requirements and limitations:
739
740* :class:`~django.contrib.gis.admin.OSMGeoAdmin` requires that the
741  :ref:`spherical mercator projection be added <addgoogleprojection>`
742  to the to be added to the ``spatial_ref_sys`` table (PostGIS 1.3 and
743  below, only).
744* The PROJ.4 datum shifting files must be installed (see the
745  :ref:`PROJ.4 installation instructions <proj4>` for more details).
746
747If you meet these requirements, then just substitute in the ``OSMGeoAdmin``
748option class in your ``admin.py`` file::
749
750    admin.site.register(WorldBorders, admin.OSMGeoAdmin)
751
752.. rubric:: Footnotes
753
754.. [#] Special thanks to Bj?¸rn Sandvik of `thematicmapping.org <http://thematicmapping.org>`_ for providing and maintaining this data set.
755.. [#] GeoDjango basic apps was written by Dane Springmeyer, Josh Livni, and Christopher Schmidt.
756.. [#] Here the point is for the `University of Houston Law Center <http://www.law.uh.edu/>`_ .
757.. [#] Open Geospatial Consortium, Inc., `OpenGIS Simple Feature Specification For SQL <http://www.opengis.org/docs/99-049.pdf>`_, Document 99-049.