~ubuntu-branches/ubuntu/quantal/python-django/quantal

« back to all changes in this revision

Viewing changes to django/contrib/gis/tests/distapp/tests.py

  • Committer: Bazaar Package Importer
  • Author(s): Chris Lamb
  • Date: 2009-07-29 11:26:28 UTC
  • mfrom: (1.1.8 upstream) (4.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20090729112628-pg09ino8sz0sj21t
Tags: 1.1-1
* New upstream release.
* Merge from experimental:
  - Ship FastCGI initscript and /etc/default file in python-django's examples
    directory (Closes: #538863)
  - Drop "05_10539-sphinx06-compatibility.diff"; it has been applied
    upstream.
  - Bump Standards-Version to 3.8.2.

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
from django.contrib.gis.gdal import DataSource
6
6
from django.contrib.gis.geos import GEOSGeometry, Point, LineString
7
7
from django.contrib.gis.measure import D # alias for Distance
8
 
from django.contrib.gis.db.models import GeoQ
9
 
from django.contrib.gis.tests.utils import oracle, postgis, no_oracle
 
8
from django.contrib.gis.tests.utils import oracle, postgis, spatialite, no_oracle, no_spatialite
10
9
 
11
 
from models import AustraliaCity, Interstate, SouthTexasCity, SouthTexasCityFt, CensusZipcode, SouthTexasZipcode
12
 
from data import au_cities, interstates, stx_cities, stx_zips
 
10
from models import AustraliaCity, Interstate, SouthTexasInterstate, \
 
11
    SouthTexasCity, SouthTexasCityFt, CensusZipcode, SouthTexasZipcode
 
12
from data import au_cities, interstates, stx_interstates, stx_cities, stx_zips
13
13
 
14
14
class DistanceTest(unittest.TestCase):
15
15
 
32
32
        # Loading up the cities.
33
33
        def load_cities(city_model, data_tup):
34
34
            for name, x, y in data_tup:
35
 
                c = city_model(name=name, point=Point(x, y, srid=4326))
36
 
                c.save()
37
 
        
 
35
                city_model(name=name, point=Point(x, y, srid=4326)).save()
 
36
 
 
37
        def load_interstates(imodel, data_tup):
 
38
            for name, wkt in data_tup:
 
39
                imodel(name=name, path=wkt).save()
 
40
 
38
41
        load_cities(SouthTexasCity, stx_cities)
39
42
        load_cities(SouthTexasCityFt, stx_cities)
40
43
        load_cities(AustraliaCity, au_cities)
42
45
        self.assertEqual(9, SouthTexasCity.objects.count())
43
46
        self.assertEqual(9, SouthTexasCityFt.objects.count())
44
47
        self.assertEqual(11, AustraliaCity.objects.count())
45
 
        
 
48
 
46
49
        # Loading up the South Texas Zip Codes.
47
50
        for name, wkt in stx_zips:
48
51
            poly = GEOSGeometry(wkt, srid=4269)
52
55
        self.assertEqual(4, CensusZipcode.objects.count())
53
56
 
54
57
        # Loading up the Interstates.
55
 
        for name, wkt in interstates:
56
 
            Interstate(name=name, line=GEOSGeometry(wkt, srid=4326)).save()
 
58
        load_interstates(Interstate, interstates)
 
59
        load_interstates(SouthTexasInterstate, stx_interstates)
 
60
 
57
61
        self.assertEqual(1, Interstate.objects.count())
 
62
        self.assertEqual(1, SouthTexasInterstate.objects.count())
58
63
 
 
64
    @no_spatialite
59
65
    def test02_dwithin(self):
60
66
        "Testing the `dwithin` lookup type."
61
67
        # Distances -- all should be equal (except for the
63
69
        # approximate).
64
70
        tx_dists = [(7000, 22965.83), D(km=7), D(mi=4.349)]
65
71
        au_dists = [(0.5, 32000), D(km=32), D(mi=19.884)]
66
 
        
 
72
 
67
73
        # Expected cities for Australia and Texas.
68
74
        tx_cities = ['Downtown Houston', 'Southside Place']
69
75
        au_cities = ['Mittagong', 'Shellharbour', 'Thirroul', 'Wollongong']
86
92
            if isinstance(dist, tuple):
87
93
                if oracle: dist = dist[1]
88
94
                else: dist = dist[0]
89
 
                
 
95
 
90
96
            # Creating the query set.
91
97
            qs = AustraliaCity.objects.order_by('name')
92
98
            if type_error:
93
99
                # A TypeError should be raised on PostGIS when trying to pass
94
 
                # Distance objects into a DWithin query using a geodetic field.  
 
100
                # Distance objects into a DWithin query using a geodetic field.
95
101
                self.assertRaises(TypeError, AustraliaCity.objects.filter, point__dwithin=(self.au_pnt, dist))
96
102
            else:
97
103
                self.assertEqual(au_cities, self.get_names(qs.filter(point__dwithin=(self.au_pnt, dist))))
98
 
                                 
 
104
 
99
105
    def test03a_distance_method(self):
100
106
        "Testing the `distance` GeoQuerySet method on projected coordinate systems."
101
107
        # The point for La Grange, TX
102
108
        lagrange = GEOSGeometry('POINT(-96.876369 29.905320)', 4326)
103
 
        # Reference distances in feet and in meters. Got these values from 
 
109
        # Reference distances in feet and in meters. Got these values from
104
110
        # using the provided raw SQL statements.
105
111
        #  SELECT ST_Distance(point, ST_Transform(ST_GeomFromText('POINT(-96.876369 29.905320)', 4326), 32140)) FROM distapp_southtexascity;
106
112
        m_distances = [147075.069813, 139630.198056, 140888.552826,
107
113
                       138809.684197, 158309.246259, 212183.594374,
108
114
                       70870.188967, 165337.758878, 139196.085105]
109
115
        #  SELECT ST_Distance(point, ST_Transform(ST_GeomFromText('POINT(-96.876369 29.905320)', 4326), 2278)) FROM distapp_southtexascityft;
 
116
        # Oracle 11 thinks this is not a projected coordinate system, so it's s
 
117
        # not tested.
110
118
        ft_distances = [482528.79154625, 458103.408123001, 462231.860397575,
111
119
                        455411.438904354, 519386.252102563, 696139.009211594,
112
120
                        232513.278304279, 542445.630586414, 456679.155883207]
115
123
        # with different projected coordinate systems.
116
124
        dist1 = SouthTexasCity.objects.distance(lagrange, field_name='point')
117
125
        dist2 = SouthTexasCity.objects.distance(lagrange)  # Using GEOSGeometry parameter
118
 
        dist3 = SouthTexasCityFt.objects.distance(lagrange.ewkt) # Using EWKT string parameter.
119
 
        dist4 = SouthTexasCityFt.objects.distance(lagrange)
 
126
        if spatialite or oracle:
 
127
            dist_qs = [dist1, dist2]
 
128
        else:
 
129
            dist3 = SouthTexasCityFt.objects.distance(lagrange.ewkt) # Using EWKT string parameter.
 
130
            dist4 = SouthTexasCityFt.objects.distance(lagrange)
 
131
            dist_qs = [dist1, dist2, dist3, dist4]
120
132
 
121
133
        # Original query done on PostGIS, have to adjust AlmostEqual tolerance
122
134
        # for Oracle.
124
136
        else: tol = 5
125
137
 
126
138
        # Ensuring expected distances are returned for each distance queryset.
127
 
        for qs in [dist1, dist2, dist3, dist4]:
 
139
        for qs in dist_qs:
128
140
            for i, c in enumerate(qs):
129
141
                self.assertAlmostEqual(m_distances[i], c.distance.m, tol)
130
142
                self.assertAlmostEqual(ft_distances[i], c.distance.survey_ft, tol)
131
143
 
 
144
    @no_spatialite
132
145
    def test03b_distance_method(self):
133
146
        "Testing the `distance` GeoQuerySet method on geodetic coordnate systems."
134
147
        if oracle: tol = 2
139
152
        if not oracle:
140
153
            # PostGIS is limited to disance queries only to/from point geometries,
141
154
            # ensuring a TypeError is raised if something else is put in.
142
 
            self.assertRaises(TypeError, AustraliaCity.objects.distance, 'LINESTRING(0 0, 1 1)')
143
 
            self.assertRaises(TypeError, AustraliaCity.objects.distance, LineString((0, 0), (1, 1)))
 
155
            self.assertRaises(ValueError, AustraliaCity.objects.distance, 'LINESTRING(0 0, 1 1)')
 
156
            self.assertRaises(ValueError, AustraliaCity.objects.distance, LineString((0, 0), (1, 1)))
144
157
 
145
158
        # Got the reference distances using the raw SQL statements:
146
159
        #  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));
163
176
        "Testing the `distance` GeoQuerySet method used with `transform` on a geographic field."
164
177
        # Normally you can't compute distances from a geometry field
165
178
        # that is not a PointField (on PostGIS).
166
 
        self.assertRaises(TypeError, CensusZipcode.objects.distance, self.stx_pnt)
167
 
        
 
179
        self.assertRaises(ValueError, CensusZipcode.objects.distance, self.stx_pnt)
 
180
 
168
181
        # We'll be using a Polygon (created by buffering the centroid
169
182
        # of 77005 to 100m) -- which aren't allowed in geographic distance
170
183
        # queries normally, however our field has been transformed to
182
195
        # however.
183
196
        buf1 = z.poly.centroid.buffer(100)
184
197
        buf2 = buf1.transform(4269, clone=True)
 
198
        ref_zips = ['77002', '77025', '77401']
 
199
 
185
200
        for buf in [buf1, buf2]:
186
201
            qs = CensusZipcode.objects.exclude(name='77005').transform(32140).distance(buf)
187
 
            self.assertEqual(['77002', '77025', '77401'], self.get_names(qs))
 
202
            self.assertEqual(ref_zips, self.get_names(qs))
188
203
            for i, z in enumerate(qs):
189
204
                self.assertAlmostEqual(z.distance.m, dists_m[i], 5)
190
205
 
194
209
        # (thus, Houston and Southside place will be excluded as tested in
195
210
        # the `test02_dwithin` above).
196
211
        qs1 = SouthTexasCity.objects.filter(point__distance_gte=(self.stx_pnt, D(km=7))).filter(point__distance_lte=(self.stx_pnt, D(km=20)))
197
 
        qs2 = SouthTexasCityFt.objects.filter(point__distance_gte=(self.stx_pnt, D(km=7))).filter(point__distance_lte=(self.stx_pnt, D(km=20)))
198
 
        for qs in qs1, qs2:
 
212
 
 
213
        # Can't determine the units on SpatiaLite from PROJ.4 string, and
 
214
        # Oracle 11 incorrectly thinks it is not projected.
 
215
        if spatialite or oracle:
 
216
            dist_qs = (qs1,)
 
217
        else:
 
218
            qs2 = SouthTexasCityFt.objects.filter(point__distance_gte=(self.stx_pnt, D(km=7))).filter(point__distance_lte=(self.stx_pnt, D(km=20)))
 
219
            dist_qs = (qs1, qs2)
 
220
 
 
221
        for qs in dist_qs:
199
222
            cities = self.get_names(qs)
200
223
            self.assertEqual(cities, ['Bellaire', 'Pearland', 'West University Place'])
201
224
 
206
229
        # If we add a little more distance 77002 should be included.
207
230
        qs = SouthTexasZipcode.objects.exclude(name='77005').filter(poly__distance_lte=(z.poly, D(m=300)))
208
231
        self.assertEqual(['77002', '77025', '77401'], self.get_names(qs))
209
 
        
 
232
 
 
233
    @no_spatialite
210
234
    def test05_geodetic_distance_lookups(self):
211
235
        "Testing distance lookups on geodetic coordinate systems."
212
236
        if not oracle:
216
240
            self.assertRaises(TypeError,
217
241
                              AustraliaCity.objects.filter(point__distance_lte=(mp, D(km=100))))
218
242
            # Too many params (4 in this case) should raise a ValueError.
219
 
            self.assertRaises(ValueError, 
 
243
            self.assertRaises(ValueError,
220
244
                              AustraliaCity.objects.filter, point__distance_lte=('POINT(5 23)', D(km=100), 'spheroid', '4'))
221
245
 
222
246
        # Not enough params should raise a ValueError.
235
259
        d1, d2 = D(yd=19500), D(nm=400) # Yards (~17km) & Nautical miles.
236
260
 
237
261
        # Normal geodetic distance lookup (uses `distance_sphere` on PostGIS.
238
 
        gq1 = GeoQ(point__distance_lte=(wollongong.point, d1))
239
 
        gq2 = GeoQ(point__distance_gte=(wollongong.point, d2))
 
262
        gq1 = Q(point__distance_lte=(wollongong.point, d1))
 
263
        gq2 = Q(point__distance_gte=(wollongong.point, d2))
240
264
        qs1 = AustraliaCity.objects.exclude(name='Wollongong').filter(gq1 | gq2)
241
265
 
242
266
        # Geodetic distance lookup but telling GeoDjango to use `distance_spheroid`
243
267
        # instead (we should get the same results b/c accuracy variance won't matter
244
 
        # in this test case). Using `Q` instead of `GeoQ` to be different (post-qsrf
245
 
        # it doesn't matter).
 
268
        # in this test case).
246
269
        if postgis:
247
270
            gq3 = Q(point__distance_lte=(wollongong.point, d1, 'spheroid'))
248
271
            gq4 = Q(point__distance_gte=(wollongong.point, d2, 'spheroid'))
270
293
        "Testing the `length` GeoQuerySet method."
271
294
        # Reference query (should use `length_spheroid`).
272
295
        # SELECT ST_length_spheroid(ST_GeomFromText('<wkt>', 4326) 'SPHEROID["WGS 84",6378137,298.257223563, AUTHORITY["EPSG","7030"]]');
273
 
        len_m = 473504.769553813
274
 
        qs = Interstate.objects.length()
275
 
        if oracle: tol = 2
276
 
        else: tol = 5
277
 
        self.assertAlmostEqual(len_m, qs[0].length.m, tol)
278
 
 
 
296
        len_m1 = 473504.769553813
 
297
        len_m2 = 4617.668
 
298
 
 
299
        if spatialite:
 
300
            # Does not support geodetic coordinate systems.
 
301
            self.assertRaises(ValueError, Interstate.objects.length)
 
302
        else:
 
303
            qs = Interstate.objects.length()
 
304
            if oracle: tol = 2
 
305
            else: tol = 5
 
306
            self.assertAlmostEqual(len_m1, qs[0].length.m, tol)
 
307
 
 
308
        # Now doing length on a projected coordinate system.
 
309
        i10 = SouthTexasInterstate.objects.length().get(name='I-10')
 
310
        self.assertAlmostEqual(len_m2, i10.length.m, 2)
 
311
 
 
312
    @no_spatialite
279
313
    def test08_perimeter(self):
280
314
        "Testing the `perimeter` GeoQuerySet method."
281
315
        # Reference query: