pax_global_header00006660000000000000000000000064140542573040014516gustar00rootroot0000000000000052 comment=b837c2cdd8811dff04f95cdfd0234efe6c638eab cligj-0.7.2/000077500000000000000000000000001405425730400126145ustar00rootroot00000000000000cligj-0.7.2/.gitignore000077500000000000000000000012771405425730400146160ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so # Distribution / packaging .Python env/ venv/ build/ develop-eggs/ dist/ downloads/ eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .cache nosetests.xml coverage.xml # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ # PyCharm IDE .idea/ cligj-0.7.2/.travis.yml000077500000000000000000000005021405425730400147250ustar00rootroot00000000000000sudo: false language: python python: - "3.6" - "3.7" - "3.8" install: - "pip install coveralls" - "pip install -e .[test]" script: - py.test --cov cligj --cov-report term-missing after_success: - coveralls deploy: on: tags: true provider: pypi distributions: "sdist bdist_wheel" user: __token__ cligj-0.7.2/CHANGES.txt000066400000000000000000000034341405425730400144310ustar00rootroot00000000000000Changes ======= 0.7.1 (2010-11-20) ------------------ - Future deprecation warning added in 0.7.0 has been changed. Cligj version 1.0.0 will support Python versions >= 3.6. 0.7.0 (2010-10-21) ------------------ - Warn about deprecation of support for Python versions < 3.7 in 1.0.0 (#33). - Warn about future change in --sequence default when the option is used (#31). 0.6.0 (2020-10-19) ------------------ No changes since 0.6b1. 0.6b1 (2020-10-14) ------------------ Future changes: - Feature sequences, not collections, will be the default form of output in version 1.0 (#20). Bug fixes: - Always use `encoding='utf-8'` when opening input GeoJSON (#27). - Improve docstrings (#22). 0.5.0 (2018-09-26) ------------------ - Version 0.4.0 is not compatible with click 7, but version 0.5.0 is (#23). - The features_in_arg handler now takes sequences of geometry objects (#14). - The iter_features function has a new per-feature callback and is ready for use in other projects like Fiona (#15). - The plugins module has been removed (#17). 0.4.0 (2015-12-17) ------------------ - Introduces a click argument, `features_in_arg`, which utilizes a click callback to normalize the input of geojson features (#9). - Release from tagged Travis CI builds (#10). 0.3.0 (2015-08-12) ------------------ - Deprecation of the cligj.plugins module (#6). Please switch to the click-plugins module: https://github.com/click-contrib/click-plugins. The cligj.plugins module will be removed from cligj at version 1.0. 0.2.0 (2015-05-28) ------------------ - Addition of a pluggable command group class and a corresponding click-style decorator (#2, #3). 0.1.0 (2015-01-06) ------------------ - Initial release: a collection of GeoJSON-related command line arguments and options for use with Click (#1). cligj-0.7.2/LICENSE000066400000000000000000000026751405425730400136330ustar00rootroot00000000000000Copyright (c) 2014, Mapbox All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of cligj nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cligj-0.7.2/MANIFEST.in000066400000000000000000000000611405425730400143470ustar00rootroot00000000000000include CHANGES.txt include LICENSE graft tests cligj-0.7.2/README.rst000066400000000000000000000107611405425730400143100ustar00rootroot00000000000000cligj ====== .. image:: https://travis-ci.com/mapbox/cligj.svg :target: https://travis-ci.com/mapbox/cligj .. image:: https://coveralls.io/repos/mapbox/cligj/badge.png?branch=master :target: https://coveralls.io/r/mapbox/cligj?branch=master Common arguments and options for GeoJSON processing commands, using Click. `cligj` is for Python developers who create command line interfaces for geospatial data. `cligj` allows you to quickly build consistent, well-tested and interoperable CLIs for handling GeoJSON. Arguments --------- ``files_in_arg`` Multiple files ``files_inout_arg`` Multiple files, last of which is an output file. ``features_in_arg`` GeoJSON Features input which accepts multiple representations of GeoJSON features and returns the input data as an iterable of GeoJSON Feature-like dictionaries Options -------- ``verbose_opt`` ``quiet_opt`` ``format_opt`` JSON formatting options ~~~~~~~~~~~~~~~~~~~~~~~ ``indent_opt`` ``compact_opt`` Coordinate precision option ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``precision_opt`` Geographic (default), projected, or Mercator switch ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``projection_geographic_opt`` ``projection_projected_opt`` ``projection_mercator_opt`` Feature collection or feature sequence switch ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``sequence_opt`` ``use_rs_opt`` GeoJSON output mode option ~~~~~~~~~~~~~~~~~~~~~~~~~~ ``geojson_type_collection_opt`` ``geojson_type_feature_opt`` ``def geojson_type_bbox_opt`` Example ------- Here's an example of a command that writes out GeoJSON features as a collection or, optionally, a sequence of individual features. Since most software that reads and writes GeoJSON expects a text containing a single feature collection, that's the default, and a LF-delimited sequence of texts containing one GeoJSON feature each is a feature that is turned on using the ``--sequence`` option. To write sequences of feature texts that conform to the `GeoJSON Text Sequences standard `__ (and might contain pretty-printed JSON) with the ASCII Record Separator (0x1e) as a delimiter, use the ``--rs`` option .. warning:: Future change warning GeoJSON sequences (`--sequence`), not collections (`--no-sequence`), will be the default in version 1.0.0. .. code-block:: python import click import cligj import json def process_features(features): for feature in features: # TODO process feature here yield feature @click.command() @cligj.features_in_arg @cligj.sequence_opt @cligj.use_rs_opt def pass_features(features, sequence, use_rs): if sequence: for feature in process_features(features): if use_rs: click.echo(u'\x1e', nl=False) click.echo(json.dumps(feature)) else: click.echo(json.dumps( {'type': 'FeatureCollection', 'features': list(process_features(features))})) On the command line, the generated help text explains the usage .. code-block:: console Usage: pass_features [OPTIONS] FEATURES... Options: --sequence / --no-sequence Write a LF-delimited sequence of texts containing individual objects or write a single JSON text containing a feature collection object (the default). --rs / --no-rs Use RS (0x1E) as a prefix for individual texts in a sequence as per http://tools.ietf.org/html /draft-ietf-json-text-sequence-13 (default is False). --help Show this message and exit. And can be used like this .. code-block:: console $ cat data.geojson {'type': 'FeatureCollection', 'features': [{'type': 'Feature', 'id': '1'}, {'type': 'Feature', 'id': '2'}]} $ pass_features data.geojson {'type': 'FeatureCollection', 'features': [{'type': 'Feature', 'id': '1'}, {'type': 'Feature', 'id': '2'}]} $ cat data.geojson | pass_features {'type': 'FeatureCollection', 'features': [{'type': 'Feature', 'id': '1'}, {'type': 'Feature', 'id': '2'}]} $ cat data.geojson | pass_features --sequence {'type': 'Feature', 'id': '1'} {'type': 'Feature', 'id': '2'} $ cat data.geojson | pass_features --sequence --rs ^^{'type': 'Feature', 'id': '1'} ^^{'type': 'Feature', 'id': '2'} In this example, ``^^`` represents 0x1e. cligj-0.7.2/cligj/000077500000000000000000000000001405425730400137045ustar00rootroot00000000000000cligj-0.7.2/cligj/__init__.py000077500000000000000000000074441405425730400160310ustar00rootroot00000000000000"""cligj A package of arguments, options, and parsers for the Python GeoJSON ecosystem. """ import sys from warnings import warn import click from .features import normalize_feature_inputs __version__ = "0.7.2" if sys.version_info < (3, 6): warn("cligj 1.0.0 will require Python >= 3.6", FutureWarning) # Multiple input files. files_in_arg = click.argument( 'files', nargs=-1, type=click.Path(resolve_path=True), required=True, metavar="INPUTS...") # Multiple files, last of which is an output file. files_inout_arg = click.argument( 'files', nargs=-1, type=click.Path(resolve_path=True), required=True, metavar="INPUTS... OUTPUT") # Features from files, command line args, or stdin. # Returns the input data as an iterable of GeoJSON Feature-like # dictionaries. features_in_arg = click.argument( 'features', nargs=-1, callback=normalize_feature_inputs, metavar="FEATURES...") # Options. verbose_opt = click.option( '--verbose', '-v', count=True, help="Increase verbosity.") quiet_opt = click.option( '--quiet', '-q', count=True, help="Decrease verbosity.") # Format driver option. format_opt = click.option( '-f', '--format', '--driver', 'driver', default='GTiff', help="Output format driver") # JSON formatting options. indent_opt = click.option( '--indent', type=int, default=None, help="Indentation level for JSON output") compact_opt = click.option( '--compact/--not-compact', default=False, help="Use compact separators (',', ':').") # Coordinate precision option. precision_opt = click.option( '--precision', type=int, default=-1, help="Decimal precision of coordinates.") # Geographic (default), projected, or Mercator switch. projection_geographic_opt = click.option( '--geographic', 'projection', flag_value='geographic', default=True, help="Output in geographic coordinates (the default).") projection_projected_opt = click.option( '--projected', 'projection', flag_value='projected', help="Output in dataset's own, projected coordinates.") projection_mercator_opt = click.option( '--mercator', 'projection', flag_value='mercator', help="Output in Web Mercator coordinates.") # Feature collection or feature sequence switch. sequence_opt = click.option( '--sequence/--no-sequence', default=False, help="Write a LF-delimited sequence of texts containing individual " "objects or write a single JSON text containing a feature " "collection object (the default).", callback=lambda ctx, param, value: warn( "Sequences of Features, not FeatureCollections, will be the default in version 1.0.0", FutureWarning, ) or value, ) use_rs_opt = click.option( '--rs/--no-rs', 'use_rs', default=False, help="Use RS (0x1E) as a prefix for individual texts in a sequence " "as per http://tools.ietf.org/html/draft-ietf-json-text-sequence-13 " "(default is False).") def geojson_type_collection_opt(default=False): """GeoJSON FeatureCollection output mode""" return click.option( '--collection', 'geojson_type', flag_value='collection', default=default, help="Output as GeoJSON feature collection(s).") def geojson_type_feature_opt(default=False): """GeoJSON Feature or Feature sequence output mode""" return click.option( '--feature', 'geojson_type', flag_value='feature', default=default, help="Output as GeoJSON feature(s).") def geojson_type_bbox_opt(default=False): """GeoJSON bbox output mode""" return click.option( '--bbox', 'geojson_type', flag_value='bbox', default=default, help="Output as GeoJSON bounding box array(s).") cligj-0.7.2/cligj/features.py000066400000000000000000000153711405425730400161030ustar00rootroot00000000000000"""Feature parsing and normalization""" from itertools import chain import json import re import click def normalize_feature_inputs(ctx, param, value): """Click callback that normalizes feature input values. Returns a generator over features from the input value. Parameters ---------- ctx: a Click context param: the name of the argument or option value: object The value argument may be one of the following: 1. A list of paths to files containing GeoJSON feature collections or feature sequences. 2. A list of string-encoded coordinate pairs of the form "[lng, lat]", or "lng, lat", or "lng lat". If no value is provided, features will be read from stdin. Yields ------ Mapping A GeoJSON Feature represented by a Python mapping """ for feature_like in value or ('-',): try: with click.open_file(feature_like, encoding="utf-8") as src: for feature in iter_features(iter(src)): yield feature except IOError: coords = list(coords_from_query(feature_like)) yield { 'type': 'Feature', 'properties': {}, 'geometry': { 'type': 'Point', 'coordinates': coords}} def iter_features(geojsonfile, func=None): """Extract GeoJSON features from a text file object. Given a file-like object containing a single GeoJSON feature collection text or a sequence of GeoJSON features, iter_features() iterates over lines of the file and yields GeoJSON features. Parameters ---------- geojsonfile: a file-like object The geojsonfile implements the iterator protocol and yields lines of JSON text. func: function, optional A function that will be applied to each extracted feature. It takes a feature object and may return a replacement feature or None -- in which case iter_features does not yield. Yields ------ Mapping A GeoJSON Feature represented by a Python mapping """ func = func or (lambda x: x) first_line = next(geojsonfile) # Does the geojsonfile contain RS-delimited JSON sequences? if first_line.startswith(u'\x1e'): text_buffer = first_line.strip(u'\x1e') for line in geojsonfile: if line.startswith(u'\x1e'): if text_buffer: obj = json.loads(text_buffer) if 'coordinates' in obj: obj = to_feature(obj) newfeat = func(obj) if newfeat: yield newfeat text_buffer = line.strip(u'\x1e') else: text_buffer += line # complete our parsing with a for-else clause. else: obj = json.loads(text_buffer) if 'coordinates' in obj: obj = to_feature(obj) newfeat = func(obj) if newfeat: yield newfeat # If not, it may contains LF-delimited GeoJSON objects or a single # multi-line pretty-printed GeoJSON object. else: # Try to parse LF-delimited sequences of features or feature # collections produced by, e.g., `jq -c ...`. try: obj = json.loads(first_line) if obj['type'] == 'Feature': newfeat = func(obj) if newfeat: yield newfeat for line in geojsonfile: newfeat = func(json.loads(line)) if newfeat: yield newfeat elif obj['type'] == 'FeatureCollection': for feat in obj['features']: newfeat = func(feat) if newfeat: yield newfeat elif 'coordinates' in obj: newfeat = func(to_feature(obj)) if newfeat: yield newfeat for line in geojsonfile: newfeat = func(to_feature(json.loads(line))) if newfeat: yield newfeat # Indented or pretty-printed GeoJSON features or feature # collections will fail out of the try clause above since # they'll have no complete JSON object on their first line. # To handle these, we slurp in the entire file and parse its # text. except ValueError: text = "".join(chain([first_line], geojsonfile)) obj = json.loads(text) if obj['type'] == 'Feature': newfeat = func(obj) if newfeat: yield newfeat elif obj['type'] == 'FeatureCollection': for feat in obj['features']: newfeat = func(feat) if newfeat: yield newfeat elif 'coordinates' in obj: newfeat = func(to_feature(obj)) if newfeat: yield newfeat def to_feature(obj): """Converts an object to a GeoJSON Feature Returns feature verbatim or wraps geom in a feature with empty properties. Raises ------ ValueError Returns ------- Mapping A GeoJSON Feature represented by a Python mapping """ if obj['type'] == 'Feature': return obj elif 'coordinates' in obj: return { 'type': 'Feature', 'properties': {}, 'geometry': obj} else: raise ValueError("Object is not a feature or geometry") def iter_query(query): """Accept a filename, stream, or string. Returns an iterator over lines of the query.""" try: itr = click.open_file(query).readlines() except IOError: itr = [query] return itr def coords_from_query(query): """Transform a query line into a (lng, lat) pair of coordinates.""" try: coords = json.loads(query) except ValueError: query = query.replace(',', ' ') vals = query.split() coords = [float(v) for v in vals] return tuple(coords[:2]) def normalize_feature_objects(feature_objs): """Takes an iterable of GeoJSON-like Feature mappings or an iterable of objects with a geo interface and normalizes it to the former.""" for obj in feature_objs: if ( hasattr(obj, "__geo_interface__") and "type" in obj.__geo_interface__.keys() and obj.__geo_interface__["type"] == "Feature" ): yield obj.__geo_interface__ elif isinstance(obj, dict) and "type" in obj and obj["type"] == "Feature": yield obj else: raise ValueError("Did not recognize object as GeoJSON Feature") cligj-0.7.2/setup.cfg000066400000000000000000000000421405425730400144310ustar00rootroot00000000000000[metadata] license_file = LICENSE cligj-0.7.2/setup.py000077500000000000000000000016631405425730400143370ustar00rootroot00000000000000from codecs import open as codecs_open from setuptools import setup, find_packages with open("cligj/__init__.py") as f: for line in f: if "__version__" in line: version = line.split("=")[1].strip().strip('"').strip("'") continue with codecs_open('README.rst', encoding='utf-8') as f: long_description = f.read() setup( name="cligj", version=version, description=u"Click params for commmand line interfaces to GeoJSON", long_description=long_description, classifiers=[], keywords="", author=u"Sean Gillies", author_email="sean@mapbox.com", url="https://github.com/mapbox/cligj", license="BSD", packages=find_packages(exclude=["ez_setup", "examples", "tests"]), include_package_data=True, zip_safe=False, python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, <4", install_requires=["click >= 4.0"], extras_require={"test": ["pytest-cov"],}, ) cligj-0.7.2/tests/000077500000000000000000000000001405425730400137565ustar00rootroot00000000000000cligj-0.7.2/tests/conftest.py000066400000000000000000000002031405425730400161500ustar00rootroot00000000000000from click.testing import CliRunner import pytest @pytest.fixture(scope='function') def runner(request): return CliRunner() cligj-0.7.2/tests/onepoint.geojson000066400000000000000000000001651405425730400172010ustar00rootroot00000000000000{"geometry": {"coordinates": [-122.7282, 45.5801], "type": "Point"}, "id": "0", "properties": {}, "type": "Feature"} cligj-0.7.2/tests/point_pretty_geom.txt000066400000000000000000000000771405425730400202720ustar00rootroot00000000000000{ "coordinates": [-122.7282, 45.5801], "type": "Point" } cligj-0.7.2/tests/test_cli.py000077500000000000000000000134751405425730400161530ustar00rootroot00000000000000import os import os.path import sys import click import pytest import cligj def test_files_in(runner): @click.command() @cligj.files_in_arg def cmd(files): for f in files: click.echo(f) result = runner.invoke(cmd, ['1.tif', '2.tif']) assert not result.exception assert result.output.splitlines() == [ os.path.join(os.getcwd(), '1.tif'), os.path.join(os.getcwd(), '2.tif'), ] def test_files_inout(runner): @click.command() @cligj.files_inout_arg def cmd(files): for f in files: click.echo(f) result = runner.invoke(cmd, ['1.tif', '2.tif']) assert not result.exception assert result.output.splitlines() == [ os.path.join(os.getcwd(), '1.tif'), os.path.join(os.getcwd(), '2.tif'), ] def test_verbose(runner): @click.command() @cligj.verbose_opt def cmd(verbose): click.echo("%s" % verbose) result = runner.invoke(cmd, ['-vv']) assert not result.exception assert result.output.splitlines() == ['2'] def test_quiet(runner): @click.command() @cligj.quiet_opt def cmd(quiet): click.echo("%s" % quiet) result = runner.invoke(cmd, ['-qq']) assert not result.exception assert result.output.splitlines() == ['2'] def test_format(runner): @click.command() @cligj.format_opt def cmd(driver): click.echo("%s" % driver) result = runner.invoke(cmd, ['--driver', 'lol']) assert not result.exception assert result.output.splitlines() == ['lol'] result = runner.invoke(cmd, ['--format', 'lol']) assert not result.exception assert result.output.splitlines() == ['lol'] result = runner.invoke(cmd, ['-f', 'lol']) assert not result.exception assert result.output.splitlines() == ['lol'] result = runner.invoke(cmd) assert not result.exception assert result.output.splitlines() == ['GTiff'] def test_indent(runner): @click.command() @cligj.indent_opt def cmd(indent): click.echo("%s" % indent) result = runner.invoke(cmd, ['--indent', '2']) assert not result.exception assert result.output.splitlines() == ['2'] result = runner.invoke(cmd) assert not result.exception assert result.output.splitlines() == ['None'] def test_compact(runner): @click.command() @cligj.compact_opt def cmd(compact): click.echo("%s" % compact) result = runner.invoke(cmd, ['--compact']) assert not result.exception assert result.output.splitlines() == ['True'] result = runner.invoke(cmd, ['--not-compact']) assert not result.exception assert result.output.splitlines() == ['False'] result = runner.invoke(cmd) assert not result.exception assert result.output.splitlines() == ['False'] def test_precision(runner): @click.command() @cligj.precision_opt def cmd(precision): click.echo("%s" % precision) result = runner.invoke(cmd, ['--precision', '2']) assert not result.exception assert result.output.splitlines() == ['2'] result = runner.invoke(cmd) assert not result.exception assert result.output.splitlines() == ['-1'] def test_projection(runner): @click.command() @cligj.projection_geographic_opt @cligj.projection_projected_opt @cligj.projection_mercator_opt def cmd(projection): click.echo("%s" % projection) result = runner.invoke(cmd, ['--geographic']) assert not result.exception assert result.output.splitlines() == ['geographic'] result = runner.invoke(cmd, ['--projected']) assert not result.exception assert result.output.splitlines() == ['projected'] result = runner.invoke(cmd, ['--mercator']) assert not result.exception assert result.output.splitlines() == ['mercator'] result = runner.invoke(cmd) assert not result.exception assert result.output.splitlines() == ['geographic'] @pytest.mark.filterwarnings("ignore") @pytest.mark.parametrize( ("opt", "val"), [ ("--sequence", True), ("--no-sequence", False), (None, cligj.__version__.startswith("1.0")), ], ) def test_sequence(runner, opt, val): """True becomes the default in 1.0""" @click.command() @cligj.sequence_opt def cmd(sequence): click.echo(str(sequence)) result = runner.invoke(cmd, [opt] if opt is not None else []) assert not result.exception assert result.output.splitlines() == [str(val)] @pytest.mark.skipif(sys.version_info < (3,), reason="Requires Python 3") @pytest.mark.xfail(cligj.__version__.startswith("1.0"), reason="No warning in 1.0") def test_sequence_warns(runner): """Warn about --sequence until 1.0""" @click.command() @cligj.sequence_opt def cmd(sequence): click.echo(str(sequence)) with pytest.warns(FutureWarning): result = runner.invoke(cmd, ["--sequence"]) @pytest.mark.filterwarnings("ignore") @pytest.mark.parametrize(("opt", "val"), [("--rs", True), (None, False)]) def test_sequence_rs(runner, opt, val): @click.command() @cligj.sequence_opt @cligj.use_rs_opt def cmd(sequence, use_rs): click.echo(str(sequence)) click.echo(str(use_rs)) result = runner.invoke(cmd, ["--sequence"] + ([opt] if opt is not None else [])) assert not result.exception assert result.output.splitlines() == ["True", str(val)] @pytest.mark.parametrize( ("opt", "val"), [("--collection", "collection"), ("--feature", "feature"), ("--bbox", "bbox")], ) def test_geojson_type(runner, opt, val): @click.command() @cligj.geojson_type_collection_opt(True) @cligj.geojson_type_feature_opt(False) @cligj.geojson_type_bbox_opt(False) def cmd(geojson_type): click.echo(str(geojson_type)) result = runner.invoke(cmd, [opt]) assert not result.exception assert result.output.splitlines() == [val] cligj-0.7.2/tests/test_features.py000066400000000000000000000120031405425730400172010ustar00rootroot00000000000000import json import sys import pytest from cligj.features import \ coords_from_query, iter_query, to_feature, \ normalize_feature_inputs, normalize_feature_objects def test_iter_query_string(): assert iter_query("lolwut") == ["lolwut"] def test_iter_query_file(tmpdir): filename = str(tmpdir.join('test.txt')) with open(filename, 'w') as f: f.write("lolwut") assert iter_query(filename) == ["lolwut"] def test_coords_from_query_json(): assert coords_from_query("[-100, 40]") == (-100, 40) def test_coords_from_query_csv(): assert coords_from_query("-100, 40") == (-100, 40) def test_coords_from_query_ws(): assert coords_from_query("-100 40") == (-100, 40) @pytest.fixture def expected_features(): with open("tests/twopoints.geojson") as src: fc = json.loads(src.read()) return fc['features'] def _geoms(features): geoms = [] for feature in features: geoms.append(feature['geometry']) return geoms def test_featurecollection_file(expected_features): features = normalize_feature_inputs( None, 'features', ["tests/twopoints.geojson"]) assert _geoms(features) == _geoms(expected_features) def test_featurecollection_pretty_file(expected_features): features = normalize_feature_inputs( None, 'features', ["tests/twopoints-pretty.json"]) assert _geoms(features) == _geoms(expected_features) def test_featurecollection_stdin(expected_features): sys.stdin = open("tests/twopoints.geojson", 'r') features = normalize_feature_inputs(None, 'features', []) assert _geoms(features) == _geoms(expected_features) def test_featuresequence(expected_features): features = normalize_feature_inputs( None, 'features', ["tests/twopoints_seq.txt"]) assert _geoms(features) == _geoms(expected_features) # TODO test path to sequence files fail def test_featuresequence_stdin(expected_features): sys.stdin = open("tests/twopoints_seq.txt", 'r') features = normalize_feature_inputs(None, 'features', []) assert _geoms(features) == _geoms(expected_features) def test_singlefeature(expected_features): features = normalize_feature_inputs( None, 'features', ["tests/onepoint.geojson"]) assert _geoms(features) == _geoms([expected_features[0]]) def test_singlefeature_stdin(expected_features): sys.stdin = open("tests/onepoint.geojson", 'r') features = normalize_feature_inputs(None, 'features', []) assert _geoms(features) == _geoms([expected_features[0]]) def test_featuresequencers(expected_features): features = normalize_feature_inputs( None, 'features', ["tests/twopoints_seqrs.txt"]) assert _geoms(features) == _geoms(expected_features) def test_featuresequencers_stdin(expected_features): sys.stdin = open("tests/twopoints_seqrs.txt", 'r') features = normalize_feature_inputs(None, 'features', []) assert _geoms(features) == _geoms(expected_features) def test_coordarrays(expected_features): inputs = ["[-122.7282, 45.5801]", "[-121.3153, 44.0582]"] features = normalize_feature_inputs(None, 'features', inputs) assert _geoms(features) == _geoms(expected_features) def test_coordpairs_comma(expected_features): inputs = ["-122.7282, 45.5801", "-121.3153, 44.0582"] features = normalize_feature_inputs(None, 'features', inputs) assert _geoms(features) == _geoms(expected_features) def test_coordpairs_space(expected_features): inputs = ["-122.7282 45.5801", "-121.3153 44.0582"] features = normalize_feature_inputs(None, 'features', inputs) assert _geoms(features) == _geoms(expected_features) def test_geometrysequence(expected_features): features = normalize_feature_inputs(None, 'features', ["tests/twopoints_geom_seq.txt"]) assert _geoms(features) == _geoms(expected_features) def test_geometrysequencers(expected_features): features = normalize_feature_inputs(None, 'features', ["tests/twopoints_geom_seqrs.txt"]) assert _geoms(features) == _geoms(expected_features) def test_geometrypretty(expected_features): features = normalize_feature_inputs(None, 'features', ["tests/point_pretty_geom.txt"]) assert _geoms(features)[0] == _geoms(expected_features)[0] class MockGeo(object): def __init__(self, feature): self.__geo_interface__ = feature def test_normalize_feature_objects(expected_features): objs = [MockGeo(f) for f in expected_features] assert expected_features == list(normalize_feature_objects(objs)) assert expected_features == list(normalize_feature_objects(expected_features)) def test_normalize_feature_objects_bad(expected_features): objs = [MockGeo(f) for f in expected_features] objs.append(MockGeo(dict())) with pytest.raises(ValueError): list(normalize_feature_objects(objs)) def test_to_feature(expected_features): geom = expected_features[0]['geometry'] feat = {'type': 'Feature', 'properties': {}, 'geometry': geom} assert to_feature(feat) == to_feature(geom) with pytest.raises(ValueError): assert to_feature({'type': 'foo'}) cligj-0.7.2/tests/twopoints-pretty.json000066400000000000000000000044421405425730400202500ustar00rootroot00000000000000{ "features": [ { "bbox": [ -122.9292140099711, 45.37948199034149, -122.44106199104115, 45.858097009742835 ], "center": [ -122.7282, 45.5801 ], "context": [ { "id": "postcode.2503633822", "text": "97203" }, { "id": "region.3470299826", "text": "Oregon" }, { "id": "country.4150104525", "short_code": "us", "text": "United States" } ], "geometry": { "coordinates": [ -122.7282, 45.5801 ], "type": "Point" }, "id": "place.42767", "place_name": "Portland, Oregon, United States", "properties": {}, "relevance": 0.999, "text": "Portland", "type": "Feature" }, { "bbox": [ -121.9779540096568, 43.74737999114854, -120.74788099000016, 44.32812500969035 ], "center": [ -121.3153, 44.0582 ], "context": [ { "id": "postcode.3332732485", "text": "97701" }, { "id": "region.3470299826", "text": "Oregon" }, { "id": "country.4150104525", "short_code": "us", "text": "United States" } ], "geometry": { "coordinates": [ -121.3153, 44.0582 ], "type": "Point" }, "id": "place.3965", "place_name": "Bend, Oregon, United States", "properties": {}, "relevance": 0.999, "text": "Bend", "type": "Feature" } ], "type": "FeatureCollection" } cligj-0.7.2/tests/twopoints.geojson000066400000000000000000000020711405425730400174120ustar00rootroot00000000000000{"features": [{"bbox": [-122.9292140099711, 45.37948199034149, -122.44106199104115, 45.858097009742835], "center": [-122.7282, 45.5801], "context": [{"id": "postcode.2503633822", "text": "97203"}, {"id": "region.3470299826", "text": "Oregon"}, {"id": "country.4150104525", "short_code": "us", "text": "United States"}], "geometry": {"coordinates": [-122.7282, 45.5801], "type": "Point"}, "id": "place.42767", "place_name": "Portland, Oregon, United States", "properties": {}, "relevance": 0.999, "text": "Portland", "type": "Feature"}, {"bbox": [-121.9779540096568, 43.74737999114854, -120.74788099000016, 44.32812500969035], "center": [-121.3153, 44.0582], "context": [{"id": "postcode.3332732485", "text": "97701"}, {"id": "region.3470299826", "text": "Oregon"}, {"id": "country.4150104525", "short_code": "us", "text": "United States"}], "geometry": {"coordinates": [-121.3153, 44.0582], "type": "Point"}, "id": "place.3965", "place_name": "Bend, 😀regon, United States", "properties": {}, "relevance": 0.999, "text": "Bend", "type": "Feature"}], "type": "FeatureCollection"} cligj-0.7.2/tests/twopoints_geom_seq.txt000066400000000000000000000001561405425730400204460ustar00rootroot00000000000000{"coordinates": [-122.7282, 45.5801], "type": "Point"} {"coordinates": [-121.3153, 44.0582], "type": "Point"} cligj-0.7.2/tests/twopoints_geom_seqrs.txt000066400000000000000000000002041405425730400210050ustar00rootroot00000000000000{ "coordinates": [-122.7282, 45.5801], "type": "Point" } { "coordinates": [-121.3153, 44.0582], "type": "Point" } cligj-0.7.2/tests/twopoints_seq.txt000066400000000000000000000003521405425730400174350ustar00rootroot00000000000000{"geometry": {"coordinates": [-122.7282, 45.5801], "type": "Point"}, "id": "0", "properties": {}, "type": "Feature"} {"geometry": {"coordinates": [-121.3153, 44.0582], "type": "Point"}, "id": "1", "properties": {}, "type": "Feature"} cligj-0.7.2/tests/twopoints_seqrs.txt000066400000000000000000000003761405425730400200100ustar00rootroot00000000000000{"geometry": {"coordinates": [-122.7282, 45.5801], "type": "Point"}, "id": "0", "properties": {}, "type": "Feature"} {"geometry": { "coordinates": [-121.3153, 44.0582], "type": "Point"}, "id": "1", "properties": {}, "type": "Feature"}