= !GeoDjango = [[TOC(GeoDjangoBackground, GeoDjango, GeoDjangoInstall, GeoDjangoModelAPI, GeoDjangoDatabaseAPI, GEOSGeometry, GeoDjangoExtras)]] The [http://code.djangoproject.com/browser/django/branches/gis GIS] branch intends to be a world-class geographic web framework. Our goal is to make it as easy as possible to build GIS web applications and harness the power of spatially enabled data. ''News:'' * !GeoDjango was merged to trunk on 5th August 2008! * An IRC channel is available for discussion on all things !GeoDjango -- join #geodjango on freenode. * !GeoDjango was featured in a [http://en.oreilly.com/where2008/public/schedule/detail/1665 presentation] and [http://en.oreilly.com/where2008/public/schedule/detail/1666 tutorial session] at [http://en.oreilly.com/where2008/public/content/home O'Reilly's Where 2.0 Conference], May 12-14, 2008 in Burlingame, California. * The project was [http://www.foss4g2007.org/presentations/view.php?abstract_id=222 presented] at [http://www.foss4g2007.org/ FOSS4G 2007] on September 25, 2007. Thanks again for your support! '''Note:''' The documentation has been re-factored into several pages. See the table of contents on the right for a complete listing of topics. Other !GeoDjango pages include: * [wiki:GeoDjangoBackground Background Information] * [wiki:GeoDjangoInstall Installation Instructions] * [wiki:GeoDjangoModelAPI Model API] * [wiki:GeoDjangoDatabaseAPI Database API] * [wiki:GEOSGeometry GEOS Geometries] (in progress). * [wiki:OGRGeometry OGR Geometries] (in progress). * [wiki:GeoDjangoExtras Extra Features] (in progress). The wiki documentation is woefully behind, please use the [http://geodjango.org/docs/ 1.0 preview documentation]. When in doubt, use `help` on the feature in question, as most [browser:django/branches/gis/django/contrib/gis GeoDjango modules] and routines have docstrings. == Roadmap == '''Note:''' In order to minimize confusion, please don't modify the official roadmap below. If you have a suggestion, place it in the [wiki:GeoDjango#Suggestions suggestion section] or ask your question in the [wiki:GeoDjango#FAQ FAQ]. If your request is urgent, submit a ticket requesting your feature or bugfix (make sure to specify the component as 'GIS'). Thanks for your cooperation! === Pending Features === The following is a discussion of features we hope to implement in !GeoDjango. Unless stated otherwise, features in this list are not prioritized. If you have a pet feature you may accelerate its development by submitting code yourself. * Support for additional !JavaScript mapping frameworks (e.g., !OpenLayers, Yahoo Maps, MS Live, !MaqQuest, etc.) * WMS views. * Support additional spatial databases: * Maybe sqlite (via [http://www.gaia-gis.it/spatialite/ SpatialLite]), DB2 and SQL Server 2008 === Milestones === Significant !GeoDjango changesets are mentioned below: * r8219: Merged the `gis` branch into trunk. * r7980: Merged in `newforms-admin` changesets from trunk, added geographic support for the admin. * r7641: Refactor of the `GeoQuerySet`; added many new features from the PostGIS API. * r7482: Merged with `queryset-refactor` changes from trunk. * r7104: Added support for distance queries on models using geodetic coordinate systems. * r6886: Added support for distance queries. * r6527: MySQL backend added. * r6524: Oracle Spatial backend added. * r6467: The `GeoMixin` was completely deprecated. * r6108: Created the `maps` module, which includes rudimentary support for generating Google Maps. * r5657: "Lazy-Geometry" support was added (courtesy of Robert Coup's excellent patch in #4322) -- deprecating most of the [wiki:GeoDjangoDatabaseAPI#ExtraInstanceMethods extra instance methods]. * r5529: Added utilities for importing vector data (e.g., SHP files) directly into !GeoDjango models. * The [source:django/branches/gis/django/contrib/gis/utils/LayerMapping.py LayerMapping] class will import vector data from GDAL-supported data sources. This is still 'beta', and requires installation of the [wiki:GeoDjango#GDAL GDAL library]. * r5397: Added a `ctypes` interface for [http://www.gdal.org/ogr/ogr_arch.html GDAL/OGR] in [source:django/branches/gis/django/contrib/gis/gdal django.contrib.gis.gdal]. * `OGRGeometry`: Wraps [http://www.gdal.org/ogr/classOGRGeometry.html OGR geometries], may be accessed with an extra instance method (e.g., `z.get_poly_ogr()`). * `SpatialReference`: Wraps [http://www.gdal.org/ogr/classOGRSpatialReference.html OGR Spatial Reference] objects, may be used to transform OGR geometries. * `DataSource`: Wraps [http://www.gdal.org/ogr/classOGRDataSource.html OGR Data Source] objects, may be used to explore GDAL-supported data sources * r5008: Added a `ctypes` interface for [http://geos.refractions.net/ GEOS] in [source:django/branches/gis/django/contrib/gis/gdal django.contrib.gis.geos]. * `GEOSGeometry`: Wraps [http://geos.refractions.net/ro/doxygen_docs/html/classgeos_1_1geom_1_1Geometry.html GEOS geometries]; all accessed geometry fields are returned as `GEOSGeometry` instances. * r4851: Added PostGIS indexing capability. == Implementation == === Design Issues === * Client JS/Flash framework. The `maps` module will be added soon, support for the following APIs will be implemented, or is being considered * [browser:django/branches/gis/django/contrib/gis/maps/google django.contrib.gis.maps.google]: Contains the `GoogleMap` object, which generates basic Google Maps API !JavaScript from customizable templates. Automatic zoom-level determination is included. * `django.contrib.gis.maps.openlayers`: Similar plans are in place for a [http://www.openlayers.org/ OpenLayers] module, which is ''the'' open source web mapping framework. Well-tested, BSD-licensed, and includes multitudes of features; for example, !OpenLayers supports WMS/WFS/tiles as well as Google, MS Virtual Earth, and Yahoo layers. ''See also'' ticket #5472. * [http://developer.yahoo.com/maps/ Yahoo!] , [http://platform.mapquest.com/specs-free.html MapQuest], and [http://www.mapstraction.com/ Mapstraction] are all possibilities for API hooks. * Mapping Framework (generating custom tiles, layers, labels, etc.) * [http://www.mapnik.org/ Mapnik] is a modern C++ engine that was designed for use with Python and generates beautiful maps. However, documentation is lacking and installation is difficult. First to use AGG rendering. * [http://mapserver.gis.umn.edu/ Mapserver] is the leading open source map renderer. !MapServer is well documented and has strong community support. Uses text-based configuration files, which may be difficult to set up. AGG rendering and dynamic loading of configuration files (e.g., from a string) has been added to version 5.0. * WMS Server * I'm not satisfied with any of the current WMS/WFS implementations. One implemented in Django would be desirable, e.g., `django.contrib.gis.wms`. Thoughts anyone? (OWSLib looks good, see below) === Collaboration === * PCL (Python Cartographic Library), now part of [http://www.gispython.org/ GIS Python], has done a lot of good work already. Let's apply the DRY principle. Strong opportunities for collaboration with regards to: * Mapping framework * WMS/WMF Framework -- [http://trac.gispython.org/projects/PCL/browser/OWSLib/trunk OWSLib] looks excellent for this (BSD licensed and has unit tests!) * Utilities * Database representation ideas * GEOS support, [http://zcologia.com/news/ Sean Gillies] (of PCL) was the maintainer of the old SWIG bindings, and is working on [http://trac.gispython.org/projects/PCL/wiki/ShapeLy ShapeLy], a !GeoDjango-inspired GEOS ctypes interface. * [http://code.google.com/p/django-coordinatesfield/ CoordinatesField]. * Jannis Leidel has already come up with a way to manipulate points in the admin interface, BSD licensed. * [http://exogen.case.edu/projects/geopy/ geopy] * Brian Beck has written a good foundation for geocoding and distance calculations, BSD licensed. === Suggestions === * Decide what to do about "invalid" shape files, for example, float SHP field with ' ' value. == FAQ == * Place your questions '''here'''. * '''Q:''' When will geodjango be merged into trunk? * The branch was merged into trunk on August 5th, 2008 in r8219. * '''Q:''' When dealing with points (say, degrees) from, do they need to be converted to be useful on the back-end data, assuming -that- data is in degrees? Is it enough to have the same datum and origin? (Reading the intro above is likely to answer the question.) * My (JDunck) reading indicates yes. Given the same coordinate system (i.e. datum, origin, and axes), degrees are useful without conversion. * '''Q:''' Can this implementation work with [http://dev.mysql.com/doc/refman/5.0/en/spatial-extensions.html MySQL spatial-extensions]. If not, it's planned? * ~~No, unfortunately support is not planned.~~ Support for MySQL was added as of r6527. The changes required for the Oracle backend made it relatively easy to implement one for MySQL. However, MySQL is limited to bounding-box queries only, and many advanced features (e.g., coordinate transformation, `geometry_columns` and `spatial_ref_sys` tables) are missing because of MySQL's implementation. Furthermore, MySQL's spatial indexes are limited to MyISAM tables, which are not transactional. * '''Q:''' Is this going to be a WMS Server/WMS Client/Both? OWSLib is just a WMS Client from what I have seen (from ruckc) * WMS Server first, client capability a possibility in certain situations (i.e. you want to cache data from another WMS server). Yes, OWSLib is a client, but it contains code for validating the correct parameters to send to a WMS server, thus it can be adapted into a Django view that validates whether the proper WMS parameters were given. Mapnik has an `ogcserver` module that can parse the correct parameters for WMS 1.1.1 and 1.3.0, however since it is licensed under the LGPL it cannot be easily incorporated into this branch (unlike OWSLib). * '''Q:''' Per [http://groups.google.com/group/django-developers/browse_thread/thread/9f6c70864bfa7f2e/ this discussion] in `django-developers`, "I can do spatial queries if they are directly between two models with polygon fields but I can't seem to get at the spatial queries through foreign keys." * Geographic queries require `GeoManager`, even if the model does not have a geographic field itself (in the case of a foreign key to a geo-field). The reply in the discussion gives detail into why. * '''Q:''' Why are the OGRGeometry methods `transform` and `transform_to` separate? * They are no longer separate, as `transform` now accepts `CoordTransform` and `SpatialReference` objects, as well as string WKT and integer SRID values (`transform` checks the input type and dispatches to the correct routine). The former reason for the separation was that each mapped to the separate OGR routines [http://www.gdal.org/ogr/ogr__api_8h.html#59a5b3f954b11cfbf6e78807c28d6090 OGR_G_Transform] and [http://www.gdal.org/ogr/ogr__api_8h.html#43af4c2127cea0a5059692a62c0feb63 OGR_G_TransformTo]. For better performance use `CoordTransform` objects when you will be performing the same transformation repeatedly. * '''Q:''' Why don't `Q` objects work? * `Q` object functionality is for use with models that use `QuerySet`. Because !GeoDjango models use `GeoQuerySet`, use the `GeoQ` object instead: {{{ >>> from django.contrib.gis.db.models import GeoQ >>> from geoapp.models import City, Country, State # See django.contrib.gis.tests.geoapp >>> tx = Country.objects.get(name='Texas') >>> ks = State.objects.get(name='Kansas') >>> City.objects.filter(GeoQ(point__within=tx.mpoly) | GeoQ(point__within=ks.poly)) [, , ] }}} == Example == === Geographic Models === Here is an example of how the model API currently works (assume this example is in geoapp/models.py): {{{ #!python from django.contrib.gis.db import models class District(models.Model): name = models.CharField(max_length=35) num = models.IntegerField() poly = models.PolygonField() objects = models.GeoManager() class School(models.Model): name = models.CharField(max_length=35) point = models.PointField() objects = models.GeoManager() }}} '''Notes''': The `GeoMixin` syntax is no longer needed as of r6467. By default, a GiST index will be created for the School `PointField`s fields. This behavior can be turned off by using `models.PointField(spatial_index=False)`. === Using syncdb === Both `manage.py` commands `sqlall` and `syncdb` work on geographic models: {{{ $ python manage.py sqlall geoapp }}} {{{ #!sql BEGIN; CREATE TABLE "geoapp_school" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(35) NOT NULL ) ; CREATE TABLE "geoapp_district" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(35) NOT NULL, "num" integer NOT NULL ) ; SELECT AddGeometryColumn('geoapp_school', 'point', 4326, 'POINT', 2); ALTER TABLE "geoapp_school" ALTER "point" SET NOT NULL; CREATE INDEX "geoapp_school_point_id" ON "geoapp_school" USING GIST ( "point" GIST_GEOMETRY_OPS ); SELECT AddGeometryColumn('geoapp_district', 'poly', 4326, 'POLYGON', 2); ALTER TABLE "geoapp_district" ALTER "poly" SET NOT NULL; CREATE INDEX "geoapp_district_poly_id" ON "geoapp_district" USING GIST ( "poly" GIST_GEOMETRY_OPS ); COMMIT; }}} {{{ $ python manage.py syncdb }}} '''Note:''' The geometry columns are created outside of the `CREATE TABLE` statements by the `AddGeometryColumn`. This is done according to the OpenGIS specfication. * ''See'' Open GIS Consortium, Inc., ''[http://www.opengis.org/docs/99-049.pdf OpenGIS Simple Feature Specification For SQL]'', Document 99-049 (May 5, 1999), at Ch. 2.3.8 (Geometry Values and Spatial Reference Systems, pg. 39). === Spatial Queries === After a geographic model has been created, the PostGIS additions to the API may be used. Geographic queries are done normally by using `filter()` and `exclude()` on geometry-enabled models using geographic lookup types (''see'' the [wiki:GeoDjangoDatabaseAPI Database API] for lookup types). In the following example, the `bbcontains` lookup type is used which is the same as the PostGIS `&&` operator. It looks to see if the ''bounding box'' of the polygon contains the specific point. The next example uses the PostGIS `Contains()` function, which calls GEOS library to test if the ''polygon'' actually contains the specific point, not just the bounding box. {{{ #!python >>> from geoapp.models import District, School >>> qs1 = District.objects.filter(poly__bbcontains='POINT(-95.362293 29.756539)') >>> qs2 = District.objects.filter(poly__contains='POINT(-95.362293 29.756539)') }}} Both spatial queries and normal queries using `filter()` may be used in the same query. For example, the following query set will only show school districts that have 'Houston' in their name and contain the given point within their polygon boundary: {{{ #!python >>> qs = District.objects.filter(name__contains='Houston').filter(poly__contains='POINT(-95.362293 29.756539)') }}} In PostGIS versions 1.2.2 and above, Refractions introduced a feature called "inline index magic" that automatically combines the indexed bounding box queries with their more computationally expensive counterparts. Prior to this revision the following filter could be used to get the same performance benefits: {{{ #!python >>> qs = District.objects.filter(poly__bbcontains='POINT(-95.362293 29.756539)').filter(poly__contains='POINT(-95.362293 29.756539)') }}} === Lazy-Geometries === Geographic fields on models are proxies to [wiki:GEOSGeometry GEOS Geometry] objects, allowing for many interesting possibilities: {{{ #!python >>> from django.contrib.gis.geos import fromstr >>> pnt = fromstr('POINT(-95.362293 29.756539)', srid=4326) >>> hisd = District.objects.get(name='Houston ISD') >>> hisd.poly.contains(pnt) True >>> hisd.poly.within(pnt) False }}}