1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

gis: added tests, added precision keyword, and generally improved GeoQuerySet.kml(); commented, improved, and added tests for GeometryProxy.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5806 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2007-08-06 01:27:22 +00:00
parent e982954602
commit d2ca3b8b8f
6 changed files with 135 additions and 25 deletions

View File

@ -1,7 +1,17 @@
"""
The PostGIS spatial database backend module.
"""
from query import get_geo_where_clause, GEOM_FUNC_PREFIX, POSTGIS_TERMS
from query import \
get_geo_where_clause, GEOM_FUNC_PREFIX, POSTGIS_TERMS, \
MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2
from creation import create_spatial_db
from field import PostGISField
# Whether PostGIS has AsKML() support.
if MAJOR_VERSION == 1:
# AsKML() only supported in versions 1.2.1+
if MINOR_VERSION1 == 3:
ASKML = 'ST_AsKML'
elif MINOR_VERSION1 == 2 and MINOR_VERSION2 >= 1:
ASKML = 'AsKML'

View File

@ -7,5 +7,5 @@ class GeoManager(Manager):
def get_query_set(self):
return GeoQuerySet(model=self.model)
def kml(self, field_name):
return self.get_query_set().kml(field_name)
def kml(self, field_name, **kwargs):
return self.get_query_set().kml(field_name, **kwargs)

View File

@ -1,30 +1,45 @@
"""
The GeometryProxy object, allows for lazy-geometries.
The GeometryProxy object, allows for lazy-geometries. The proxy uses
Python descriptors for instantiating and setting GEOS Geometry objects
corresponding to geographic model fields.
Thanks to Robert Coup for providing this functionality (see #4322).
"""
# GEOS Routines
from types import NoneType, StringType, UnicodeType
from django.contrib.gis.geos import GEOSGeometry, GEOSException
# TODO: docstrings & comments
# TODO: docstrings
class GeometryProxy(object):
def __init__(self, field):
"Proxy initializes on the given GeometryField."
self._field = field
def __get__(self, obj, type=None):
# Getting the value of the field.
geom_value = obj.__dict__[self._field.attname]
if (geom_value is None) or (isinstance(geom_value, GEOSGeometry)):
# If the value of the field is None, or is already a GEOS Geometry
# no more work is needed.
geom = geom_value
else:
# Otherwise, a GEOSGeometry object is built using the field's contents,
# and the model's corresponding attribute is set.
geom = GEOSGeometry(geom_value)
setattr(obj, self._field.attname, geom)
return geom
def __set__(self, obj, value):
if isinstance(value, GEOSGeometry):
if value and ((value.srid is None) and (self._field._srid is not None)):
value.srid = self._field._srid
if isinstance(value, GEOSGeometry) and (value.geom_type.upper() == self._field._geom):
# Getting set with GEOS Geometry; geom_type must match that of the field.
# If value's SRID is not set, setting it to the field's SRID.
if value.srid is None: value.srid = self._field._srid
elif isinstance(value, (NoneType, StringType, UnicodeType)):
# Getting set with None, WKT, or HEX
pass
else:
raise TypeError, 'cannot set %s GeometryProxy with value of type: %s' % (self._field._geom, type(value))
obj.__dict__[self._field.attname] = value
return value

View File

@ -1,9 +1,10 @@
from django.db.models.query import Q, QuerySet
import operator
from django.core.exceptions import ImproperlyConfigured
from django.db import backend
from django.db.models.query import Q, QuerySet
from django.db.models.fields import FieldDoesNotExist
from django.contrib.gis.db.models.fields import GeometryField
from django.contrib.gis.db.backend import parse_lookup # parse_lookup depends on the spatial database backend.
from django.db.models.fields import FieldDoesNotExist
import operator
class GeoQ(Q):
"Geographical query encapsulation object."
@ -37,10 +38,21 @@ class GeoQuerySet(QuerySet):
clone._filters = clone._filters & reduce(operator.and_, map(mapper, args))
return clone
def kml(self, field_name):
field = self.model._meta.get_field(field_name)
def kml(self, field_name, precision=8):
"""Returns KML representation of the given field name in a `kml`
attribute on each element of the QuerySet."""
# Is KML output supported?
try:
from django.contrib.gis.db.backend.postgis import ASKML
except ImportError:
raise ImproperlyConfigured, 'AsKML() only available in PostGIS versions 1.2.1 and greater.'
# Is the given field name a geographic field?
field = self.model._meta.get_field(field_name)
if not isinstance(field, GeometryField):
raise TypeError, 'KML output only available on GeometryField fields.'
field_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
backend.quote_name(field.column))
return self.extra(select={'kml':'AsKML(%s,6)' % field_col})
# Adding the AsKML function call to the SELECT part of the SQL.
return self.extra(select={'kml':'%s(%s,%s)' % (ASKML, field_col, precision)})

View File

@ -1,10 +1,10 @@
import unittest
from models import Country, City, State
from django.contrib.gis.geos import fromstr
from django.contrib.gis.geos import fromstr, Point, LineString, LinearRing, Polygon
class GeoModelTest(unittest.TestCase):
def test001_initial_sql(self):
def test01_initial_sql(self):
"Testing geographic initial SQL."
# Ensuring that data was loaded from initial SQL.
@ -12,7 +12,80 @@ class GeoModelTest(unittest.TestCase):
self.assertEqual(8, City.objects.count())
self.assertEqual(3, State.objects.count())
def test002_contains_contained(self):
def test02_proxy(self):
"Testing Lazy-Geometry support (using the GeometryProxy)."
#### Testing on a Point
pnt = Point(0, 0)
nullcity = City(name='NullCity', point=pnt)
nullcity.save()
# Making sure TypeError is thrown when trying to set with an
# incompatible type.
for bad in [5, 2.0, LineString((0, 0), (1, 1))]:
try:
nullcity.point = bad
except TypeError:
pass
else:
self.fail('Should throw a TypeError')
# Now setting with a compatible GEOS Geometry, saving, and ensuring
# the save took, notice no SRID is explicitly set.
new = Point(5, 23)
nullcity.point = new
# Ensuring that the SRID is automatically set to that of the
# field after assignment, but before saving.
self.assertEqual(4326, nullcity.point.srid)
nullcity.save()
# Ensuring the point was saved correctly after saving
self.assertEqual(new, City.objects.get(name='NullCity').point)
# Setting the X and Y of the Point
nullcity.point.x = 23
nullcity.point.y = 5
# Checking assignments pre & post-save.
self.assertNotEqual(Point(23, 5), City.objects.get(name='NullCity').point)
nullcity.save()
self.assertEqual(Point(23, 5), City.objects.get(name='NullCity').point)
nullcity.delete()
#### Testing on a Polygon
shell = LinearRing((0, 0), (0, 100), (100, 100), (100, 0), (0, 0))
inner = LinearRing((40, 40), (40, 60), (60, 60), (60, 40), (40, 40))
# Creating a State object using a built Polygon
ply = Polygon(shell.clone(), inner.clone())
nullstate = State(name='NullState', poly=ply)
self.assertEqual(4326, nullstate.poly.srid) # SRID auto-set from None
nullstate.save()
self.assertEqual(ply, State.objects.get(name='NullState').poly)
# Changing the interior ring on the poly attribute.
new_inner = LinearRing((30, 30), (30, 70), (70, 70), (70, 30), (30, 30))
nullstate.poly[1] = new_inner.clone()
ply[1] = new_inner
self.assertEqual(4326, nullstate.poly.srid)
nullstate.save()
self.assertEqual(ply, State.objects.get(name='NullState').poly)
nullstate.delete()
def test03_kml(self):
"Testing KML output from the database using GeoManager.kml()."
# Should throw an error trying to get KML from a non-geometry field.
try:
qs = City.objects.all().kml('name')
except TypeError:
pass
else:
self.fail('Expected a TypeError exception')
# Ensuring the KML is as expected.
ptown = City.objects.kml('point', precision=9).get(name='Pueblo')
self.assertEqual('<Point><coordinates>-104.609252,38.255001,0</coordinates></Point>', ptown.kml)
def test10_contains_contained(self):
"Testing the 'contained' and 'contains' lookup types."
# Getting Texas, yes we were a country -- once ;)
@ -47,7 +120,7 @@ class GeoModelTest(unittest.TestCase):
self.assertEqual(0, len(Country.objects.filter(mpoly__contains=pueblo.point))) # Query w/GEOSGeometry object
self.assertEqual(0, len(Country.objects.filter(mpoly__contains=okcity.point.wkt))) # Qeury w/WKT
def test003_lookup_insert_transform(self):
def test11_lookup_insert_transform(self):
"Testing automatic transform for lookups and inserts."
# San Antonio in 'WGS84' (SRID 4326) and 'NAD83(HARN) / Texas Centric Lambert Conformal' (SRID 3084)
@ -69,7 +142,7 @@ class GeoModelTest(unittest.TestCase):
self.assertAlmostEqual(wgs_pnt.x, sa.point.x, 6)
self.assertAlmostEqual(wgs_pnt.y, sa.point.y, 6)
def test004_null_geometries(self):
def test12_null_geometries(self):
"Testing NULL geometry support."
# Querying for both NULL and Non-NULL values.
@ -90,7 +163,7 @@ class GeoModelTest(unittest.TestCase):
nmi = State(name='Northern Mariana Islands', poly=None)
nmi.save()
def test005_left_right(self):
def test13_left_right(self):
"Testing the 'left' and 'right' lookup types."
# Left: A << B => true if xmax(A) < xmin(B)

View File

@ -268,7 +268,7 @@ class LayerMapping:
# Creating the CoordTransform object
ct = CoordTransform(self.source_srs, target_srs)
except Exception, msg:
raise Exception, 'Could not translate between the data source and model geometry.'
raise Exception, 'Could not translate between the data source and model geometry: %s' % msg
for feat in self.layer:
# The keyword arguments for model construction