mirror of
https://github.com/django/django.git
synced 2025-08-04 10:59:45 +00:00
Fixed #12930, #12832, #11538 -- Refactored and merged the GeoDjango documentation into the rest of the Django docs.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12856 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
667ced2abc
commit
459c71e332
25 changed files with 7101 additions and 5 deletions
758
docs/ref/contrib/gis/tutorial.txt
Normal file
758
docs/ref/contrib/gis/tutorial.txt
Normal file
|
@ -0,0 +1,758 @@
|
|||
==================
|
||||
GeoDjango Tutorial
|
||||
==================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
GeoDjango is an add-on for Django that turns it into a world-class geographic
|
||||
web framework. GeoDjango strives to make at as simple as possible to create
|
||||
geographic web applications, like location-based services. Some features include:
|
||||
|
||||
* Django model fields for `OGC`_ geometries.
|
||||
* Extensions to Django's ORM for the querying and manipulation of spatial data.
|
||||
* Loosely-coupled, high-level Python interfaces for GIS geometry operations and
|
||||
data formats.
|
||||
* Editing of geometry fields inside the admin.
|
||||
|
||||
This tutorial assumes a familiarity with Django; thus, if you're brand new to
|
||||
Django please read through the :ref:`regular tutorial <intro-tutorial01>` to introduce
|
||||
yourself with basic Django concepts.
|
||||
|
||||
.. note::
|
||||
|
||||
GeoDjango has special prerequisites overwhat is required by Django --
|
||||
please consult the :ref:`installation documentation <ref-gis-install>`
|
||||
for more details.
|
||||
|
||||
This tutorial is going to guide you through guide the user through the creation
|
||||
of a geographic web application for viewing the `world borders`_. [#]_ Some of
|
||||
the code used in this tutorial is taken from and/or inspired by the
|
||||
`GeoDjango basic apps`_ project. [#]_
|
||||
|
||||
.. note::
|
||||
|
||||
Proceed through the tutorial sections sequentially for step-by-step
|
||||
instructions.
|
||||
|
||||
.. _OGC: http://www.opengeospatial.org/
|
||||
.. _world borders: http://thematicmapping.org/downloads/world_borders.php
|
||||
.. _GeoDjango basic apps: http://code.google.com/p/geodjango-basic-apps/
|
||||
|
||||
Setting Up
|
||||
==========
|
||||
|
||||
Create a Spatial Database
|
||||
-------------------------
|
||||
|
||||
.. note::
|
||||
|
||||
MySQL and Oracle users can skip this section because spatial types
|
||||
are already built into the database.
|
||||
|
||||
First, a spatial database needs to be created for our project. If using
|
||||
PostgreSQL and PostGIS, then the following commands will
|
||||
create the database from a :ref:`spatial database template <spatialdb_template>`::
|
||||
|
||||
$ createdb -T template_postgis geodjango
|
||||
|
||||
.. note::
|
||||
|
||||
This command must be issued by a database user that has permissions to
|
||||
create a database. Here is an example set of commands to create such
|
||||
a user::
|
||||
|
||||
$ sudo su - postgres
|
||||
$ createuser --createdb geo
|
||||
$ exit
|
||||
|
||||
Replace ``geo`` to correspond to the system login user name will be
|
||||
connecting to the database. For example, ``johndoe`` if that is the
|
||||
system user that will be running GeoDjango.
|
||||
|
||||
Users of SQLite and SpatiaLite should consult the instructions on how
|
||||
to create a :ref:`SpatiaLite database <create_spatialite_db>`.
|
||||
|
||||
Create GeoDjango Project
|
||||
------------------------
|
||||
|
||||
Use the ``django-admin.py`` script like normal to create a ``geodjango`` project::
|
||||
|
||||
$ django-admin.py startproject geodjango
|
||||
|
||||
With the project initialized, now create a ``world`` Django application within
|
||||
the ``geodjango`` project::
|
||||
|
||||
$ cd geodjango
|
||||
$ python manage.py startapp world
|
||||
|
||||
Configure ``settings.py``
|
||||
-------------------------
|
||||
|
||||
The ``geodjango`` project settings are stored in the ``settings.py`` file. Edit
|
||||
the database connection settings appropriately::
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.contrib.gis.db.backends.postgis',
|
||||
'NAME': 'geodjango',
|
||||
'USER': 'geo',
|
||||
}
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
These database settings are for Django 1.2 and above.
|
||||
|
||||
In addition, modify the :setting:`INSTALLED_APPS` setting to include
|
||||
:mod:`django.contrib.admin`, :mod:`django.contrib.gis`,
|
||||
and ``world`` (our newly created application)::
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.gis',
|
||||
'world'
|
||||
)
|
||||
|
||||
Geographic Data
|
||||
===============
|
||||
|
||||
.. _worldborders:
|
||||
|
||||
World Borders
|
||||
-------------
|
||||
|
||||
The world borders data is available in this `zip file`__. Create a data directory
|
||||
in the ``world`` application, download the world borders data, and unzip.
|
||||
On GNU/Linux platforms the following commands should do it::
|
||||
|
||||
$ mkdir world/data
|
||||
$ cd world/data
|
||||
$ wget http://thematicmapping.org/downloads/TM_WORLD_BORDERS-0.3.zip
|
||||
$ unzip TM_WORLD_BORDERS-0.3.zip
|
||||
$ cd ../..
|
||||
|
||||
The world borders ZIP file contains a set of data files collectively known as
|
||||
an `ESRI Shapefile`__, one of the most popular geospatial data formats. When
|
||||
unzipped the world borders data set includes files with the following extensions:
|
||||
|
||||
* ``.shp``: Holds the vector data for the world borders geometries.
|
||||
* ``.shx``: Spatial index file for geometries stored in the ``.shp``.
|
||||
* ``.dbf``: Database file for holding non-geometric attribute data
|
||||
(e.g., integer and character fields).
|
||||
* ``.prj``: Contains the spatial reference information for the geographic
|
||||
data stored in the shapefile.
|
||||
|
||||
__ http://thematicmapping.org/downloads/TM_WORLD_BORDERS-0.3.zip
|
||||
__ http://en.wikipedia.org/wiki/Shapefile
|
||||
|
||||
Use ``ogrinfo`` to examine spatial data
|
||||
---------------------------------------
|
||||
|
||||
The GDAL ``ogrinfo`` utility is excellent for examining metadata about
|
||||
shapefiles (or other vector data sources)::
|
||||
|
||||
$ ogrinfo world/data/TM_WORLD_BORDERS-0.3.shp
|
||||
INFO: Open of `world/data/TM_WORLD_BORDERS-0.3.shp'
|
||||
using driver `ESRI Shapefile' successful.
|
||||
1: TM_WORLD_BORDERS-0.3 (Polygon)
|
||||
|
||||
Here ``ogrinfo`` is telling us that the shapefile has one layer, and that
|
||||
layer contains polygon data. To find out more we'll specify the layer name
|
||||
and use the ``-so`` option to get only important summary information::
|
||||
|
||||
$ ogrinfo -so world/data/TM_WORLD_BORDERS-0.3.shp TM_WORLD_BORDERS-0.3
|
||||
INFO: Open of `world/data/TM_WORLD_BORDERS-0.3.shp'
|
||||
using driver `ESRI Shapefile' successful.
|
||||
|
||||
Layer name: TM_WORLD_BORDERS-0.3
|
||||
Geometry: Polygon
|
||||
Feature Count: 246
|
||||
Extent: (-180.000000, -90.000000) - (180.000000, 83.623596)
|
||||
Layer SRS WKT:
|
||||
GEOGCS["GCS_WGS_1984",
|
||||
DATUM["WGS_1984",
|
||||
SPHEROID["WGS_1984",6378137.0,298.257223563]],
|
||||
PRIMEM["Greenwich",0.0],
|
||||
UNIT["Degree",0.0174532925199433]]
|
||||
FIPS: String (2.0)
|
||||
ISO2: String (2.0)
|
||||
ISO3: String (3.0)
|
||||
UN: Integer (3.0)
|
||||
NAME: String (50.0)
|
||||
AREA: Integer (7.0)
|
||||
POP2005: Integer (10.0)
|
||||
REGION: Integer (3.0)
|
||||
SUBREGION: Integer (3.0)
|
||||
LON: Real (8.3)
|
||||
LAT: Real (7.3)
|
||||
|
||||
This detailed summary information tells us the number of features in the layer
|
||||
(246), the geographical extent, the spatial reference system ("SRS WKT"),
|
||||
as well as detailed information for each attribute field. For example,
|
||||
``FIPS: String (2.0)`` indicates that there's a ``FIPS`` character field
|
||||
with a maximum length of 2; similarly, ``LON: Real (8.3)`` is a floating-point
|
||||
field that holds a maximum of 8 digits up to three decimal places. Although
|
||||
this information may be found right on the `world borders`_ website, this shows
|
||||
you how to determine this information yourself when such metadata is not
|
||||
provided.
|
||||
|
||||
Geographic Models
|
||||
=================
|
||||
|
||||
Defining a Geographic Model
|
||||
---------------------------
|
||||
|
||||
Now that we've examined our world borders data set using ``ogrinfo``, we can
|
||||
create a GeoDjango model to represent this data::
|
||||
|
||||
from django.contrib.gis.db import models
|
||||
|
||||
class WorldBorders(models.Model):
|
||||
# Regular Django fields corresponding to the attributes in the
|
||||
# world borders shapefile.
|
||||
name = models.CharField(max_length=50)
|
||||
area = models.IntegerField()
|
||||
pop2005 = models.IntegerField('Population 2005')
|
||||
fips = models.CharField('FIPS Code', max_length=2)
|
||||
iso2 = models.CharField('2 Digit ISO', max_length=2)
|
||||
iso3 = models.CharField('3 Digit ISO', max_length=3)
|
||||
un = models.IntegerField('United Nations Code')
|
||||
region = models.IntegerField('Region Code')
|
||||
subregion = models.IntegerField('Sub-Region Code')
|
||||
lon = models.FloatField()
|
||||
lat = models.FloatField()
|
||||
|
||||
# GeoDjango-specific: a geometry field (MultiPolygonField), and
|
||||
# overriding the default manager with a GeoManager instance.
|
||||
mpoly = models.MultiPolygonField()
|
||||
objects = models.GeoManager()
|
||||
|
||||
# So the model is pluralized correctly in the admin.
|
||||
class Meta:
|
||||
verbose_name_plural = "World Borders"
|
||||
|
||||
# Returns the string representation of the model.
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
Two important things to note:
|
||||
|
||||
1. The ``models`` module is imported from :mod:`django.contrib.gis.db`.
|
||||
2. The model overrides its default manager with
|
||||
:class:`~django.contrib.gis.db.models.GeoManager`; this is *required*
|
||||
to perform spatial queries.
|
||||
|
||||
When declaring a geometry field on your model the default spatial reference system
|
||||
is WGS84 (meaning the `SRID`__ is 4326) -- in other words, the field coordinates are in
|
||||
longitude/latitude pairs in units of degrees. If you want the coordinate system to be
|
||||
different, then SRID of the geometry field may be customized by setting the ``srid``
|
||||
with an integer corresponding to the coordinate system of your choice.
|
||||
|
||||
__ http://en.wikipedia.org/wiki/SRID
|
||||
|
||||
Run ``syncdb``
|
||||
--------------
|
||||
|
||||
After you've defined your model, it needs to be synced with the spatial database.
|
||||
First, let's look at the SQL that will generate the table for the ``WorldBorders``
|
||||
model::
|
||||
|
||||
$ python manage.py sqlall world
|
||||
|
||||
This management command should produce the following output::
|
||||
|
||||
BEGIN;
|
||||
CREATE TABLE "world_worldborders" (
|
||||
"id" serial NOT NULL PRIMARY KEY,
|
||||
"name" varchar(50) NOT NULL,
|
||||
"area" integer NOT NULL,
|
||||
"pop2005" integer NOT NULL,
|
||||
"fips" varchar(2) NOT NULL,
|
||||
"iso2" varchar(2) NOT NULL,
|
||||
"iso3" varchar(3) NOT NULL,
|
||||
"un" integer NOT NULL,
|
||||
"region" integer NOT NULL,
|
||||
"subregion" integer NOT NULL,
|
||||
"lon" double precision NOT NULL,
|
||||
"lat" double precision NOT NULL
|
||||
)
|
||||
;
|
||||
SELECT AddGeometryColumn('world_worldborders', 'mpoly', 4326, 'MULTIPOLYGON', 2);
|
||||
ALTER TABLE "world_worldborders" ALTER "mpoly" SET NOT NULL;
|
||||
CREATE INDEX "world_worldborders_mpoly_id" ON "world_worldborders" USING GIST ( "mpoly" GIST_GEOMETRY_OPS );
|
||||
COMMIT;
|
||||
|
||||
If satisfied, you may then create this table in the database by running the
|
||||
``syncdb`` management command::
|
||||
|
||||
$ python manage.py syncdb
|
||||
Creating table world_worldborders
|
||||
Installing custom SQL for world.WorldBorders model
|
||||
|
||||
The ``syncdb`` command may also prompt you to create an admin user; go ahead and
|
||||
do so (not required now, may be done at any point in the future using the
|
||||
``createsuperuser`` management command).
|
||||
|
||||
Importing Spatial Data
|
||||
======================
|
||||
|
||||
This section will show you how to take the data from the world borders
|
||||
shapefile and import it into GeoDjango models using the :ref:`ref-layermapping`.
|
||||
There are many different different ways to import data in to a
|
||||
spatial database -- besides the tools included within GeoDjango, you
|
||||
may also use the following to populate your spatial database:
|
||||
|
||||
* `ogr2ogr`_: Command-line utility, included with GDAL, that
|
||||
supports loading a multitude of vector data formats into
|
||||
the PostGIS, MySQL, and Oracle spatial databases.
|
||||
* `shp2pgsql`_: This utility is included with PostGIS and only supports
|
||||
ESRI shapefiles.
|
||||
|
||||
.. _ogr2ogr: http://www.gdal.org/ogr/ogr2ogr.html
|
||||
.. _shp2pgsql: http://postgis.refractions.net/documentation/manual-1.5/ch04.html#shp2pgsql_usage
|
||||
|
||||
.. _gdalinterface:
|
||||
|
||||
GDAL Interface
|
||||
--------------
|
||||
|
||||
Earlier we used the the ``ogrinfo`` to explore the contents of the world borders
|
||||
shapefile. Included within GeoDjango is an interface to GDAL's powerful OGR
|
||||
library -- in other words, you'll be able explore all the vector data sources
|
||||
that OGR supports via a Pythonic API.
|
||||
|
||||
First, invoke the Django shell::
|
||||
|
||||
$ python manage.py shell
|
||||
|
||||
If the :ref:`worldborders` data was downloaded like earlier in the
|
||||
tutorial, then we can determine the path using Python's built-in
|
||||
``os`` module::
|
||||
|
||||
>>> import os
|
||||
>>> from geodjango import world
|
||||
>>> world_shp = os.path.abspath(os.path.join(os.path.dirname(world.__file__),
|
||||
... 'data/TM_WORLD_BORDERS-0.3.shp'))
|
||||
|
||||
Now, the world borders shapefile may be opened using GeoDjango's
|
||||
:class:`~django.contrib.gis.gdal.DataSource` interface::
|
||||
|
||||
>>> from django.contrib.gis.gdal import *
|
||||
>>> ds = DataSource(world_shp)
|
||||
>>> print ds
|
||||
/ ... /geodjango/world/data/TM_WORLD_BORDERS-0.3.shp (ESRI Shapefile)
|
||||
|
||||
Data source objects can have different layers of geospatial features; however,
|
||||
shapefiles are only allowed to have one layer::
|
||||
|
||||
>>> print len(ds)
|
||||
1
|
||||
>>> lyr = ds[0]
|
||||
>>> print lyr
|
||||
TM_WORLD_BORDERS-0.3
|
||||
|
||||
You can see what the geometry type of the layer is and how many features it
|
||||
contains::
|
||||
|
||||
>>> print lyr.geom_type
|
||||
Polygon
|
||||
>>> print len(lyr)
|
||||
246
|
||||
|
||||
.. note::
|
||||
|
||||
Unfortunately the shapefile data format does not allow for greater
|
||||
specificity with regards to geometry types. This shapefile, like
|
||||
many others, actually includes ``MultiPolygon`` geometries in its
|
||||
features. You need to watch out for this when creating your models
|
||||
as a GeoDjango ``PolygonField`` will not accept a ``MultiPolygon``
|
||||
type geometry -- thus a ``MultiPolygonField`` is used in our model's
|
||||
definition instead.
|
||||
|
||||
The :class:`~django.contrib.gis.gdal.Layer` may also have a spatial reference
|
||||
system associated with it -- if it does, the ``srs`` attribute will return a
|
||||
:class:`~django.contrib.gis.gdal.SpatialReference` object::
|
||||
|
||||
>>> srs = lyr.srs
|
||||
>>> print srs
|
||||
GEOGCS["GCS_WGS_1984",
|
||||
DATUM["WGS_1984",
|
||||
SPHEROID["WGS_1984",6378137.0,298.257223563]],
|
||||
PRIMEM["Greenwich",0.0],
|
||||
UNIT["Degree",0.0174532925199433]]
|
||||
>>> srs.proj4 # PROJ.4 representation
|
||||
'+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs '
|
||||
|
||||
Here we've noticed that the shapefile is in the popular WGS84 spatial reference
|
||||
system -- in other words, the data uses units of degrees longitude and latitude.
|
||||
|
||||
In addition, shapefiles also support attribute fields that may contain
|
||||
additional data. Here are the fields on the World Borders layer:
|
||||
|
||||
>>> print lyr.fields
|
||||
['FIPS', 'ISO2', 'ISO3', 'UN', 'NAME', 'AREA', 'POP2005', 'REGION', 'SUBREGION', 'LON', 'LAT']
|
||||
|
||||
Here we are examining the OGR types (e.g., whether a field is an integer or
|
||||
a string) associated with each of the fields:
|
||||
|
||||
>>> [fld.__name__ for fld in lyr.field_types]
|
||||
['OFTString', 'OFTString', 'OFTString', 'OFTInteger', 'OFTString', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTReal', 'OFTReal']
|
||||
|
||||
You can iterate over each feature in the layer and extract information from both
|
||||
the feature's geometry (accessed via the ``geom`` attribute) as well as the
|
||||
feature's attribute fields (whose **values** are accessed via ``get()``
|
||||
method)::
|
||||
|
||||
>>> for feat in lyr:
|
||||
... print feat.get('NAME'), feat.geom.num_points
|
||||
...
|
||||
Guernsey 18
|
||||
Jersey 26
|
||||
South Georgia South Sandwich Islands 338
|
||||
Taiwan 363
|
||||
|
||||
:class:`~django.contrib.gis.gdal.Layer` objects may be sliced::
|
||||
|
||||
>>> lyr[0:2]
|
||||
[<django.contrib.gis.gdal.feature.Feature object at 0x2f47690>, <django.contrib.gis.gdal.feature.Feature object at 0x2f47650>]
|
||||
|
||||
And individual features may be retrieved by their feature ID::
|
||||
|
||||
>>> feat = lyr[234]
|
||||
>>> print feat.get('NAME')
|
||||
San Marino
|
||||
|
||||
Here the boundary geometry for San Marino is extracted and looking
|
||||
exported to WKT and GeoJSON::
|
||||
|
||||
>>> geom = feat.geom
|
||||
>>> print geom.wkt
|
||||
POLYGON ((12.415798 43.957954,12.450554 ...
|
||||
>>> print geom.json
|
||||
{ "type": "Polygon", "coordinates": [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ...
|
||||
|
||||
|
||||
``LayerMapping``
|
||||
----------------
|
||||
|
||||
We're going to dive right in -- create a file called ``load.py`` inside the
|
||||
``world`` application, and insert the following::
|
||||
|
||||
import os
|
||||
from django.contrib.gis.utils import LayerMapping
|
||||
from models import WorldBorders
|
||||
|
||||
world_mapping = {
|
||||
'fips' : 'FIPS',
|
||||
'iso2' : 'ISO2',
|
||||
'iso3' : 'ISO3',
|
||||
'un' : 'UN',
|
||||
'name' : 'NAME',
|
||||
'area' : 'AREA',
|
||||
'pop2005' : 'POP2005',
|
||||
'region' : 'REGION',
|
||||
'subregion' : 'SUBREGION',
|
||||
'lon' : 'LON',
|
||||
'lat' : 'LAT',
|
||||
'mpoly' : 'MULTIPOLYGON',
|
||||
}
|
||||
|
||||
world_shp = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data/TM_WORLD_BORDERS-0.3.shp'))
|
||||
|
||||
def run(verbose=True):
|
||||
lm = LayerMapping(WorldBorders, world_shp, world_mapping,
|
||||
transform=False, encoding='iso-8859-1')
|
||||
|
||||
lm.save(strict=True, verbose=verbose)
|
||||
|
||||
A few notes about what's going on:
|
||||
|
||||
* Each key in the ``world_mapping`` dictionary corresponds to a field in the
|
||||
``WorldBorders`` model, and the value is the name of the shapefile field
|
||||
that data will be loaded from.
|
||||
* The key ``mpoly`` for the geometry field is ``MULTIPOLYGON``, the
|
||||
geometry type we wish to import as. Even if simple polygons are encountered
|
||||
in the shapefile they will automatically be converted into collections prior
|
||||
to insertion into the database.
|
||||
* The path to the shapefile is not absolute -- in other words, if you move the
|
||||
``world`` application (with ``data`` subdirectory) to a different location,
|
||||
then the script will still work.
|
||||
* The ``transform`` keyword is set to ``False`` because the data in the
|
||||
shapefile does not need to be converted -- it's already in WGS84 (SRID=4326).
|
||||
* The ``encoding`` keyword is set to the character encoding of string values in
|
||||
the shapefile. This ensures that string values are read and saved correctly
|
||||
from their original encoding system.
|
||||
|
||||
Afterwards, invoke the Django shell from the ``geodjango`` project directory::
|
||||
|
||||
$ python manage.py shell
|
||||
|
||||
Next, import the ``load`` module, call the ``run`` routine, and watch ``LayerMapping``
|
||||
do the work::
|
||||
|
||||
>>> from world import load
|
||||
>>> load.run()
|
||||
|
||||
|
||||
.. _ogrinspect-intro:
|
||||
|
||||
Try ``ogrinspect``
|
||||
------------------
|
||||
Now that you've seen how to define geographic models and import data with the
|
||||
:ref:`ref-layermapping`, it's possible to further automate this process with
|
||||
use of the :djadmin:`ogrinspect` management command. The :djadmin:`ogrinspect`
|
||||
command introspects a GDAL-supported vector data source (e.g., a shapefile) and
|
||||
generates a model definition and ``LayerMapping`` dictionary automatically.
|
||||
|
||||
The general usage of the command goes as follows::
|
||||
|
||||
$ python manage.py ogrinspect [options] <data_source> <model_name> [options]
|
||||
|
||||
Where ``data_source`` is the path to the GDAL-supported data source and
|
||||
``model_name`` is the name to use for the model. Command-line options may
|
||||
be used to further define how the model is generated.
|
||||
|
||||
For example, the following command nearly reproduces the ``WorldBorders`` model
|
||||
and mapping dictionary created above, automatically::
|
||||
|
||||
$ python manage.py ogrinspect world/data/TM_WORLD_BORDERS-0.3.shp WorldBorders --srid=4326 --mapping --multi
|
||||
|
||||
A few notes about the command-line options given above:
|
||||
|
||||
* The ``--srid=4326`` option sets the SRID for the geographic field.
|
||||
* The ``--mapping`` option tells ``ogrinspect`` to also generate a
|
||||
mapping dictionary for use with :class:`~django.contrib.gis.utils.LayerMapping`.
|
||||
* The ``--multi`` option is specified so that the geographic field is a
|
||||
:class:`~django.contrib.gis.db.models.MultiPolygonField` instead of just a
|
||||
:class:`~django.contrib.gis.db.models.PolygonField`.
|
||||
|
||||
The command produces the following output, which may be copied
|
||||
directly into the ``models.py`` of a GeoDjango application::
|
||||
|
||||
# This is an auto-generated Django model module created by ogrinspect.
|
||||
from django.contrib.gis.db import models
|
||||
|
||||
class WorldBorders(models.Model):
|
||||
fips = models.CharField(max_length=2)
|
||||
iso2 = models.CharField(max_length=2)
|
||||
iso3 = models.CharField(max_length=3)
|
||||
un = models.IntegerField()
|
||||
name = models.CharField(max_length=50)
|
||||
area = models.IntegerField()
|
||||
pop2005 = models.IntegerField()
|
||||
region = models.IntegerField()
|
||||
subregion = models.IntegerField()
|
||||
lon = models.FloatField()
|
||||
lat = models.FloatField()
|
||||
geom = models.MultiPolygonField(srid=4326)
|
||||
objects = models.GeoManager()
|
||||
|
||||
# Auto-generated `LayerMapping` dictionary for WorldBorders model
|
||||
worldborders_mapping = {
|
||||
'fips' : 'FIPS',
|
||||
'iso2' : 'ISO2',
|
||||
'iso3' : 'ISO3',
|
||||
'un' : 'UN',
|
||||
'name' : 'NAME',
|
||||
'area' : 'AREA',
|
||||
'pop2005' : 'POP2005',
|
||||
'region' : 'REGION',
|
||||
'subregion' : 'SUBREGION',
|
||||
'lon' : 'LON',
|
||||
'lat' : 'LAT',
|
||||
'geom' : 'MULTIPOLYGON',
|
||||
}
|
||||
|
||||
Spatial Queries
|
||||
===============
|
||||
|
||||
Spatial Lookups
|
||||
---------------
|
||||
GeoDjango extends the Django ORM and allows the use of spatial lookups.
|
||||
Let's do an example where we find the ``WorldBorder`` model that contains
|
||||
a point. First, fire up the management shell::
|
||||
|
||||
$ python manage.py shell
|
||||
|
||||
Now, define a point of interest [#]_::
|
||||
|
||||
>>> pnt_wkt = 'POINT(-95.3385 29.7245)'
|
||||
|
||||
The ``pnt_wkt`` string represents the point at -95.3385 degrees longitude,
|
||||
and 29.7245 degrees latitude. The geometry is in a format known as
|
||||
Well Known Text (WKT), an open standard issued by the Open Geospatial
|
||||
Consortium (OGC). [#]_ Import the ``WorldBorders`` model, and perform
|
||||
a ``contains`` lookup using the ``pnt_wkt`` as the parameter::
|
||||
|
||||
>>> from world.models import WorldBorders
|
||||
>>> qs = WorldBorders.objects.filter(mpoly__contains=pnt_wkt)
|
||||
>>> qs
|
||||
[<WorldBorders: United States>]
|
||||
|
||||
Here we retrieved a ``GeoQuerySet`` that has only one model: the one
|
||||
for the United States (which is what we would expect). Similarly,
|
||||
a :ref:`GEOS geometry object <ref-geos>` may also be used -- here the ``intersects``
|
||||
spatial lookup is combined with the ``get`` method to retrieve
|
||||
only the ``WorldBorders`` instance for San Marino instead of a queryset::
|
||||
|
||||
>>> from django.contrib.gis.geos import Point
|
||||
>>> pnt = Point(12.4604, 43.9420)
|
||||
>>> sm = WorldBorders.objects.get(mpoly__intersects=pnt)
|
||||
>>> sm
|
||||
<WorldBorders: San Marino>
|
||||
|
||||
The ``contains`` and ``intersects`` lookups are just a subset of what's
|
||||
available -- the :ref:`ref-gis-db-api` documentation has more.
|
||||
|
||||
Automatic Spatial Transformations
|
||||
---------------------------------
|
||||
When querying the spatial database GeoDjango automatically transforms
|
||||
geometries if they're in a different coordinate system. In the following
|
||||
example, the coordinate will be expressed in terms of `EPSG SRID 32140`__,
|
||||
a coordinate system specific to south Texas **only** and in units of
|
||||
**meters** and not degrees::
|
||||
|
||||
>>> from django.contrib.gis.geos import *
|
||||
>>> pnt = Point(954158.1, 4215137.1, srid=32140)
|
||||
|
||||
Note that ``pnt`` may also constructed with EWKT, an "extended" form of
|
||||
WKT that includes the SRID::
|
||||
|
||||
>>> pnt = GEOSGeometry('SRID=32140;POINT(954158.1 4215137.1)')
|
||||
|
||||
When using GeoDjango's ORM, it will automatically wrap geometry values
|
||||
in transformation SQL, allowing the developer to work at a higher level
|
||||
of abstraction::
|
||||
|
||||
>>> qs = WorldBorders.objects.filter(mpoly__intersects=pnt)
|
||||
>>> qs.query.as_sql() # Generating the SQL
|
||||
('SELECT "world_worldborders"."id", "world_worldborders"."name", "world_worldborders"."area",
|
||||
"world_worldborders"."pop2005", "world_worldborders"."fips", "world_worldborders"."iso2",
|
||||
"world_worldborders"."iso3", "world_worldborders"."un", "world_worldborders"."region",
|
||||
"world_worldborders"."subregion", "world_worldborders"."lon", "world_worldborders"."lat",
|
||||
"world_worldborders"."mpoly" FROM "world_worldborders"
|
||||
WHERE ST_Intersects("world_worldborders"."mpoly", ST_Transform(%s, 4326))',
|
||||
(<django.contrib.gis.db.backend.postgis.adaptor.PostGISAdaptor object at 0x25641b0>,))
|
||||
>>> qs # printing evaluates the queryset
|
||||
[<WorldBorders: United States>]
|
||||
|
||||
__ http://spatialreference.org/ref/epsg/32140/
|
||||
|
||||
Lazy Geometries
|
||||
---------------
|
||||
Geometries come to GeoDjango in a standardized textual representation. Upon
|
||||
access of the geometry field, GeoDjango creates a `GEOS geometry object <ref-geos>`,
|
||||
exposing powerful functionality, such as serialization properties for
|
||||
popular geospatial formats::
|
||||
|
||||
>>> sm = WorldBorders.objects.get(name='San Marino')
|
||||
>>> sm.mpoly
|
||||
<MultiPolygon object at 0x24c6798>
|
||||
>>> sm.mpoly.wkt # WKT
|
||||
MULTIPOLYGON (((12.4157980000000006 43.9579540000000009, 12.4505540000000003 43.9797209999999978, ...
|
||||
>>> sm.mpoly.wkb # WKB (as Python binary buffer)
|
||||
<read-only buffer for 0x1fe2c70, size -1, offset 0 at 0x2564c40>
|
||||
>>> sm.mpoly.geojson # GeoJSON (requires GDAL)
|
||||
'{ "type": "MultiPolygon", "coordinates": [ [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ...
|
||||
|
||||
This includes access to all of the advanced geometric operations provided by
|
||||
the GEOS library::
|
||||
|
||||
>>> pnt = Point(12.4604, 43.9420)
|
||||
>>> sm.mpoly.contains(pnt)
|
||||
True
|
||||
>>> pnt.contains(sm.mpoly)
|
||||
False
|
||||
|
||||
``GeoQuerySet`` Methods
|
||||
-----------------------
|
||||
|
||||
|
||||
Putting your data on the map
|
||||
============================
|
||||
|
||||
Google
|
||||
------
|
||||
|
||||
Geographic Admin
|
||||
----------------
|
||||
|
||||
GeoDjango extends :ref:`Django's admin application <ref-contrib-admin>` to
|
||||
enable support for editing geometry fields.
|
||||
|
||||
Basics
|
||||
^^^^^^
|
||||
|
||||
GeoDjango also supplements the Django admin by allowing users to create
|
||||
and modify geometries on a JavaScript slippy map (powered by `OpenLayers`_).
|
||||
|
||||
Let's dive in again -- create a file called ``admin.py`` inside the
|
||||
``world`` application, and insert the following::
|
||||
|
||||
from django.contrib.gis import admin
|
||||
from models import WorldBorders
|
||||
|
||||
admin.site.register(WorldBorders, admin.GeoModelAdmin)
|
||||
|
||||
Next, edit your ``urls.py`` in the ``geodjango`` project folder to look
|
||||
as follows::
|
||||
|
||||
from django.conf.urls.defaults import *
|
||||
from django.contrib.gis import admin
|
||||
|
||||
admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^admin/(.*)', include(admin.site.urls)),
|
||||
)
|
||||
|
||||
Start up the Django development server::
|
||||
|
||||
$ python manage.py runserver
|
||||
|
||||
Finally, browse to ``http://localhost:8000/admin/``, and log in with the admin
|
||||
user created after running ``syncdb``. Browse to any of the ``WorldBorders``
|
||||
entries -- the borders may be edited by clicking on a polygon and dragging
|
||||
the vertexes to the desired position.
|
||||
|
||||
.. _OpenLayers: http://openlayers.org/
|
||||
.. _Open Street Map: http://openstreetmap.org/
|
||||
.. _Vector Map Level 0: http://earth-info.nga.mil/publications/vmap0.html
|
||||
.. _Metacarta: http://metacarta.com
|
||||
|
||||
.. _osmgeoadmin-intro:
|
||||
|
||||
``OSMGeoAdmin``
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
With the :class:`~django.contrib.gis.admin.OSMGeoAdmin`, GeoDjango uses
|
||||
a `Open Street Map`_ layer in the admin.
|
||||
This provides more context (including street and thoroughfare details) than
|
||||
available with the :class:`~django.contrib.gis.admin.GeoModelAdmin`
|
||||
(which uses the `Vector Map Level 0`_ WMS data set hosted at `Metacarta`_).
|
||||
|
||||
First, there are some important requirements and limitations:
|
||||
|
||||
* :class:`~django.contrib.gis.admin.OSMGeoAdmin` requires that the
|
||||
:ref:`spherical mercator projection be added <addgoogleprojection>`
|
||||
to the to be added to the ``spatial_ref_sys`` table (PostGIS 1.3 and
|
||||
below, only).
|
||||
* The PROJ.4 datum shifting files must be installed (see the
|
||||
:ref:`PROJ.4 installation instructions <proj4>` for more details).
|
||||
|
||||
If you meet these requirements, then just substitute in the ``OSMGeoAdmin``
|
||||
option class in your ``admin.py`` file::
|
||||
|
||||
admin.site.register(WorldBorders, admin.OSMGeoAdmin)
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#] Special thanks to Bjørn Sandvik of `thematicmapping.org <http://thematicmapping.org>`_ for providing and maintaining this data set.
|
||||
.. [#] GeoDjango basic apps was written by Dane Springmeyer, Josh Livni, and Christopher Schmidt.
|
||||
.. [#] Here the point is for the `University of Houston Law Center <http://www.law.uh.edu/>`_ .
|
||||
.. [#] Open Geospatial Consortium, Inc., `OpenGIS Simple Feature Specification For SQL <http://www.opengis.org/docs/99-049.pdf>`_, Document 99-049.
|
Loading…
Add table
Add a link
Reference in a new issue