| ================== |
| 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 ``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) |
| >>> print qs.query # 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)) |
| >>> 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. |