Permalink
Browse files

gis: `LayerMapping`: Improved the internals (i.e., checking every fea…

…ture in OGR Layer is no longer needed, removed unnecessary class constants); added real support `ForeignKey` model fields; added `field_types` property to `Layer`; fixed county shapefile because of Harris County, Georgia.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6992 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent 7e322b5 commit b11172e4e72299d34c27eff635ad034f84a0cdb6 @jbronn jbronn committed Jan 3, 2008
@@ -5,15 +5,16 @@
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
from django.contrib.gis.gdal.error import OGRException, OGRIndexError, SRSException
from django.contrib.gis.gdal.feature import Feature
+from django.contrib.gis.gdal.field import FIELD_CLASSES
from django.contrib.gis.gdal.geometries import OGRGeomType
from django.contrib.gis.gdal.srs import SpatialReference
# GDAL ctypes function prototypes.
from django.contrib.gis.gdal.prototypes.ds import \
get_extent, get_fd_geom_type, get_fd_name, get_feature, get_feature_count, \
get_field_count, get_field_defn, get_field_name, get_field_precision, \
- get_field_width, get_layer_defn, get_layer_srs, get_next_feature, \
- reset_reading
+ get_field_width, get_field_type, get_layer_defn, get_layer_srs, \
+ get_next_feature, reset_reading
from django.contrib.gis.gdal.prototypes.srs import clone_srs
# For more information, see the OGR C API source code:
@@ -107,10 +108,24 @@ def srs(self):
@property
def fields(self):
- "Returns a list of the fields available in this Layer."
+ """
+ Returns a list of string names corresponding to each of the Fields
+ available in this Layer.
+ """
return [get_field_name(get_field_defn(self._ldefn, i))
for i in xrange(self.num_fields) ]
+ @property
+ def field_types(self):
+ """
+ Returns a list of the types of fields in this Layer. For example,
+ the list [OFTInteger, OFTReal, OFTString] would be returned for
+ an OGR layer that had an integer, a floating-point, and string
+ fields.
+ """
+ return [FIELD_CLASSES[get_field_type(get_field_defn(self._ldefn, i))]
+ for i in xrange(self.num_fields)]
+
@property
def field_widths(self):
"Returns a list of the maximum field widths for the features."
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,12 @@
from django.contrib.gis.db import models
+class State(models.Model):
+ name = models.CharField(max_length=20)
+ objects = models.GeoManager()
+
class County(models.Model):
name = models.CharField(max_length=25)
+ state = models.ForeignKey(State)
mpoly = models.MultiPolygonField(srid=4269) # Multipolygon in NAD83
objects = models.GeoManager()
@@ -26,6 +31,7 @@ class Interstate(models.Model):
# Mapping dictionaries for the models above.
co_mapping = {'name' : 'Name',
+ 'state' : {'name' : 'State'}, # ForeignKey's use another mapping dictionary for the _related_ Model (State in this case).
'mpoly' : 'MULTIPOLYGON', # Will convert POLYGON features into MULTIPOLYGONS.
}
@@ -2,8 +2,8 @@
from copy import copy
from datetime import date
from decimal import Decimal
-from models import City, County, CountyFeat, Interstate, city_mapping, co_mapping, cofeat_mapping, inter_mapping
-from django.contrib.gis.utils.layermapping import LayerMapping, LayerMapError, InvalidDecimal
+from models import City, County, CountyFeat, Interstate, State, city_mapping, co_mapping, cofeat_mapping, inter_mapping
+from django.contrib.gis.utils.layermapping import LayerMapping, LayerMapError, InvalidDecimal, MissingForeignKey
from django.contrib.gis.gdal import DataSource
shp_path = os.path.dirname(__file__)
@@ -111,8 +111,8 @@ def test03_layermap_strict(self):
self.assertAlmostEqual(p1[0], p2[0], 6)
self.assertAlmostEqual(p1[1], p2[1], 6)
- def test04_layermap_unique_multigeometry(self):
- "Testing the `unique`, and `transform` keywords and geometry collection conversion."
+ def test04_layermap_unique_multigeometry_fk(self):
+ "Testing the `unique`, and `transform`, geometry collection conversion, and ForeignKey mappings."
# All the following should work.
try:
# Telling LayerMapping that we want no transformations performed on the data.
@@ -135,6 +135,23 @@ def test04_layermap_unique_multigeometry(self):
# No source reference system defined in the shapefile, should raise an error.
self.assertRaises(LayerMapError, LayerMapping, County, co_shp, co_mapping)
+ # Passing in invalid ForeignKey mapping parameters -- must be a dictionary
+ # mapping for the model the ForeignKey points to.
+ bad_fk_map1 = copy(co_mapping); bad_fk_map1['state'] = 'name'
+ bad_fk_map2 = copy(co_mapping); bad_fk_map2['state'] = {'nombre' : 'State'}
+ self.assertRaises(TypeError, LayerMapping, County, co_shp, bad_fk_map1, transform=False)
+ self.assertRaises(LayerMapError, LayerMapping, County, co_shp, bad_fk_map2, transform=False)
+
+ # There exist no State models for the ForeignKey mapping to work -- should raise
+ # a MissingForeignKey exception (this error would be ignored if the `strict`
+ # keyword is not set).
+ lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique='name', silent=True, strict=True)
+ self.assertRaises(MissingForeignKey, lm.save)
+
+ # Now creating the state models so the ForeignKey mapping may work.
+ co, hi, tx = State(name='Colorado'), State(name='Hawaii'), State(name='Texas')
+ co.save(), hi.save(), tx.save()
+
# If a mapping is specified as a collection, all OGR fields that
# are not collections will be converted into them. For example,
# a Point column would be converted to MultiPoint. Other things being done
@@ -148,21 +165,26 @@ def test04_layermap_unique_multigeometry(self):
# appended to the geometry collection of the unique model. Thus,
# all of the various islands in Honolulu county will be in in one
# database record with a MULTIPOLYGON type.
- lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique='name', silent=True)
+ lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique='name', silent=True, strict=True)
lm.save()
# A reference that doesn't use the unique keyword; a new database record will
# created for each polygon.
- lm = LayerMapping(CountyFeat, co_shp, cofeat_mapping, transform=False, silent=True)
+ lm = LayerMapping(CountyFeat, co_shp, cofeat_mapping, transform=False, silent=True, strict=True)
lm.save()
# Dictionary to hold what's expected in the shapefile.
- exp = {'names' : ('Bexar', 'Galveston', 'Harris', 'Honolulu', 'Pueblo'),
- 'num' : (1, 2, 2, 19, 1), # Number of polygons for each.
- }
- for name, n in zip(exp['names'], exp['num']):
- c = County.objects.get(name=name) # Should only be one record.
+ names = ('Bexar', 'Galveston', 'Harris', 'Honolulu', 'Pueblo')
+ nums = (1, 2, 1, 19, 1) # Number of polygons for each.
+ states = ('Texas', 'Texas', 'Texas', 'Hawaii', 'Colorado')
+
+ for name, n, st in zip(names, nums, states):
+ # Should only be one record b/c of `unique` keyword.
+ c = County.objects.get(name=name)
self.assertEqual(n, len(c.mpoly))
+ self.assertEqual(st, c.state.name) # Checking ForeignKey mapping.
+
+ # Multiple records because `unique` was not set.
qs = CountyFeat.objects.filter(name=name)
self.assertEqual(n, qs.count())
Oops, something went wrong.

0 comments on commit b11172e

Please sign in to comment.