Fixed `LayerMapping` to work with PostGIS geography fields; removed `LayerMapping.geometry_column` and replaced with `LayerMapping.geometry_field` because getting the `geometry_columns` entry was completely unnecessary, and only the geometry field instance is needed; cleaned up and fleshed out the `geogapp` tests.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@11983 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
7626f851e3
commit
ac6273d675
|
@ -11,3 +11,10 @@ class Zipcode(models.Model):
|
||||||
poly = models.PolygonField(geography=True)
|
poly = models.PolygonField(geography=True)
|
||||||
objects = models.GeoManager()
|
objects = models.GeoManager()
|
||||||
def __unicode__(self): return self.name
|
def __unicode__(self): return self.name
|
||||||
|
|
||||||
|
class County(models.Model):
|
||||||
|
name = models.CharField(max_length=25)
|
||||||
|
state = models.CharField(max_length=20)
|
||||||
|
mpoly = models.MultiPolygonField(geography=True)
|
||||||
|
objects = models.GeoManager()
|
||||||
|
def __unicode__(self): return ' County, '.join([self.name, self.state])
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
"""
|
"""
|
||||||
This file demonstrates two different styles of tests (one doctest and one
|
Tests for geography support in PostGIS 1.5+
|
||||||
unittest). These will both pass when you run "manage.py test".
|
|
||||||
|
|
||||||
Replace these with more appropriate tests for your application.
|
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
|
from django.contrib.gis import gdal
|
||||||
from django.contrib.gis.measure import D
|
from django.contrib.gis.measure import D
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from models import City, Zipcode
|
from models import City, County, Zipcode
|
||||||
|
|
||||||
class GeographyTest(TestCase):
|
class GeographyTest(TestCase):
|
||||||
|
|
||||||
|
@ -15,17 +14,22 @@ class GeographyTest(TestCase):
|
||||||
self.assertEqual(8, City.objects.count())
|
self.assertEqual(8, City.objects.count())
|
||||||
|
|
||||||
def test02_distance_lookup(self):
|
def test02_distance_lookup(self):
|
||||||
"Testing GeoQuerySet distance lookup support on non-point geometry fields."
|
"Testing GeoQuerySet distance lookup support on non-point geography fields."
|
||||||
z = Zipcode.objects.get(code='77002')
|
z = Zipcode.objects.get(code='77002')
|
||||||
cities = list(City.objects
|
cities1 = list(City.objects
|
||||||
.filter(point__distance_lte=(z.poly, D(mi=500)))
|
.filter(point__distance_lte=(z.poly, D(mi=500)))
|
||||||
.order_by('name')
|
.order_by('name')
|
||||||
.values_list('name', flat=True))
|
.values_list('name', flat=True))
|
||||||
self.assertEqual(['Dallas', 'Houston', 'Oklahoma City'], cities)
|
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)
|
||||||
|
|
||||||
def test03_distance_method(self):
|
def test03_distance_method(self):
|
||||||
"Testing GeoQuerySet.distance() support on non-point geometry fields."
|
"Testing GeoQuerySet.distance() support on non-point geography fields."
|
||||||
# Can't do this with geometry fields:
|
# `GeoQuerySet.distance` is not allowed geometry fields.
|
||||||
htown = City.objects.get(name='Houston')
|
htown = City.objects.get(name='Houston')
|
||||||
qs = Zipcode.objects.distance(htown.point)
|
qs = Zipcode.objects.distance(htown.point)
|
||||||
|
|
||||||
|
@ -39,3 +43,32 @@ class GeographyTest(TestCase):
|
||||||
self.assertRaises(ValueError, City.objects.filter(point__within=z.poly).count)
|
self.assertRaises(ValueError, City.objects.filter(point__within=z.poly).count)
|
||||||
# `@` operator not available.
|
# `@` operator not available.
|
||||||
self.assertRaises(ValueError, City.objects.filter(point__contained=z.poly).count)
|
self.assertRaises(ValueError, City.objects.filter(point__contained=z.poly).count)
|
||||||
|
|
||||||
|
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.
|
||||||
|
if not gdal.HAS_GDAL: return
|
||||||
|
from django.contrib.gis.utils import LayerMapping
|
||||||
|
|
||||||
|
# Getting the shapefile and mapping dictionary.
|
||||||
|
shp_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', 'data'))
|
||||||
|
co_shp = os.path.join(shp_path, 'counties', 'counties.shp')
|
||||||
|
co_mapping = {'name' : 'Name',
|
||||||
|
'state' : 'State',
|
||||||
|
'mpoly' : 'MULTIPOLYGON',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reference county names, number of polygons, and state names.
|
||||||
|
names = ['Bexar', 'Galveston', 'Harris', 'Honolulu', 'Pueblo']
|
||||||
|
num_polys = [1, 2, 1, 19, 1] # Number of polygons for each.
|
||||||
|
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)
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
# Create your views here.
|
|
|
@ -97,7 +97,7 @@ class LayerMapping(object):
|
||||||
if self.spatial_backend.mysql:
|
if self.spatial_backend.mysql:
|
||||||
transform = False
|
transform = False
|
||||||
else:
|
else:
|
||||||
self.geo_col = self.geometry_column()
|
self.geo_field = self.geometry_field()
|
||||||
|
|
||||||
# Checking the source spatial reference system, and getting
|
# Checking the source spatial reference system, and getting
|
||||||
# the coordinate transformation object (unless the `transform`
|
# the coordinate transformation object (unless the `transform`
|
||||||
|
@ -426,41 +426,20 @@ class LayerMapping(object):
|
||||||
SpatialRefSys = self.spatial_backend.spatial_ref_sys()
|
SpatialRefSys = self.spatial_backend.spatial_ref_sys()
|
||||||
try:
|
try:
|
||||||
# Getting the target spatial reference system
|
# Getting the target spatial reference system
|
||||||
target_srs = SpatialRefSys.objects.get(srid=self.geo_col.srid).srs
|
target_srs = SpatialRefSys.objects.get(srid=self.geo_field.srid).srs
|
||||||
|
|
||||||
# Creating the CoordTransform object
|
# Creating the CoordTransform object
|
||||||
return CoordTransform(self.source_srs, target_srs)
|
return CoordTransform(self.source_srs, target_srs)
|
||||||
except Exception, msg:
|
except Exception, msg:
|
||||||
raise LayerMapError('Could not translate between the data source and model geometry: %s' % msg)
|
raise LayerMapError('Could not translate between the data source and model geometry: %s' % msg)
|
||||||
|
|
||||||
def geometry_column(self):
|
def geometry_field(self):
|
||||||
"Returns the GeometryColumn model associated with the geographic column."
|
"Returns the GeometryField instance associated with the geographic column."
|
||||||
# Use the `get_field_by_name` on the model's options so that we
|
# Use the `get_field_by_name` on the model's options so that we
|
||||||
# get the correct model if there's model inheritance -- otherwise
|
# get the correct field instance if there's model inheritance.
|
||||||
# the returned model is None.
|
|
||||||
opts = self.model._meta
|
opts = self.model._meta
|
||||||
fld, model, direct, m2m = opts.get_field_by_name(self.geom_field)
|
fld, model, direct, m2m = opts.get_field_by_name(self.geom_field)
|
||||||
if model is None: model = self.model
|
return fld
|
||||||
|
|
||||||
# Trying to get the `GeometryColumns` object that corresponds to the
|
|
||||||
# the geometry field.
|
|
||||||
try:
|
|
||||||
db_table = model._meta.db_table
|
|
||||||
geo_col = fld.column
|
|
||||||
|
|
||||||
if self.spatial_backend.oracle:
|
|
||||||
# Making upper case for Oracle.
|
|
||||||
db_table = db_table.upper()
|
|
||||||
geo_col = geo_col.upper()
|
|
||||||
|
|
||||||
GeometryColumns = self.spatial_backend.geometry_columns()
|
|
||||||
|
|
||||||
gc_kwargs = { GeometryColumns.table_name_col() : db_table,
|
|
||||||
GeometryColumns.geom_col_name() : geo_col,
|
|
||||||
}
|
|
||||||
return GeometryColumns.objects.get(**gc_kwargs)
|
|
||||||
except Exception, msg:
|
|
||||||
raise LayerMapError('Geometry column does not exist for model. (did you run syncdb?):\n %s' % msg)
|
|
||||||
|
|
||||||
def make_multi(self, geom_type, model_field):
|
def make_multi(self, geom_type, model_field):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue