app/django/contrib/gis/utils/ogrinspect.py
changeset 323 ff1a9aa48cfd
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
       
     1 """
       
     2 This module is for inspecting OGR data sources and generating either
       
     3 models for GeoDjango and/or mapping dictionaries for use with the
       
     4 `LayerMapping` utility.
       
     5 
       
     6 Author: Travis Pinney, Dane Springmeyer, & Justin Bronn
       
     7 """
       
     8 from itertools import izip
       
     9 # Requires GDAL to use.
       
    10 from django.contrib.gis.gdal import DataSource
       
    11 from django.contrib.gis.gdal.field import OFTDate, OFTDateTime, OFTInteger, OFTReal, OFTString, OFTTime
       
    12 
       
    13 def mapping(data_source, geom_name='geom', layer_key=0, multi_geom=False):
       
    14     """
       
    15     Given a DataSource, generates a dictionary that may be used 
       
    16     for invoking the LayerMapping utility.
       
    17 
       
    18     Keyword Arguments:
       
    19      `geom_name` => The name of the geometry field to use for the model.
       
    20      
       
    21      `layer_key` => The key for specifying which layer in the DataSource to use;
       
    22        defaults to 0 (the first layer).  May be an integer index or a string
       
    23        identifier for the layer.
       
    24 
       
    25      `multi_geom` => Boolean (default: False) - specify as multigeometry.
       
    26     """
       
    27     if isinstance(data_source, basestring):
       
    28         # Instantiating the DataSource from the string.
       
    29         data_source = DataSource(data_source)
       
    30     elif isinstance(data_source, DataSource):
       
    31         pass
       
    32     else:
       
    33         raise TypeError('Data source parameter must be a string or a DataSource object.')
       
    34     
       
    35     # Creating the dictionary.
       
    36     _mapping = {}
       
    37 
       
    38     # Generating the field name for each field in the layer.
       
    39     for field in data_source[layer_key].fields:
       
    40         mfield = field.lower()
       
    41         if mfield[-1:] == '_': mfield += 'field'
       
    42         _mapping[mfield] = field
       
    43     gtype = data_source[layer_key].geom_type
       
    44     if multi_geom and gtype.num in (1, 2, 3): prefix = 'MULTI'
       
    45     else: prefix = ''
       
    46     _mapping[geom_name] = prefix + str(gtype).upper()
       
    47     return _mapping
       
    48 
       
    49 def ogrinspect(*args, **kwargs):
       
    50     """
       
    51     Given a data source (either a string or a DataSource object) and a string
       
    52     model name this function will generate a GeoDjango model.
       
    53 
       
    54     Usage:
       
    55     
       
    56     >>> from django.contrib.gis.utils import ogrinspect
       
    57     >>> ogrinspect('/path/to/shapefile.shp','NewModel')
       
    58     
       
    59     ...will print model definition to stout
       
    60     
       
    61     or put this in a python script and use to redirect the output to a new
       
    62     model like:
       
    63     
       
    64     $ python generate_model.py > myapp/models.py
       
    65     
       
    66     # generate_model.py 
       
    67     from django.contrib.gis.utils import ogrinspect
       
    68     shp_file = 'data/mapping_hacks/world_borders.shp'
       
    69     model_name = 'WorldBorders'
       
    70 
       
    71     print ogrinspect(shp_file, model_name, multi_geom=True, srid=4326,
       
    72                      geom_name='shapes', blank=True)
       
    73                      
       
    74     Required Arguments
       
    75      `datasource` => string or DataSource object to file pointer
       
    76      
       
    77      `model name` => string of name of new model class to create
       
    78       
       
    79     Optional Keyword Arguments
       
    80      `geom_name` => For specifying the model name for the Geometry Field. 
       
    81        Otherwise will default to `geom`
       
    82 
       
    83      `layer_key` => The key for specifying which layer in the DataSource to use;
       
    84        defaults to 0 (the first layer).  May be an integer index or a string
       
    85        identifier for the layer.
       
    86 
       
    87      `srid` => The SRID to use for the Geometry Field.  If it can be determined,
       
    88        the SRID of the datasource is used.
       
    89       
       
    90      `multi_geom` => Boolean (default: False) - specify as multigeometry.
       
    91      
       
    92      `name_field` => String - specifies a field name to return for the
       
    93        `__unicode__` function (which will be generated if specified).
       
    94      
       
    95      `imports` => Boolean (default: True) - set to False to omit the 
       
    96        `from django.contrib.gis.db import models` code from the 
       
    97        autogenerated models thus avoiding duplicated imports when building
       
    98        more than one model by batching ogrinspect()
       
    99      
       
   100      `decimal` => Boolean or sequence (default: False).  When set to True
       
   101        all generated model fields corresponding to the `OFTReal` type will
       
   102        be `DecimalField` instead of `FloatField`.  A sequence of specific
       
   103        field names to generate as `DecimalField` may also be used.
       
   104 
       
   105      `blank` => Boolean or sequence (default: False).  When set to True all
       
   106        generated model fields will have `blank=True`.  If the user wants to 
       
   107        give specific fields to have blank, then a list/tuple of OGR field
       
   108        names may be used.
       
   109 
       
   110      `null` => Boolean (default: False) - When set to True all generated
       
   111        model fields will have `null=True`.  If the user wants to specify
       
   112        give specific fields to have null, then a list/tuple of OGR field
       
   113        names may be used.
       
   114      
       
   115     Note: This routine calls the _ogrinspect() helper to do the heavy lifting.
       
   116     """
       
   117     return '\n'.join(s for s in _ogrinspect(*args, **kwargs))
       
   118 
       
   119 def _ogrinspect(data_source, model_name, geom_name='geom', layer_key=0, srid=None,
       
   120                 multi_geom=False, name_field=None, imports=True, 
       
   121                 decimal=False, blank=False, null=False):
       
   122     """
       
   123     Helper routine for `ogrinspect` that generates GeoDjango models corresponding
       
   124     to the given data source.  See the `ogrinspect` docstring for more details.
       
   125     """
       
   126     # Getting the DataSource
       
   127     if isinstance(data_source, str):
       
   128         data_source = DataSource(data_source)
       
   129     elif isinstance(data_source, DataSource):
       
   130         pass
       
   131     else:
       
   132         raise TypeError('Data source parameter must be a string or a DataSource object.')
       
   133 
       
   134     # Getting the layer corresponding to the layer key and getting
       
   135     # a string listing of all OGR fields in the Layer.
       
   136     layer = data_source[layer_key]
       
   137     ogr_fields = layer.fields
       
   138 
       
   139     # Creating lists from the `null`, `blank`, and `decimal`
       
   140     # keyword arguments.
       
   141     def process_kwarg(kwarg):
       
   142         if isinstance(kwarg, (list, tuple)):
       
   143             return [s.lower() for s in kwarg] 
       
   144         elif kwarg:
       
   145             return [s.lower() for s in ogr_fields]
       
   146         else:
       
   147             return []
       
   148     null_fields = process_kwarg(null)
       
   149     blank_fields = process_kwarg(blank)
       
   150     decimal_fields = process_kwarg(decimal)
       
   151 
       
   152     # Gets the `null` and `blank` keywords for the given field name.
       
   153     def get_kwargs_str(field_name):
       
   154         kwlist = []
       
   155         if field_name.lower() in null_fields: kwlist.append('null=True')
       
   156         if field_name.lower() in blank_fields: kwlist.append('blank=True')
       
   157         if kwlist: return ', ' + ', '.join(kwlist)
       
   158         else: return ''
       
   159 
       
   160     # For those wishing to disable the imports.
       
   161     if imports:
       
   162         yield '# This is an auto-generated Django model module created by ogrinspect.'
       
   163         yield 'from django.contrib.gis.db import models'
       
   164         yield ''
       
   165 
       
   166     yield 'class %s(models.Model):' % model_name
       
   167     
       
   168     for field_name, width, precision, field_type in izip(ogr_fields, layer.field_widths, layer.field_precisions, layer.field_types):
       
   169         # The model field name.
       
   170         mfield = field_name.lower()
       
   171         if mfield[-1:] == '_': mfield += 'field'
       
   172         
       
   173         # Getting the keyword args string.
       
   174         kwargs_str = get_kwargs_str(field_name)
       
   175 
       
   176         if field_type is OFTReal:
       
   177             # By default OFTReals are mapped to `FloatField`, however, they
       
   178             # may also be mapped to `DecimalField` if specified in the 
       
   179             # `decimal` keyword.
       
   180             if field_name.lower() in decimal_fields:
       
   181                 yield '    %s = models.DecimalField(max_digits=%d, decimal_places=%d%s)' % (mfield, width, precision, kwargs_str)
       
   182             else:
       
   183                 yield '    %s = models.FloatField(%s)' % (mfield, kwargs_str[2:])
       
   184         elif field_type is OFTInteger:
       
   185             yield '    %s = models.IntegerField(%s)' % (mfield, kwargs_str[2:])
       
   186         elif field_type is OFTString:
       
   187             yield '    %s = models.CharField(max_length=%s%s)' % (mfield, width, kwargs_str)
       
   188         elif field_type is OFTDate:
       
   189             yield '    %s = models.DateField(%s)' % (mfield, kwargs_str[2:])
       
   190         elif field_type is OFTDateTime:
       
   191             yield '    %s = models.DateTimeField(%s)' % (mfield, kwargs_str[2:])
       
   192         elif field_type is OFTDate:
       
   193             yield '    %s = models.TimeField(%s)' % (mfield, kwargs_str[2:])
       
   194         else:
       
   195             raise TypeError('Unknown field type %s in %s' % (fld_type, mfield))
       
   196     
       
   197     # TODO: Autodetection of multigeometry types (see #7218).
       
   198     gtype = layer.geom_type
       
   199     if multi_geom and gtype.num in (1, 2, 3):
       
   200         geom_field = 'Multi%s' % gtype.django
       
   201     else:
       
   202         geom_field = gtype.django
       
   203 
       
   204     # Setting up the SRID keyword string.
       
   205     if srid is None:
       
   206         if layer.srs is None:
       
   207             srid_str = 'srid=-1'
       
   208         else:
       
   209             srid = layer.srs.srid
       
   210             if srid is None:
       
   211                 srid_str = 'srid=-1'
       
   212             elif srid == 4326:
       
   213                 # WGS84 is already the default.
       
   214                 srid_str = ''
       
   215             else:
       
   216                 srid_str = 'srid=%s' % srid
       
   217     else:
       
   218         srid_str = 'srid=%s' % srid
       
   219 
       
   220     yield '    %s = models.%s(%s)' % (geom_name, geom_field, srid_str)
       
   221     yield '    objects = models.GeoManager()'
       
   222 
       
   223     if name_field:
       
   224         yield ''
       
   225         yield '    def __unicode__(self): return self.%s' % name_field