mirror of
				https://github.com/django/django.git
				synced 2025-11-03 21:25:09 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			776 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			776 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
==================
 | 
						|
GeoDjango Tutorial
 | 
						|
==================
 | 
						|
 | 
						|
Introduction
 | 
						|
============
 | 
						|
 | 
						|
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 and raster data.
 | 
						|
* Extensions to Django's ORM for querying and manipulating spatial data.
 | 
						|
* Loosely-coupled, high-level Python interfaces for GIS geometry and raster
 | 
						|
  operations and data manipulation in different formats.
 | 
						|
* Editing geometry fields from the admin.
 | 
						|
 | 
						|
This tutorial assumes familiarity with Django; thus, if you're brand new to
 | 
						|
Django, please read through the :doc:`regular tutorial </intro/tutorial01>` to
 | 
						|
familiarize yourself with Django first.
 | 
						|
 | 
						|
.. note::
 | 
						|
 | 
						|
    GeoDjango has additional requirements beyond what Django requires --
 | 
						|
    please consult the :doc:`installation documentation <install/index>`
 | 
						|
    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
 | 
						|
-------------------------
 | 
						|
 | 
						|
Typically no special setup is required, so you can create a database as you
 | 
						|
would for any other project. We provide some tips for selected databases:
 | 
						|
 | 
						|
* :doc:`install/postgis`
 | 
						|
* :doc:`install/spatialite`
 | 
						|
 | 
						|
Create a New Project
 | 
						|
------------------------
 | 
						|
 | 
						|
Use the standard ``django-admin`` script to create a project called
 | 
						|
``geodjango``:
 | 
						|
 | 
						|
.. code-block:: console
 | 
						|
 | 
						|
    $ django-admin startproject geodjango
 | 
						|
 | 
						|
This will initialize a new project. Now, create a ``world`` Django application
 | 
						|
within the ``geodjango`` project:
 | 
						|
 | 
						|
.. code-block:: console
 | 
						|
 | 
						|
    $ cd geodjango
 | 
						|
    $ python manage.py startapp world
 | 
						|
 | 
						|
Configure ``settings.py``
 | 
						|
-------------------------
 | 
						|
 | 
						|
The ``geodjango`` project settings are stored in the ``geodjango/settings.py``
 | 
						|
file. Edit the database connection settings to match your setup::
 | 
						|
 | 
						|
    DATABASES = {
 | 
						|
        'default': {
 | 
						|
             'ENGINE': 'django.contrib.gis.db.backends.postgis',
 | 
						|
             'NAME': 'geodjango',
 | 
						|
             'USER': 'geo',
 | 
						|
         }
 | 
						|
    }
 | 
						|
 | 
						|
In addition, modify the :setting:`INSTALLED_APPS` setting to include
 | 
						|
:mod:`django.contrib.admin`, :mod:`django.contrib.gis`,
 | 
						|
and ``world`` (your newly created application)::
 | 
						|
 | 
						|
    INSTALLED_APPS = [
 | 
						|
        'django.contrib.admin',
 | 
						|
        'django.contrib.auth',
 | 
						|
        'django.contrib.contenttypes',
 | 
						|
        'django.contrib.sessions',
 | 
						|
        'django.contrib.messages',
 | 
						|
        'django.contrib.staticfiles',
 | 
						|
        '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, use the following commands:
 | 
						|
 | 
						|
.. code-block:: console
 | 
						|
 | 
						|
    $ 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 dataset 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
 | 
						|
__ https://en.wikipedia.org/wiki/Shapefile
 | 
						|
 | 
						|
Use ``ogrinfo`` to examine spatial data
 | 
						|
---------------------------------------
 | 
						|
 | 
						|
The GDAL ``ogrinfo`` utility allows examining the metadata of shapefiles or
 | 
						|
other vector data sources:
 | 
						|
 | 
						|
.. code-block:: console
 | 
						|
 | 
						|
    $ 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)
 | 
						|
 | 
						|
``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:: console
 | 
						|
 | 
						|
    $ 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 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
 | 
						|
=================
 | 
						|
 | 
						|
Defining a Geographic Model
 | 
						|
---------------------------
 | 
						|
 | 
						|
Now that you've examined your dataset using ``ogrinfo``, create a GeoDjango
 | 
						|
model to represent this data::
 | 
						|
 | 
						|
    from django.contrib.gis.db import models
 | 
						|
 | 
						|
    class WorldBorder(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)
 | 
						|
        mpoly = models.MultiPolygonField()
 | 
						|
 | 
						|
        # Returns the string representation of the model.
 | 
						|
        def __str__(self):              # __unicode__ on Python 2
 | 
						|
            return self.name
 | 
						|
 | 
						|
Note that the ``models`` module is imported from ``django.contrib.gis.db``.
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
__ https://en.wikipedia.org/wiki/SRID
 | 
						|
 | 
						|
Run ``migrate``
 | 
						|
---------------
 | 
						|
 | 
						|
After defining your model, you need to sync it with the database. First,
 | 
						|
create a database migration:
 | 
						|
 | 
						|
.. code-block:: console
 | 
						|
 | 
						|
    $ python manage.py makemigrations
 | 
						|
    Migrations for 'world':
 | 
						|
      0001_initial.py:
 | 
						|
        - Create model WorldBorder
 | 
						|
 | 
						|
Let's look at the SQL that will generate the table for the ``WorldBorder``
 | 
						|
model:
 | 
						|
 | 
						|
.. code-block:: console
 | 
						|
 | 
						|
    $ python manage.py sqlmigrate world 0001
 | 
						|
 | 
						|
This command should produce the following output:
 | 
						|
 | 
						|
.. code-block:: sql
 | 
						|
 | 
						|
    BEGIN;
 | 
						|
    --
 | 
						|
    -- Create model WorldBorder
 | 
						|
    --
 | 
						|
    CREATE TABLE "world_worldborder" (
 | 
						|
        "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
 | 
						|
        "mpoly" geometry(MULTIPOLYGON,4326) NOT NULL
 | 
						|
    )
 | 
						|
    ;
 | 
						|
    CREATE INDEX "world_worldborder_mpoly_id" ON "world_worldborder" USING GIST ( "mpoly" );
 | 
						|
    COMMIT;
 | 
						|
 | 
						|
If this looks correct, run :djadmin:`migrate` to create this table in the
 | 
						|
database:
 | 
						|
 | 
						|
.. code-block:: console
 | 
						|
 | 
						|
    $ python manage.py migrate
 | 
						|
    Operations to perform:
 | 
						|
      Apply all migrations: admin, world, contenttypes, auth, sessions
 | 
						|
    Running migrations:
 | 
						|
      ...
 | 
						|
      Applying world.0001_initial... OK
 | 
						|
 | 
						|
Importing Spatial Data
 | 
						|
======================
 | 
						|
 | 
						|
This section will show you how to import the world borders shapefile into the
 | 
						|
database via GeoDjango models using the :doc:`layermapping`.
 | 
						|
 | 
						|
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`_: 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.net/docs/manual-1.5/ch04.html#shp2pgsql_usage
 | 
						|
 | 
						|
.. _gdalinterface:
 | 
						|
 | 
						|
GDAL Interface
 | 
						|
--------------
 | 
						|
 | 
						|
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:
 | 
						|
 | 
						|
.. code-block:: console
 | 
						|
 | 
						|
    $ python manage.py shell
 | 
						|
 | 
						|
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
 | 
						|
    >>> import world
 | 
						|
    >>> world_shp = os.path.abspath(os.path.join(os.path.dirname(world.__file__),
 | 
						|
    ...                             'data', 'TM_WORLD_BORDERS-0.3.shp'))
 | 
						|
 | 
						|
Now, open the world borders shapefile using GeoDjango's
 | 
						|
:class:`~django.contrib.gis.gdal.DataSource` interface::
 | 
						|
 | 
						|
    >>> from django.contrib.gis.gdal import DataSource
 | 
						|
    >>> 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 the layer's geometry type 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, 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
 | 
						|
: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 '
 | 
						|
 | 
						|
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:
 | 
						|
 | 
						|
    >>> print(lyr.fields)
 | 
						|
    ['FIPS', 'ISO2', 'ISO3', 'UN', 'NAME', 'AREA', 'POP2005', 'REGION', 'SUBREGION', 'LON', 'LAT']
 | 
						|
 | 
						|
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']
 | 
						|
 | 
						|
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
 | 
						|
 | 
						|
Boundary geometries may be exported as 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``
 | 
						|
----------------
 | 
						|
 | 
						|
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
 | 
						|
    from models import WorldBorder
 | 
						|
 | 
						|
    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(WorldBorder, 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
 | 
						|
  ``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 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,
 | 
						|
  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 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:
 | 
						|
 | 
						|
.. code-block:: console
 | 
						|
 | 
						|
    $ 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
 | 
						|
:doc:`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:
 | 
						|
 | 
						|
.. code-block:: console
 | 
						|
 | 
						|
    $ python manage.py ogrinspect [options] <data_source> <model_name> [options]
 | 
						|
 | 
						|
``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 ``WorldBorder`` model
 | 
						|
and mapping dictionary created above, automatically:
 | 
						|
 | 
						|
.. code-block:: console
 | 
						|
 | 
						|
    $ python manage.py ogrinspect world/data/TM_WORLD_BORDERS-0.3.shp WorldBorder \
 | 
						|
        --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 WorldBorder(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)
 | 
						|
 | 
						|
    # Auto-generated `LayerMapping` dictionary for WorldBorder 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 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:: console
 | 
						|
 | 
						|
    $ 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,
 | 
						|
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::
 | 
						|
 | 
						|
    >>> from world.models import WorldBorder
 | 
						|
    >>> WorldBorder.objects.filter(mpoly__contains=pnt_wkt)
 | 
						|
    <QuerySet [<WorldBorder: United States>]>
 | 
						|
 | 
						|
Here, you retrieved a ``QuerySet`` with only one model: the border of the
 | 
						|
United States (exactly what you would expect).
 | 
						|
 | 
						|
Similarly, you may also use a :doc:`GEOS geometry object <geos>`.
 | 
						|
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)
 | 
						|
    >>> WorldBorder.objects.get(mpoly__intersects=pnt)
 | 
						|
    <WorldBorder: San Marino>
 | 
						|
 | 
						|
The ``contains`` and ``intersects`` lookups are just a subset of the
 | 
						|
available queries -- the :doc:`db-api` documentation has more.
 | 
						|
 | 
						|
Automatic Spatial Transformations
 | 
						|
---------------------------------
 | 
						|
When doing spatial queries, GeoDjango automatically transforms
 | 
						|
geometries if they're in a different coordinate system.  In the following
 | 
						|
example, coordinates will be expressed in `EPSG SRID 32140`__,
 | 
						|
a coordinate system specific to south Texas **only** and in units of
 | 
						|
**meters**, not degrees::
 | 
						|
 | 
						|
    >>> from django.contrib.gis.geos import Point, GEOSGeometry
 | 
						|
    >>> pnt = Point(954158.1, 4215137.1, srid=32140)
 | 
						|
 | 
						|
Note that ``pnt`` may also be constructed with EWKT, an "extended" form of
 | 
						|
WKT that includes the SRID::
 | 
						|
 | 
						|
    >>> pnt = GEOSGeometry('SRID=32140;POINT(954158.1 4215137.1)')
 | 
						|
 | 
						|
GeoDjango's ORM will automatically wrap geometry values
 | 
						|
in transformation SQL, allowing the developer to work at a higher level
 | 
						|
of abstraction::
 | 
						|
 | 
						|
    >>> qs = WorldBorder.objects.filter(mpoly__intersects=pnt)
 | 
						|
    >>> print(qs.query) # Generating the SQL
 | 
						|
    SELECT "world_worldborder"."id", "world_worldborder"."name", "world_worldborder"."area",
 | 
						|
    "world_worldborder"."pop2005", "world_worldborder"."fips", "world_worldborder"."iso2",
 | 
						|
    "world_worldborder"."iso3", "world_worldborder"."un", "world_worldborder"."region",
 | 
						|
    "world_worldborder"."subregion", "world_worldborder"."lon", "world_worldborder"."lat",
 | 
						|
    "world_worldborder"."mpoly" FROM "world_worldborder"
 | 
						|
    WHERE ST_Intersects("world_worldborder"."mpoly", ST_Transform(%s, 4326))
 | 
						|
    >>> qs # printing evaluates the queryset
 | 
						|
    <QuerySet [<WorldBorder: United States>]>
 | 
						|
 | 
						|
__ http://spatialreference.org/ref/epsg/32140/
 | 
						|
 | 
						|
.. admonition:: Raw queries
 | 
						|
 | 
						|
    When using :doc:`raw queries </topics/db/sql>`, you should generally wrap
 | 
						|
    your geometry fields with the ``asText()`` SQL function (or ``ST_AsText``
 | 
						|
    for PostGIS) so that the field value will be recognized by GEOS::
 | 
						|
 | 
						|
        City.objects.raw('SELECT id, name, asText(point) from myapp_city')
 | 
						|
 | 
						|
    This is not absolutely required by PostGIS, but generally you should only
 | 
						|
    use raw queries when you know exactly what you are doing.
 | 
						|
 | 
						|
Lazy Geometries
 | 
						|
---------------
 | 
						|
GeoDjango loads geometries in a standardized textual representation.  When the
 | 
						|
geometry field is first accessed, GeoDjango creates a `GEOS geometry object
 | 
						|
<ref-geos>`, exposing powerful functionality, such as serialization properties
 | 
						|
for popular geospatial formats::
 | 
						|
 | 
						|
    >>> sm = WorldBorder.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
 | 
						|
 | 
						|
Geographic annotations
 | 
						|
----------------------
 | 
						|
 | 
						|
GeoDjango also offers a set of geographic annotations to compute distances and
 | 
						|
several other operations (intersection, difference, etc.). See the
 | 
						|
:doc:`functions` documentation.
 | 
						|
 | 
						|
 | 
						|
Putting your data on the map
 | 
						|
============================
 | 
						|
 | 
						|
Geographic Admin
 | 
						|
----------------
 | 
						|
 | 
						|
GeoDjango extends :doc:`Django's admin application </ref/contrib/admin/index>`
 | 
						|
with 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 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 as follows::
 | 
						|
 | 
						|
    from django.conf.urls import url, include
 | 
						|
    from django.contrib.gis import admin
 | 
						|
 | 
						|
    urlpatterns = [
 | 
						|
        url(r'^admin/', admin.site.urls),
 | 
						|
    ]
 | 
						|
 | 
						|
Create an admin user:
 | 
						|
 | 
						|
.. code-block:: console
 | 
						|
 | 
						|
    $ python manage.py createsuperuser
 | 
						|
 | 
						|
Next, start up the Django development server:
 | 
						|
 | 
						|
.. code-block:: console
 | 
						|
 | 
						|
    $ python manage.py runserver
 | 
						|
 | 
						|
Finally, browse to ``http://localhost:8000/admin/``, and log in with the user
 | 
						|
you just created. Browse to any of the ``WorldBorder`` 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://www.openstreetmap.org/
 | 
						|
.. _Vector Map Level 0: http://earth-info.nga.mil/publications/vmap0.html
 | 
						|
.. _OSGeo: http://www.osgeo.org
 | 
						|
 | 
						|
.. _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 dataset hosted at `OSGeo`_).
 | 
						|
 | 
						|
First, there are some important requirements:
 | 
						|
 | 
						|
* :class:`~django.contrib.gis.admin.OSMGeoAdmin` requires that
 | 
						|
  :doc:`GDAL <gdal>` is installed.
 | 
						|
 | 
						|
* The PROJ.4 datum shifting files must be installed (see the
 | 
						|
  :ref:`PROJ.4 installation instructions <proj4>` for more details).
 | 
						|
 | 
						|
If you meet this requirement, 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
 | 
						|
       <http://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
 | 
						|
       <http://www.law.uh.edu/>`_.
 | 
						|
.. [#] Open Geospatial Consortium, Inc., `OpenGIS Simple Feature Specification
 | 
						|
       For SQL <http://www.opengeospatial.org/standards/sfs>`_.
 |