Source code for rastervision.core.data.vector_transformer.label_maker.filter

# Copied from https://github.com/developmentseed/label-maker/blob/master/label_maker/filter.py
# flake8: noqa

# pylint: disable=eval-used,too-many-return-statements
"""Create a feature filtering function from a Mapbox GL Filter."""

# Python port of https://github.com/mapbox/mapbox-gl-js/blob/c9900db279db776f493ce8b6749966cedc2d6b8a/src/style-spec/feature_filter/index.js


[docs]def create_filter(filt): """Create a feature filtering function from a Mapbox GL Filter. Given a filter expressed as nested lists, return a new function that evaluates whether a given feature (with a .properties or .tags property) passes its test. More information: - https://www.mapbox.com/mapbox-gl-js/style-spec/#other-filter - https://github.com/mapbox/mapbox-gl-js/tree/master/src/style-spec/feature_filter Parameters ------------ filt: list Mapbox GL filter Returns -------- func: function A function which evaluates whether a GeoJSON feature meets the input filter criteria """ def func(f): """evaluates whether a given feature passes its filter""" p = f.get('properties', {}) if f else {} # pylint: disable=unused-variable return eval(_compile(filt)) return func
def _compile(filt): """Return a string represented the compiled filter function""" if not filt: return 'True' op = filt[0] if len(filt) == 1: return 'False' if op == 'any' else 'True' if op in ['==', '!=', '<', '>', '<=', '>=']: return _compile_comparison_op(filt[1], filt[2], op) elif op == 'any': return _compile_logical_op(filt[1:], ' or ') elif op == 'all': return _compile_logical_op(filt[1:], ' and ') elif op == 'none': return _compile_negation(_compile_logical_op(filt[1:], ' or ')) elif op == 'in': return _compile_in_op(filt[1], filt[2:]) elif op == '!in': return _compile_negation(_compile_in_op(filt[1], filt[2:])) elif op == 'has': return _compile_has_op(filt[1]) elif op == '!has': return _compile_negation(_compile_has_op(filt[1])) return 'True' def _compile_property_reference(prop: str) -> str: """Find the correct reference on the input feature""" if prop == '$type': return 'f.get("geometry").get("type")' elif prop == '$id': return 'f.get("id")' out = 'p' key_crumbs = prop.split('.') for k in key_crumbs[:-1]: out += f'.get("{k}", dict())' out += f'.get("{key_crumbs[-1]}")' return out def _compile_comparison_op(prop, value, op): """Combine two values with a comparison operator""" left = _compile_property_reference(prop) right = _stringify(value) return left + op + right def _compile_logical_op(expressions, op): """Join multiple logical expressions""" return op.join(map(_compile, expressions)) def _compile_in_op(prop, values): """Test if a property is within a list of values""" return '{} in {}'.format(_compile_property_reference(prop), values) def _compile_has_op(prop): """Test if a property exists on a feature""" return '"id" in f' if prop == '$id' else '{} in p'.format(_stringify(prop)) def _compile_negation(expression): """Negate the input expression""" return 'not ({})'.format(expression) def _stringify(s): """Convert input to string, wrap with quotes if already a string""" return '"{}"'.format(s) if isinstance(s, str) else str(s)