PageRenderTime 52ms CodeModel.GetById 12ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 1ms

/django/contrib/gis/utils/ogrinspect.py

https://code.google.com/p/mango-py/
Python | 225 lines | 198 code | 3 blank | 24 comment | 9 complexity | 2d9f41fba6c71de56fcedd71519a6420 MD5 | raw file
  1"""
  2This module is for inspecting OGR data sources and generating either
  3models for GeoDjango and/or mapping dictionaries for use with the
  4`LayerMapping` utility.
  5
  6Author: Travis Pinney, Dane Springmeyer, & Justin Bronn
  7"""
  8from itertools import izip
  9# Requires GDAL to use.
 10from django.contrib.gis.gdal import DataSource
 11from django.contrib.gis.gdal.field import OFTDate, OFTDateTime, OFTInteger, OFTReal, OFTString, OFTTime
 12
 13def 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
 49def 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
119def _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' % (field_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