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:
parent
357ef5d975
commit
68f9a8ee8e
@ -114,8 +114,9 @@ class GeometryField(Field):
|
|||||||
|
|
||||||
def get_db_prep_lookup(self, lookup_type, value):
|
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."
|
"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 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):
|
if isinstance(value, GEOSGeometry):
|
||||||
# GEOSGeometry instance passed in.
|
# GEOSGeometry instance passed in.
|
||||||
if value.srid != self._srid:
|
if value.srid != self._srid:
|
||||||
|
@ -50,6 +50,9 @@ POSTGIS_GEOMETRY_FUNCTIONS = {
|
|||||||
'relate' : 'Relate',
|
'relate' : 'Relate',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Any other lookup types that do not require a mapping.
|
||||||
|
MISC_TERMS = ['isnull']
|
||||||
|
|
||||||
# The quotation used for postgis (uses single quotes).
|
# The quotation used for postgis (uses single quotes).
|
||||||
def quotename(value, dbl=False):
|
def quotename(value, dbl=False):
|
||||||
if dbl: return '"%s"' % value
|
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
|
# These are the PostGIS-customized QUERY_TERMS, combines both the operators
|
||||||
# and the geometry functions.
|
# and the geometry functions.
|
||||||
POSTGIS_TERMS = list(POSTGIS_OPERATORS.keys()) # Getting the operators first
|
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
|
POSTGIS_TERMS = tuple(POSTGIS_TERMS) # Making immutable
|
||||||
|
|
||||||
def get_geo_where_clause(lookup_type, table_prefix, field_name, value):
|
def get_geo_where_clause(lookup_type, table_prefix, field_name, value):
|
||||||
|
@ -9,3 +9,8 @@ class City(models.Model, models.GeoMixin):
|
|||||||
name = models.CharField(maxlength=30)
|
name = models.CharField(maxlength=30)
|
||||||
point = models.PointField()
|
point = models.PointField()
|
||||||
objects = models.GeoManager()
|
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()
|
||||||
|
@ -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 ('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 ('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 ('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
3
django/contrib/gis/tests/geoapp/sql/state.sql
Normal file
3
django/contrib/gis/tests/geoapp/sql/state.sql
Normal file
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from models import Country, City
|
from models import Country, City, State
|
||||||
from django.contrib.gis.geos import fromstr, Point
|
from django.contrib.gis.geos import fromstr
|
||||||
|
|
||||||
class GeoModelTest(unittest.TestCase):
|
class GeoModelTest(unittest.TestCase):
|
||||||
|
|
||||||
@ -9,8 +9,9 @@ class GeoModelTest(unittest.TestCase):
|
|||||||
|
|
||||||
# Ensuring that data was loaded from initial SQL.
|
# Ensuring that data was loaded from initial SQL.
|
||||||
self.assertEqual(2, Country.objects.count())
|
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):
|
def test002_contains_contained(self):
|
||||||
"Testing the 'contained' and 'contains' lookup types."
|
"Testing the 'contained' and 'contains' lookup types."
|
||||||
|
|
||||||
@ -22,23 +23,24 @@ class GeoModelTest(unittest.TestCase):
|
|||||||
# _bounding box_ of the Geometries.
|
# _bounding box_ of the Geometries.
|
||||||
qs = City.objects.filter(point__contained=texas.mpoly)
|
qs = City.objects.filter(point__contained=texas.mpoly)
|
||||||
self.assertEqual(3, qs.count())
|
self.assertEqual(3, qs.count())
|
||||||
city_names = [c.name for c in qs]
|
cities = ['Houston', 'Dallas', 'Oklahoma City']
|
||||||
self.assertEqual(True, 'Houston' in city_names)
|
for c in qs: self.assertEqual(True, c.name in cities)
|
||||||
self.assertEqual(True, 'Dallas' in city_names)
|
|
||||||
self.assertEqual(True, 'Oklahoma City' in city_names)
|
|
||||||
|
|
||||||
# Pulling out some cities.
|
# Pulling out some cities.
|
||||||
houston = City.objects.get(name='Houston')
|
houston = City.objects.get(name='Houston')
|
||||||
wellington = City.objects.get(name='Wellington')
|
wellington = City.objects.get(name='Wellington')
|
||||||
pueblo = City.objects.get(name='Pueblo')
|
pueblo = City.objects.get(name='Pueblo')
|
||||||
okcity = City.objects.get(name='Oklahoma City')
|
okcity = City.objects.get(name='Oklahoma City')
|
||||||
|
lawrence = City.objects.get(name='Lawrence')
|
||||||
|
|
||||||
# Now testing contains on the countries using the points for
|
# Now testing contains on the countries using the points for
|
||||||
# Houston and Wellington.
|
# Houston and Wellington.
|
||||||
tx = Country.objects.get(mpoly__contains=houston.point) # Query w/GEOSGeometry
|
tx = Country.objects.get(mpoly__contains=houston.point) # Query w/GEOSGeometry
|
||||||
nz = Country.objects.get(mpoly__contains=wellington.point.hex) # Query w/EWKBHEX
|
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('Texas', tx.name)
|
||||||
self.assertEqual('New Zealand', nz.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)
|
# Pueblo and Oklahoma City (even though OK City is within the bounding box of Texas)
|
||||||
# are not contained in Texas or New Zealand.
|
# are not contained in Texas or New Zealand.
|
||||||
@ -48,7 +50,7 @@ class GeoModelTest(unittest.TestCase):
|
|||||||
def test003_lookup_insert_transform(self):
|
def test003_lookup_insert_transform(self):
|
||||||
"Testing automatic transform for lookups and inserts."
|
"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_4326 = 'POINT (-98.493183 29.424170)'
|
||||||
sa_3084 = 'POINT (1645978.362408288754523 6276356.025927528738976)' # Used ogr.py in gdal 1.4.1 for this transform
|
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.x, sa.point.x, 6)
|
||||||
self.assertAlmostEqual(wgs_pnt.y, sa.point.y, 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():
|
def suite():
|
||||||
s = unittest.TestSuite()
|
s = unittest.TestSuite()
|
||||||
s.addTest(unittest.makeSuite(GeoModelTest))
|
s.addTest(unittest.makeSuite(GeoModelTest))
|
||||||
|
@ -80,7 +80,7 @@ def _create_with_shell(db_name, verbosity=1, autoclobber=False):
|
|||||||
else:
|
else:
|
||||||
raise Exception, 'Unknown error occurred in creating database: %s' % output
|
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."
|
"This Python routine creates a spatial database based on settings.py."
|
||||||
|
|
||||||
# Making sure we're using PostgreSQL and psycopg2
|
# 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
|
settings.DATABASE_NAME = db_name
|
||||||
|
|
||||||
# Syncing the database
|
# Syncing the database
|
||||||
syncdb(verbosity, interactive=False)
|
syncdb(verbosity, interactive=interactive)
|
||||||
|
|
||||||
# Get a cursor (even though we don't need one yet). This has
|
# Get a cursor (even though we don't need one yet). This has
|
||||||
# the side effect of initializing the test database.
|
# the side effect of initializing the test database.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user