1. Luke Plant
  2. django

Commits

jbr...@bcc190cf-cafb-0310-a4f2-bffc1f526a37  committed e4a645b

gis: Added the `ogrinspect` management command.

  • Participants
  • Parent commits 30392ee
  • Branches gis

Comments (0)

Files changed (3)

File django/contrib/gis/management/base.py

View file
+from django.core.management.base import BaseCommand, CommandError
+
+class ArgsCommand(BaseCommand):
+    """
+    Command class for commands that take multiple arguments.
+    """
+    args = '<arg arg ...>'
+
+    def handle(self, *args, **options):
+        if not args:
+            raise CommandError('Must provide the following arguments: %s' % self.args)
+        return self.handle_args(*args, **options)
+
+    def handle_args(self, *args, **options):
+        raise NotImplementedError()

File django/contrib/gis/management/commands/ogrinspect.py

View file
+import os, sys
+from optparse import make_option
+from django.contrib.gis import gdal
+from django.contrib.gis.management.base import ArgsCommand, CommandError
+
+def layer_option(option, opt, value, parser):
+    """
+    Callback for `make_option` for the `ogrinspect` `layer_key`
+    keyword option which may be an integer or a string.
+    """
+    try:
+        dest = int(value)
+    except ValueError:
+        dest = value
+    setattr(parser.values, option.dest, dest)
+
+def list_option(option, opt, value, parser):
+    """
+    Callback for `make_option` for `ogrinspect` keywords that require
+    a string list.  If the string is 'True'/'true' then the option 
+    value will be a boolean instead.
+    """
+    if value.lower() == 'true':
+        dest = True
+    else:
+        dest = [s for s in value.split(',')]
+    setattr(parser.values, option.dest, dest)
+    
+class Command(ArgsCommand):
+    help = ('Inspects the given OGR-compatible data source (e.g., a shapefile) and outputs\n'
+            'a GeoDjango model with the given model name. For example:\n'
+            ' ./manage.py ogrinspect zipcode.shp Zipcode')
+    args = '[data_source] [model_name]'
+
+    option_list = ArgsCommand.option_list + (
+        make_option('--blank', dest='blank', type='string', action='callback',  
+                    callback=list_option, default=False,
+                    help='Use a comma separated list of OGR field names to add '
+                    'the `blank=True` option to the field definition.  Set with'
+                    '`true` to apply to all applicable fields.'),
+        make_option('--decimal', dest='decimal', type='string', action='callback', 
+                    callback=list_option, default=False,
+                    help='Use a comma separated list of OGR float fields to '
+                    'generate `DecimalField` instead of the default '
+                    '`FloatField`. Set to `true` to apply to all OGR float fields.'),
+        make_option('--geom-name', dest='geom_name', type='string', default='geom',
+                    help='Specifies the model name for the Geometry Field '
+                    '(defaults to `geom`)'),
+        make_option('--layer', dest='layer_key', type='string', action='callback', 
+                    callback=layer_option, default=0,
+                    help='The key for specifying which layer in the OGR data '
+                    'source to use. Defaults to 0 (the first layer). May be '
+                    'an integer or a string identifier for the layer.'),
+        make_option('--multi-geom', action='store_true', dest='multi_geom', default=False,
+                    help='Treat the geometry in the data source as a geometry collection.'),
+        make_option('--name-field', dest='name_field',
+                    help='Specifies a field name to return for the `__unicode__` function.'),
+        make_option('--no-imports', action='store_false', dest='imports', default=True,
+                    help='Do not include `from django.contrib.gis.db import models` '
+                    'statement.'),
+        make_option('--null', dest='null', type='string', action='callback',  
+                    callback=list_option, default=False,
+                    help='Use a comma separated list of OGR field names to add '
+                    'the `null=True` option to the field definition.  Set with'
+                    '`true` to apply to all applicable fields.'),
+        make_option('--srid', dest='srid',
+                    help='The SRID to use for the Geometry Field.  If it can be '
+                    'determined, the SRID of the data source is used.'),
+        make_option('--mapping', action='store_true', dest='mapping',
+                    help='Generate mapping dictionary for use with `LayerMapping`.')
+        )
+
+    requires_model_validation = False
+
+    def handle_args(self, *args, **options):
+        try:
+            data_source, model_name = args
+        except ValueError:
+            raise CommandError('Invalid arguments, must provide: %s' % self.args)
+
+        if not gdal.HAS_GDAL:
+            raise CommandError('GDAL is required to inspect geospatial data sources.')
+
+        # TODO: Support non file-based OGR datasources.
+        if not os.path.isfile(data_source):
+            raise CommandError('The given data source cannot be found: "%s"' % data_source)
+        
+        # Removing options with `None` values.
+        options = dict([(k, v) for k, v in options.items() if not v is None])
+
+        # Getting the OGR DataSource from the string parameter.
+        try:
+            ds = gdal.DataSource(data_source)
+        except gdal.OGRException, msg:
+            raise CommandError(msg)
+
+        # Whether the user wants to generate the LayerMapping dictionary as well.
+        show_mapping = options.pop('mapping', False)
+
+        # Returning the output of ogrinspect with the given arguments
+        # and options.
+        from django.contrib.gis.utils.ogrinspect import _ogrinspect, mapping
+        output = [s for s in _ogrinspect(ds, model_name, **options)]
+        if show_mapping:
+            # Constructing the keyword arguments for `mapping`, and
+            # calling it on the data source.
+            kwargs = {'geom_name' : options['geom_name'],
+                      'layer_key' : options['layer_key'],
+                      'multi_geom' : options['multi_geom'],
+                      }
+            mapping_dict = mapping(ds, **kwargs)
+            # This extra legwork is so that the dictionary definition comes
+            # out in the same order as the fields in the model definition.
+            rev_mapping = dict([(v, k) for k, v in mapping_dict.items()])
+            output.extend(['', '# Auto-generated `LayerMapping` dictionary for %s model' % model_name, 
+                           '%s_mapping = {' % model_name.lower()])
+            output.extend(["    '%s' : '%s'," % (rev_mapping[ogr_fld], ogr_fld) for ogr_fld in ds[options['layer_key']].fields])
+            output.extend(["    '%s' : '%s'," % (options['geom_name'], mapping_dict[options['geom_name']]), '}'])
+        return '\n'.join(output)

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

View file
 from django.contrib.gis.gdal import DataSource
 from django.contrib.gis.gdal.field import OFTDate, OFTDateTime, OFTInteger, OFTReal, OFTString, OFTTime
 
-def mapping(data_source, geom_name='geom', layer_key=0):
+def mapping(data_source, geom_name='geom', layer_key=0, multi_geom=False):
     """
     Given a DataSource, generates a dictionary that may be used 
     for invoking the LayerMapping utility.
      `layer_key` => The key for specifying which layer in the DataSource to use;
        defaults to 0 (the first layer).  May be an integer index or a string
        identifier for the layer.
+
+     `multi_geom` => Boolean (default: False) - specify as multigeometry.
     """
     if isinstance(data_source, basestring):
         # Instantiating the DataSource from the string.
         if mfield[-1:] == '_': mfield += 'field'
         _mapping[mfield] = field
     gtype = data_source[layer_key].geom_type
-    _mapping[geom_name] = str(gtype).upper()
-
+    if multi_geom and gtype.num in (1, 2, 3): prefix = 'MULTI'
+    else: prefix = ''
+    _mapping[geom_name] = prefix + str(gtype).upper()
     return _mapping
 
 def ogrinspect(*args, **kwargs):