1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
import argparse
from django.contrib.gis import gdal
from django.core.management.base import BaseCommand, CommandError
from django.utils.inspect import get_func_args
class LayerOptionAction(argparse.Action):
"""
Custom argparse action for the `ogrinspect` `layer_key` keyword option
which may be an integer or a string.
"""
def __call__(self, parser, namespace, value, option_string=None):
try:
setattr(namespace, self.dest, int(value))
except ValueError:
setattr(namespace, self.dest, value)
class ListOptionAction(argparse.Action):
"""
Custom argparse action for `ogrinspect` keywords that require
a string list. If the string is 'True'/'true' then the option
value will be a boolean instead.
"""
def __call__(self, parser, namespace, value, option_string=None):
if value.lower() == "true":
setattr(namespace, self.dest, True)
else:
setattr(namespace, self.dest, value.split(","))
class Command(BaseCommand):
help = (
"Inspects the given OGR-compatible data source (e.g., a shapefile) and "
"outputs\na GeoDjango model with the given model name. For example:\n"
" ./manage.py ogrinspect zipcode.shp Zipcode"
)
requires_system_checks = []
def add_arguments(self, parser):
parser.add_argument("data_source", help="Path to the data source.")
parser.add_argument("model_name", help="Name of the model to create.")
parser.add_argument(
"--blank",
action=ListOptionAction,
default=False,
help="Use a comma separated list of OGR field names to add "
"the `blank=True` option to the field definition. Set to `true` "
"to apply to all applicable fields.",
)
parser.add_argument(
"--decimal",
action=ListOptionAction,
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.",
)
parser.add_argument(
"--geom-name",
default="geom",
help="Specifies the model name for the Geometry Field (defaults to `geom`)",
)
parser.add_argument(
"--layer",
dest="layer_key",
action=LayerOptionAction,
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.",
)
parser.add_argument(
"--multi-geom",
action="store_true",
help="Treat the geometry in the data source as a geometry collection.",
)
parser.add_argument(
"--name-field",
help="Specifies a field name to return for the __str__() method.",
)
parser.add_argument(
"--no-imports",
action="store_false",
dest="imports",
help="Do not include `from django.contrib.gis.db import models` statement.",
)
parser.add_argument(
"--null",
action=ListOptionAction,
default=False,
help="Use a comma separated list of OGR field names to add "
"the `null=True` option to the field definition. Set to `true` "
"to apply to all applicable fields.",
)
parser.add_argument(
"--srid",
help="The SRID to use for the Geometry Field. If it can be "
"determined, the SRID of the data source is used.",
)
parser.add_argument(
"--mapping",
action="store_true",
help="Generate mapping dictionary for use with `LayerMapping`.",
)
def handle(self, *args, **options):
data_source, model_name = options.pop("data_source"), options.pop("model_name")
# Getting the OGR DataSource from the string parameter.
try:
ds = gdal.DataSource(data_source)
except gdal.GDALException as msg:
raise CommandError(msg)
# Returning the output of ogrinspect with the given arguments
# and options.
from django.contrib.gis.utils.ogrinspect import _ogrinspect, mapping
# Filter options to params accepted by `_ogrinspect`
ogr_options = {
k: v
for k, v in options.items()
if k in get_func_args(_ogrinspect) and v is not None
}
output = [s for s in _ogrinspect(ds, model_name, **ogr_options)]
if options["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 = {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)
|