Django

Code

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

Revision 7709, 8.8 kB (checked in by jbronn, 6 months ago)

gis: Added the ogrinspect management command.

Line 
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
Note: See TracBrowser for help on using the browser.