2009-12-23 20:45:08 +00:00
|
|
|
"""
|
2014-07-26 20:55:31 +00:00
|
|
|
Tests for geography support in PostGIS
|
2009-12-23 20:45:08 +00:00
|
|
|
"""
|
2013-07-29 17:19:04 +00:00
|
|
|
from __future__ import unicode_literals
|
2011-10-17 18:45:22 +00:00
|
|
|
|
2009-12-25 14:37:57 +00:00
|
|
|
import os
|
2013-07-01 12:22:27 +00:00
|
|
|
from unittest import skipUnless
|
2011-10-17 18:45:22 +00:00
|
|
|
|
2013-05-11 03:08:45 +00:00
|
|
|
from django.contrib.gis.gdal import HAS_GDAL
|
|
|
|
from django.contrib.gis.geos import HAS_GEOS
|
2009-12-23 20:45:08 +00:00
|
|
|
from django.contrib.gis.measure import D
|
2014-08-21 16:47:57 +00:00
|
|
|
from django.test import TestCase, skipUnlessDBFeature
|
2012-12-08 10:13:52 +00:00
|
|
|
from django.utils._os import upath
|
2011-10-17 18:45:22 +00:00
|
|
|
|
2015-02-10 15:07:44 +00:00
|
|
|
from ..utils import oracle, postgis
|
|
|
|
|
2013-05-11 03:08:45 +00:00
|
|
|
if HAS_GEOS:
|
|
|
|
from .models import City, County, Zipcode
|
2011-10-17 18:45:22 +00:00
|
|
|
|
2009-12-23 20:45:08 +00:00
|
|
|
|
2014-08-21 16:47:57 +00:00
|
|
|
@skipUnlessDBFeature("gis_enabled")
|
2009-12-23 20:45:08 +00:00
|
|
|
class GeographyTest(TestCase):
|
2014-07-26 17:15:54 +00:00
|
|
|
fixtures = ['initial']
|
2009-12-23 20:45:08 +00:00
|
|
|
|
|
|
|
def test01_fixture_load(self):
|
|
|
|
"Ensure geography features loaded properly."
|
|
|
|
self.assertEqual(8, City.objects.count())
|
|
|
|
|
2014-08-21 16:47:57 +00:00
|
|
|
@skipUnlessDBFeature("supports_distances_lookups", "supports_distance_geodetic")
|
2009-12-23 20:45:08 +00:00
|
|
|
def test02_distance_lookup(self):
|
2009-12-25 14:37:57 +00:00
|
|
|
"Testing GeoQuerySet distance lookup support on non-point geography fields."
|
2009-12-23 20:45:08 +00:00
|
|
|
z = Zipcode.objects.get(code='77002')
|
2009-12-25 14:37:57 +00:00
|
|
|
cities1 = list(City.objects
|
|
|
|
.filter(point__distance_lte=(z.poly, D(mi=500)))
|
|
|
|
.order_by('name')
|
|
|
|
.values_list('name', flat=True))
|
|
|
|
cities2 = list(City.objects
|
|
|
|
.filter(point__dwithin=(z.poly, D(mi=500)))
|
|
|
|
.order_by('name')
|
|
|
|
.values_list('name', flat=True))
|
|
|
|
for cities in [cities1, cities2]:
|
|
|
|
self.assertEqual(['Dallas', 'Houston', 'Oklahoma City'], cities)
|
2009-12-23 20:45:08 +00:00
|
|
|
|
2014-08-21 16:47:57 +00:00
|
|
|
@skipUnlessDBFeature("has_distance_method", "supports_distance_geodetic")
|
2009-12-23 20:45:08 +00:00
|
|
|
def test03_distance_method(self):
|
2009-12-25 14:37:57 +00:00
|
|
|
"Testing GeoQuerySet.distance() support on non-point geography fields."
|
|
|
|
# `GeoQuerySet.distance` is not allowed geometry fields.
|
2009-12-23 20:45:08 +00:00
|
|
|
htown = City.objects.get(name='Houston')
|
2013-09-08 15:05:16 +00:00
|
|
|
Zipcode.objects.distance(htown.point)
|
2009-12-23 20:45:08 +00:00
|
|
|
|
2014-08-21 16:47:57 +00:00
|
|
|
@skipUnless(postgis, "This is a PostGIS-specific test")
|
2009-12-23 20:45:08 +00:00
|
|
|
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:
|
2013-11-02 21:02:56 +00:00
|
|
|
# http://postgis.refractions.net/documentation/manual-1.5/ch08.html#PostGIS_GeographyFunctions
|
2009-12-23 20:45:08 +00:00
|
|
|
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)
|
2009-12-25 14:37:57 +00:00
|
|
|
|
2010-09-12 02:07:04 +00:00
|
|
|
# Regression test for #14060, `~=` was never really implemented for PostGIS.
|
|
|
|
htown = City.objects.get(name='Houston')
|
|
|
|
self.assertRaises(ValueError, City.objects.get, point__exact=htown.point)
|
|
|
|
|
2013-05-11 03:08:45 +00:00
|
|
|
@skipUnless(HAS_GDAL, "GDAL is required.")
|
2009-12-25 14:37:57 +00:00
|
|
|
def test05_geography_layermapping(self):
|
|
|
|
"Testing LayerMapping support on models with geography fields."
|
|
|
|
# There is a similar test in `layermap` that uses the same data set,
|
|
|
|
# but the County model here is a bit different.
|
|
|
|
from django.contrib.gis.utils import LayerMapping
|
|
|
|
|
|
|
|
# Getting the shapefile and mapping dictionary.
|
2012-12-08 10:13:52 +00:00
|
|
|
shp_path = os.path.realpath(os.path.join(os.path.dirname(upath(__file__)), '..', 'data'))
|
2009-12-25 14:37:57 +00:00
|
|
|
co_shp = os.path.join(shp_path, 'counties', 'counties.shp')
|
2013-10-27 01:27:42 +00:00
|
|
|
co_mapping = {'name': 'Name',
|
|
|
|
'state': 'State',
|
|
|
|
'mpoly': 'MULTIPOLYGON',
|
2009-12-25 14:37:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# Reference county names, number of polygons, and state names.
|
|
|
|
names = ['Bexar', 'Galveston', 'Harris', 'Honolulu', 'Pueblo']
|
2013-11-02 21:02:56 +00:00
|
|
|
num_polys = [1, 2, 1, 19, 1] # Number of polygons for each.
|
2009-12-25 14:37:57 +00:00
|
|
|
st_names = ['Texas', 'Texas', 'Texas', 'Hawaii', 'Colorado']
|
|
|
|
|
|
|
|
lm = LayerMapping(County, co_shp, co_mapping, source_srs=4269, unique='name')
|
|
|
|
lm.save(silent=True, strict=True)
|
|
|
|
|
|
|
|
for c, name, num_poly, state in zip(County.objects.order_by('name'), names, num_polys, st_names):
|
|
|
|
self.assertEqual(4326, c.mpoly.srid)
|
|
|
|
self.assertEqual(num_poly, len(c.mpoly))
|
|
|
|
self.assertEqual(name, c.name)
|
|
|
|
self.assertEqual(state, c.state)
|
2010-10-12 17:13:27 +00:00
|
|
|
|
2014-08-21 16:47:57 +00:00
|
|
|
@skipUnlessDBFeature("has_area_method", "supports_distance_geodetic")
|
2010-10-12 17:13:27 +00:00
|
|
|
def test06_geography_area(self):
|
|
|
|
"Testing that Area calculations work on geography columns."
|
|
|
|
# SELECT ST_Area(poly) FROM geogapp_zipcode WHERE code='77002';
|
2014-09-24 00:47:08 +00:00
|
|
|
ref_area = 5439100.95415646 if oracle else 5439084.70637573
|
2010-10-12 17:13:27 +00:00
|
|
|
tol = 5
|
|
|
|
z = Zipcode.objects.area().get(code='77002')
|
|
|
|
self.assertAlmostEqual(z.area.sq_m, ref_area, tol)
|