app/django/contrib/gis/management/commands/ogrinspect.py
changeset 323 ff1a9aa48cfd
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
       
     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)