Added tests for PostGIS geography support; added `proj_version_tuple` to PostGIS spatial backend operations; made `distapp` tests take into account different datums in PROJ.4 4.7; and added svn:ignore properties for recently-added directories.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@11971 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
18d754d681
commit
e1f6b4b82f
|
@ -450,6 +450,19 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
|
||||||
|
|
||||||
return (version, major, minor1, minor2)
|
return (version, major, minor1, minor2)
|
||||||
|
|
||||||
|
def proj_version_tuple(self):
|
||||||
|
"""
|
||||||
|
Return the version of PROJ.4 used by PostGIS as a tuple of the
|
||||||
|
major, minor, and subminor release numbers.
|
||||||
|
"""
|
||||||
|
proj_regex = re.compile(r'(\d+)\.(\d+)\.(\d+)')
|
||||||
|
proj_ver_str = self.postgis_proj_version()
|
||||||
|
m = proj_regex.search(proj_ver_str)
|
||||||
|
if m:
|
||||||
|
return tuple(map(int, [m.group(1), m.group(2), m.group(3)]))
|
||||||
|
else:
|
||||||
|
raise Exception('Could not determine PROJ.4 version from PostGIS.')
|
||||||
|
|
||||||
def num_params(self, lookup_type, num_param):
|
def num_params(self, lookup_type, num_param):
|
||||||
"""
|
"""
|
||||||
Helper routine that returns a boolean indicating whether the number of
|
Helper routine that returns a boolean indicating whether the number of
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import sys, unittest
|
import sys
|
||||||
from django.test.simple import run_tests
|
|
||||||
from django.utils.importlib import import_module
|
def run_tests(*args, **kwargs):
|
||||||
|
from django.test.simple import run_tests as base_run_tests
|
||||||
|
return base_run_tests(*args, **kwargs)
|
||||||
|
|
||||||
def geo_suite():
|
def geo_suite():
|
||||||
"""
|
"""
|
||||||
|
@ -14,6 +16,8 @@ def geo_suite():
|
||||||
from django.contrib.gis.gdal import HAS_GDAL
|
from django.contrib.gis.gdal import HAS_GDAL
|
||||||
from django.contrib.gis.utils import HAS_GEOIP
|
from django.contrib.gis.utils import HAS_GEOIP
|
||||||
from django.contrib.gis.tests.utils import postgis, mysql
|
from django.contrib.gis.tests.utils import postgis, mysql
|
||||||
|
from django.db import connection
|
||||||
|
from django.utils.importlib import import_module
|
||||||
|
|
||||||
gis_tests = []
|
gis_tests = []
|
||||||
|
|
||||||
|
@ -23,13 +27,17 @@ def geo_suite():
|
||||||
|
|
||||||
# Tests that require use of a spatial database (e.g., creation of models)
|
# Tests that require use of a spatial database (e.g., creation of models)
|
||||||
test_apps = ['geoapp', 'relatedapp']
|
test_apps = ['geoapp', 'relatedapp']
|
||||||
|
if postgis and connection.ops.geography:
|
||||||
|
# Test geography support with PostGIS 1.5+.
|
||||||
|
test_apps.append('geogapp')
|
||||||
|
|
||||||
# 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 = [
|
||||||
'test_measure',
|
'test_measure',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Tests applications that require a test spatial db.
|
if HAS_GDAL:
|
||||||
|
# These tests require GDAL.
|
||||||
if not mysql:
|
if not mysql:
|
||||||
test_apps.append('distapp')
|
test_apps.append('distapp')
|
||||||
|
|
||||||
|
@ -37,8 +45,6 @@ def geo_suite():
|
||||||
if postgis and GEOS_PREPARE:
|
if postgis and GEOS_PREPARE:
|
||||||
test_apps.append('geo3d')
|
test_apps.append('geo3d')
|
||||||
|
|
||||||
if HAS_GDAL:
|
|
||||||
# These tests require GDAL.
|
|
||||||
test_suite_names.extend(['test_spatialrefsys', 'test_geoforms'])
|
test_suite_names.extend(['test_spatialrefsys', 'test_geoforms'])
|
||||||
test_apps.append('layermap')
|
test_apps.append('layermap')
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import os, unittest
|
import os, unittest
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
|
from django.db import connection
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.contrib.gis.gdal import DataSource
|
from django.contrib.gis.gdal import DataSource
|
||||||
from django.contrib.gis.geos import GEOSGeometry, Point, LineString
|
from django.contrib.gis.geos import GEOSGeometry, Point, LineString
|
||||||
|
@ -157,9 +158,28 @@ class DistanceTest(unittest.TestCase):
|
||||||
|
|
||||||
# Got the reference distances using the raw SQL statements:
|
# Got the reference distances using the raw SQL statements:
|
||||||
# SELECT ST_distance_spheroid(point, ST_GeomFromText('POINT(151.231341 -33.952685)', 4326), 'SPHEROID["WGS 84",6378137.0,298.257223563]') FROM distapp_australiacity WHERE (NOT (id = 11));
|
# SELECT ST_distance_spheroid(point, ST_GeomFromText('POINT(151.231341 -33.952685)', 4326), 'SPHEROID["WGS 84",6378137.0,298.257223563]') FROM distapp_australiacity WHERE (NOT (id = 11));
|
||||||
spheroid_distances = [60504.0628825298, 77023.948962654, 49154.8867507115, 90847.435881812, 217402.811862568, 709599.234619957, 640011.483583758, 7772.00667666425, 1047861.7859506, 1165126.55237647]
|
|
||||||
# SELECT ST_distance_sphere(point, ST_GeomFromText('POINT(151.231341 -33.952685)', 4326)) FROM distapp_australiacity WHERE (NOT (id = 11)); st_distance_sphere
|
# SELECT ST_distance_sphere(point, ST_GeomFromText('POINT(151.231341 -33.952685)', 4326)) FROM distapp_australiacity WHERE (NOT (id = 11)); st_distance_sphere
|
||||||
sphere_distances = [60580.7612632291, 77143.7785056615, 49199.2725132184, 90804.4414289463, 217712.63666124, 709131.691061906, 639825.959074112, 7786.80274606706, 1049200.46122281, 1162619.7297006]
|
if connection.ops.postgis and connection.ops.proj_version_tuple() >= (4, 7, 0):
|
||||||
|
# PROJ.4 versions 4.7+ have updated datums, and thus different
|
||||||
|
# distance values.
|
||||||
|
spheroid_distances = [60504.0628957201, 77023.9489850262, 49154.8867574404,
|
||||||
|
90847.4358768573, 217402.811919332, 709599.234564757,
|
||||||
|
640011.483550888, 7772.00667991925, 1047861.78619339,
|
||||||
|
1165126.55236034]
|
||||||
|
sphere_distances = [60580.9693849267, 77144.0435286473, 49199.4415344719,
|
||||||
|
90804.7533823494, 217713.384600405, 709134.127242793,
|
||||||
|
639828.157159169, 7786.82949717788, 1049204.06569028,
|
||||||
|
1162623.7238134]
|
||||||
|
|
||||||
|
else:
|
||||||
|
spheroid_distances = [60504.0628825298, 77023.948962654, 49154.8867507115,
|
||||||
|
90847.435881812, 217402.811862568, 709599.234619957,
|
||||||
|
640011.483583758, 7772.00667666425, 1047861.7859506,
|
||||||
|
1165126.55237647]
|
||||||
|
sphere_distances = [60580.7612632291, 77143.7785056615, 49199.2725132184,
|
||||||
|
90804.4414289463, 217712.63666124, 709131.691061906,
|
||||||
|
639825.959074112, 7786.80274606706, 1049200.46122281,
|
||||||
|
1162619.7297006]
|
||||||
|
|
||||||
# Testing with spheroid distances first.
|
# Testing with spheroid distances first.
|
||||||
qs = AustraliaCity.objects.exclude(id=hillsdale.id).distance(hillsdale.point, spheroid=True)
|
qs = AustraliaCity.objects.exclude(id=hillsdale.id).distance(hillsdale.point, spheroid=True)
|
||||||
|
@ -230,15 +250,20 @@ class DistanceTest(unittest.TestCase):
|
||||||
qs = SouthTexasZipcode.objects.exclude(name='77005').filter(poly__distance_lte=(z.poly, D(m=300)))
|
qs = SouthTexasZipcode.objects.exclude(name='77005').filter(poly__distance_lte=(z.poly, D(m=300)))
|
||||||
self.assertEqual(['77002', '77025', '77401'], self.get_names(qs))
|
self.assertEqual(['77002', '77025', '77401'], self.get_names(qs))
|
||||||
|
|
||||||
@no_spatialite
|
|
||||||
def test05_geodetic_distance_lookups(self):
|
def test05_geodetic_distance_lookups(self):
|
||||||
"Testing distance lookups on geodetic coordinate systems."
|
"Testing distance lookups on geodetic coordinate systems."
|
||||||
if not oracle:
|
if not oracle:
|
||||||
# Oracle doesn't have this limitation -- PostGIS only allows geodetic
|
# Oracle doesn't have this limitation -- PostGIS only allows geodetic
|
||||||
# distance queries from Points to PointFields.
|
# distance queries from Points to PointFields on geometry columns (geography
|
||||||
|
# columns don't have that limitation).
|
||||||
mp = GEOSGeometry('MULTIPOINT(0 0, 5 23)')
|
mp = GEOSGeometry('MULTIPOINT(0 0, 5 23)')
|
||||||
self.assertRaises(ValueError, len,
|
self.assertRaises(ValueError, len,
|
||||||
AustraliaCity.objects.filter(point__distance_lte=(mp, D(km=100))))
|
AustraliaCity.objects.filter(point__distance_lte=(mp, D(km=100))))
|
||||||
|
|
||||||
|
# Ensured that a ValueError was raised, none of the rest of the test is
|
||||||
|
# support on this backend, so bail now.
|
||||||
|
if spatialite: return
|
||||||
|
|
||||||
# Too many params (4 in this case) should raise a ValueError.
|
# Too many params (4 in this case) should raise a ValueError.
|
||||||
self.assertRaises(ValueError, len,
|
self.assertRaises(ValueError, len,
|
||||||
AustraliaCity.objects.filter(point__distance_lte=('POINT(5 23)', D(km=100), 'spheroid', '4')))
|
AustraliaCity.objects.filter(point__distance_lte=('POINT(5 23)', D(km=100), 'spheroid', '4')))
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"pk": 1,
|
||||||
|
"model": "geogapp.city",
|
||||||
|
"fields": {
|
||||||
|
"name": "Houston",
|
||||||
|
"point": "POINT (-95.363151 29.763374)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": 2,
|
||||||
|
"model": "geogapp.city",
|
||||||
|
"fields": {
|
||||||
|
"name": "Dallas",
|
||||||
|
"point": "POINT (-96.801611 32.782057)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": 3,
|
||||||
|
"model": "geogapp.city",
|
||||||
|
"fields": {
|
||||||
|
"name": "Oklahoma City",
|
||||||
|
"point": "POINT (-97.521157 34.464642)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": 4,
|
||||||
|
"model": "geogapp.city",
|
||||||
|
"fields": {
|
||||||
|
"name": "Wellington",
|
||||||
|
"point": "POINT (174.783117 -41.315268)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": 5,
|
||||||
|
"model": "geogapp.city",
|
||||||
|
"fields": {
|
||||||
|
"name": "Pueblo",
|
||||||
|
"point": "POINT (-104.609252 38.255001)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": 6,
|
||||||
|
"model": "geogapp.city",
|
||||||
|
"fields": {
|
||||||
|
"name": "Lawrence",
|
||||||
|
"point": "POINT (-95.235060 38.971823)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": 7,
|
||||||
|
"model": "geogapp.city",
|
||||||
|
"fields": {
|
||||||
|
"name": "Chicago",
|
||||||
|
"point": "POINT (-87.650175 41.850385)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": 8,
|
||||||
|
"model": "geogapp.city",
|
||||||
|
"fields": {
|
||||||
|
"name": "Victoria",
|
||||||
|
"point": "POINT (-123.305196 48.462611)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": 1,
|
||||||
|
"model": "geogapp.zipcode",
|
||||||
|
"fields" : {
|
||||||
|
"code" : "77002",
|
||||||
|
"poly" : "SRID=4269;POLYGON ((-95.365015 29.772327, -95.362415 29.772327, -95.360915 29.771827, -95.354615 29.771827, -95.351515 29.772527, -95.350915 29.765327, -95.351015 29.762436, -95.350115 29.760328, -95.347515 29.758528, -95.352315 29.753928, -95.356415 29.756328, -95.358215 29.754028, -95.360215 29.756328, -95.363415 29.757128, -95.364014 29.75638, -95.363415 29.753928, -95.360015 29.751828, -95.361815 29.749528, -95.362715 29.750028, -95.367516 29.744128, -95.369316 29.745128, -95.373916 29.744128, -95.380116 29.738028, -95.387916 29.727929, -95.388516 29.729629, -95.387916 29.732129, -95.382916 29.737428, -95.376616 29.742228, -95.372616 29.747228, -95.378601 29.750846, -95.378616 29.752028, -95.378616 29.754428, -95.376016 29.754528, -95.374616 29.759828, -95.373616 29.761128, -95.371916 29.763928, -95.372316 29.768727, -95.365884 29.76791, -95.366015 29.767127, -95.358715 29.765327, -95.358615 29.766327, -95.359115 29.767227, -95.360215 29.767027, -95.362783 29.768267, -95.365315 29.770527, -95.365015 29.772327))"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": 2,
|
||||||
|
"model": "geogapp.zipcode",
|
||||||
|
"fields" : {
|
||||||
|
"code" : "77005",
|
||||||
|
"poly" : "SRID=4269;POLYGON ((-95.447918 29.727275, -95.428017 29.728729, -95.421117 29.729029, -95.418617 29.727629, -95.418517 29.726429, -95.402117 29.726629, -95.402117 29.725729, -95.395316 29.725729, -95.391916 29.726229, -95.389716 29.725829, -95.396517 29.715429, -95.397517 29.715929, -95.400917 29.711429, -95.411417 29.715029, -95.418417 29.714729, -95.418317 29.70623, -95.440818 29.70593, -95.445018 29.70683, -95.446618 29.70763, -95.447418 29.71003, -95.447918 29.727275))"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": 3,
|
||||||
|
"model": "geogapp.zipcode",
|
||||||
|
"fields" : {
|
||||||
|
"code" : "77025",
|
||||||
|
"poly" : "SRID=4269;POLYGON ((-95.418317 29.70623, -95.414717 29.706129, -95.414617 29.70533, -95.418217 29.70533, -95.419817 29.69533, -95.419484 29.694196, -95.417166 29.690901, -95.414517 29.69433, -95.413317 29.69263, -95.412617 29.68973, -95.412817 29.68753, -95.414087 29.685055, -95.419165 29.685428, -95.421617 29.68513, -95.425717 29.67983, -95.425017 29.67923, -95.424517 29.67763, -95.427418 29.67763, -95.438018 29.664631, -95.436713 29.664411, -95.440118 29.662231, -95.439218 29.661031, -95.437718 29.660131, -95.435718 29.659731, -95.431818 29.660331, -95.441418 29.656631, -95.441318 29.656331, -95.441818 29.656131, -95.441718 29.659031, -95.441118 29.661031, -95.446718 29.656431, -95.446518 29.673431, -95.446918 29.69013, -95.447418 29.71003, -95.446618 29.70763, -95.445018 29.70683, -95.440818 29.70593, -95.418317 29.70623))"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pk": 4,
|
||||||
|
"model": "geogapp.zipcode",
|
||||||
|
"fields" : {
|
||||||
|
"code" : "77401",
|
||||||
|
"poly" : "SRID=4269;POLYGON ((-95.447918 29.727275, -95.447418 29.71003, -95.446918 29.69013, -95.454318 29.68893, -95.475819 29.68903, -95.475819 29.69113, -95.484419 29.69103, -95.484519 29.69903, -95.480419 29.70133, -95.480419 29.69833, -95.474119 29.69833, -95.474119 29.70453, -95.472719 29.71283, -95.468019 29.71293, -95.468219 29.720229, -95.464018 29.720229, -95.464118 29.724529, -95.463018 29.725929, -95.459818 29.726129, -95.459918 29.720329, -95.451418 29.720429, -95.451775 29.726303, -95.451318 29.727029, -95.447918 29.727275))"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,13 @@
|
||||||
|
from django.contrib.gis.db import models
|
||||||
|
|
||||||
|
class City(models.Model):
|
||||||
|
name = models.CharField(max_length=30)
|
||||||
|
point = models.PointField(geography=True)
|
||||||
|
objects = models.GeoManager()
|
||||||
|
def __unicode__(self): return self.name
|
||||||
|
|
||||||
|
class Zipcode(models.Model):
|
||||||
|
code = models.CharField(max_length=10)
|
||||||
|
poly = models.PolygonField(geography=True)
|
||||||
|
objects = models.GeoManager()
|
||||||
|
def __unicode__(self): return self.name
|
|
@ -0,0 +1,41 @@
|
||||||
|
"""
|
||||||
|
This file demonstrates two different styles of tests (one doctest and one
|
||||||
|
unittest). These will both pass when you run "manage.py test".
|
||||||
|
|
||||||
|
Replace these with more appropriate tests for your application.
|
||||||
|
"""
|
||||||
|
from django.contrib.gis.measure import D
|
||||||
|
from django.test import TestCase
|
||||||
|
from models import City, Zipcode
|
||||||
|
|
||||||
|
class GeographyTest(TestCase):
|
||||||
|
|
||||||
|
def test01_fixture_load(self):
|
||||||
|
"Ensure geography features loaded properly."
|
||||||
|
self.assertEqual(8, City.objects.count())
|
||||||
|
|
||||||
|
def test02_distance_lookup(self):
|
||||||
|
"Testing GeoQuerySet distance lookup support on non-point geometry fields."
|
||||||
|
z = Zipcode.objects.get(code='77002')
|
||||||
|
cities = list(City.objects
|
||||||
|
.filter(point__distance_lte=(z.poly, D(mi=500)))
|
||||||
|
.order_by('name')
|
||||||
|
.values_list('name', flat=True))
|
||||||
|
self.assertEqual(['Dallas', 'Houston', 'Oklahoma City'], cities)
|
||||||
|
|
||||||
|
def test03_distance_method(self):
|
||||||
|
"Testing GeoQuerySet.distance() support on non-point geometry fields."
|
||||||
|
# Can't do this with geometry fields:
|
||||||
|
htown = City.objects.get(name='Houston')
|
||||||
|
qs = Zipcode.objects.distance(htown.point)
|
||||||
|
|
||||||
|
def test04_invalid_operators_functions(self):
|
||||||
|
"Ensuring exceptions are raised for operators & functions invalid on geography fields."
|
||||||
|
# Only a subset of the geometry functions & operator are available
|
||||||
|
# to PostGIS geography types. For more information, visit:
|
||||||
|
# http://postgis.refractions.net/documentation/manual-1.5/ch08.html#PostGIS_GeographyFunctions
|
||||||
|
z = Zipcode.objects.get(code='77002')
|
||||||
|
# ST_Within not available.
|
||||||
|
self.assertRaises(ValueError, City.objects.filter(point__within=z.poly).count)
|
||||||
|
# `@` operator not available.
|
||||||
|
self.assertRaises(ValueError, City.objects.filter(point__contained=z.poly).count)
|
|
@ -0,0 +1 @@
|
||||||
|
# Create your views here.
|
Loading…
Reference in New Issue