/docs/ref/contrib/gis/tutorial.txt
Plain Text | 757 lines | 578 code | 179 blank | 0 comment | 0 complexity | 0d0b528fc509f942a56e49ed00a10b0f MD5 | raw file
Possible License(s): BSD-3-Clause
1================== 2GeoDjango Tutorial 3================== 4 5Introduction 6============ 7 8GeoDjango is an add-on for Django that turns it into a world-class geographic 9Web framework. GeoDjango strives to make at as simple as possible to create 10geographic Web applications, like location-based services. Some features include: 11 12* Django model fields for `OGC`_ geometries. 13* Extensions to Django's ORM for the querying and manipulation of spatial data. 14* Loosely-coupled, high-level Python interfaces for GIS geometry operations and 15 data formats. 16* Editing of geometry fields inside the admin. 17 18This tutorial assumes a familiarity with Django; thus, if you're brand new to 19Django please read through the :doc:`regular tutorial </intro/tutorial01>` to introduce 20yourself with basic Django concepts. 21 22.. note:: 23 24 GeoDjango has special prerequisites overwhat is required by Django -- 25 please consult the :ref:`installation documentation <ref-gis-install>` 26 for more details. 27 28This tutorial will guide you through the creation of a geographic Web 29application for viewing the `world borders`_. [#]_ Some of the code 30used in this tutorial is taken from and/or inspired by the `GeoDjango 31basic apps`_ project. [#]_ 32 33.. note:: 34 35 Proceed through the tutorial sections sequentially for step-by-step 36 instructions. 37 38.. _OGC: http://www.opengeospatial.org/ 39.. _world borders: http://thematicmapping.org/downloads/world_borders.php 40.. _GeoDjango basic apps: http://code.google.com/p/geodjango-basic-apps/ 41 42Setting Up 43========== 44 45Create a Spatial Database 46------------------------- 47 48.. note:: 49 50 MySQL and Oracle users can skip this section because spatial types 51 are already built into the database. 52 53First, a spatial database needs to be created for our project. If using 54PostgreSQL and PostGIS, then the following commands will 55create the database from a :ref:`spatial database template <spatialdb_template>`:: 56 57 $ createdb -T template_postgis geodjango 58 59.. note:: 60 61 This command must be issued by a database user that has permissions to 62 create a database. Here is an example set of commands to create such 63 a user:: 64 65 $ sudo su - postgres 66 $ createuser --createdb geo 67 $ exit 68 69 Replace ``geo`` to correspond to the system login user name will be 70 connecting to the database. For example, ``johndoe`` if that is the 71 system user that will be running GeoDjango. 72 73Users of SQLite and SpatiaLite should consult the instructions on how 74to create a :ref:`SpatiaLite database <create_spatialite_db>`. 75 76Create GeoDjango Project 77------------------------ 78 79Use the ``django-admin.py`` script like normal to create a ``geodjango`` project:: 80 81 $ django-admin.py startproject geodjango 82 83With the project initialized, now create a ``world`` Django application within 84the ``geodjango`` project:: 85 86 $ cd geodjango 87 $ python manage.py startapp world 88 89Configure ``settings.py`` 90------------------------- 91 92The ``geodjango`` project settings are stored in the ``settings.py`` file. Edit 93the database connection settings appropriately:: 94 95 DATABASES = { 96 'default': { 97 'ENGINE': 'django.contrib.gis.db.backends.postgis', 98 'NAME': 'geodjango', 99 'USER': 'geo', 100 } 101 } 102 103.. note:: 104 105 These database settings are for Django 1.2 and above. 106 107In addition, modify the :setting:`INSTALLED_APPS` setting to include 108:mod:`django.contrib.admin`, :mod:`django.contrib.gis`, 109and ``world`` (our newly created application):: 110 111 INSTALLED_APPS = ( 112 'django.contrib.auth', 113 'django.contrib.contenttypes', 114 'django.contrib.sessions', 115 'django.contrib.sites', 116 'django.contrib.admin', 117 'django.contrib.gis', 118 'world' 119 ) 120 121Geographic Data 122=============== 123 124.. _worldborders: 125 126World Borders 127------------- 128 129The world borders data is available in this `zip file`__. Create a data directory 130in the ``world`` application, download the world borders data, and unzip. 131On GNU/Linux platforms the following commands should do it:: 132 133 $ mkdir world/data 134 $ cd world/data 135 $ wget http://thematicmapping.org/downloads/TM_WORLD_BORDERS-0.3.zip 136 $ unzip TM_WORLD_BORDERS-0.3.zip 137 $ cd ../.. 138 139The world borders ZIP file contains a set of data files collectively known as 140an `ESRI Shapefile`__, one of the most popular geospatial data formats. When 141unzipped the world borders data set includes files with the following extensions: 142 143* ``.shp``: Holds the vector data for the world borders geometries. 144* ``.shx``: Spatial index file for geometries stored in the ``.shp``. 145* ``.dbf``: Database file for holding non-geometric attribute data 146 (e.g., integer and character fields). 147* ``.prj``: Contains the spatial reference information for the geographic 148 data stored in the shapefile. 149 150__ http://thematicmapping.org/downloads/TM_WORLD_BORDERS-0.3.zip 151__ http://en.wikipedia.org/wiki/Shapefile 152 153Use ``ogrinfo`` to examine spatial data 154--------------------------------------- 155 156The GDAL ``ogrinfo`` utility is excellent for examining metadata about 157shapefiles (or other vector data sources):: 158 159 $ ogrinfo world/data/TM_WORLD_BORDERS-0.3.shp 160 INFO: Open of `world/data/TM_WORLD_BORDERS-0.3.shp' 161 using driver `ESRI Shapefile' successful. 162 1: TM_WORLD_BORDERS-0.3 (Polygon) 163 164Here ``ogrinfo`` is telling us that the shapefile has one layer, and that 165layer contains polygon data. To find out more we'll specify the layer name 166and use the ``-so`` option to get only important summary information:: 167 168 $ ogrinfo -so world/data/TM_WORLD_BORDERS-0.3.shp TM_WORLD_BORDERS-0.3 169 INFO: Open of `world/data/TM_WORLD_BORDERS-0.3.shp' 170 using driver `ESRI Shapefile' successful. 171 172 Layer name: TM_WORLD_BORDERS-0.3 173 Geometry: Polygon 174 Feature Count: 246 175 Extent: (-180.000000, -90.000000) - (180.000000, 83.623596) 176 Layer SRS WKT: 177 GEOGCS["GCS_WGS_1984", 178 DATUM["WGS_1984", 179 SPHEROID["WGS_1984",6378137.0,298.257223563]], 180 PRIMEM["Greenwich",0.0], 181 UNIT["Degree",0.0174532925199433]] 182 FIPS: String (2.0) 183 ISO2: String (2.0) 184 ISO3: String (3.0) 185 UN: Integer (3.0) 186 NAME: String (50.0) 187 AREA: Integer (7.0) 188 POP2005: Integer (10.0) 189 REGION: Integer (3.0) 190 SUBREGION: Integer (3.0) 191 LON: Real (8.3) 192 LAT: Real (7.3) 193 194This detailed summary information tells us the number of features in the layer 195(246), the geographical extent, the spatial reference system ("SRS WKT"), 196as well as detailed information for each attribute field. For example, 197``FIPS: String (2.0)`` indicates that there's a ``FIPS`` character field 198with a maximum length of 2; similarly, ``LON: Real (8.3)`` is a floating-point 199field that holds a maximum of 8 digits up to three decimal places. Although 200this information may be found right on the `world borders`_ Web site, this shows 201you how to determine this information yourself when such metadata is not 202provided. 203 204Geographic Models 205================= 206 207Defining a Geographic Model 208--------------------------- 209 210Now that we've examined our world borders data set using ``ogrinfo``, we can 211create a GeoDjango model to represent this data:: 212 213 from django.contrib.gis.db import models 214 215 class WorldBorders(models.Model): 216 # Regular Django fields corresponding to the attributes in the 217 # world borders shapefile. 218 name = models.CharField(max_length=50) 219 area = models.IntegerField() 220 pop2005 = models.IntegerField('Population 2005') 221 fips = models.CharField('FIPS Code', max_length=2) 222 iso2 = models.CharField('2 Digit ISO', max_length=2) 223 iso3 = models.CharField('3 Digit ISO', max_length=3) 224 un = models.IntegerField('United Nations Code') 225 region = models.IntegerField('Region Code') 226 subregion = models.IntegerField('Sub-Region Code') 227 lon = models.FloatField() 228 lat = models.FloatField() 229 230 # GeoDjango-specific: a geometry field (MultiPolygonField), and 231 # overriding the default manager with a GeoManager instance. 232 mpoly = models.MultiPolygonField() 233 objects = models.GeoManager() 234 235 # So the model is pluralized correctly in the admin. 236 class Meta: 237 verbose_name_plural = "World Borders" 238 239 # Returns the string representation of the model. 240 def __unicode__(self): 241 return self.name 242 243Two important things to note: 244 2451. The ``models`` module is imported from :mod:`django.contrib.gis.db`. 2462. The model overrides its default manager with 247 :class:`~django.contrib.gis.db.models.GeoManager`; this is *required* 248 to perform spatial queries. 249 250When declaring a geometry field on your model the default spatial reference system 251is WGS84 (meaning the `SRID`__ is 4326) -- in other words, the field coordinates are in 252longitude/latitude pairs in units of degrees. If you want the coordinate system to be 253different, then SRID of the geometry field may be customized by setting the ``srid`` 254with an integer corresponding to the coordinate system of your choice. 255 256__ http://en.wikipedia.org/wiki/SRID 257 258Run ``syncdb`` 259-------------- 260 261After you've defined your model, it needs to be synced with the spatial database. 262First, let's look at the SQL that will generate the table for the ``WorldBorders`` 263model:: 264 265 $ python manage.py sqlall world 266 267This management command should produce the following output:: 268 269 BEGIN; 270 CREATE TABLE "world_worldborders" ( 271 "id" serial NOT NULL PRIMARY KEY, 272 "name" varchar(50) NOT NULL, 273 "area" integer NOT NULL, 274 "pop2005" integer NOT NULL, 275 "fips" varchar(2) NOT NULL, 276 "iso2" varchar(2) NOT NULL, 277 "iso3" varchar(3) NOT NULL, 278 "un" integer NOT NULL, 279 "region" integer NOT NULL, 280 "subregion" integer NOT NULL, 281 "lon" double precision NOT NULL, 282 "lat" double precision NOT NULL 283 ) 284 ; 285 SELECT AddGeometryColumn('world_worldborders', 'mpoly', 4326, 'MULTIPOLYGON', 2); 286 ALTER TABLE "world_worldborders" ALTER "mpoly" SET NOT NULL; 287 CREATE INDEX "world_worldborders_mpoly_id" ON "world_worldborders" USING GIST ( "mpoly" GIST_GEOMETRY_OPS ); 288 COMMIT; 289 290If satisfied, you may then create this table in the database by running the 291``syncdb`` management command:: 292 293 $ python manage.py syncdb 294 Creating table world_worldborders 295 Installing custom SQL for world.WorldBorders model 296 297The ``syncdb`` command may also prompt you to create an admin user; go ahead and 298do so (not required now, may be done at any point in the future using the 299``createsuperuser`` management command). 300 301Importing Spatial Data 302====================== 303 304This section will show you how to take the data from the world borders 305shapefile and import it into GeoDjango models using the :ref:`ref-layermapping`. 306There are many different different ways to import data in to a 307spatial database -- besides the tools included within GeoDjango, you 308may also use the following to populate your spatial database: 309 310* `ogr2ogr`_: Command-line utility, included with GDAL, that 311 supports loading a multitude of vector data formats into 312 the PostGIS, MySQL, and Oracle spatial databases. 313* `shp2pgsql`_: This utility is included with PostGIS and only supports 314 ESRI shapefiles. 315 316.. _ogr2ogr: http://www.gdal.org/ogr2ogr.html 317.. _shp2pgsql: http://postgis.refractions.net/documentation/manual-1.5/ch04.html#shp2pgsql_usage 318 319.. _gdalinterface: 320 321GDAL Interface 322-------------- 323 324Earlier we used the ``ogrinfo`` to explore the contents of the world borders 325shapefile. Included within GeoDjango is an interface to GDAL's powerful OGR 326library -- in other words, you'll be able explore all the vector data sources 327that OGR supports via a Pythonic API. 328 329First, invoke the Django shell:: 330 331 $ python manage.py shell 332 333If the :ref:`worldborders` data was downloaded like earlier in the 334tutorial, then we can determine the path using Python's built-in 335``os`` module:: 336 337 >>> import os 338 >>> from geodjango import world 339 >>> world_shp = os.path.abspath(os.path.join(os.path.dirname(world.__file__), 340 ... 'data/TM_WORLD_BORDERS-0.3.shp')) 341 342Now, the world borders shapefile may be opened using GeoDjango's 343:class:`~django.contrib.gis.gdal.DataSource` interface:: 344 345 >>> from django.contrib.gis.gdal import * 346 >>> ds = DataSource(world_shp) 347 >>> print ds 348 / ... /geodjango/world/data/TM_WORLD_BORDERS-0.3.shp (ESRI Shapefile) 349 350Data source objects can have different layers of geospatial features; however, 351shapefiles are only allowed to have one layer:: 352 353 >>> print len(ds) 354 1 355 >>> lyr = ds[0] 356 >>> print lyr 357 TM_WORLD_BORDERS-0.3 358 359You can see what the geometry type of the layer is and how many features it 360contains:: 361 362 >>> print lyr.geom_type 363 Polygon 364 >>> print len(lyr) 365 246 366 367.. note:: 368 369 Unfortunately the shapefile data format does not allow for greater 370 specificity with regards to geometry types. This shapefile, like 371 many others, actually includes ``MultiPolygon`` geometries in its 372 features. You need to watch out for this when creating your models 373 as a GeoDjango ``PolygonField`` will not accept a ``MultiPolygon`` 374 type geometry -- thus a ``MultiPolygonField`` is used in our model's 375 definition instead. 376 377The :class:`~django.contrib.gis.gdal.Layer` may also have a spatial reference 378system associated with it -- if it does, the ``srs`` attribute will return a 379:class:`~django.contrib.gis.gdal.SpatialReference` object:: 380 381 >>> srs = lyr.srs 382 >>> print srs 383 GEOGCS["GCS_WGS_1984", 384 DATUM["WGS_1984", 385 SPHEROID["WGS_1984",6378137.0,298.257223563]], 386 PRIMEM["Greenwich",0.0], 387 UNIT["Degree",0.0174532925199433]] 388 >>> srs.proj4 # PROJ.4 representation 389 '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs ' 390 391Here we've noticed that the shapefile is in the popular WGS84 spatial reference 392system -- in other words, the data uses units of degrees longitude and latitude. 393 394In addition, shapefiles also support attribute fields that may contain 395additional data. Here are the fields on the World Borders layer: 396 397 >>> print lyr.fields 398 ['FIPS', 'ISO2', 'ISO3', 'UN', 'NAME', 'AREA', 'POP2005', 'REGION', 'SUBREGION', 'LON', 'LAT'] 399 400Here we are examining the OGR types (e.g., whether a field is an integer or 401a string) associated with each of the fields: 402 403 >>> [fld.__name__ for fld in lyr.field_types] 404 ['OFTString', 'OFTString', 'OFTString', 'OFTInteger', 'OFTString', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTInteger', 'OFTReal', 'OFTReal'] 405 406You can iterate over each feature in the layer and extract information from both 407the feature's geometry (accessed via the ``geom`` attribute) as well as the 408feature's attribute fields (whose **values** are accessed via ``get()`` 409method):: 410 411 >>> for feat in lyr: 412 ... print feat.get('NAME'), feat.geom.num_points 413 ... 414 Guernsey 18 415 Jersey 26 416 South Georgia South Sandwich Islands 338 417 Taiwan 363 418 419:class:`~django.contrib.gis.gdal.Layer` objects may be sliced:: 420 421 >>> lyr[0:2] 422 [<django.contrib.gis.gdal.feature.Feature object at 0x2f47690>, <django.contrib.gis.gdal.feature.Feature object at 0x2f47650>] 423 424And individual features may be retrieved by their feature ID:: 425 426 >>> feat = lyr[234] 427 >>> print feat.get('NAME') 428 San Marino 429 430Here the boundary geometry for San Marino is extracted and looking 431exported to WKT and GeoJSON:: 432 433 >>> geom = feat.geom 434 >>> print geom.wkt 435 POLYGON ((12.415798 43.957954,12.450554 ... 436 >>> print geom.json 437 { "type": "Polygon", "coordinates": [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ... 438 439 440``LayerMapping`` 441---------------- 442 443We're going to dive right in -- create a file called ``load.py`` inside the 444``world`` application, and insert the following:: 445 446 import os 447 from django.contrib.gis.utils import LayerMapping 448 from models import WorldBorders 449 450 world_mapping = { 451 'fips' : 'FIPS', 452 'iso2' : 'ISO2', 453 'iso3' : 'ISO3', 454 'un' : 'UN', 455 'name' : 'NAME', 456 'area' : 'AREA', 457 'pop2005' : 'POP2005', 458 'region' : 'REGION', 459 'subregion' : 'SUBREGION', 460 'lon' : 'LON', 461 'lat' : 'LAT', 462 'mpoly' : 'MULTIPOLYGON', 463 } 464 465 world_shp = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data/TM_WORLD_BORDERS-0.3.shp')) 466 467 def run(verbose=True): 468 lm = LayerMapping(WorldBorders, world_shp, world_mapping, 469 transform=False, encoding='iso-8859-1') 470 471 lm.save(strict=True, verbose=verbose) 472 473A few notes about what's going on: 474 475* Each key in the ``world_mapping`` dictionary corresponds to a field in the 476 ``WorldBorders`` model, and the value is the name of the shapefile field 477 that data will be loaded from. 478* The key ``mpoly`` for the geometry field is ``MULTIPOLYGON``, the 479 geometry type we wish to import as. Even if simple polygons are encountered 480 in the shapefile they will automatically be converted into collections prior 481 to insertion into the database. 482* The path to the shapefile is not absolute -- in other words, if you move the 483 ``world`` application (with ``data`` subdirectory) to a different location, 484 then the script will still work. 485* The ``transform`` keyword is set to ``False`` because the data in the 486 shapefile does not need to be converted -- it's already in WGS84 (SRID=4326). 487* The ``encoding`` keyword is set to the character encoding of string values in 488 the shapefile. This ensures that string values are read and saved correctly 489 from their original encoding system. 490 491Afterwards, invoke the Django shell from the ``geodjango`` project directory:: 492 493 $ python manage.py shell 494 495Next, import the ``load`` module, call the ``run`` routine, and watch ``LayerMapping`` 496do the work:: 497 498 >>> from world import load 499 >>> load.run() 500 501 502.. _ogrinspect-intro: 503 504Try ``ogrinspect`` 505------------------ 506Now that you've seen how to define geographic models and import data with the 507:ref:`ref-layermapping`, it's possible to further automate this process with 508use of the :djadmin:`ogrinspect` management command. The :djadmin:`ogrinspect` 509command introspects a GDAL-supported vector data source (e.g., a shapefile) and 510generates a model definition and ``LayerMapping`` dictionary automatically. 511 512The general usage of the command goes as follows:: 513 514 $ python manage.py ogrinspect [options] <data_source> <model_name> [options] 515 516Where ``data_source`` is the path to the GDAL-supported data source and 517``model_name`` is the name to use for the model. Command-line options may 518be used to further define how the model is generated. 519 520For example, the following command nearly reproduces the ``WorldBorders`` model 521and mapping dictionary created above, automatically:: 522 523 $ python manage.py ogrinspect world/data/TM_WORLD_BORDERS-0.3.shp WorldBorders --srid=4326 --mapping --multi 524 525A few notes about the command-line options given above: 526 527* The ``--srid=4326`` option sets the SRID for the geographic field. 528* The ``--mapping`` option tells ``ogrinspect`` to also generate a 529 mapping dictionary for use with :class:`~django.contrib.gis.utils.LayerMapping`. 530* The ``--multi`` option is specified so that the geographic field is a 531 :class:`~django.contrib.gis.db.models.MultiPolygonField` instead of just a 532 :class:`~django.contrib.gis.db.models.PolygonField`. 533 534The command produces the following output, which may be copied 535directly into the ``models.py`` of a GeoDjango application:: 536 537 # This is an auto-generated Django model module created by ogrinspect. 538 from django.contrib.gis.db import models 539 540 class WorldBorders(models.Model): 541 fips = models.CharField(max_length=2) 542 iso2 = models.CharField(max_length=2) 543 iso3 = models.CharField(max_length=3) 544 un = models.IntegerField() 545 name = models.CharField(max_length=50) 546 area = models.IntegerField() 547 pop2005 = models.IntegerField() 548 region = models.IntegerField() 549 subregion = models.IntegerField() 550 lon = models.FloatField() 551 lat = models.FloatField() 552 geom = models.MultiPolygonField(srid=4326) 553 objects = models.GeoManager() 554 555 # Auto-generated `LayerMapping` dictionary for WorldBorders model 556 worldborders_mapping = { 557 'fips' : 'FIPS', 558 'iso2' : 'ISO2', 559 'iso3' : 'ISO3', 560 'un' : 'UN', 561 'name' : 'NAME', 562 'area' : 'AREA', 563 'pop2005' : 'POP2005', 564 'region' : 'REGION', 565 'subregion' : 'SUBREGION', 566 'lon' : 'LON', 567 'lat' : 'LAT', 568 'geom' : 'MULTIPOLYGON', 569 } 570 571Spatial Queries 572=============== 573 574Spatial Lookups 575--------------- 576GeoDjango extends the Django ORM and allows the use of spatial lookups. 577Let's do an example where we find the ``WorldBorder`` model that contains 578a point. First, fire up the management shell:: 579 580 $ python manage.py shell 581 582Now, define a point of interest [#]_:: 583 584 >>> pnt_wkt = 'POINT(-95.3385 29.7245)' 585 586The ``pnt_wkt`` string represents the point at -95.3385 degrees longitude, 587and 29.7245 degrees latitude. The geometry is in a format known as 588Well Known Text (WKT), an open standard issued by the Open Geospatial 589Consortium (OGC). [#]_ Import the ``WorldBorders`` model, and perform 590a ``contains`` lookup using the ``pnt_wkt`` as the parameter:: 591 592 >>> from world.models import WorldBorders 593 >>> qs = WorldBorders.objects.filter(mpoly__contains=pnt_wkt) 594 >>> qs 595 [<WorldBorders: United States>] 596 597Here we retrieved a ``GeoQuerySet`` that has only one model: the one 598for the United States (which is what we would expect). Similarly, 599a :ref:`GEOS geometry object <ref-geos>` may also be used -- here the ``intersects`` 600spatial lookup is combined with the ``get`` method to retrieve 601only the ``WorldBorders`` instance for San Marino instead of a queryset:: 602 603 >>> from django.contrib.gis.geos import Point 604 >>> pnt = Point(12.4604, 43.9420) 605 >>> sm = WorldBorders.objects.get(mpoly__intersects=pnt) 606 >>> sm 607 <WorldBorders: San Marino> 608 609The ``contains`` and ``intersects`` lookups are just a subset of what's 610available -- the :ref:`ref-gis-db-api` documentation has more. 611 612Automatic Spatial Transformations 613--------------------------------- 614When querying the spatial database GeoDjango automatically transforms 615geometries if they're in a different coordinate system. In the following 616example, the coordinate will be expressed in terms of `EPSG SRID 32140`__, 617a coordinate system specific to south Texas **only** and in units of 618**meters** and not degrees:: 619 620 >>> from django.contrib.gis.geos import * 621 >>> pnt = Point(954158.1, 4215137.1, srid=32140) 622 623Note that ``pnt`` may also constructed with EWKT, an "extended" form of 624WKT that includes the SRID:: 625 626 >>> pnt = GEOSGeometry('SRID=32140;POINT(954158.1 4215137.1)') 627 628When using GeoDjango's ORM, it will automatically wrap geometry values 629in transformation SQL, allowing the developer to work at a higher level 630of abstraction:: 631 632 >>> qs = WorldBorders.objects.filter(mpoly__intersects=pnt) 633 >>> print qs.query # Generating the SQL 634 SELECT "world_worldborders"."id", "world_worldborders"."name", "world_worldborders"."area", 635 "world_worldborders"."pop2005", "world_worldborders"."fips", "world_worldborders"."iso2", 636 "world_worldborders"."iso3", "world_worldborders"."un", "world_worldborders"."region", 637 "world_worldborders"."subregion", "world_worldborders"."lon", "world_worldborders"."lat", 638 "world_worldborders"."mpoly" FROM "world_worldborders" 639 WHERE ST_Intersects("world_worldborders"."mpoly", ST_Transform(%s, 4326)) 640 >>> qs # printing evaluates the queryset 641 [<WorldBorders: United States>] 642 643__ http://spatialreference.org/ref/epsg/32140/ 644 645Lazy Geometries 646--------------- 647Geometries come to GeoDjango in a standardized textual representation. Upon 648access of the geometry field, GeoDjango creates a `GEOS geometry object <ref-geos>`, 649exposing powerful functionality, such as serialization properties for 650popular geospatial formats:: 651 652 >>> sm = WorldBorders.objects.get(name='San Marino') 653 >>> sm.mpoly 654 <MultiPolygon object at 0x24c6798> 655 >>> sm.mpoly.wkt # WKT 656 MULTIPOLYGON (((12.4157980000000006 43.9579540000000009, 12.4505540000000003 43.9797209999999978, ... 657 >>> sm.mpoly.wkb # WKB (as Python binary buffer) 658 <read-only buffer for 0x1fe2c70, size -1, offset 0 at 0x2564c40> 659 >>> sm.mpoly.geojson # GeoJSON (requires GDAL) 660 '{ "type": "MultiPolygon", "coordinates": [ [ [ [ 12.415798, 43.957954 ], [ 12.450554, 43.979721 ], ... 661 662This includes access to all of the advanced geometric operations provided by 663the GEOS library:: 664 665 >>> pnt = Point(12.4604, 43.9420) 666 >>> sm.mpoly.contains(pnt) 667 True 668 >>> pnt.contains(sm.mpoly) 669 False 670 671``GeoQuerySet`` Methods 672----------------------- 673 674 675Putting your data on the map 676============================ 677 678Google 679------ 680 681Geographic Admin 682---------------- 683 684GeoDjango extends :doc:`Django's admin application </ref/contrib/admin/index>` 685to enable support for editing geometry fields. 686 687Basics 688^^^^^^ 689 690GeoDjango also supplements the Django admin by allowing users to create 691and modify geometries on a JavaScript slippy map (powered by `OpenLayers`_). 692 693Let's dive in again -- create a file called ``admin.py`` inside the 694``world`` application, and insert the following:: 695 696 from django.contrib.gis import admin 697 from models import WorldBorders 698 699 admin.site.register(WorldBorders, admin.GeoModelAdmin) 700 701Next, edit your ``urls.py`` in the ``geodjango`` project folder to look 702as follows:: 703 704 from django.conf.urls.defaults import * 705 from django.contrib.gis import admin 706 707 admin.autodiscover() 708 709 urlpatterns = patterns('', 710 (r'^admin/', include(admin.site.urls)), 711 ) 712 713Start up the Django development server:: 714 715 $ python manage.py runserver 716 717Finally, browse to ``http://localhost:8000/admin/``, and log in with the admin 718user created after running ``syncdb``. Browse to any of the ``WorldBorders`` 719entries -- the borders may be edited by clicking on a polygon and dragging 720the vertexes to the desired position. 721 722.. _OpenLayers: http://openlayers.org/ 723.. _Open Street Map: http://openstreetmap.org/ 724.. _Vector Map Level 0: http://earth-info.nga.mil/publications/vmap0.html 725.. _Metacarta: http://metacarta.com 726 727.. _osmgeoadmin-intro: 728 729``OSMGeoAdmin`` 730^^^^^^^^^^^^^^^ 731 732With the :class:`~django.contrib.gis.admin.OSMGeoAdmin`, GeoDjango uses 733a `Open Street Map`_ layer in the admin. 734This provides more context (including street and thoroughfare details) than 735available with the :class:`~django.contrib.gis.admin.GeoModelAdmin` 736(which uses the `Vector Map Level 0`_ WMS data set hosted at `Metacarta`_). 737 738First, there are some important requirements and limitations: 739 740* :class:`~django.contrib.gis.admin.OSMGeoAdmin` requires that the 741 :ref:`spherical mercator projection be added <addgoogleprojection>` 742 to the to be added to the ``spatial_ref_sys`` table (PostGIS 1.3 and 743 below, only). 744* The PROJ.4 datum shifting files must be installed (see the 745 :ref:`PROJ.4 installation instructions <proj4>` for more details). 746 747If you meet these requirements, then just substitute in the ``OSMGeoAdmin`` 748option class in your ``admin.py`` file:: 749 750 admin.site.register(WorldBorders, admin.OSMGeoAdmin) 751 752.. rubric:: Footnotes 753 754.. [#] Special thanks to Bj?¸rn Sandvik of `thematicmapping.org <http://thematicmapping.org>`_ for providing and maintaining this data set. 755.. [#] GeoDjango basic apps was written by Dane Springmeyer, Josh Livni, and Christopher Schmidt. 756.. [#] Here the point is for the `University of Houston Law Center <http://www.law.uh.edu/>`_ . 757.. [#] Open Geospatial Consortium, Inc., `OpenGIS Simple Feature Specification For SQL <http://www.opengis.org/docs/99-049.pdf>`_, Document 99-049.