From 8e5573e99abd5351dfbd6de1214662ecf7f6c9ba Mon Sep 17 00:00:00 2001 From: shepdl Date: Sat, 17 Nov 2012 22:20:49 -0800 Subject: [PATCH] Fixed #19243 - Edited GeoDjango Tutorial for consistency and style. --- docs/ref/contrib/gis/tutorial.txt | 281 +++++++++++++++--------------- 1 file changed, 136 insertions(+), 145 deletions(-) diff --git a/docs/ref/contrib/gis/tutorial.txt b/docs/ref/contrib/gis/tutorial.txt index ec265342b3..5000622ad4 100644 --- a/docs/ref/contrib/gis/tutorial.txt +++ b/docs/ref/contrib/gis/tutorial.txt @@ -5,28 +5,28 @@ GeoDjango Tutorial Introduction ============ -GeoDjango is an add-on for Django that turns it into a world-class geographic -Web framework. GeoDjango strives to make it as simple as possible to create -geographic Web applications, like location-based services. Some features -include: +GeoDjango is an included contrib module for Django that turns it into a +world-class geographic Web framework. GeoDjango strives to make it as simple +as possible to create geographic Web applications, like location-based services. +Its features include: * Django model fields for `OGC`_ geometries. -* Extensions to Django's ORM for the querying and manipulation of spatial data. +* Extensions to Django's ORM for querying and manipulating spatial data. * Loosely-coupled, high-level Python interfaces for GIS geometry operations and data formats. -* Editing of geometry fields inside the admin. +* Editing geometry fields from the admin. -This tutorial assumes a familiarity with Django; thus, if you're brand new to -Django please read through the :doc:`regular tutorial ` to -introduce yourself with basic Django concepts. +This tutorial assumes familiarity with Django; thus, if you're brand new to +Django, please read through the :doc:`regular tutorial ` to +familiarize yourself with Django first. .. note:: - GeoDjango has special prerequisites overwhat is required by Django -- + GeoDjango has additional requirements beyond what Django requires -- please consult the :ref:`installation documentation ` for more details. -This tutorial will guide you through the creation of a geographic Web +This tutorial will guide you 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. [#]_ @@ -51,10 +51,10 @@ Create a Spatial Database 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 -`: +First, create a spatial database for your project. + +If you are using PostGIS, create the database from the :ref:`spatial database +template `: .. code-block:: bash @@ -62,9 +62,9 @@ create the database from a :ref:`spatial database template .. 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: + This command must be issued by a database user with enough privileges to + create a database. To create a user with ``CREATE DATABASE`` privileges in + PostgreSQL, use the following commands: .. code-block:: bash @@ -72,25 +72,24 @@ create the database from a :ref:`spatial database template $ createuser --createdb geo $ exit - Replace ``geo`` with the system login user name that will be - connecting to the database. For example, ``johndoe`` if that is the - system user that will be running GeoDjango. + Replace ``geo`` with your Postgres database user's username. + (In PostgreSQL, this user will also be an OS-level user.) -Users of SQLite and SpatiaLite should consult the instructions on how +If you are using SQLite and SpatiaLite, consult the instructions on how to create a :ref:`SpatiaLite database `. -Create GeoDjango Project +Create a New Project ------------------------ -Use the ``django-admin.py`` script like normal to create a ``geodjango`` -project: +Use the standard ``django-admin.py`` script to create a project called +``geodjango``: .. code-block:: bash $ django-admin.py startproject geodjango -With the project initialized, now create a ``world`` Django application within -the ``geodjango`` project: +This will initialize a new project. Now, create a ``world`` Django application +within the ``geodjango`` project: .. code-block:: bash @@ -101,7 +100,7 @@ Configure ``settings.py`` ------------------------- The ``geodjango`` project settings are stored in the ``geodjango/settings.py`` -file. Edit the database connection settings appropriately:: +file. Edit the database connection settings to match your setup:: DATABASES = { 'default': { @@ -113,7 +112,7 @@ file. Edit the database connection settings appropriately:: In addition, modify the :setting:`INSTALLED_APPS` setting to include :mod:`django.contrib.admin`, :mod:`django.contrib.gis`, -and ``world`` (our newly created application):: +and ``world`` (your newly created application):: INSTALLED_APPS = ( 'django.contrib.auth', @@ -135,9 +134,9 @@ Geographic Data World Borders ------------- -The world borders data is available in this `zip file`__. Create a data +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: +unzip. On GNU/Linux platforms, use the following commands: .. code-block:: bash @@ -149,7 +148,7 @@ unzip. On GNU/Linux platforms the following commands should do it: 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 +unzipped, the world borders dataset includes files with the following extensions: * ``.shp``: Holds the vector data for the world borders geometries. @@ -165,8 +164,8 @@ __ 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): +The GDAL ``ogrinfo`` utility allows examining the metadata of shapefiles or +other vector data sources: .. code-block:: bash @@ -175,9 +174,9 @@ shapefiles (or other vector data sources): 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 such -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`` tells us that the shapefile has one layer, and that this +layer contains polygon data. To find out more, we'll specify the layer name +and use the ``-so`` option to get only the important summary information: .. code-block:: bash @@ -208,14 +207,11 @@ and use the ``-so`` option to get only important summary information: 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`_ Web site, this -shows you how to determine this information yourself when such metadata is not -provided. +(246), the geographic bounds of the data, the spatial reference system +("SRS WKT"), as well as type information for each attribute field. For example, +``FIPS: String (2.0)`` indicates that the ``FIPS`` character field has +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. Geographic Models ================= @@ -223,8 +219,8 @@ 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:: +Now that you've examined your dataset using ``ogrinfo``, create a GeoDjango +model to represent this data:: from django.contrib.gis.db import models @@ -252,32 +248,30 @@ create a GeoDjango model to represent this data:: def __unicode__(self): return self.name -Two important things to note: +Please note two important things: 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. +2. You must override the model's default manager with + :class:`~django.contrib.gis.db.models.GeoManager` 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. +The default spatial reference system for geometry fields is WGS84 (meaning +the `SRID`__ is 4326) -- in other words, the field coordinates are in +longitude, latitude pairs in units of degrees. To use a different +coordinate system, set the SRID of the geometry field with the ``srid`` +argument. Use an integer representing the coordinate system's EPSG code. __ 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 +After defining your model, you need to sync it with the database. First, +let's look at the SQL that will generate the table for the ``WorldBorder`` model:: $ python manage.py sqlall world -This management command should produce the following output: +This command should produce the following output: .. code-block:: sql @@ -302,32 +296,28 @@ This management command should produce the following output: CREATE INDEX "world_worldborder_mpoly_id" ON "world_worldborder" USING GIST ( "mpoly" GIST_GEOMETRY_OPS ); COMMIT; -If satisfied, you may then create this table in the database by running the -``syncdb`` management command:: +If this looks correct, run ``syncdb`` to create this table in the database:: $ python manage.py syncdb Creating table world_worldborder Installing custom SQL for world.WorldBorder 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). +The ``syncdb`` command may also prompt you to create an admin user. Either +do so now, or later by running ``django-admin.py createsuperuser``. 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 +This section will show you how to import the world borders +shapefile into the database via GeoDjango models using the :ref:`ref-layermapping`. -There are many 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: +There are many different ways to import data into a spatial database -- +besides the tools included within GeoDjango, you may also use the following: -* `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`_: A command-line utility included with GDAL that + can import many vector data formats into PostGIS, MySQL, and Oracle databases. +* `shp2pgsql`_: This utility included with PostGIS imports ESRI shapefiles into + PostGIS. .. _ogr2ogr: http://www.gdal.org/ogr2ogr.html .. _shp2pgsql: http://postgis.refractions.net/documentation/manual-1.5/ch04.html#shp2pgsql_usage @@ -337,10 +327,9 @@ populate your spatial database: GDAL Interface -------------- -Earlier we used 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. +Earlier, you used ``ogrinfo`` to examine the contents of the world borders +shapefile. GeoDjango also includes a Pythonic interface to GDAL's powerful OGR +library that can work with all the vector data sources that OGR supports. First, invoke the Django shell: @@ -348,8 +337,8 @@ 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 +If you downloaded the :ref:`worldborders` data earlier in the +tutorial, then you can determine its path using Python's built-in ``os`` module:: >>> import os @@ -357,7 +346,7 @@ tutorial, then we can determine the path using Python's built-in >>> 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 +Now, open the world borders shapefile using GeoDjango's :class:`~django.contrib.gis.gdal.DataSource` interface:: >>> from django.contrib.gis.gdal import DataSource @@ -374,8 +363,7 @@ shapefiles are only allowed to have one layer:: >>> print(lyr) TM_WORLD_BORDERS-0.3 -You can see what the geometry type of the layer is and how many features it -contains:: +You can see the layer's geometry type and how many features it contains:: >>> print(lyr.geom_type) Polygon @@ -384,16 +372,16 @@ contains:: .. note:: - Unfortunately the shapefile data format does not allow for greater + 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. + many others, actually includes ``MultiPolygon`` geometries, not Polygons. + It's important to use a more general field type in models: a + GeoDjango ``MultiPolygonField`` will accept a ``Polygon`` geometry, but a + ``PolygonField`` will not accept a ``MultiPolygon`` type geometry. This + is why the ``WorldBorder`` model defined above uses a ``MultiPolygonField``. 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 +system associated with it. If it does, the ``srs`` attribute will return a :class:`~django.contrib.gis.gdal.SpatialReference` object:: >>> srs = lyr.srs @@ -406,9 +394,9 @@ system associated with it -- if it does, the ``srs`` attribute will return a >>> 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. +This shapefile is in the popular WGS84 spatial reference +system -- in other words, the data uses longitude, latitude pairs in +units of degrees. In addition, shapefiles also support attribute fields that may contain additional data. Here are the fields on the World Borders layer: @@ -416,8 +404,8 @@ 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: +The following code will let you examine the OGR types (e.g. integer or +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'] @@ -446,8 +434,7 @@ And individual features may be retrieved by their feature ID:: >>> print(feat.get('NAME')) San Marino -Here the boundary geometry for San Marino is extracted and looking -exported to WKT and GeoJSON:: +Boundary geometries may be exported as WKT and GeoJSON:: >>> geom = feat.geom >>> print(geom.wkt) @@ -459,8 +446,9 @@ exported to WKT and GeoJSON:: ``LayerMapping`` ---------------- -We're going to dive right in -- create a file called ``load.py`` inside the -``world`` application, and insert the following:: +To import the data, use a LayerMapping in a Python script. +Create a file called ``load.py`` inside the ``world`` application, +with the following code:: import os from django.contrib.gis.utils import LayerMapping @@ -492,20 +480,20 @@ We're going to dive right in -- create a file called ``load.py`` inside the A few notes about what's going on: * Each key in the ``world_mapping`` dictionary corresponds to a field in the - ``WorldBorder`` model, and the value is the name of the shapefile field + ``WorldBorder`` model. 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. + geometry type GeoDjango will import the field as. Even simple polygons in + the shapefile 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 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. +* The ``encoding`` keyword is set to the character encoding of the 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: @@ -513,8 +501,8 @@ 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:: +Next, import the ``load`` module, call the ``run`` routine, and watch +``LayerMapping`` do the work:: >>> from world import load >>> load.run() @@ -536,7 +524,7 @@ The general usage of the command goes as follows: $ python manage.py ogrinspect [options] [options] -Where ``data_source`` is the path to the GDAL-supported data source and +``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. @@ -600,9 +588,9 @@ 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: +GeoDjango adds spatial lookups to the Django ORM. For example, you +can find the country in the ``WorldBorder`` table that contains +a particular point. First, fire up the management shell: .. code-block:: bash @@ -613,8 +601,8 @@ 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 +29.7245 degrees latitude. The geometry is in a format known as +Well Known Text (WKT), a standard issued by the Open Geospatial Consortium (OGC). [#]_ Import the ``WorldBorder`` model, and perform a ``contains`` lookup using the ``pnt_wkt`` as the parameter:: @@ -623,11 +611,13 @@ a ``contains`` lookup using the ``pnt_wkt`` as the parameter:: >>> qs [] -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 ` may also be used -- here the -``intersects`` spatial lookup is combined with the ``get`` method to retrieve -only the ``WorldBorder`` instance for San Marino instead of a queryset:: +Here, you retrieved a ``GeoQuerySet`` with only one model: the border of +the United States (exactly what you would expect). + +Similarly, you may also use a :ref:`GEOS geometry object `. +Here, you can combine the ``intersects`` spatial lookup with the ``get`` +method to retrieve only the ``WorldBorder`` instance for San Marino instead +of a queryset:: >>> from django.contrib.gis.geos import Point >>> pnt = Point(12.4604, 43.9420) @@ -635,16 +625,16 @@ only the ``WorldBorder`` instance for San Marino instead of a queryset:: >>> sm -The ``contains`` and ``intersects`` lookups are just a subset of what's -available -- the :ref:`ref-gis-db-api` documentation has more. +The ``contains`` and ``intersects`` lookups are just a subset of the +available queries -- the :ref:`ref-gis-db-api` documentation has more. Automatic Spatial Transformations --------------------------------- -When querying the spatial database GeoDjango automatically transforms +When doing spatial queries, 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`__, +example, coordinates will be expressed in `EPSG SRID 32140`__, a coordinate system specific to south Texas **only** and in units of -**meters** and not degrees:: +**meters**, not degrees:: >>> from django.contrib.gis.geos import Point, GEOSGeometry >>> pnt = Point(954158.1, 4215137.1, srid=32140) @@ -654,7 +644,7 @@ 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 +GeoDjango's ORM will automatically wrap geometry values in transformation SQL, allowing the developer to work at a higher level of abstraction:: @@ -675,7 +665,7 @@ __ http://spatialreference.org/ref/epsg/32140/ When using :doc:`raw queries `, you should generally wrap your geometry fields with the ``asText()`` SQL function (or ``ST_AsText`` - for PostGIS) so as the field value will be recognized by GEOS:: + for PostGIS) so that the field value will be recognized by GEOS:: City.objects.raw('SELECT id, name, asText(point) from myapp_city') @@ -684,8 +674,8 @@ __ 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 +GeoDjango loads geometries in a standardized textual representation. When the +geometry field is first accessed, GeoDjango creates a `GEOS geometry object `, exposing powerful functionality, such as serialization properties for popular geospatial formats:: @@ -715,14 +705,11 @@ the GEOS library:: Putting your data on the map ============================ -Google ------- - Geographic Admin ---------------- GeoDjango extends :doc:`Django's admin application ` -to enable support for editing geometry fields. +with support for editing geometry fields. Basics ^^^^^^ @@ -730,16 +717,15 @@ 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:: +Let's dive right in. Create a file called ``admin.py`` inside the +``world`` application with the following code:: from django.contrib.gis import admin from models import WorldBorder admin.site.register(WorldBorder, admin.GeoModelAdmin) -Next, edit your ``urls.py`` in the ``geodjango`` application folder to look -as follows:: +Next, edit your ``urls.py`` in the ``geodjango`` application folder as follows:: from django.conf.urls import patterns, url, include from django.contrib.gis import admin @@ -775,9 +761,9 @@ 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 `OSGeo`_). +(which uses the `Vector Map Level 0`_ WMS dataset hosted at `OSGeo`_). -First, there are some important requirements and limitations: +First, there are some important requirements: * :class:`~django.contrib.gis.admin.OSMGeoAdmin` requires that the :ref:`spherical mercator projection be added ` @@ -785,14 +771,19 @@ First, there are some important requirements and limitations: * The PROJ.4 datum shifting files must be installed (see the :ref:`PROJ.4 installation instructions ` for more details). -If you meet these requirements, then just substitute in the ``OSMGeoAdmin`` +If you meet these requirements, then just substitute the ``OSMGeoAdmin`` option class in your ``admin.py`` file:: admin.site.register(WorldBorder, admin.OSMGeoAdmin) .. rubric:: Footnotes -.. [#] Special thanks to Bjørn Sandvik of `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 `_. -.. [#] Open Geospatial Consortium, Inc., `OpenGIS Simple Feature Specification For SQL `_. +.. [#] Special thanks to Bjørn Sandvik of `thematicmapping.org + `_ for providing and maintaining this + dataset. +.. [#] GeoDjango basic apps was written by Dane Springmeyer, Josh Livni, and + Christopher Schmidt. +.. [#] This point is the `University of Houston Law Center + `_. +.. [#] Open Geospatial Consortium, Inc., `OpenGIS Simple Feature Specification + For SQL `_.