Django

Code

Changeset 6980

Show
Ignore:
Timestamp:
12/29/07 01:48:08 (8 months ago)
Author:
jbronn
Message:

gis: LayerMapping: Added the unique keyword parameter and tests for the transform keyword and geometry collection conversion.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/gis/django/contrib/gis/tests/layermap/models.py

    r6708 r6980  
    11from django.contrib.gis.db import models 
     2 
     3class County(models.Model): 
     4    name = models.CharField(max_length=25) 
     5    mpoly = models.MultiPolygonField(srid=4269) # Multipolygon in NAD83 
     6    objects = models.GeoManager() 
     7 
     8class CountyFeat(models.Model): 
     9    name = models.CharField(max_length=25) 
     10    poly = models.PolygonField(srid=4269) 
     11    objects = models.GeoManager() 
    212 
    313class City(models.Model): 
     
    1424    path = models.LineStringField() 
    1525    objects = models.GeoManager() 
    16      
    17 # Mapping dictionary for the City model. 
     26 
     27# Mapping dictionaries for the models above. 
     28co_mapping = {'name' : 'Name', 
     29              'mpoly' : 'MULTIPOLYGON', # Will convert POLYGON features into MULTIPOLYGONS. 
     30              } 
     31 
     32cofeat_mapping = {'name' : 'Name', 
     33                  'poly' : 'POLYGON', 
     34                  } 
     35 
    1836city_mapping = {'name' : 'Name', 
    1937                'population' : 'Population', 
     
    2341                } 
    2442 
    25 # Mapping dictionary for the Interstate model. 
    2643inter_mapping = {'name' : 'Name', 
    2744                 'length' : 'Length', 
  • django/branches/gis/django/contrib/gis/tests/layermap/tests.py

    r6708 r6980  
    33from datetime import date 
    44from decimal import Decimal 
    5 from models import City, Interstate, city_mapping, inter_mapping 
     5from models import City, County, CountyFeat, Interstate, city_mapping, co_mapping, cofeat_mapping, inter_mapping 
    66from django.contrib.gis.utils.layermapping import LayerMapping, LayerMapError, InvalidDecimal 
    77from django.contrib.gis.gdal import DataSource 
     
    99shp_path = os.path.dirname(__file__) 
    1010city_shp = os.path.join(shp_path, 'cities/cities.shp') 
     11co_shp = os.path.join(shp_path, 'counties/counties.shp') 
    1112inter_shp = os.path.join(shp_path, 'interstates/interstates.shp') 
    1213 
     
    111112                self.assertAlmostEqual(p1[1], p2[1], 6) 
    112113 
     114    def test04_layermap_unique_multigeometry(self): 
     115        "Testing the `unique`, and `transform` keywords and geometry collection conversion." 
     116        # All the following should work. 
     117        try: 
     118            # Telling LayerMapping that we want no transformations performed on the data. 
     119            lm = LayerMapping(County, co_shp, co_mapping, transform=False) 
     120         
     121            # Specifying the source spatial reference system via the `source_srs` keyword. 
     122            lm = LayerMapping(County, co_shp, co_mapping, source_srs=4269) 
     123            lm = LayerMapping(County, co_shp, co_mapping, source_srs='NAD83') 
     124 
     125            # Unique may take tuple or string parameters. 
     126            for arg in ('name', ('name', 'mpoly')): 
     127                lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique=arg) 
     128        except: 
     129            self.fail('No exception should be raised for proper use of keywords.') 
     130             
     131        # Testing invalid params for the `unique` keyword. 
     132        for e, arg in ((TypeError, 5.0), (ValueError, 'foobar'), (ValueError, ('name', 'mpolygon'))): 
     133            self.assertRaises(e, LayerMapping, County, co_shp, co_mapping, transform=False, unique=arg) 
     134 
     135        # No source reference system defined in the shapefile, should raise an error. 
     136        self.assertRaises(LayerMapError, LayerMapping, County, co_shp, co_mapping) 
     137 
     138        # If a mapping is specified as a collection, all OGR fields that 
     139        # are not collections will be converted into them.  For example, 
     140        # a Point column would be converted to MultiPoint. Other things being done 
     141        # w/the keyword args: 
     142        #  `transform=False`: Specifies that no transform is to be done; this  
     143        #    has the effect of ignoring the spatial reference check (because the 
     144        #    county shapefile does not have implicit spatial reference info). 
     145        #  
     146        #  `unique='name'`: Creates models on the condition that they have  
     147        #    unique county names; geometries from each feature however will be 
     148        #    appended to the geometry collection of the unique model.  Thus, 
     149        #    all of the various islands in Honolulu county will be in in one 
     150        #    database record with a MULTIPOLYGON type. 
     151        lm = LayerMapping(County, co_shp, co_mapping, transform=False, unique='name', silent=True) 
     152        lm.save() 
     153 
     154        # A reference that doesn't use the unique keyword; a new database record will 
     155        # created for each polygon. 
     156        lm = LayerMapping(CountyFeat, co_shp, cofeat_mapping, transform=False, silent=True) 
     157        lm.save() 
     158 
     159        # Dictionary to hold what's expected in the shapefile. 
     160        exp = {'names' : ('Bexar', 'Galveston', 'Harris', 'Honolulu', 'Pueblo'), 
     161               'num' : (1, 2, 2, 19, 1), # Number of polygons for each. 
     162               } 
     163        for name, n in zip(exp['names'], exp['num']): 
     164            c = County.objects.get(name=name) # Should only be one record. 
     165            self.assertEqual(n, len(c.mpoly)) 
     166            qs = CountyFeat.objects.filter(name=name) 
     167            self.assertEqual(n, qs.count()) 
     168             
    113169def suite(): 
    114170    s = unittest.TestSuite() 
  • django/branches/gis/django/contrib/gis/utils/layermapping.py

    r6708 r6980  
    6464  transform: 
    6565   Setting this to False will disable all coordinate transformations.   
     66 
     67  unique: 
     68   Setting this to the name, or a tuple of names, from the given 
     69   model will create models unique only to the given name(s). 
     70   Geometries will from each feature will be added into the collection 
     71   associated with the unique model.  Forces transaction mode to 
     72   be 'autocommit'. 
    6673 
    6774Example: 
     
    183190                 source_srs=None, encoding=None, check=True,  
    184191                 progress=False, interval=1000, strict=False, silent=False, 
    185                  transaction_mode='commit_on_success', transform=True): 
     192                 transaction_mode='commit_on_success', transform=True, 
     193                 unique=False): 
    186194        "Takes the Django model, the data source, and the mapping (dictionary)" 
    187195 
     
    209217        # the coordinate transformation object (unless the `transform` 
    210218        # keyword is set to False) 
    211         self.source_srs = self.check_srs(source_srs) 
    212         self.transform = transform and self.coord_transform() 
     219        if transform: 
     220            self.source_srs = self.check_srs(source_srs) 
     221            self.transform = self.coord_transform() 
     222        else: 
     223            self.transform = transform 
    213224 
    214225        # Checking the layer -- intitialization of the object will fail if 
     
    232243        else: 
    233244            self.encoding = None 
     245 
     246        if unique: 
     247            self.check_unique(unique) 
     248            transaction_mode = 'autocommit' # Has to be set to autocommit. 
     249            self.unique = unique 
     250        else: 
     251            self.unique = None 
    234252 
    235253        # Setting the transaction decorator with the function in the  
     
    314332        else: 
    315333            return sr 
     334 
     335    def check_unique(self, unique): 
     336        "Checks the `unique` keyword parameter -- may be a sequence or string." 
     337        # Getting the geometry field; only the first encountered GeometryField 
     338        # will be used. 
     339        self.geom_field = False 
     340        for model_field, ogr_fld in self.mapping.items(): 
     341            if ogr_fld in self.OGC_TYPES: 
     342                self.geom_field = model_field 
     343                break 
     344 
     345        if isinstance(unique, (list, tuple)): 
     346            # List of fields to determine uniqueness with 
     347            for attr in unique:  
     348                if not attr in self.mapping: raise ValueError 
     349        elif isinstance(unique, basestring): 
     350            # Only a single field passed in. 
     351            if unique not in self.mapping: raise ValueError 
     352        else: 
     353            raise TypeError('Unique keyword argument must be set with a tuple, list, or string.') 
    316354 
    317355    def coord_transform(self): 
     
    371409             
    372410        return kwargs, all_prepped 
     411 
     412    def unique_kwargs(self, kwargs): 
     413        """ 
     414        Given the feature keyword arguments (from `feature_kwargs`) this routine 
     415        will construct and return the uniqueness keyword arguments -- a subset 
     416        of the feature kwargs. 
     417        """ 
     418        if isinstance(self.unique, basestring): 
     419            return {self.unique : kwargs[self.unique]} 
     420        else: 
     421            return dict((fld, kwargs[fld]) for fld in self.unique) 
    373422 
    374423    def verify_field(self, fld, model_field): 
     
    486535                    # Constructing the model using the keyword args 
    487536                    if all_prepped: 
    488                         m = self.model(**kwargs) 
     537                        if self.unique: 
     538                            # If we want unique models on a particular field, handle the 
     539                            # geometry appropriately. 
     540                            try: 
     541                                # Getting the keyword arguments and retrieving 
     542                                # the unique model. 
     543                                u_kwargs = self.unique_kwargs(kwargs) 
     544                                m = self.model.objects.get(**u_kwargs) 
     545 
     546                                # Getting the geometry (in OGR form), creating  
     547                                # one from the kwargs WKT, adding in additional  
     548                                # geometries, and update the attribute with the  
     549                                # just-updated geometry WKT. 
     550                                geom = getattr(m, self.geom_field).ogr 
     551                                new = OGRGeometry(kwargs[self.geom_field]) 
     552                                for g in new: geom.add(g)  
     553                                setattr(m, self.geom_field, geom.wkt) 
     554                            except ObjectDoesNotExist: 
     555                                # No unique model exists yet, create. 
     556                                m = self.model(**kwargs) 
     557                        else: 
     558                            m = self.model(**kwargs) 
     559 
    489560                        try: 
     561                            # Attempting to save. 
    490562                            m.save() 
    491563                            num_saved += 1