mirror of
https://github.com/django/django.git
synced 2025-07-04 01:39:20 +00:00
gis: Added preliminary spatial backend for MySQL (which only supports MBR queries), and added limited test suite for it; updated a few comments in the Oracle backend.
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6527 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
58fc789765
commit
712df76528
@ -43,6 +43,13 @@ elif settings.DATABASE_ENGINE == 'oracle':
|
|||||||
create_spatial_db, get_geo_where_clause, gqn, \
|
create_spatial_db, get_geo_where_clause, gqn, \
|
||||||
ASGML, GEOM_SELECT, TRANSFORM, UNION
|
ASGML, GEOM_SELECT, TRANSFORM, UNION
|
||||||
SPATIAL_BACKEND = 'oracle'
|
SPATIAL_BACKEND = 'oracle'
|
||||||
|
elif settings.DATABASE_ENGINE == 'mysql':
|
||||||
|
from django.contrib.gis.db.backend.mysql import \
|
||||||
|
MySQLGeoField as GeoBackendField, \
|
||||||
|
MYSQL_GIS_TERMS as GIS_TERMS, \
|
||||||
|
create_spatial_db, get_geo_where_clause, gqn, \
|
||||||
|
GEOM_SELECT
|
||||||
|
SPATIAL_BACKEND = 'mysql'
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError('No Geographic Backend exists for %s' % settings.DATABASE_ENGINE)
|
raise NotImplementedError('No Geographic Backend exists for %s' % settings.DATABASE_ENGINE)
|
||||||
|
|
||||||
|
12
django/contrib/gis/db/backend/mysql/__init__.py
Normal file
12
django/contrib/gis/db/backend/mysql/__init__.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
"""
|
||||||
|
The MySQL spatial database backend module.
|
||||||
|
|
||||||
|
Please note that MySQL only supports bounding box queries, also
|
||||||
|
known as MBRs (Minimum Bounding Rectangles). Moreover, spatial
|
||||||
|
indices may only be used on MyISAM tables -- if you need
|
||||||
|
transactions, take a look at PostGIS.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.contrib.gis.db.backend.mysql.creation import create_spatial_db
|
||||||
|
from django.contrib.gis.db.backend.mysql.field import MySQLGeoField, gqn
|
||||||
|
from django.contrib.gis.db.backend.mysql.query import get_geo_where_clause, MYSQL_GIS_TERMS, GEOM_SELECT
|
5
django/contrib/gis/db/backend/mysql/creation.py
Normal file
5
django/contrib/gis/db/backend/mysql/creation.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from django.test.utils import create_test_db
|
||||||
|
|
||||||
|
def create_spatial_db(test=True, verbosity=1, autoclobber=False):
|
||||||
|
if not test: raise NotImplementedError('This uses `create_test_db` from test/utils.py')
|
||||||
|
create_test_db(verbosity, autoclobber)
|
92
django/contrib/gis/db/backend/mysql/field.py
Normal file
92
django/contrib/gis/db/backend/mysql/field.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import re
|
||||||
|
from types import StringType, UnicodeType
|
||||||
|
from django.db import connection
|
||||||
|
from django.db.models.fields import Field # Django base Field class
|
||||||
|
from django.contrib.gis.geos import GEOSGeometry
|
||||||
|
from django.contrib.gis.db.backend.util import GeoFieldSQL
|
||||||
|
from django.contrib.gis.db.backend.mysql.query import MYSQL_GIS_TERMS, GEOM_FROM_TEXT
|
||||||
|
|
||||||
|
# Quotename & geographic quotename, respectively.
|
||||||
|
qn = connection.ops.quote_name
|
||||||
|
def gqn(value):
|
||||||
|
if isinstance(value, UnicodeType): value = value.encode('ascii')
|
||||||
|
return "'%s'" % value
|
||||||
|
|
||||||
|
class MySQLGeoField(Field):
|
||||||
|
"""
|
||||||
|
The backend-specific geographic field for MySQL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _geom_index(self, style, db_table):
|
||||||
|
"""
|
||||||
|
Creates a spatial index for the geometry column. If MyISAM tables are
|
||||||
|
used an R-Tree index is created, otherwise a B-Tree index is created.
|
||||||
|
Thus, for best spatial performance, you should use MyISAM tables
|
||||||
|
(which do not support transactions). For more information, see Ch.
|
||||||
|
17.6.1 of the MySQL 5.0 documentation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Getting the index name.
|
||||||
|
idx_name = '%s_%s_id' % (db_table, self.column)
|
||||||
|
|
||||||
|
sql = style.SQL_KEYWORD('CREATE SPATIAL INDEX ') + \
|
||||||
|
style.SQL_TABLE(qn(idx_name)) + \
|
||||||
|
style.SQL_KEYWORD(' ON ') + \
|
||||||
|
style.SQL_TABLE(qn(db_table)) + '(' + \
|
||||||
|
style.SQL_FIELD(qn(self.column)) + ');'
|
||||||
|
return sql
|
||||||
|
|
||||||
|
def _post_create_sql(self, style, db_table):
|
||||||
|
"""
|
||||||
|
Returns SQL that will be executed after the model has been
|
||||||
|
created.
|
||||||
|
"""
|
||||||
|
# Getting the geometric index for this Geometry column.
|
||||||
|
if self._index:
|
||||||
|
return (self._geom_index(style, db_table),)
|
||||||
|
else:
|
||||||
|
return ()
|
||||||
|
|
||||||
|
def db_type(self):
|
||||||
|
"The OpenGIS name is returned for the MySQL database column type."
|
||||||
|
return self._geom
|
||||||
|
|
||||||
|
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 lookup_type in MYSQL_GIS_TERMS:
|
||||||
|
# special case for isnull lookup
|
||||||
|
if lookup_type == 'isnull': return GeoFieldSQL([], [])
|
||||||
|
|
||||||
|
# When the input is not a GEOS geometry, attempt to construct one
|
||||||
|
# from the given string input.
|
||||||
|
if isinstance(value, GEOSGeometry):
|
||||||
|
pass
|
||||||
|
elif isinstance(value, (StringType, UnicodeType)):
|
||||||
|
try:
|
||||||
|
value = GEOSGeometry(value)
|
||||||
|
except GEOSException:
|
||||||
|
raise TypeError("Could not create geometry from lookup value: %s" % str(value))
|
||||||
|
else:
|
||||||
|
raise TypeError('Cannot use parameter of %s type as lookup parameter.' % type(value))
|
||||||
|
|
||||||
|
return GeoFieldSQL(['%s(%%s)' % GEOM_FROM_TEXT], [value])
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise TypeError("Field has invalid lookup: %s" % lookup_type)
|
||||||
|
|
||||||
|
def get_db_prep_save(self, value):
|
||||||
|
"Prepares the value for saving in the database."
|
||||||
|
if not bool(value): return None
|
||||||
|
if isinstance(value, GEOSGeometry):
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
raise TypeError('Geometry Proxy should only return GEOSGeometry objects.')
|
||||||
|
|
||||||
|
def get_placeholder(self, value):
|
||||||
|
"""
|
||||||
|
Nothing special happens here because MySQL does not support transformations.
|
||||||
|
"""
|
||||||
|
return '%s(%%s)' % GEOM_FROM_TEXT
|
54
django/contrib/gis/db/backend/mysql/query.py
Normal file
54
django/contrib/gis/db/backend/mysql/query.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
This module contains the spatial lookup types, and the get_geo_where_clause()
|
||||||
|
routine for MySQL
|
||||||
|
"""
|
||||||
|
from django.db import connection
|
||||||
|
qn = connection.ops.quote_name
|
||||||
|
|
||||||
|
# WARNING: MySQL is NOT compliant w/the OpenGIS specification and
|
||||||
|
# _every_ one of these lookup types is on the _bounding box_ only.
|
||||||
|
MYSQL_GIS_FUNCTIONS = {
|
||||||
|
'bbcontains' : 'MBRContains', # For consistency w/PostGIS API
|
||||||
|
'contained' : 'MBRWithin', # (ditto)
|
||||||
|
'contains' : 'MBRContains',
|
||||||
|
'disjoint' : 'MBRDisjoint',
|
||||||
|
'equals' : 'MBREqual',
|
||||||
|
'exact' : 'MBREqual',
|
||||||
|
'intersects' : 'MBRIntersects',
|
||||||
|
'overlaps' : 'MBROverlaps',
|
||||||
|
'same_as' : 'MBREqual',
|
||||||
|
'touches' : 'MBRTouches',
|
||||||
|
'within' : 'MBRWithin',
|
||||||
|
}
|
||||||
|
|
||||||
|
# This lookup type does not require a mapping.
|
||||||
|
MISC_TERMS = ['isnull']
|
||||||
|
|
||||||
|
# Assacceptable lookup types for Oracle spatial.
|
||||||
|
MYSQL_GIS_TERMS = MYSQL_GIS_FUNCTIONS.keys()
|
||||||
|
MYSQL_GIS_TERMS += MISC_TERMS
|
||||||
|
MYSQL_GIS_TERMS = tuple(MYSQL_GIS_TERMS) # Making immutable
|
||||||
|
|
||||||
|
def get_geo_where_clause(lookup_type, table_prefix, field_name, value):
|
||||||
|
"Returns the SQL WHERE clause for use in MySQL spatial SQL construction."
|
||||||
|
if table_prefix.endswith('.'):
|
||||||
|
table_prefix = qn(table_prefix[:-1])+'.'
|
||||||
|
field_name = qn(field_name)
|
||||||
|
|
||||||
|
# See if a MySQL Geometry function matches the lookup type next
|
||||||
|
lookup_info = MYSQL_GIS_FUNCTIONS.get(lookup_type, False)
|
||||||
|
if lookup_info:
|
||||||
|
return "%s(%s, %%s)" % (lookup_info, table_prefix + field_name)
|
||||||
|
|
||||||
|
# Handling 'isnull' lookup type
|
||||||
|
# TODO: Is this needed because MySQL cannot handle NULL
|
||||||
|
# geometries in its spatial indices.
|
||||||
|
if lookup_type == 'isnull':
|
||||||
|
return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or ''))
|
||||||
|
|
||||||
|
raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type))
|
||||||
|
|
||||||
|
# To ease implementation, WKT is passed to/from MySQL.
|
||||||
|
GEOM_FROM_TEXT = 'GeomFromText'
|
||||||
|
GEOM_FROM_WKB = 'GeomFromWKB'
|
||||||
|
GEOM_SELECT = 'AsText(%s)'
|
@ -1,8 +1,10 @@
|
|||||||
"""
|
"""
|
||||||
The Oracle spatial database backend module.
|
The Oracle spatial database backend module.
|
||||||
|
|
||||||
Please note that WKT support is broken on the XE version, and this will
|
Please note that WKT support is broken on the XE version, and thus
|
||||||
not work.
|
this backend will not work on such platforms. Specifically, XE lacks
|
||||||
|
support for an internal JVM, and Java libraries are required to use
|
||||||
|
the WKT constructors.
|
||||||
"""
|
"""
|
||||||
from django.contrib.gis.db.backend.oracle.creation import create_spatial_db
|
from django.contrib.gis.db.backend.oracle.creation import create_spatial_db
|
||||||
from django.contrib.gis.db.backend.oracle.field import OracleSpatialField, gqn
|
from django.contrib.gis.db.backend.oracle.field import OracleSpatialField, gqn
|
||||||
|
@ -35,7 +35,7 @@ def get_geo_where_clause(lookup_type, table_prefix, field_name, value):
|
|||||||
table_prefix = qn(table_prefix[:-1])+'.'
|
table_prefix = qn(table_prefix[:-1])+'.'
|
||||||
field_name = qn(field_name)
|
field_name = qn(field_name)
|
||||||
|
|
||||||
# See if a PostGIS Geometry function matches the lookup type next
|
# See if a Oracle Geometry function matches the lookup type next
|
||||||
lookup_info = ORACLE_GEOMETRY_FUNCTIONS.get(lookup_type, False)
|
lookup_info = ORACLE_GEOMETRY_FUNCTIONS.get(lookup_type, False)
|
||||||
if lookup_info:
|
if lookup_info:
|
||||||
# Lookup types that are tuples take tuple arguments, e.g., 'relate' and
|
# Lookup types that are tuples take tuple arguments, e.g., 'relate' and
|
||||||
|
@ -2,6 +2,7 @@ import sys
|
|||||||
from copy import copy
|
from copy import copy
|
||||||
from unittest import TestSuite, TextTestRunner
|
from unittest import TestSuite, TextTestRunner
|
||||||
from django.contrib.gis.gdal import HAS_GDAL
|
from django.contrib.gis.gdal import HAS_GDAL
|
||||||
|
from django.contrib.gis.tests.utils import mysql
|
||||||
|
|
||||||
# Tests that do not require setting up and tearing down a spatial database.
|
# Tests that do not require setting up and tearing down a spatial database.
|
||||||
test_suite_names = [
|
test_suite_names = [
|
||||||
@ -84,8 +85,12 @@ def run_tests(module_list, verbosity=1, interactive=True):
|
|||||||
test_suite = suite()
|
test_suite = suite()
|
||||||
for test_model in test_models:
|
for test_model in test_models:
|
||||||
module_name = 'django.contrib.gis.tests.%s' % test_model
|
module_name = 'django.contrib.gis.tests.%s' % test_model
|
||||||
|
if mysql:
|
||||||
|
test_module_name = 'tests_mysql'
|
||||||
|
else:
|
||||||
|
test_module_name = 'tests'
|
||||||
settings.INSTALLED_APPS.append(module_name)
|
settings.INSTALLED_APPS.append(module_name)
|
||||||
tsuite = getattr(__import__('django.contrib.gis.tests.%s' % test_model, globals(), locals(), ['tests']), 'tests')
|
tsuite = getattr(__import__('django.contrib.gis.tests.%s' % test_model, globals(), locals(), [test_module_name]), test_module_name)
|
||||||
test_suite.addTest(tsuite.suite())
|
test_suite.addTest(tsuite.suite())
|
||||||
|
|
||||||
# Resetting the loaded flag to take into account what we appended to the INSTALLED_APPS
|
# Resetting the loaded flag to take into account what we appended to the INSTALLED_APPS
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
from django.contrib.gis.db import models
|
from django.contrib.gis.db import models
|
||||||
|
from django.contrib.gis.tests.utils import mysql
|
||||||
|
|
||||||
|
# MySQL spatial indices can't handle NULL geometries.
|
||||||
|
null_flag = not mysql
|
||||||
|
|
||||||
class Country(models.Model):
|
class Country(models.Model):
|
||||||
name = models.CharField(max_length=30)
|
name = models.CharField(max_length=30)
|
||||||
@ -12,7 +16,7 @@ class City(models.Model):
|
|||||||
|
|
||||||
class State(models.Model):
|
class State(models.Model):
|
||||||
name = models.CharField(max_length=30)
|
name = models.CharField(max_length=30)
|
||||||
poly = models.PolygonField(null=True) # Allowing NULL geometries here.
|
poly = models.PolygonField(null=null_flag) # Allowing NULL geometries here.
|
||||||
objects = models.GeoManager()
|
objects = models.GeoManager()
|
||||||
|
|
||||||
class Feature(models.Model):
|
class Feature(models.Model):
|
||||||
|
8
django/contrib/gis/tests/geoapp/sql/city.mysql.sql
Normal file
8
django/contrib/gis/tests/geoapp/sql/city.mysql.sql
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
INSERT INTO geoapp_city (`name`, `point`) VALUES ('Houston', GeomFromText('POINT (-95.363151 29.763374)'));
|
||||||
|
INSERT INTO geoapp_city (`name`, `point`) VALUES ('Dallas', GeomFromText('POINT (-96.801611 32.782057)'));
|
||||||
|
INSERT INTO geoapp_city (`name`, `point`) VALUES ('Oklahoma City', GeomFromText('POINT (-97.521157 34.464642)'));
|
||||||
|
INSERT INTO geoapp_city (`name`, `point`) VALUES ('Wellington', GeomFromText('POINT (174.783117 -41.315268)'));
|
||||||
|
INSERT INTO geoapp_city (`name`, `point`) VALUES ('Pueblo', GeomFromText('POINT (-104.609252 38.255001)'));
|
||||||
|
INSERT INTO geoapp_city (`name`, `point`) VALUES ('Lawrence', GeomFromText('POINT (-95.235060 38.971823)'));
|
||||||
|
INSERT INTO geoapp_city (`name`, `point`) VALUES ('Chicago', GeomFromText('POINT (-87.650175 41.850385)'));
|
||||||
|
INSERT INTO geoapp_city (`name`, `point`) VALUES ('Victoria', GeomFromText('POINT (-123.305196 48.462611)'));
|
4
django/contrib/gis/tests/geoapp/sql/country.mysql.sql
Normal file
4
django/contrib/gis/tests/geoapp/sql/country.mysql.sql
Normal file
File diff suppressed because one or more lines are too long
5
django/contrib/gis/tests/geoapp/sql/state.mysql.sql
Normal file
5
django/contrib/gis/tests/geoapp/sql/state.mysql.sql
Normal file
File diff suppressed because one or more lines are too long
167
django/contrib/gis/tests/geoapp/tests_mysql.py
Normal file
167
django/contrib/gis/tests/geoapp/tests_mysql.py
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
"""
|
||||||
|
A limited test module is used for a limited spatial database.
|
||||||
|
"""
|
||||||
|
import os, unittest
|
||||||
|
from models import Country, City, State, Feature
|
||||||
|
from django.contrib.gis import gdal
|
||||||
|
from django.contrib.gis.geos import *
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
|
||||||
|
class GeoModelTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test01_initial_sql(self):
|
||||||
|
"Testing geographic initial SQL."
|
||||||
|
# Ensuring that data was loaded from initial SQL.
|
||||||
|
self.assertEqual(2, Country.objects.count())
|
||||||
|
self.assertEqual(8, City.objects.count())
|
||||||
|
self.assertEqual(2, State.objects.count())
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
ns = State.objects.get(name='NullState')
|
||||||
|
self.assertEqual(ply, ns.poly)
|
||||||
|
|
||||||
|
# Testing the `ogr` and `srs` lazy-geometry properties.
|
||||||
|
if gdal.HAS_GDAL:
|
||||||
|
self.assertEqual(True, isinstance(ns.poly.ogr, gdal.OGRGeometry))
|
||||||
|
self.assertEqual(ns.poly.wkb, ns.poly.ogr.wkb)
|
||||||
|
self.assertEqual(True, isinstance(ns.poly.srs, gdal.SpatialReference))
|
||||||
|
self.assertEqual('WGS 84', ns.poly.srs.name)
|
||||||
|
|
||||||
|
# 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_contains_contained(self):
|
||||||
|
"Testing the 'contained', 'contains', and 'bbcontains' lookup types."
|
||||||
|
# Getting Texas, yes we were a country -- once ;)
|
||||||
|
texas = Country.objects.get(name='Texas')
|
||||||
|
|
||||||
|
# Seeing what cities are in Texas, should get Houston and Dallas,
|
||||||
|
# and Oklahoma City because MySQL 'within' only checks on the
|
||||||
|
# _bounding box_ of the Geometries.
|
||||||
|
qs = City.objects.filter(point__within=texas.mpoly)
|
||||||
|
self.assertEqual(3, qs.count())
|
||||||
|
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 is not contained in Texas or New Zealand.
|
||||||
|
self.assertEqual(0, len(Country.objects.filter(mpoly__contains=pueblo.point))) # Query w/GEOSGeometry object
|
||||||
|
|
||||||
|
# OK City is contained w/in bounding box of Texas.
|
||||||
|
qs = Country.objects.filter(mpoly__bbcontains=okcity.point)
|
||||||
|
self.assertEqual(1, len(qs))
|
||||||
|
self.assertEqual('Texas', qs[0].name)
|
||||||
|
|
||||||
|
def test04_equals(self):
|
||||||
|
"Testing the 'same_as' and 'equals' lookup types."
|
||||||
|
pnt = fromstr('POINT (-95.363151 29.763374)', srid=4326)
|
||||||
|
c1 = City.objects.get(point=pnt)
|
||||||
|
c2 = City.objects.get(point__same_as=pnt)
|
||||||
|
c3 = City.objects.get(point__equals=pnt)
|
||||||
|
for c in [c1, c2, c3]: self.assertEqual('Houston', c.name)
|
||||||
|
|
||||||
|
def test05_geometryfield(self):
|
||||||
|
"Testing GeometryField."
|
||||||
|
f1 = Feature(name='Point', geom=Point(1, 1))
|
||||||
|
f2 = Feature(name='LineString', geom=LineString((0, 0), (1, 1), (5, 5)))
|
||||||
|
f3 = Feature(name='Polygon', geom=Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))))
|
||||||
|
f4 = Feature(name='GeometryCollection',
|
||||||
|
geom=GeometryCollection(Point(2, 2), LineString((0, 0), (2, 2)),
|
||||||
|
Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0)))))
|
||||||
|
f1.save()
|
||||||
|
f2.save()
|
||||||
|
f3.save()
|
||||||
|
f4.save()
|
||||||
|
|
||||||
|
f_1 = Feature.objects.get(name='Point')
|
||||||
|
self.assertEqual(True, isinstance(f_1.geom, Point))
|
||||||
|
self.assertEqual((1.0, 1.0), f_1.geom.tuple)
|
||||||
|
f_2 = Feature.objects.get(name='LineString')
|
||||||
|
self.assertEqual(True, isinstance(f_2.geom, LineString))
|
||||||
|
self.assertEqual(((0.0, 0.0), (1.0, 1.0), (5.0, 5.0)), f_2.geom.tuple)
|
||||||
|
|
||||||
|
f_3 = Feature.objects.get(name='Polygon')
|
||||||
|
self.assertEqual(True, isinstance(f_3.geom, Polygon))
|
||||||
|
f_4 = Feature.objects.get(name='GeometryCollection')
|
||||||
|
self.assertEqual(True, isinstance(f_4.geom, GeometryCollection))
|
||||||
|
self.assertEqual(f_3.geom, f_4.geom[2])
|
||||||
|
|
||||||
|
def test06_mysql_limitations(self):
|
||||||
|
"Testing that union(), kml(), gml() raise exceptions."
|
||||||
|
self.assertRaises(ImproperlyConfigured, City.objects.union, 'point')
|
||||||
|
self.assertRaises(ImproperlyConfigured, State.objects.all().kml, 'poly')
|
||||||
|
self.assertRaises(ImproperlyConfigured, Country.objects.all().gml, 'mpoly')
|
||||||
|
|
||||||
|
def suite():
|
||||||
|
s = unittest.TestSuite()
|
||||||
|
s.addTest(unittest.makeSuite(GeoModelTest))
|
||||||
|
return s
|
@ -1,6 +1,7 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from django.contrib.gis.models import SpatialRefSys
|
from django.contrib.gis.tests.utils import mysql, no_mysql, oracle, postgis
|
||||||
from django.contrib.gis.tests.utils import oracle, postgis
|
if not mysql:
|
||||||
|
from django.contrib.gis.models import SpatialRefSys
|
||||||
|
|
||||||
test_srs = ({'srid' : 4326,
|
test_srs = ({'srid' : 4326,
|
||||||
'auth_name' : ('EPSG', True),
|
'auth_name' : ('EPSG', True),
|
||||||
@ -26,6 +27,7 @@ test_srs = ({'srid' : 4326,
|
|||||||
|
|
||||||
class SpatialRefSysTest(unittest.TestCase):
|
class SpatialRefSysTest(unittest.TestCase):
|
||||||
|
|
||||||
|
@no_mysql
|
||||||
def test01_retrieve(self):
|
def test01_retrieve(self):
|
||||||
"Testing retrieval of SpatialRefSys model objects."
|
"Testing retrieval of SpatialRefSys model objects."
|
||||||
for sd in test_srs:
|
for sd in test_srs:
|
||||||
@ -46,6 +48,7 @@ class SpatialRefSysTest(unittest.TestCase):
|
|||||||
self.assertEqual(sd['srtext'], srs.wkt)
|
self.assertEqual(sd['srtext'], srs.wkt)
|
||||||
self.assertEqual(sd['proj4'], srs.proj4text)
|
self.assertEqual(sd['proj4'], srs.proj4text)
|
||||||
|
|
||||||
|
@no_mysql
|
||||||
def test02_osr(self):
|
def test02_osr(self):
|
||||||
"Testing getting OSR objects from SpatialRefSys model objects."
|
"Testing getting OSR objects from SpatialRefSys model objects."
|
||||||
for sd in test_srs:
|
for sd in test_srs:
|
||||||
@ -61,6 +64,7 @@ class SpatialRefSysTest(unittest.TestCase):
|
|||||||
self.assertEqual(sd['proj4'], srs.proj4)
|
self.assertEqual(sd['proj4'], srs.proj4)
|
||||||
self.assertEqual(sd['srtext'], srs.wkt)
|
self.assertEqual(sd['srtext'], srs.wkt)
|
||||||
|
|
||||||
|
@no_mysql
|
||||||
def test03_ellipsoid(self):
|
def test03_ellipsoid(self):
|
||||||
"Testing the ellipsoid property."
|
"Testing the ellipsoid property."
|
||||||
for sd in test_srs:
|
for sd in test_srs:
|
||||||
|
@ -14,7 +14,9 @@ def no_backend(test_func, backend):
|
|||||||
# spatial backends.
|
# spatial backends.
|
||||||
def no_oracle(func): return no_backend(func, 'oracle')
|
def no_oracle(func): return no_backend(func, 'oracle')
|
||||||
def no_postgis(func): return no_backend(func, 'postgresql_psycopg2')
|
def no_postgis(func): return no_backend(func, 'postgresql_psycopg2')
|
||||||
|
def no_mysql(func): return no_backend(func, 'mysql')
|
||||||
|
|
||||||
# Shortcut booleans to omit only portions of tests.
|
# Shortcut booleans to omit only portions of tests.
|
||||||
oracle = settings.DATABASE_ENGINE == 'oracle'
|
oracle = settings.DATABASE_ENGINE == 'oracle'
|
||||||
postgis = settings.DATABASE_ENGINE == 'postgresql_psycopg2'
|
postgis = settings.DATABASE_ENGINE == 'postgresql_psycopg2'
|
||||||
|
mysql = settings.DATABASE_ENGINE == 'mysql'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user