"""$ fio load""" from functools import partial import click import cligj import fiona from fiona.fio import options, with_context_env from fiona.model import Feature, Geometry from fiona.transform import transform_geom @click.command(short_help="Load GeoJSON to a dataset in another format.") @click.argument("output", required=True) @click.option("-f", "--format", "--driver", "driver", help="Output format driver name.") @options.src_crs_opt @click.option( "--dst-crs", "--dst_crs", help="Destination CRS. Defaults to --src-crs when not given.", ) @cligj.features_in_arg @click.option( "--layer", metavar="INDEX|NAME", callback=options.cb_layer, help="Load features into specified layer. Layers use " "zero-based numbering when accessed by index.", ) @options.creation_opt @options.open_opt @click.option("--append", is_flag=True, help="Open destination layer in append mode.") @click.pass_context @with_context_env def load( ctx, output, driver, src_crs, dst_crs, features, layer, creation_options, open_options, append, ): """Load features from JSON to a file in another format. The input is a GeoJSON feature collection or optionally a sequence of GeoJSON feature objects. """ dst_crs = dst_crs or src_crs if src_crs and dst_crs and src_crs != dst_crs: transformer = partial( transform_geom, src_crs, dst_crs, antimeridian_cutting=True ) else: def transformer(x): return Geometry.from_dict(**x) def feature_gen(): """Convert stream of JSON to features. Yields ------ Feature """ try: for feat in features: feat["geometry"] = transformer(Geometry.from_dict(**feat["geometry"])) yield Feature.from_dict(**feat) except TypeError: raise click.ClickException("Invalid input.") source = feature_gen() # Use schema of first feature as a template. # TODO: schema specified on command line? try: first = next(source) except TypeError: raise click.ClickException("Invalid input.") # TODO: this inference of a property's type from its value needs some # work. It works reliably only for the basic JSON serializable types. # The fio-load command does require JSON input but that may change # someday. schema = {"geometry": first.geometry.type} schema["properties"] = { k: type(v if v is not None else "").__name__ for k, v in first.properties.items() } if append: opener = fiona.open(output, "a", layer=layer, **open_options) else: opener = fiona.open( output, "w", driver=driver, crs=dst_crs, schema=schema, layer=layer, **creation_options ) with opener as dst: dst.write(first) dst.writerecords(source)