|
1 import os, sys |
|
2 from optparse import make_option |
|
3 from django.contrib.gis import gdal |
|
4 from django.contrib.gis.management.base import ArgsCommand, CommandError |
|
5 |
|
6 def layer_option(option, opt, value, parser): |
|
7 """ |
|
8 Callback for `make_option` for the `ogrinspect` `layer_key` |
|
9 keyword option which may be an integer or a string. |
|
10 """ |
|
11 try: |
|
12 dest = int(value) |
|
13 except ValueError: |
|
14 dest = value |
|
15 setattr(parser.values, option.dest, dest) |
|
16 |
|
17 def list_option(option, opt, value, parser): |
|
18 """ |
|
19 Callback for `make_option` for `ogrinspect` keywords that require |
|
20 a string list. If the string is 'True'/'true' then the option |
|
21 value will be a boolean instead. |
|
22 """ |
|
23 if value.lower() == 'true': |
|
24 dest = True |
|
25 else: |
|
26 dest = [s for s in value.split(',')] |
|
27 setattr(parser.values, option.dest, dest) |
|
28 |
|
29 class Command(ArgsCommand): |
|
30 help = ('Inspects the given OGR-compatible data source (e.g., a shapefile) and outputs\n' |
|
31 'a GeoDjango model with the given model name. For example:\n' |
|
32 ' ./manage.py ogrinspect zipcode.shp Zipcode') |
|
33 args = '[data_source] [model_name]' |
|
34 |
|
35 option_list = ArgsCommand.option_list + ( |
|
36 make_option('--blank', dest='blank', type='string', action='callback', |
|
37 callback=list_option, default=False, |
|
38 help='Use a comma separated list of OGR field names to add ' |
|
39 'the `blank=True` option to the field definition. Set with' |
|
40 '`true` to apply to all applicable fields.'), |
|
41 make_option('--decimal', dest='decimal', type='string', action='callback', |
|
42 callback=list_option, default=False, |
|
43 help='Use a comma separated list of OGR float fields to ' |
|
44 'generate `DecimalField` instead of the default ' |
|
45 '`FloatField`. Set to `true` to apply to all OGR float fields.'), |
|
46 make_option('--geom-name', dest='geom_name', type='string', default='geom', |
|
47 help='Specifies the model name for the Geometry Field ' |
|
48 '(defaults to `geom`)'), |
|
49 make_option('--layer', dest='layer_key', type='string', action='callback', |
|
50 callback=layer_option, default=0, |
|
51 help='The key for specifying which layer in the OGR data ' |
|
52 'source to use. Defaults to 0 (the first layer). May be ' |
|
53 'an integer or a string identifier for the layer.'), |
|
54 make_option('--multi-geom', action='store_true', dest='multi_geom', default=False, |
|
55 help='Treat the geometry in the data source as a geometry collection.'), |
|
56 make_option('--name-field', dest='name_field', |
|
57 help='Specifies a field name to return for the `__unicode__` function.'), |
|
58 make_option('--no-imports', action='store_false', dest='imports', default=True, |
|
59 help='Do not include `from django.contrib.gis.db import models` ' |
|
60 'statement.'), |
|
61 make_option('--null', dest='null', type='string', action='callback', |
|
62 callback=list_option, default=False, |
|
63 help='Use a comma separated list of OGR field names to add ' |
|
64 'the `null=True` option to the field definition. Set with' |
|
65 '`true` to apply to all applicable fields.'), |
|
66 make_option('--srid', dest='srid', |
|
67 help='The SRID to use for the Geometry Field. If it can be ' |
|
68 'determined, the SRID of the data source is used.'), |
|
69 make_option('--mapping', action='store_true', dest='mapping', |
|
70 help='Generate mapping dictionary for use with `LayerMapping`.') |
|
71 ) |
|
72 |
|
73 requires_model_validation = False |
|
74 |
|
75 def handle_args(self, *args, **options): |
|
76 try: |
|
77 data_source, model_name = args |
|
78 except ValueError: |
|
79 raise CommandError('Invalid arguments, must provide: %s' % self.args) |
|
80 |
|
81 if not gdal.HAS_GDAL: |
|
82 raise CommandError('GDAL is required to inspect geospatial data sources.') |
|
83 |
|
84 # TODO: Support non file-based OGR datasources. |
|
85 if not os.path.isfile(data_source): |
|
86 raise CommandError('The given data source cannot be found: "%s"' % data_source) |
|
87 |
|
88 # Removing options with `None` values. |
|
89 options = dict([(k, v) for k, v in options.items() if not v is None]) |
|
90 |
|
91 # Getting the OGR DataSource from the string parameter. |
|
92 try: |
|
93 ds = gdal.DataSource(data_source) |
|
94 except gdal.OGRException, msg: |
|
95 raise CommandError(msg) |
|
96 |
|
97 # Whether the user wants to generate the LayerMapping dictionary as well. |
|
98 show_mapping = options.pop('mapping', False) |
|
99 |
|
100 # Returning the output of ogrinspect with the given arguments |
|
101 # and options. |
|
102 from django.contrib.gis.utils.ogrinspect import _ogrinspect, mapping |
|
103 output = [s for s in _ogrinspect(ds, model_name, **options)] |
|
104 if show_mapping: |
|
105 # Constructing the keyword arguments for `mapping`, and |
|
106 # calling it on the data source. |
|
107 kwargs = {'geom_name' : options['geom_name'], |
|
108 'layer_key' : options['layer_key'], |
|
109 'multi_geom' : options['multi_geom'], |
|
110 } |
|
111 mapping_dict = mapping(ds, **kwargs) |
|
112 # This extra legwork is so that the dictionary definition comes |
|
113 # out in the same order as the fields in the model definition. |
|
114 rev_mapping = dict([(v, k) for k, v in mapping_dict.items()]) |
|
115 output.extend(['', '# Auto-generated `LayerMapping` dictionary for %s model' % model_name, |
|
116 '%s_mapping = {' % model_name.lower()]) |
|
117 output.extend([" '%s' : '%s'," % (rev_mapping[ogr_fld], ogr_fld) for ogr_fld in ds[options['layer_key']].fields]) |
|
118 output.extend([" '%s' : '%s'," % (options['geom_name'], mapping_dict[options['geom_name']]), '}']) |
|
119 return '\n'.join(output) |