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

gis: fixed support for 'isnull' lookup type on geometry columns, and added corresponding tests; added tests for 'left' and 'right' lookup types; added interactive keyword to create_spatial_db().

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5773 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2007-07-28 18:59:44 +00:00
parent 357ef5d975
commit 68f9a8ee8e
8 changed files with 91 additions and 15 deletions

View File

@ -114,8 +114,9 @@ class GeometryField(Field):
def get_db_prep_lookup(self, lookup_type, value):
"Returns field's value prepared for database lookup, accepts WKT and GEOS Geometries for the value."
if not bool(value): return None
if lookup_type in POSTGIS_TERMS:
if lookup_type == 'isnull': return [value] # special case for NULL geometries.
if not bool(value): return [None] # If invalid value passed in.
if isinstance(value, GEOSGeometry):
# GEOSGeometry instance passed in.
if value.srid != self._srid:

View File

@ -50,6 +50,9 @@ POSTGIS_GEOMETRY_FUNCTIONS = {
'relate' : 'Relate',
}
# Any other lookup types that do not require a mapping.
MISC_TERMS = ['isnull']
# The quotation used for postgis (uses single quotes).
def quotename(value, dbl=False):
if dbl: return '"%s"' % value
@ -58,7 +61,8 @@ def quotename(value, dbl=False):
# These are the PostGIS-customized QUERY_TERMS, combines both the operators
# and the geometry functions.
POSTGIS_TERMS = list(POSTGIS_OPERATORS.keys()) # Getting the operators first
POSTGIS_TERMS.extend(list(POSTGIS_GEOMETRY_FUNCTIONS.keys())) # Adding on the Geometry Functions
POSTGIS_TERMS += list(POSTGIS_GEOMETRY_FUNCTIONS.keys()) # Adding on the Geometry Functions
POSTGIS_TERMS += MISC_TERMS # Adding any other miscellaneous terms (e.g., 'isnull')
POSTGIS_TERMS = tuple(POSTGIS_TERMS) # Making immutable
def get_geo_where_clause(lookup_type, table_prefix, field_name, value):

View File

@ -9,3 +9,8 @@ class City(models.Model, models.GeoMixin):
name = models.CharField(maxlength=30)
point = models.PointField()
objects = models.GeoManager()
class State(models.Model, models.GeoMixin):
name = models.CharField(maxlength=30)
poly = models.PolygonField(null=True) # Allowing NULL geometries here.
objects = models.GeoManager()

View File

@ -2,4 +2,7 @@ INSERT INTO geoapp_city ("name", "point") VALUES ('Houston', 'SRID=4326;POINT (-
INSERT INTO geoapp_city ("name", "point") VALUES ('Dallas', 'SRID=4326;POINT (-96.801611 32.782057)');
INSERT INTO geoapp_city ("name", "point") VALUES ('Oklahoma City', 'SRID=4326;POINT (-97.521157 34.464642)');
INSERT INTO geoapp_city ("name", "point") VALUES ('Wellington', 'SRID=4326;POINT (174.783117 -41.315268)');
INSERT INTO geoapp_city ("name", "point") VALUES ('Pueblo', 'SRID=4326;POINT (-104.609252 38.255001)');
INSERT INTO geoapp_city ("name", "point") VALUES ('Pueblo', 'SRID=4326;POINT (-104.609252 38.255001)');
INSERT INTO geoapp_city ("name", "point") VALUES ('Lawrence', 'SRID=4326;POINT (-95.235060 38.971823)');
INSERT INTO geoapp_city ("name", "point") VALUES ('Chicago', 'SRID=4326;POINT (-87.650175 41.850385)');
INSERT INTO geoapp_city ("name", "point") VALUES ('Victoria', 'SRID=4326;POINT (-123.305196 48.462611)');

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
import unittest
from models import Country, City
from django.contrib.gis.geos import fromstr, Point
from models import Country, City, State
from django.contrib.gis.geos import fromstr
class GeoModelTest(unittest.TestCase):
@ -9,8 +9,9 @@ class GeoModelTest(unittest.TestCase):
# Ensuring that data was loaded from initial SQL.
self.assertEqual(2, Country.objects.count())
self.assertEqual(5, City.objects.count())
self.assertEqual(8, City.objects.count())
self.assertEqual(3, State.objects.count())
def test002_contains_contained(self):
"Testing the 'contained' and 'contains' lookup types."
@ -22,23 +23,24 @@ class GeoModelTest(unittest.TestCase):
# _bounding box_ of the Geometries.
qs = City.objects.filter(point__contained=texas.mpoly)
self.assertEqual(3, qs.count())
city_names = [c.name for c in qs]
self.assertEqual(True, 'Houston' in city_names)
self.assertEqual(True, 'Dallas' in city_names)
self.assertEqual(True, 'Oklahoma City' in city_names)
cities = ['Houston', 'Dallas', 'Oklahoma City']
for c in qs: self.assertEqual(True, c.name in cities)
# Pulling out some cities.
houston = City.objects.get(name='Houston')
wellington = City.objects.get(name='Wellington')
pueblo = City.objects.get(name='Pueblo')
okcity = City.objects.get(name='Oklahoma City')
lawrence = City.objects.get(name='Lawrence')
# Now testing contains on the countries using the points for
# Houston and Wellington.
tx = Country.objects.get(mpoly__contains=houston.point) # Query w/GEOSGeometry
nz = Country.objects.get(mpoly__contains=wellington.point.hex) # Query w/EWKBHEX
ks = State.objects.get(poly__contains=lawrence.point)
self.assertEqual('Texas', tx.name)
self.assertEqual('New Zealand', nz.name)
self.assertEqual('Kansas', ks.name)
# Pueblo and Oklahoma City (even though OK City is within the bounding box of Texas)
# are not contained in Texas or New Zealand.
@ -48,7 +50,7 @@ class GeoModelTest(unittest.TestCase):
def test003_lookup_insert_transform(self):
"Testing automatic transform for lookups and inserts."
# San Antonio in WGS84 and NAD83(HARN) / Texas Centric Lambert Conformal
# San Antonio in 'WGS84' (SRID 4326) and 'NAD83(HARN) / Texas Centric Lambert Conformal' (SRID 3084)
sa_4326 = 'POINT (-98.493183 29.424170)'
sa_3084 = 'POINT (1645978.362408288754523 6276356.025927528738976)' # Used ogr.py in gdal 1.4.1 for this transform
@ -67,6 +69,64 @@ 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):
"Testing NULL geometry support."
# Querying for both NULL and Non-NULL values.
nullqs = State.objects.filter(poly__isnull=True)
validqs = State.objects.filter(poly__isnull=False)
# Puerto Rico should be NULL (it's a commonwealth unincorporated territory)
self.assertEqual(1, len(nullqs))
self.assertEqual('Puerto Rico', nullqs[0].name)
# The valid states should be Colorado & Kansas
self.assertEqual(2, len(validqs))
state_names = [s.name for s in validqs]
self.assertEqual(True, 'Colorado' in state_names)
self.assertEqual(True, 'Kansas' in state_names)
# Saving another commonwealth w/a NULL geometry.
nmi = State(name='Northern Mariana Islands', poly=None)
nmi.save()
def test005_left_right(self):
"Testing the left ('<<') right ('>>') operators."
# Left: A << B => true if xmax(A) < xmin(B)
# Right: A >> B => true if xmin(A) > xmax(B)
# See: BOX2D_left() and BOX2D_right() in lwgeom_box2dfloat4.c in PostGIS source.
# Getting the borders for Colorado & Kansas
co_border = State.objects.get(name='Colorado').poly
ks_border = State.objects.get(name='Kansas').poly
# Note: Wellington has an 'X' value of 174, so it will not be considered
# to the left of CO.
# These cities should be strictly to the right of the CO border.
cities = ['Houston', 'Dallas', 'San Antonio', 'Oklahoma City',
'Lawrence', 'Chicago', 'Wellington']
qs = City.objects.filter(point__right=co_border)
self.assertEqual(7, len(qs))
for c in qs: self.assertEqual(True, c.name in cities)
# These cities should be strictly to the right of the KS border.
cities = ['Chicago', 'Wellington']
qs = City.objects.filter(point__right=ks_border)
self.assertEqual(2, len(qs))
for c in qs: self.assertEqual(True, c.name in cities)
# Note: Wellington has an 'X' value of 174, so it will not be considered
# to the left of CO.
vic = City.objects.get(point__left=co_border)
self.assertEqual('Victoria', vic.name)
cities = ['Pueblo', 'Victoria']
qs = City.objects.filter(point__left=ks_border)
self.assertEqual(2, len(qs))
for c in qs: self.assertEqual(True, c.name in cities)
def suite():
s = unittest.TestSuite()
s.addTest(unittest.makeSuite(GeoModelTest))

View File

@ -80,7 +80,7 @@ def _create_with_shell(db_name, verbosity=1, autoclobber=False):
else:
raise Exception, 'Unknown error occurred in creating database: %s' % output
def create_spatial_db(test=False, verbosity=1, autoclobber=False):
def create_spatial_db(test=False, verbosity=1, autoclobber=False, interactive=False):
"This Python routine creates a spatial database based on settings.py."
# Making sure we're using PostgreSQL and psycopg2
@ -113,7 +113,7 @@ def create_spatial_db(test=False, verbosity=1, autoclobber=False):
settings.DATABASE_NAME = db_name
# Syncing the database
syncdb(verbosity, interactive=False)
syncdb(verbosity, interactive=interactive)
# Get a cursor (even though we don't need one yet). This has
# the side effect of initializing the test database.