Changeset 6687
- Timestamp:
- 11/17/07 15:57:12 (8 months ago)
- Files:
-
- django/branches/gis/django/contrib/gis/tests/geoapp/tests.py (modified) (2 diffs)
- django/branches/gis/django/contrib/gis/tests/__init__.py (modified) (6 diffs)
- django/branches/gis/django/contrib/gis/tests/layermap (added)
- django/branches/gis/django/contrib/gis/tests/layermap/cities (added)
- django/branches/gis/django/contrib/gis/tests/layermap/cities/cities.dbf (added)
- django/branches/gis/django/contrib/gis/tests/layermap/cities/cities.prj (added)
- django/branches/gis/django/contrib/gis/tests/layermap/cities/cities.shp (added)
- django/branches/gis/django/contrib/gis/tests/layermap/cities/cities.shx (added)
- django/branches/gis/django/contrib/gis/tests/layermap/__init__.py (added)
- django/branches/gis/django/contrib/gis/tests/layermap/interstates (added)
- django/branches/gis/django/contrib/gis/tests/layermap/interstates/interstates.dbf (added)
- django/branches/gis/django/contrib/gis/tests/layermap/interstates/interstates.prj (added)
- django/branches/gis/django/contrib/gis/tests/layermap/interstates/interstates.shp (added)
- django/branches/gis/django/contrib/gis/tests/layermap/interstates/interstates.shx (added)
- django/branches/gis/django/contrib/gis/tests/layermap/models.py (added)
- django/branches/gis/django/contrib/gis/tests/layermap/tests.py (added)
- django/branches/gis/django/contrib/gis/utils/layermapping.py (modified) (7 diffs)
- django/branches/gis/django/contrib/gis/utils/ogrinfo.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/gis/django/contrib/gis/tests/geoapp/tests.py
r6596 r6687 78 78 79 79 # Creating a State object using a built Polygon 80 ply = Polygon(shell .clone(), inner.clone())80 ply = Polygon(shell, inner) 81 81 nullstate = State(name='NullState', poly=ply) 82 82 self.assertEqual(4326, nullstate.poly.srid) # SRID auto-set from None … … 95 95 # Changing the interior ring on the poly attribute. 96 96 new_inner = LinearRing((30, 30), (30, 70), (70, 70), (70, 30), (30, 30)) 97 n ullstate.poly[1] = new_inner.clone()97 ns.poly[1] = new_inner 98 98 ply[1] = new_inner 99 self.assertEqual(4326, n ullstate.poly.srid)100 n ullstate.save()99 self.assertEqual(4326, ns.poly.srid) 100 ns.save() 101 101 self.assertEqual(ply, State.objects.get(name='NullState').poly) 102 n ullstate.delete()102 ns.delete() 103 103 104 104 @no_oracle # Oracle does not support KML. django/branches/gis/django/contrib/gis/tests/__init__.py
r6527 r6687 3 3 from unittest import TestSuite, TextTestRunner 4 4 from django.contrib.gis.gdal import HAS_GDAL 5 from django.contrib.gis.tests.utils import mysql 5 try: 6 from django.contrib.gis.tests.utils import mysql 7 except: 8 mysql = False 9 10 # Tests that require use of a spatial database (e.g., creation of models) 11 test_models = ['geoapp'] 6 12 7 13 # Tests that do not require setting up and tearing down a spatial database. … … 11 17 ] 12 18 if HAS_GDAL: 19 test_models += ['layermap'] 13 20 test_suite_names += [ 14 21 'test_gdal_driver', … … 21 28 else: 22 29 print >>sys.stderr, "GDAL not available - no GDAL tests will be run." 23 24 test_models = ['geoapp']25 30 26 31 def suite(): … … 41 46 42 47 In order to run geographic model tests the DATABASE_USER will require 43 superuser priviliges. To accomplish this outside the `postgres` user,44 create your own PostgreSQL database as a user:48 superuser priviliges. To accomplish this outside the `postgres` user, 49 create your own PostgreSQL database as a user: 45 50 (1) Initialize database: `initdb -D /path/to/user/db` 46 51 (2) If there's already a Postgres instance on the machine, it will need … … 50 55 51 56 On Windows platforms simply use the pgAdmin III utility to add superuser 52 priviliges to your database user.57 priviliges to your database user. 53 58 54 59 Make sure your settings.py matches the settings of the user database. 55 For example, set the same port number (`DATABASE_PORT=5433`).56 DATABASE_NAME or TEST_DATABSE_NAME must be set, along with DATABASE_USER.60 For example, set the same port number (`DATABASE_PORT=5433`). 61 DATABASE_NAME or TEST_DATABSE_NAME must be set, along with DATABASE_USER. 57 62 58 63 In settings.py set TEST_RUNNER='django.contrib.gis.tests.run_tests'. 59 64 60 65 Finally, this assumes that the PostGIS SQL files (lwpostgis.sql and 61 spatial_ref_sys.sql) are installed in the directory specified by62 `pg_config --sharedir` (and defaults to /usr/local/share if that fails).63 This behavior is overridden if `POSTGIS_SQL_PATH` is in your settings.66 spatial_ref_sys.sql) are installed in the directory specified by 67 `pg_config --sharedir` (and defaults to /usr/local/share if that fails). 68 This behavior is overridden if `POSTGIS_SQL_PATH` is in your settings. 64 69 65 Windows users should use the POSTGIS_SQL_PATHbecause the output66 of `pg_config` uses paths like 'C:/PROGRA~1/POSTGR~1/..'.70 Windows users should set POSTGIS_SQL_PATH manually because the output 71 of `pg_config` uses paths like 'C:/PROGRA~1/POSTGR~1/..'. 67 72 68 73 Finally, the tests may be run by invoking `./manage.py test`. … … 94 99 test_suite.addTest(tsuite.suite()) 95 100 96 # Resetting the loaded flag to take into account what we appended to the INSTALLED_APPS 97 # (since this routine is invoked through django/core/management, it caches the apps, 98 # this ensures that syncdb will see our appended models) 101 # Resetting the loaded flag to take into account what we appended to 102 # the INSTALLED_APPS (since this routine is invoked through 103 # django/core/management, it caches the apps; this ensures that syncdb 104 # will see our appended models) 99 105 from django.db.models import loading 100 106 loading._loaded = False django/branches/gis/django/contrib/gis/utils/layermapping.py
r6524 r6687 1 1 # LayerMapping -- A Django Model/OGR Layer Mapping Utility 2 2 """ 3 The LayerMapping class provides a way to map the contents of OGR3 The LayerMapping class provides a way to map the contents of OGR 4 4 vector files (e.g. SHP files) to Geographic-enabled Django models. 5 5 6 This grew out of my personal needs, specifically the code repetition6 This grew out of my personal needs, specifically the code repetition 7 7 that went into pulling geometries and fields out of an OGR layer, 8 8 converting to another coordinate system (e.g. WGS84), and then inserting 9 into a Geo graphicDjango model.10 11 This utility is still in early stages of development, so its usage9 into a GeoDjango model. 10 11 This utility is still in early stages of development, so its usage 12 12 is subject to change -- please report any bugs. 13 13 14 TODO: Unit tests and documentation. 15 16 Requirements: OGR C Library (from GDAL) required. 17 18 Usage: 14 Requirements: OGR C Library (from GDAL) required. 15 16 Usage: 19 17 lm = LayerMapping(model, source_file, mapping) where, 20 18 … … 33 31 geometry type, e.g. 'POINT', 'LINESTRING', 'POLYGON'. 34 32 35 Keyword Args:33 Keyword Args: 36 34 layer: 37 35 The index of the layer to use from the Data Source (defaults to 0) … … 46 44 For example, 'latin-1', 'utf-8', and 'cp437' are all valid 47 45 encoding parameters. 46 47 check: 48 By default, LayerMapping increments through each feature in the 49 layer to ensure that it is compatible with the given model and 50 mapping. Setting this keyword to False, disables this action, 51 which will speed up execution time for very large files. 52 53 silent: 54 By default, non-fatal error notifications are printed to stdout; this 55 keyword may be set in order to disable these notifications. 56 57 strict: 58 Setting this keyword to True will instruct the save() method to 59 cease execution on the first error encountered. 60 61 transaction_mode: 62 May be 'commit_on_success' (default) or 'autocommit'. 63 64 transform: 65 Setting this to False will disable all coordinate transformations. 48 66 49 67 Example: … … 92 110 93 111 LayerMapping just transformed the three geometries from the SHP file from their 94 source spatial reference system (WGS84) to the spatial reference system of 95 the GeoDjango model (NAD83). If no spatial reference system is defined for 96 the layer, use the `source_srs` keyword with a SpatialReference object to 97 specify one. Further, data is selectively imported from the given data source 98 fields into the model fields. 112 source spatial reference system (WGS84) to the spatial reference system of 113 the GeoDjango model (NAD83). If no spatial reference system is defined for 114 the layer, use the `source_srs` keyword with a SpatialReference object to 115 specify one. 99 116 """ 100 from types import StringType, TupleType 101 from datetime import datetime 117 from datetime import date, datetime 118 from decimal import Decimal 119 from django.core.exceptions import ObjectDoesNotExist 120 from django.db import connection, transaction 121 from django.db.models.fields.related import ForeignKey 102 122 from django.contrib.gis.db.backend import SPATIAL_BACKEND 103 from django.contrib.gis.gdal import \ 104 OGRGeometry, OGRGeomType, SpatialReference, CoordTransform, \ 105 DataSource, OGRException 106 from django.contrib.gis.gdal.field import Field, OFTInteger, OFTReal, OFTString, OFTDateTime 123 from django.contrib.gis.gdal import CoordTransform, DataSource, \ 124 OGRException, OGRGeometry, OGRGeomType, SpatialReference 125 from django.contrib.gis.gdal.field import OFTDate, OFTDateTime, OFTInteger, OFTReal, OFTString, OFTTime 107 126 from django.contrib.gis.models import GeometryColumns, SpatialRefSys 108 from django.db import connection, transaction 109 from django.core.exceptions import ObjectDoesNotExist 110 111 # A mapping of given geometry types to their OGR integer type. 112 ogc_types = {'POINT' : OGRGeomType('Point'), 113 'LINESTRING' : OGRGeomType('LineString'), 114 'POLYGON' : OGRGeomType('Polygon'), 115 'MULTIPOINT' : OGRGeomType('MultiPoint'), 116 'MULTILINESTRING' : OGRGeomType('MultiLineString'), 117 'MULTIPOLYGON' : OGRGeomType('MultiPolygon'), 118 'GEOMETRYCOLLECTION' : OGRGeomType('GeometryCollection'), 119 } 120 121 # The django.contrib.gis model types. 122 gis_fields = {'PointField' : 'POINT', 123 'LineStringField': 'LINESTRING', 124 'PolygonField': 'POLYGON', 125 'MultiPointField' : 'MULTIPOINT', 126 'MultiLineStringField' : 'MULTILINESTRING', 127 'MultiPolygonField' : 'MULTIPOLYGON', 128 } 129 130 # Acceptable 'base' types for a multi-geometry type. 131 multi_types = {'POINT' : OGRGeomType('MultiPoint'), 132 'LINESTRING' : OGRGeomType('MultiLineString'), 133 'POLYGON' : OGRGeomType('MultiPolygon'), 134 } 135 136 def map_foreign_key(django_field): 137 from django.db.models.fields.related import ForeignKey 138 139 if not django_field.__class__ is ForeignKey: 140 return django_field.__class__.__name__ 141 142 143 rf=django_field.rel.get_related_field() 144 145 return rf.get_internal_type() 146 147 # The acceptable Django field types that map to OGR fields. 148 field_types = { 149 'AutoField' : OFTInteger, 150 'IntegerField' : OFTInteger, 151 'FloatField' : OFTReal, 152 'DateTimeField' : OFTDateTime, 153 'DecimalField' : OFTReal, 154 'CharField' : OFTString, 155 'SmallIntegerField' : OFTInteger, 156 'PositiveSmallIntegerField' : OFTInteger, 157 } 158 159 def make_multi(geom_name, model_type): 160 "Determines whether the geometry should be made into a GeometryCollection." 161 if (geom_name in multi_types) and (model_type.startswith('Multi')): 162 return True 163 else: 164 return False 165 166 def check_feature(feat, model_fields, mapping): 167 "Checks the OGR layer feature." 168 169 HAS_GEO = False 170 171 # Incrementing through each model_field & ogr_field in the given mapping. 172 for model_field, ogr_field in mapping.items(): 173 174 # Making sure the given mapping model field is in the given model fields. 175 if model_field in model_fields: 176 model_type = model_fields[model_field] 177 elif model_field[:-3] in model_fields: #foreign key 178 model_type = model_fields[model_field[:-3]] 179 else: 180 raise Exception('Given mapping field "%s" not in given Model fields!' % model_field) 181 182 ### Handling if we get a geometry in the Field ### 183 if ogr_field in ogc_types: 184 # At this time, no more than one geographic field per model =( 185 if HAS_GEO: 186 raise Exception('More than one geographic field in mapping not allowed (yet).') 187 else: 188 HAS_GEO = ogr_field 189 190 # Making sure this geometry field type is a valid Django GIS field. 191 if not model_type in gis_fields: 192 raise Exception('Unknown Django GIS field type "%s"' % model_type) 193 194 # Getting the OGRGeometry, it's type (an integer) and it's name (a string) 195 geom = feat.geom 196 gtype = geom.geom_type 197 gname = geom.geom_name 198 199 if make_multi(gname, model_type): 200 # Do we have to 'upsample' into a Geometry Collection? 201 pass 202 elif gtype == ogc_types[gis_fields[model_type]]: 203 # The geometry type otherwise was expected 204 pass 205 else: 206 raise Exception('Invalid mapping geometry; model has %s, feature has %s' % (model_type, gtype)) 207 208 ## Handling other fields 209 else: 210 # Making sure the model field is 211 if not model_type in field_types: 212 raise Exception('Django field type "%s" has no OGR mapping (yet).' % model_type) 213 214 # Otherwise, we've got an OGR Field. Making sure that an 215 # index exists for the mapping OGR field. 216 try: 217 fi = feat.index(ogr_field) 218 except: 219 raise Exception('Given mapping OGR field "%s" not in given OGR layer feature!' % ogr_field) 220 221 def check_layer(layer, fields, mapping): 222 "Checks the OGR layer by incrementing through and checking each feature." 223 # Incrementing through each feature in the layer. 224 for feat in layer: 225 check_feature(feat, fields, mapping) 226 227 def check_srs(layer, source_srs): 228 "Checks the compatibility of the given spatial reference object." 229 if isinstance(source_srs, SpatialReference): 230 sr = source_srs 231 elif isinstance(source_srs, SpatialRefSys): 232 sr = source_srs.srs 233 elif isinstance(source_srs, (int, str)): 234 sr = SpatialReference(source_srs) 235 else: 236 sr = layer.srs 237 if not sr: 238 raise Exception('No source reference system defined.') 239 else: 240 return sr 241 242 class LayerMapping: 243 "A class that maps OGR Layers to Django Models." 244 245 def __init__(self, model, data, mapping, layer=0, source_srs=None, encoding=None): 127 128 # LayerMapping exceptions. 129 class LayerMapError(Exception): pass 130 class InvalidString(LayerMapError): pass 131 class InvalidDecimal(LayerMapError): pass 132 133 class LayerMapping(object): 134 "A class that maps OGR Layers to GeoDjango Models." 135 136 # A mapping of given geometry types to their OGR integer type. 137 OGC_TYPES = {'POINT' : OGRGeomType('Point'), 138 'LINESTRING' : OGRGeomType('LineString'), 139 'POLYGON' : OGRGeomType('Polygon'), 140 'MULTIPOINT' : OGRGeomType('MultiPoint'), 141 'MULTILINESTRING' : OGRGeomType('MultiLineString'), 142 'MULTIPOLYGON' : OGRGeomType('MultiPolygon'), 143 'GEOMETRYCOLLECTION' : OGRGeomType('GeometryCollection'), 144 } 145 146 # The django.contrib.gis model types. 147 GIS_FIELDS = {'PointField' : 'POINT', 148 'LineStringField': 'LINESTRING', 149 'PolygonField': 'POLYGON', 150 'MultiPointField' : 'MULTIPOINT', 151 'MultiLineStringField' : 'MULTILINESTRING', 152 'MultiPolygonField' : 'MULTIPOLYGON', 153 'GeometryCollectionField' : 'GEOMETRYCOLLECTION', 154 } 155 156 # Acceptable 'base' types for a multi-geometry type. 157 MULTI_TYPES = {'POINT' : OGRGeomType('MultiPoint'), 158 'LINESTRING' : OGRGeomType('MultiLineString'), 159 'POLYGON' : OGRGeomType('MultiPolygon'), 160 } 161 162 # The acceptable Django field types that map to OGR fields. 163 FIELD_TYPES = { 164 'AutoField' : OFTInteger, 165 'IntegerField' : OFTInteger, 166 'FloatField' : OFTReal, 167 'DateField' : OFTDate, 168 'DateTimeField' : OFTDateTime, 169 'TimeField' : OFTTime, 170 'DecimalField' : OFTReal, 171 'CharField' : OFTString, 172 'TextField' : OFTString, 173 'SmallIntegerField' : OFTInteger, 174 'PositiveSmallIntegerField' : OFTInteger, 175 } 176 177 # The acceptable transaction modes. 178 TRANSACTION_MODES = {'autocommit' : transaction.autocommit, 179 'commit_on_success' : transaction.commit_on_success, 180 } 181 182 def __init__(self, model, data, mapping, layer=0, 183 source_srs=None, encoding=None, check=True, 184 progress=False, interval=1000, strict=False, silent=False, 185 transaction_mode='commit_on_success', transform=True): 246 186 "Takes the Django model, the data source, and the mapping (dictionary)" 247 187 248 188 # Getting the field names and types from the model 249 fields = dict((f.name, map_foreign_key(f)) for f in model._meta.fields) 189 self.fields = dict((f.name, self.map_foreign_key(f)) for f in model._meta.fields) 190 self.field_classes = dict((f.name, f) for f in model._meta.fields) 191 250 192 # Getting the DataSource and its Layer 251 193 if isinstance(data, basestring): … … 255 197 self.layer = self.ds[layer] 256 198 199 # Setting the mapping 200 self.mapping = mapping 201 202 # Setting the model, and getting the geometry column associated 203 # with the model (an exception will be raised if there is no 204 # geometry column). 205 self.model = model 206 self.geo_col = self.geometry_column() 207 208 # Checking the source spatial reference system, and getting 209 # the coordinate transformation object (unless the `transform` 210 # keyword is set to False) 211 self.source_srs = self.check_srs(source_srs) 212 self.transform = transform and self.coord_transform() 213 257 214 # Checking the layer -- intitialization of the object will fail if 258 # things don't check out before hand. 259 check_layer(self.layer, fields, mapping) 260 261 # Since the layer checked out, setting the fields and the mapping. 262 self.fields = fields 263 self.mapping = mapping 264 self.model = model 265 self.source_srs = check_srs(self.layer, source_srs) 215 # things don't check out before hand. This may be time-consuming, 216 # and disabled by setting the `check` keyword to False. 217 if check: self.check_layer() 218 219 # The silent, strict, progress, and interval flags. 220 self.silent = silent 221 self.strict = strict 222 self.progress = progress 223 self.interval = interval 266 224 267 225 # Setting the encoding for OFTString fields, if specified. 268 226 if encoding: 269 227 # Making sure the encoding exists, if not a LookupError 270 # exception will be thrown.228 # exception will be thrown. 271 229 from codecs import lookup 272 230 lookup(encoding) … … 275 233 self.encoding = None 276 234 277 # Either the import will work, or it won't be committed. 278 @transaction.commit_on_success 279 def save(self, verbose=False): 280 "Runs the layer mapping on the given SHP file, and saves to the database." 281 235 # Setting the transaction decorator with the function in the 236 # transaction modes dictionary. 237 if transaction_mode in self.TRANSACTION_MODES: 238 self.transaction_decorator = self.TRANSACTION_MODES[transaction_mode] 239 self.transaction_mode = transaction_mode 240 else: 241 raise LayerMapError('Unrecognized transaction mode: %s' % transaction_mode) 242 243 def check_feature(self, feat): 244 "Checks the OGR feature against the model fields and mapping." 245 HAS_GEO = False 246 247 # Incrementing through each model_field & ogr_field in the given mapping. 248 for model_field, ogr_field in self.mapping.items(): 249 # Making sure the given mapping model field is in the given model fields. 250 if model_field in self.fields: 251 model_type = self.fields[model_field] 252 elif model_field[:-3] in self.fields: #foreign key 253 model_type = self.fields[model_field[:-3]] 254 else: 255 raise LayerMapError('Given mapping field "%s" not in given Model fields!' % model_field) 256 257 ### Handling if we get a geometry in the Field ### 258 if ogr_field in self.OGC_TYPES: 259 # At this time, no more than one geographic field per model =( 260 if HAS_GEO: 261 raise LayerMapError('More than one geographic field in mapping not allowed (yet).') 262 else: 263 HAS_GEO = ogr_field 264 265 # Making sure this geometry field type is a valid Django GIS field. 266 if not model_type in self.GIS_FIELDS: 267 raise LayerMapError('Unknown Django GIS field type "%s"' % model_type) 268 269 # Getting the OGRGeometry, it's type (an integer) and it's name (a string) 270 geom = feat.geom 271 gtype = geom.geom_type 272 gname = geom.geom_name 273 274 if self.make_multi(gname, model_type): 275 # Do we have to 'upsample' into a Geometry Collection? 276 pass 277 elif gtype == self.OGC_TYPES[self.GIS_FIELDS[model_type]]: 278 # The geometry type otherwise was expected 279 pass 280 else: 281 raise LayerMapError('Invalid mapping geometry; model has %s, feature has %s' % (model_type, gtype)) 282 ### Handling other fields ### 283 else: 284 # Making sure the model field is supported. 285 if not model_type in self.FIELD_TYPES: 286 raise LayerMapError('Django field type "%s" has no OGR mapping (yet).' % model_type) 287 288 # Otherwise, we've got an OGR Field. Making sure that an 289 # index exists for the mapping OGR field. 290 try: 291 fi = feat.index(ogr_field) 292 except: 293 raise LayerMapError('Given mapping OGR field "%s" not in given OGR layer feature!' % ogr_field) 294 295 def check_layer(self): 296 "Checks every feature in this object's layer." 297 for feat in self.layer: 298 self.check_feature(feat) 299 300 def check_srs(self, source_srs): 301 "Checks the compatibility of the given spatial reference object." 302 if isinstance(source_srs, SpatialReference): 303 sr = source_srs 304 elif isinstance(source_srs, SpatialRefSys): 305 sr = source_srs.srs 306 elif isinstance(source_srs, (int, str)): 307 sr = SpatialReference(source_srs) 308 else: 309 # Otherwise just pulling the SpatialReference from the layer 310 sr = self.layer.srs 311 312 if not sr: 313 raise LayerMapError('No source reference system defined.') 314 else: 315 return sr 316 317 def coord_transform(self): 318 "Returns the coordinate transformation object." 319 try: 320 # Getting the target spatial reference system 321 target_srs = SpatialRefSys.objects.get(srid=self.geo_col.srid).srs 322 323 # Creating the CoordTransform object 324 return CoordTransform(self.source_srs, target_srs) 325 except Exception, msg: 326 raise LayerMapError('Could not translate between the data source and model geometry: %s' % msg) 327 328 def feature_kwargs(self, feat): 329 "Returns the keyword arguments needed for saving a feature." 330 331 # The keyword arguments for model construction. 332 kwargs = {} 333 334 # The all_prepped flagged, will be set to False if there's a 335 # problem w/a ForeignKey that doesn't exist. 336 all_prepped = True 337 338 # Incrementing through each model field and OGR field in the 339 # dictionary mapping. 340 for model_field, ogr_field in self.mapping.items(): 341 is_fk = False 342 try: 343 model_type = self.fields[model_field] 344 except KeyError: #foreign key 345 # The -3 index is b/c foreign keys are appended w/'_id'. 346 model_type = self.fields[model_field[:-3]] 347 is_fk = True 348 349 if ogr_field in self.OGC_TYPES: 350 # Verify OGR geometry. 351 val = self.verify_geom(feat.geom, model_type) 352 else: 353 # Otherwise, verify OGR Field type. 354 val = self.verify_field(feat[ogr_field], model_field) 355 356 if is_fk: 357 # Handling if foreign key. 358 rel_obj = None 359 field_name = model_field[:-3] 360 try: 361 # FIXME: refactor to efficiently fetch FKs. 362 # Requires significant re-work. :-/ 363 rel = self.model._meta.get_field(field_name).rel 364 rel_obj = rel.to._default_manager.get(**{('%s__exact' % rel.field_name):val}) 365 except ObjectDoesNotExist: 366 all_prepped = False 367 368 kwargs[model_field[:-3]] = rel_obj 369 else: 370 kwargs[model_field] = val 371 372 return kwargs, all_prepped 373 374 def verify_field(self, fld, model_field): 375 """ 376 Verifies if the OGR Field contents are acceptable to the Django 377 model field. If they are, the verified value is returned, 378 otherwise the proper exception is raised. 379 """ 380 field_class = self.field_classes[model_field] 381 if isinstance(fld, OFTString): 382 if self.encoding: 383 # The encoding for OGR data sources may be specified here 384 # (e.g., 'cp437' for Census Bureau boundary files). 385 val = unicode(fld.value, self.encoding) 386 else: 387 val = fld.value 388 if len(val) > field_class.max_length: 389 raise InvalidString('%s model field maximum string length is %s, given %s characters.' % 390 (model_field, field_class.max_length, len(val))) 391 elif isinstance(fld, OFTReal): 392 try: 393 # Creating an instance of the Decimal value to use. 394 d = Decimal(str(fld.value)) 395 except: 396 raise InvalidDecimal('Could not construct decimal from: %s' % fld) 397 dtup = d.as_tuple() 398 if len(dtup[1]) > field_class.max_digits: 399 raise InvalidDecimal('More than the maximum # of digits encountered.') 400 elif len(dtup[1][dtup[2]:]) > field_class.decimal_places: 401 raise InvalidDecimal('More than the maximum # of decimal places encountered.') 402 val = d 403 else: 404 val = fld.value 405 return val 406 407 def verify_geom(self, geom, model_type): 408 "Verifies the geometry." 409 if self.make_multi(geom.geom_name, model_type): 410 # Constructing a multi-geometry type to contain the single geometry 411 multi_type = self.MULTI_TYPES[geom.geom_name] 412 g = OGRGeometry(multi_type) 413 g.add(geom) 414 else: 415 g = geom 416 417 # Transforming the geometry with our Coordinate Transformation object, 418 # but only if the class variable `transform` is set w/a CoordTransform 419 # object. 420 if self.transform: g.transform(self.transform) 421 422 # Returning the WKT of the geometry. 423 return g.wkt 424 425 def geometry_column(self): 426 "Returns the GeometryColumn model associated with the geographic column." 282 427 # Getting the GeometryColumn object. 283 428 try: … … 285 430 if SPATIAL_BACKEND == 'oracle': db_table = db_table.upper() 286 431 gc_kwargs = {GeometryColumns.table_name_col() : db_table} 287 geo_col = GeometryColumns.objects.get(**gc_kwargs) 288 except: 289 raise Exception('Geometry column does not exist. (did you run syncdb?)') 432 return GeometryColumns.objects.get(**gc_kwargs) 433 except Exception, msg: 434 raise LayerMapError('Geometry column does not exist for model. (did you run syncdb?):\n %s' % msg) 435 436 def make_multi(self, geom_name, model_type): 437 "Determines whether the geometry should be made into a GeometryCollection." 438 return (geom_name in self.MULTI_TYPES) and (model_type.startswith('Multi')) 439 440 def map_foreign_key(self, django_field): 441 "Handles fields within foreign keys for the given field." 442 if not django_field.__class__ is ForeignKey: 443 # Returning the field's class name. 444 return django_field.__class__.__name__ 445 else: 446 # Otherwise, getting the type of the related field's 447 # from the Foreign key. 448 rf = django_field.rel.get_related_field() 449 return rf.get_internal_type() 450 451 def save(self, verbose=False): 452 "Runs the layer mapping on the given SHP file, and saves to the database." 290 453 291 # Getting the coordinate system needed for transformation (with CoordTransform) 292 try: 293 # Getting the target spatial reference system 294 target_srs = SpatialRefSys.objects.get(srid=geo_col.srid).srs 295 296 # Creating the CoordTransform object 297 ct = CoordTransform(self.source_srs, target_srs) 298 except Exception, msg: 299 raise Exception('Could not translate between the data source and model geometry: %s' % msg) 300 301 for feat in self.layer: 302 # The keyword arguments for model construction 303 kwargs = {} 304 305 # Incrementing through each model field and the OGR field in the mapping 306 all_prepped = True 307 308 for model_field, ogr_field in self.mapping.items(): 309 is_fk = False 454 @self.transaction_decorator 455 def _save(): 456 num_feat = 0 457 num_saved = 0 458 459 for feat in self.layer: 460 num_feat += 1 461 # Getting the keyword arguments 310 462 try: 311 model_type = self.fields[model_field] 312 except KeyError: #foreign key 313 model_type = self.fields[model_field[:-3]] 314 is_fk = True 315 316 if ogr_field in ogc_types: 317 ## Getting the OGR geometry from the field 318 geom = feat.geom 319 320 if make_multi(geom.geom_name, model_type): 321 # Constructing a multi-geometry type to contain the single geometry 322 multi_type = multi_types[geom.geom_name] 323 g = OGRGeometry(multi_type) 324 g.add(geom) 463 kwargs, all_prepped = self.feature_kwargs(feat) 464 except LayerMapError, msg: 465 # Something borked the validation 466 if self.strict: raise 467 elif not self.silent: 468 print 'Ignoring Feature ID %s because: %s' % (feat.fid, msg) 469 else: 470 # Constructing the model using the constructed keyword args 471 if all_prepped: 472 m = self.model(**kwargs) 473 try: 474 m.save() 475 num_saved += 1 476 if verbose: print 'Saved: %s' % m 477 except SystemExit: 478 raise 479 except Exception, msg: 480 if self.transaction_mode == 'autocommit': 481 # Rolling back the transaction so that other model saves 482 # will work. 483 transaction.rollback_unless_managed() 484 if self.strict: 485 # Bailing out if the `strict` keyword is set. 486 if not self.silent: 487 print 'Failed to save the feature (id: %s) into the model with the keyword arguments:' % feat.fid 488 print kwargs 489 raise 490 elif not self.silent: 491 print 'Failed to save %s:\n %s\nContinuing' % (kwargs, msg) 325 492 else: 326 g = geom 327 328 # Transforming the geometry with our Coordinate Transformation object. 329 g.transform(ct) 330 331 # Updating the keyword args with the WKT of the transformed model. 332 val = g.wkt 333 else: 334 ## Otherwise, this is an OGR field type 335 fld = feat[ogr_field] 336 337 if isinstance(fld, OFTString) and self.encoding: 338 # The encoding for OGR data sources may be specified here 339 # (e.g., 'cp437' for Census Bureau boundary files). 340 val = unicode(fld.value, self.encoding) 341 else: 342 val = fld.value 343 344 if is_fk: 345 rel_obj = None 346 field_name = model_field[:-3] 347 try: 348 #FIXME: refactor to efficiently fetch FKs. 349 # Requires significant re-work. :-/ 350 rel = self.model._meta.get_field(field_name).rel 351 rel_obj = rel.to._default_manager.get(**{('%s__exact' % rel.field_name):val}) 352 except ObjectDoesNotExist: 353 all_prepped = False 354 355 kwargs[model_field[:-3]] = rel_obj 356 else: 357 kwargs[model_field] = val 358 359 # Constructing the model using the constructed keyword args 360 if all_prepped: 361 m = self.model(**kwargs) 362 363 # Saving the model 364 try: 365 if all_prepped: 366 m.save() 367 if verbose: print 'Saved: %s' % str(m) 368 else: 369 print "Skipping %s due to missing relation." % kwargs 370 except SystemExit: 371 raise 372 except Exception, e: 373 print "Failed to save %s\n Continuing" % kwargs 493 print 'Skipping %s due to missing relation.' % kwargs 494 495 # Printing progress information, if requested. 496 if self.progress and num_feat % self.interval == 0: 497 print 'Processed %d features, saved %d ...' % (num_feat, num_saved) 498 499 # Calling our defined function, which will use the specified 500 # trasaction mode. 501 _save() django/branches/gis/django/contrib/gis/utils/ogrinfo.py
r6423 r6687 34 34 width = max(*map(len,layer.fields)) 35 35 fmt = " %%%ss: %%s" % width 36 for i, feature in enumerate(layer[:num_features]):37 print "=== Feature %s" % i38 for f ieldin layer.fields:39 fld_typ = feature[field].__class__.__name__.replace('OFT', '')40 output = fmt % (f ield, fld_typ)41 val = feature.get(f ield)36 for j, feature in enumerate(layer[:num_features]): 37 print "=== Feature %s" % j 38 for fld_name in layer.fields: 39 type_name = feature[fld_name].type_name 40 output = fmt % (fld_name, type_name) 41 val = feature.get(fld_name) 42 42 if val: 43 43 if isinstance(val, str):
