from __future__ import division import json import logging import click from cligj import use_rs_opt from .helpers import obj_gen, eval_feature_expression from fiona.fio import with_context_env @click.command(short_help="Calculate GeoJSON property by Python expression") @click.argument('property_name') @click.argument('expression') @click.option('--overwrite', is_flag=True, default=False, help="Overwrite properties, default: False") @use_rs_opt @click.pass_context @with_context_env def calc(ctx, property_name, expression, overwrite, use_rs): """ Create a new property on GeoJSON features using the specified expression. \b The expression is evaluated in a restricted namespace containing: - sum, pow, min, max and the imported math module - shape (optional, imported from shapely.geometry if available) - bool, int, str, len, float type conversions - f (the feature to be evaluated, allows item access via javascript-style dot notation using munch) The expression will be evaluated for each feature and its return value will be added to the properties as the specified property_name. Existing properties will not be overwritten by default (an Exception is raised). Example \b $ fio cat data.shp | fio calc sumAB "f.properties.A + f.properties.B" """ logger = logging.getLogger(__name__) stdin = click.get_text_stream('stdin') try: source = obj_gen(stdin) for i, obj in enumerate(source): features = obj.get('features') or [obj] for j, feat in enumerate(features): if not overwrite and property_name in feat['properties']: raise click.UsageError( '{0} already exists in properties; ' 'rename or use --overwrite'.format(property_name)) feat['properties'][property_name] = eval_feature_expression( feat, expression) if use_rs: click.echo(u'\u001e', nl=False) click.echo(json.dumps(feat)) except Exception: logger.exception("Exception caught during processing") raise click.Abort()