diff options
author | Nishanth Amuluru | 2011-01-08 11:20:57 +0530 |
---|---|---|
committer | Nishanth Amuluru | 2011-01-08 11:20:57 +0530 |
commit | 65411d01d448ff0cd4abd14eee14cf60b5f8fc20 (patch) | |
tree | b4c404363c4c63a61d6e2f8bd26c5b057c1fb09d /parts/django/docs/ref/contrib/gis/tutorial.txt | |
parent | 2e35094d43b4cc6974172e1febf76abb50f086ec (diff) | |
download | pytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.tar.gz pytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.tar.bz2 pytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.zip |
Added buildout stuff and made changes accordingly
--HG--
rename : profile/management/__init__.py => eggs/djangorecipe-0.20-py2.6.egg/EGG-INFO/dependency_links.txt
rename : profile/management/__init__.py => eggs/djangorecipe-0.20-py2.6.egg/EGG-INFO/not-zip-safe
rename : profile/management/__init__.py => eggs/infrae.subversion-1.4.5-py2.6.egg/EGG-INFO/dependency_links.txt
rename : profile/management/__init__.py => eggs/infrae.subversion-1.4.5-py2.6.egg/EGG-INFO/not-zip-safe
rename : profile/management/__init__.py => eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/EGG-INFO/dependency_links.txt
rename : profile/management/__init__.py => eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/EGG-INFO/not-zip-safe
rename : profile/management/__init__.py => eggs/py-1.4.0-py2.6.egg/EGG-INFO/dependency_links.txt
rename : profile/management/__init__.py => eggs/py-1.4.0-py2.6.egg/EGG-INFO/not-zip-safe
rename : profile/management/__init__.py => eggs/zc.buildout-1.5.2-py2.6.egg/EGG-INFO/dependency_links.txt
rename : profile/management/__init__.py => eggs/zc.buildout-1.5.2-py2.6.egg/EGG-INFO/not-zip-safe
rename : profile/management/__init__.py => eggs/zc.recipe.egg-1.3.2-py2.6.egg/EGG-INFO/dependency_links.txt
rename : profile/management/__init__.py => eggs/zc.recipe.egg-1.3.2-py2.6.egg/EGG-INFO/not-zip-safe
rename : profile/management/__init__.py => parts/django/Django.egg-info/dependency_links.txt
rename : taskapp/models.py => parts/django/django/conf/app_template/models.py
rename : taskapp/tests.py => parts/django/django/conf/app_template/tests.py
rename : taskapp/views.py => parts/django/django/conf/app_template/views.py
rename : taskapp/views.py => parts/django/django/contrib/gis/tests/geo3d/views.py
rename : profile/management/__init__.py => parts/django/tests/modeltests/delete/__init__.py
rename : profile/management/__init__.py => parts/django/tests/modeltests/files/__init__.py
rename : profile/management/__init__.py => parts/django/tests/modeltests/invalid_models/__init__.py
rename : profile/management/__init__.py => parts/django/tests/modeltests/m2m_signals/__init__.py
rename : profile/management/__init__.py => parts/django/tests/modeltests/model_package/__init__.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/__init__.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/management/__init__.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/management/commands/__init__.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/models.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/delete_regress/__init__.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/file_storage/__init__.py
rename : profile/management/__init__.py => parts/django/tests/regressiontests/max_lengths/__init__.py
rename : profile/forms.py => pytask/profile/forms.py
rename : profile/management/__init__.py => pytask/profile/management/__init__.py
rename : profile/management/commands/seed_db.py => pytask/profile/management/commands/seed_db.py
rename : profile/models.py => pytask/profile/models.py
rename : profile/templatetags/user_tags.py => pytask/profile/templatetags/user_tags.py
rename : taskapp/tests.py => pytask/profile/tests.py
rename : profile/urls.py => pytask/profile/urls.py
rename : profile/utils.py => pytask/profile/utils.py
rename : profile/views.py => pytask/profile/views.py
rename : static/css/base.css => pytask/static/css/base.css
rename : taskapp/tests.py => pytask/taskapp/tests.py
rename : taskapp/views.py => pytask/taskapp/views.py
rename : templates/base.html => pytask/templates/base.html
rename : templates/profile/browse_notifications.html => pytask/templates/profile/browse_notifications.html
rename : templates/profile/edit.html => pytask/templates/profile/edit.html
rename : templates/profile/view.html => pytask/templates/profile/view.html
rename : templates/profile/view_notification.html => pytask/templates/profile/view_notification.html
rename : templates/registration/activate.html => pytask/templates/registration/activate.html
rename : templates/registration/activation_email.txt => pytask/templates/registration/activation_email.txt
rename : templates/registration/activation_email_subject.txt => pytask/templates/registration/activation_email_subject.txt
rename : templates/registration/logged_out.html => pytask/templates/registration/logged_out.html
rename : templates/registration/login.html => pytask/templates/registration/login.html
rename : templates/registration/logout.html => pytask/templates/registration/logout.html
rename : templates/registration/password_change_done.html => pytask/templates/registration/password_change_done.html
rename : templates/registration/password_change_form.html => pytask/templates/registration/password_change_form.html
rename : templates/registration/password_reset_complete.html => pytask/templates/registration/password_reset_complete.html
rename : templates/registration/password_reset_confirm.html => pytask/templates/registration/password_reset_confirm.html
rename : templates/registration/password_reset_done.html => pytask/templates/registration/password_reset_done.html
rename : templates/registration/password_reset_email.html => pytask/templates/registration/password_reset_email.html
rename : templates/registration/password_reset_form.html => pytask/templates/registration/password_reset_form.html
rename : templates/registration/registration_complete.html => pytask/templates/registration/registration_complete.html
rename : templates/registration/registration_form.html => pytask/templates/registration/registration_form.html
rename : utils.py => pytask/utils.py
Diffstat (limited to 'parts/django/docs/ref/contrib/gis/tutorial.txt')
-rw-r--r-- | parts/django/docs/ref/contrib/gis/tutorial.txt | 758 |
1 files changed, 758 insertions, 0 deletions
diff --git a/parts/django/docs/ref/contrib/gis/tutorial.txt b/parts/django/docs/ref/contrib/gis/tutorial.txt new file mode 100644 index 0000000..9deeb78 --- /dev/null +++ b/parts/django/docs/ref/contrib/gis/tutorial.txt @@ -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 :doc:`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 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. [#]_ + +.. 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`_ Web site, 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/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 :doc:`Django's admin application </ref/contrib/admin/index>` +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. |