diff --git a/.flake8 b/.flake8 deleted file mode 100644 index a97176c..0000000 --- a/.flake8 +++ /dev/null @@ -1,8 +0,0 @@ -[flake8] -max-line-length = 120 -show_source = true -max_complexity = 15 -exclude = mapbox_vector_tiles/Mapbox -extend-ignore = - # See https://github.com/PyCQA/pycodestyle/issues/373 - E203, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index deb11a1..67aebb0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,10 +12,10 @@ jobs: strategy: matrix: python-version: - - '3.9' - - '3.10' - - '3.11' - - '3.12' + - "3.9" + - "3.10" + - "3.11" + - "3.12" steps: - uses: actions/checkout@v3 @@ -27,7 +27,7 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - cache: 'poetry' + cache: "poetry" - name: Install dependencies run: | @@ -45,7 +45,7 @@ jobs: github-token: ${{ secrets.github_token }} flag-name: run-${{ matrix.python-version }} parallel: true - path-to-lcov: './coverage.lcov' + path-to-lcov: "./coverage.lcov" finish: needs: tests @@ -56,4 +56,4 @@ jobs: with: github-token: ${{ secrets.github_token }} parallel-finished: true - path-to-lcov: './coverage.lcov' + path-to-lcov: "./coverage.lcov" diff --git a/.gitignore b/.gitignore index 06e3be6..dd74ab2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ mapbox_vector_tile.egg-info/ .tox/ .coverage bench/fgeoms.wkt.zip +node_modules/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4d9cf22..f72d5f2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,31 +8,22 @@ repos: - id: trailing-whitespace - id: check-toml - id: detect-private-key - - id: fix-encoding-pragma - args: [ --remove ] - id: check-merge-conflict - - repo: https://github.com/MarcoGorelli/absolufy-imports - rev: v0.3.1 + - repo: https://github.com/python-poetry/poetry + rev: 1.8.0 hooks: - - id: absolufy-imports - - repo: https://github.com/asottile/pyupgrade - rev: v3.15.2 + - id: poetry-check + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.5 hooks: - - id: pyupgrade - args: [ --py39-plus ] - - repo: https://github.com/PyCQA/isort - rev: 5.13.2 + - id: ruff + args: [--fix] + types_or: [python, pyi, jupyter] + - id: ruff-format + types_or: [python, pyi, jupyter] + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v4.0.0-alpha.8 hooks: - - id: isort - - repo: https://github.com/psf/black - rev: 24.4.2 - hooks: - - id: black - - repo: https://github.com/asottile/yesqa - rev: v1.5.0 - hooks: - - id: yesqa - - repo: https://github.com/pycqa/flake8 - rev: 7.0.0 - hooks: - - id: flake8 + - id: prettier + args: ["--print-width", "120"] + require_serial: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 1594b5e..160918d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,175 +1,153 @@ -Version 2.1.0 -------------- +## Version 2.1.0 -*In development* +_In development_ - Drop the support for Python 3.8 and add the support for Python 3.12 - Update the minimum required version for `protobuf` +- Replace Isort, Flake8 and Black by Ruff in the `.pre-commit-config.yaml` file +- Use `prettier` pre-commit tool to prettify Markdown and Yaml files +- Use `poetry-check` pre-commit tool to avoid incompatibilities between `poetry.lock` and `pyproject.toml` -Version 2.0.1 -------------- +## Version 2.0.1 -* Support previous pre 2.0 encode/decode method signatures with deprecation warning. +- Support previous pre 2.0 encode/decode method signatures with deprecation warning. [#129](https://github.com/tilezen/mapbox-vector-tile/pull/129) -Version 2.0.0 -------------- +## Version 2.0.0 -* Drop Python 2 support -* Usage of `tox` for tests -* Add GitHub Actions -* Add pre-commit tool -* Regenerate the vector tile protobuf Python code to solve +- Drop Python 2 support +- Usage of `tox` for tests +- Add GitHub Actions +- Add pre-commit tool +- Regenerate the vector tile protobuf Python code to solve [#113](https://github.com/tilezen/mapbox-vector-tile/issues/113) -* Support for Python 3.11 -* Delete the `round_fn` argument as Python 2 has been dropped -* Use `pyproject.toml` and Poetry to replace the `setup.py` file -* Use `geom_type` property instead of deprecated `type` -* Add the possibility to give a coordinates transformer -* Add a `geojson` option. See [#107](https://github.com/tilezen/mapbox-vector-tile/issues/107) -* Refactor the options using the `per_layer_options` and `default_options` dictionaries. -* Add the option `max_geometry_validate_tries`. +- Support for Python 3.11 +- Delete the `round_fn` argument as Python 2 has been dropped +- Use `pyproject.toml` and Poetry to replace the `setup.py` file +- Use `geom_type` property instead of deprecated `type` +- Add the possibility to give a coordinates transformer +- Add a `geojson` option. See [#107](https://github.com/tilezen/mapbox-vector-tile/issues/107) +- Refactor the options using the `per_layer_options` and `default_options` dictionaries. +- Add the option `max_geometry_validate_tries`. +## Version 1.2.1 -Version 1.2.1 -------------- +- Add the trove classifiers to the setup.py -* Add the trove classifiers to the setup.py +## Version 1.2.0 -Version 1.2.0 -------------- +- Performance focused release, including: +- Enable Shapely speedups, when available +- Skip inners which cause exceptions +- Union inners in blocks when making valid +- Make benchmark script python3 compatible +- Fix test to support different versions of GEOS -* Performance focused release, including: -* Enable Shapely speedups, when available -* Skip inners which cause exceptions -* Union inners in blocks when making valid -* Make benchmark script python3 compatible -* Fix test to support different versions of GEOS +## Version 1.1.0 -Version 1.1.0 -------------- +- Include LICENSE & CHANGELOG.md in sdist tarballs +- Refactor geometry encoding logic, including skipping tiny geometries +- Decoded geometry is now geojson-ish dict +- Winding order is now optional +- Add benchmarking around round function and document how to improve performance +- Document performance tip for protobuf encoding with C bindings for Debian -* Include LICENSE & CHANGELOG.md in sdist tarballs -* Refactor geometry encoding logic, including skipping tiny geometries -* Decoded geometry is now geojson-ish dict -* Winding order is now optional -* Add benchmarking around round function and document how to improve performance -* Document performance tip for protobuf encoding with C bindings for Debian +## Version 1.0.0 -Version 1.0.0 -------------- +- Generate more valid polygons and multipolygons using [pyclipper](https://pypi.python.org/pypi/pyclipper) library for v2 MVT compliance (but we're still not fully v2 compliant for [other](https://github.com/tilezen/mapbox-vector-tile/issues/42) reasons). +- Handle edge cases where polygon buffer makes a multi-polygon, ensuring inner rings are dropped when subtracting them from the polygon would make it invalid, and not adding multipolygons as array elements for multipolygon constructor. +- Calculate area more properly by using PolyTree result from Clipper. +- Factor out polygon validity code into its own file. -* Generate more valid polygons and multipolygons using [pyclipper](https://pypi.python.org/pypi/pyclipper) library for v2 MVT compliance (but we're still not fully v2 compliant for [other](https://github.com/tilezen/mapbox-vector-tile/issues/42) reasons). -* Handle edge cases where polygon buffer makes a multi-polygon, ensuring inner rings are dropped when subtracting them from the polygon would make it invalid, and not adding multipolygons as array elements for multipolygon constructor. -* Calculate area more properly by using PolyTree result from Clipper. -* Factor out polygon validity code into its own file. +## Version 0.5.0 -Version 0.5.0 -------------- +- Improved results from `on_invalid_geometry_make_valid` when the geometry is self-crossing. It was possible for large parts of the geometry to be discarded, and it is now less likely. See [PR 66](https://github.com/tilezen/mapbox-vector-tile/pull/66) for more information. -* Improved results from `on_invalid_geometry_make_valid` when the geometry is self-crossing. It was possible for large parts of the geometry to be discarded, and it is now less likely. See [PR 66](https://github.com/tilezen/mapbox-vector-tile/pull/66) for more information. +## Version 0.4.0 -Version 0.4.0 -------------- +- Custom rounding functions: a `round_fn` parameter was added to the `encode` function, which allows control over how floating point coordinates are transformed to integer ones. See [PR 55](https://github.com/tilezen/mapbox-vector-tile/pull/55). +- Custom validity functions: an `on_invalid_geometry` parameter was added to the `encode` function, which is called when invalid geometry is found, or created through coordinate rounding. See [PR 46](https://github.com/tilezen/mapbox-vector-tile/pull/46). +- Winding order bug fix: See [issue 57](https://github.com/tilezen/mapbox-vector-tile/issues/57) and [PR 59](https://github.com/tilezen/mapbox-vector-tile/pull/59). +- Performance improvements: including a 2x speedup from using `tuple`s instead of `dict`s for coordinates, see [PR 56](https://github.com/tilezen/mapbox-vector-tile/pull/56). +- Improvements to PY3 compatibility: See [PR 52](https://github.com/tilezen/mapbox-vector-tile/pull/52). -* Custom rounding functions: a `round_fn` parameter was added to the `encode` function, which allows control over how floating point coordinates are transformed to integer ones. See [PR 55](https://github.com/tilezen/mapbox-vector-tile/pull/55). -* Custom validity functions: an `on_invalid_geometry` parameter was added to the `encode` function, which is called when invalid geometry is found, or created through coordinate rounding. See [PR 46](https://github.com/tilezen/mapbox-vector-tile/pull/46). -* Winding order bug fix: See [issue 57](https://github.com/tilezen/mapbox-vector-tile/issues/57) and [PR 59](https://github.com/tilezen/mapbox-vector-tile/pull/59). -* Performance improvements: including a 2x speedup from using `tuple`s instead of `dict`s for coordinates, see [PR 56](https://github.com/tilezen/mapbox-vector-tile/pull/56). -* Improvements to PY3 compatibility: See [PR 52](https://github.com/tilezen/mapbox-vector-tile/pull/52). +## Version 0.3.0 -Version 0.3.0 -------------- +- python3 compatability improvements +- travis integration +- documentation updates +- insert CMD_SEG_END for MultiPolygons +- decode multipolygons correctly +- encode tiles using version 1 -* python3 compatability improvements -* travis integration -* documentation updates -* insert CMD_SEG_END for MultiPolygons -* decode multipolygons correctly -* encode tiles using version 1 +## Version 0.2.1 -Version 0.2.1 -------------- +- include README.md in distribution to fix install -* include README.md in distribution to fix install +## Version 0.2.0 -Version 0.2.0 -------------- +- python3 updates +- enforce winding order on multipolygons +- update key/val handling +- round floating point values instead of truncating +- add option to quantize bounds +- add option to flip y coord system +- add ability to pass custom extents -* python3 updates -* enforce winding order on multipolygons -* update key/val handling -* round floating point values instead of truncating -* add option to quantize bounds -* add option to flip y coord system -* add ability to pass custom extents +## Version 0.1.0 -Version 0.1.0 -------------- +- Add compatibility with python 3 +- Handle multipolygons as single features +- Use winding order from mapbox vector tile 2.0 spec +- Support custom extents when decoding -* Add compatibility with python 3 -* Handle multipolygons as single features -* Use winding order from mapbox vector tile 2.0 spec -* Support custom extents when decoding +## Version 0.0.11 -Version 0.0.11 --------------- +- Decode string keys to utf-8 -* Decode string keys to utf-8 +## Version 0.0.10 -Version 0.0.10 --------------- +- Allow encoder to accept shapely objects directly -* Allow encoder to accept shapely objects directly +## Version 0.0.9 -Version 0.0.9 -------------- +- Handle tiles from java-vector-tile (zero pad binary integers) +- Update README -* Handle tiles from java-vector-tile (zero pad binary integers) -* Update README +## Version 0.0.8 -Version 0.0.8 -------------- +- Handle unicode properties -* Handle unicode properties +## Version 0.0.7 -Version 0.0.7 -------------- +- Update id handling behavior -* Update id handling behavior +## Version 0.0.6 -Version 0.0.6 -------------- +- Explode multipolygons into several features +- https://github.com/tilezen/mapbox-vector-tile/issues/4 +- Resolve issue when id is passed in +- More tests -* Explode multipolygons into several features -* https://github.com/tilezen/mapbox-vector-tile/issues/4 -* Resolve issue when id is passed in -* More tests +## Version 0.0.5 -Version 0.0.5 -------------- +- Removing the option of encoding floats in big endian +- Updated tests -* Removing the option of encoding floats in big endian -* Updated tests +## Version 0.0.4 -Version 0.0.4 -------------- +- Bug fix - does not try to load wkt geom if wkb succeeds -* Bug fix - does not try to load wkt geom if wkb succeeds +## Version 0.0.3 -Version 0.0.3 -------------- +- Option to encode floats in little endian -* Option to encode floats in little endian +## Version 0.0.2 -Version 0.0.2 -------------- +- WKT Support +- Better Documentation +- More tests -* WKT Support -* Better Documentation -* More tests +## Version 0.0.1 -Version 0.0.1 -------------- - -* Initial release +- Initial release diff --git a/README.md b/README.md index db0d194..a8c1962 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,10 @@ -Mapbox Vector Tile -================== +# Mapbox Vector Tile [![CI](https://github.com/tilezen/mapbox-vector-tile/actions/workflows/ci.yml/badge.svg)](https://github.com/tilezen/mapbox-vector-tile/actions/workflows/ci.yml) [![pre-commit](https://github.com/tilezen/mapbox-vector-tile/actions/workflows/pre-commit.yml/badge.svg)](https://github.com/tilezen/mapbox-vector-tile/actions/workflows/pre-commit.yml) [![Coverage Status](https://coveralls.io/repos/github/tilezen/mapbox-vector-tile/badge.svg?branch=master)](https://coveralls.io/github/tilezen/mapbox-vector-tile?branch=master) -Installation ------------- +## Installation mapbox-vector-tile is compatible with Python 3.9 or newer. It is listed on PyPi as `mapbox-vector-tile`. The recommended way to install is via `pip`: @@ -22,24 +20,22 @@ when changing the Coordinate Reference System when encoding or decoding tiles. pip install mapbox-vector-tile[proj] ``` -Encoding --------- +## Encoding Encode method expects an array of layers or at least a single valid layer. A valid layer is a dictionary with the following keys -* `name`: layer name -* `features`: an array of features. A feature is a dictionary with the following keys: +- `name`: layer name +- `features`: an array of features. A feature is a dictionary with the following keys: - * `geometry`: representation of the feature geometry in WKT, WKB, or a shapely geometry. Coordinates are relative to the tile, scaled in the range `[0, 4096)`. See below for example code to perform the necessary transformation. *Note* that `GeometryCollection` types are not supported, and will trigger a `ValueError`. - * `properties`: a dictionary with a few keys and their corresponding values. + - `geometry`: representation of the feature geometry in WKT, WKB, or a shapely geometry. Coordinates are relative to the tile, scaled in the range `[0, 4096)`. See below for example code to perform the necessary transformation. _Note_ that `GeometryCollection` types are not supported, and will trigger a `ValueError`. + - `properties`: a dictionary with a few keys and their corresponding values. The encoding operation accepts options which can be defined per layer using the `per_layer_options` argument. If there is missing layer or missing options values in the `per_layer_options`, the options of `default_options` are taken into account. Finally, global default values are used. See the docstring of the `encode` method for more details about the available options and their global default values. - ```python >>> import mapbox_vector_tile @@ -275,8 +271,7 @@ mapbox_vector_tile.encode([ ], default_options={"quantize_bounds": (0.0, 0.0, 10.0, 10.0), "extents":50}) ``` -Decoding --------- +## Decoding Decode method takes in a valid google.protobuf.message Tile and returns decoded string in the following format: @@ -368,9 +363,7 @@ Here's how you might decode a tile from a file. The `decode` function has a `geojson` option which enforces a GeoJson RFC7946 compatible result. Its default value is `True`. To enforce the behaviour of versions <2.0.0, please use `geojson=False`. - -Use native protobuf library for performance ------------------------------------------- +## Use native protobuf library for performance The c++ implementation of the underlying protobuf library is more performant than the pure python one. Depending on your operating system, you might need to [compile the C++ library](https://github.com/google/protobuf/tree/master/python#c-implementation) or install it. @@ -392,7 +385,6 @@ and then: $ protoc -I=mapbox_vector_tile/Mapbox/ --python_out=mapbox_vector_tile/Mapbox/ mapbox_vector_tile/Mapbox/vector_tile.proto -Changelog ---------- +## Changelog Click [here](https://github.com/tilezen/mapbox-vector-tile/blob/master/CHANGELOG.md) to see what changed over time in various versions. diff --git a/bench/bench_encode.py b/bench/bench_encode.py index 4761003..7e71266 100755 --- a/bench/bench_encode.py +++ b/bench/bench_encode.py @@ -36,16 +36,14 @@ def make_layers(shapes, geom_dicts=False): def run_test(layers): print("Running perf test") - i = 0 profiler = cProfile.Profile() - for layer in layers: + for i, layer in enumerate(layers): layer_description = {"features": layer, "name": "bar"} profiler.enable() encode(layer_description, default_options={"on_invalid_geometry": on_invalid_geometry_ignore}) profiler.disable() if i % 100 == 0: print(f"{i} tiles produced") - i += 1 print("Perf result :") profiler.print_stats() diff --git a/mapbox_vector_tile/__init__.py b/mapbox_vector_tile/__init__.py index a7da990..3008086 100644 --- a/mapbox_vector_tile/__init__.py +++ b/mapbox_vector_tile/__init__.py @@ -33,7 +33,7 @@ def decode(tile, per_layer_options=None, default_options=None, **kwargs): to `False`, the retrieved dictionary is a valid geojson file. Default to `True`. """ if kwargs: - warnings.warn("`decode` signature has changed, use `default_options` instead", DeprecationWarning) + warnings.warn("`decode` signature has changed, use `default_options` instead", DeprecationWarning, stacklevel=2) default_options = {**kwargs, **(default_options or {})} vector_tile = decoder.TileData(pbf_data=tile, per_layer_options=per_layer_options, default_options=default_options) message = vector_tile.get_message() @@ -80,11 +80,11 @@ def encode(layers, per_layer_options=None, default_options=None, **kwargs): to 5. """ if kwargs: - warnings.warn("`encode` signature has changed, use `default_options` instead", DeprecationWarning) + warnings.warn("`encode` signature has changed, use `default_options` instead", DeprecationWarning, stacklevel=2) default_options = {**kwargs, **(default_options or {})} vector_tile = encoder.VectorTile(default_options=default_options) if per_layer_options is None: - per_layer_options = dict() + per_layer_options = {} if isinstance(layers, list): for layer in layers: layer_name = layer["name"] diff --git a/mapbox_vector_tile/decoder.py b/mapbox_vector_tile/decoder.py index 3c66198..2f45508 100644 --- a/mapbox_vector_tile/decoder.py +++ b/mapbox_vector_tile/decoder.py @@ -4,10 +4,10 @@ CMD_LINE_TO, CMD_MOVE_TO, CMD_SEG_END, - get_decode_options, LINESTRING, POINT, POLYGON, + get_decode_options, zig_zag_decode, ) @@ -17,7 +17,7 @@ def __init__(self, pbf_data, per_layer_options=None, default_options=None): self.tile = vector_tile.tile() self.tile.ParseFromString(pbf_data) self.default_options = default_options - self.per_layer_options = per_layer_options if per_layer_options is not None else dict() + self.per_layer_options = per_layer_options if per_layer_options is not None else {} def get_message(self): tile = {} @@ -81,7 +81,7 @@ def parse_value(val): @staticmethod def _area_sign(ring): - a = sum(ring[i][0] * ring[i + 1][1] - ring[i + 1][0] * ring[i][1] for i in range(0, len(ring) - 1)) + a = sum(ring[i][0] * ring[i + 1][1] - ring[i + 1][0] * ring[i][1] for i in range(len(ring) - 1)) return -1 if a < 0 else 1 if a > 0 else 0 @staticmethod @@ -112,19 +112,18 @@ def parse_geometry(self, geom, ftype, extent, y_coord_down, transformer): # noq coords = [] elif cmd in (CMD_MOVE_TO, CMD_LINE_TO): - if coords and cmd == CMD_MOVE_TO: - if ftype in (LINESTRING, POLYGON): - # multi line string or polygon our encoder includes CMD_SEG_END to denote the end of a - # polygon ring, but this path would also handle the case where we receive a move without a - # previous close on polygons - - # for polygons, we want to ensure that it is closed - if ftype == POLYGON: - self._ensure_polygon_closed(coords) - parts.append(coords) - coords = [] - - for point in range(0, cmd_len): + if coords and cmd == CMD_MOVE_TO and ftype in (LINESTRING, POLYGON): + # multi line string or polygon our encoder includes CMD_SEG_END to denote the end of a + # polygon ring, but this path would also handle the case where we receive a move without a + # previous close on polygons + + # for polygons, we want to ensure that it is closed + if ftype == POLYGON: + self._ensure_polygon_closed(coords) + parts.append(coords) + coords = [] + + for _ in range(cmd_len): x = geom[i] i = i + 1 diff --git a/mapbox_vector_tile/encoder.py b/mapbox_vector_tile/encoder.py index 07458a9..4d1f6aa 100644 --- a/mapbox_vector_tile/encoder.py +++ b/mapbox_vector_tile/encoder.py @@ -3,13 +3,13 @@ from shapely.geometry import shape as shapely_shape from shapely.geometry.base import BaseGeometry from shapely.geometry.multipolygon import MultiPolygon -from shapely.geometry.polygon import orient, Polygon +from shapely.geometry.polygon import Polygon, orient from shapely.ops import transform from shapely.wkb import loads as load_wkb from shapely.wkt import loads as load_wkt -from mapbox_vector_tile.Mapbox import vector_tile_pb2 as vector_tile from mapbox_vector_tile.geom_encoder import GeometryEncoder +from mapbox_vector_tile.Mapbox import vector_tile_pb2 as vector_tile from mapbox_vector_tile.polygon import make_it_valid from mapbox_vector_tile.utils import get_encode_options @@ -143,11 +143,7 @@ def enforce_multipolygon_winding_order(self, shape, n_try): if not parts: return None - if len(parts) == 1: - oriented_shape = parts[0] - else: - oriented_shape = MultiPolygon(parts) - + oriented_shape = parts[0] if len(parts) == 1 else MultiPolygon(parts) oriented_shape = self.handle_shape_validity(oriented_shape, n_try) return oriented_shape @@ -203,9 +199,8 @@ def add_feature(self, feature, shape): f = self.layer.features.add() fid = feature.get("id") - if fid is not None: - if isinstance(fid, Number) and fid >= 0: - f.id = fid + if fid is not None and isinstance(fid, Number) and fid >= 0: + f.id = fid # properties properties = feature.get("properties") @@ -253,11 +248,7 @@ def _handle_attr(self, layer, feature, props): feature.tags.append(self.seen_keys_idx[k]) - if isinstance(v, bool): - values_idx = self.seen_values_bool_idx - else: - values_idx = self.seen_values_idx - + values_idx = self.seen_values_bool_idx if isinstance(v, bool) else self.seen_values_idx if v not in values_idx: values_idx[v] = self.val_idx self.val_idx += 1 diff --git a/mapbox_vector_tile/optimise.py b/mapbox_vector_tile/optimise.py index 57d23a5..a0bea0b 100644 --- a/mapbox_vector_tile/optimise.py +++ b/mapbox_vector_tile/optimise.py @@ -28,7 +28,7 @@ def _update_table(counts, table): # Sort string table by usage, so most commonly-used values are # assigned the smallest indices. Since indices are encoded as # varints, this should make best use of the space. - sort = list(sorted(((c, k) for k, c in counts.items()), reverse=True)) + sort = sorted(((c, k) for k, c in counts.items()), reverse=True) # construct the re-ordered string table new_table = [] @@ -42,7 +42,7 @@ def _update_table(counts, table): # construct a lookup table from the old to the new indices. new_indexes = {} - for i, (c, k) in enumerate(sort): + for i, (_, k) in enumerate(sort): new_indexes[k] = i return new_indexes @@ -112,7 +112,7 @@ def _decode_lines(geom): current_line.extend(geom[i:next_i]) # but we still need to decode it to figure out where each move-to command is in absolute space. - for j in range(0, run_length): + for j in range(run_length): dx = zig_zag_decode(geom[i + 1 + 2 * j]) dy = zig_zag_decode(geom[i + 2 + 2 * j]) x += dx diff --git a/mapbox_vector_tile/polygon.py b/mapbox_vector_tile/polygon.py index 2ff2ee2..610eb9a 100644 --- a/mapbox_vector_tile/polygon.py +++ b/mapbox_vector_tile/polygon.py @@ -93,10 +93,7 @@ def _polytree_node_to_shapely(node): # Check expectations: a node should be a hole, _or_ return children. This is because children of holes must # be outers, and should be on the polygons list. assert len(children) == 0 - if node.Contour: - children = [node.Contour] - else: - children = [] + children = [node.Contour] if node.Contour else [] elif node.Contour: poly = _contour_to_poly(node.Contour) @@ -106,10 +103,7 @@ def _polytree_node_to_shapely(node): # 1,000s of seconds. Instead, we can group inners together, which reduces the number of times we call the # expensive 'difference' method. block_size = 200 - if len(children) > block_size: - inners = _union_in_blocks(children, block_size) - else: - inners = _generate_polys(children) + inners = _union_in_blocks(children, block_size) if len(children) > block_size else _generate_polys(children) for inner in inners: # The difference of two valid polygons may fail, and in this situation we'd like to be able to display diff --git a/poetry.lock b/poetry.lock index 4a9b4b2..564014f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -46,63 +46,63 @@ files = [ [[package]] name = "coverage" -version = "7.5.0" +version = "7.5.2" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:432949a32c3e3f820af808db1833d6d1631664d53dd3ce487aa25d574e18ad1c"}, - {file = "coverage-7.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2bd7065249703cbeb6d4ce679c734bef0ee69baa7bff9724361ada04a15b7e3b"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbfe6389c5522b99768a93d89aca52ef92310a96b99782973b9d11e80511f932"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39793731182c4be939b4be0cdecde074b833f6171313cf53481f869937129ed3"}, - {file = "coverage-7.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85a5dbe1ba1bf38d6c63b6d2c42132d45cbee6d9f0c51b52c59aa4afba057517"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:357754dcdfd811462a725e7501a9b4556388e8ecf66e79df6f4b988fa3d0b39a"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a81eb64feded34f40c8986869a2f764f0fe2db58c0530d3a4afbcde50f314880"}, - {file = "coverage-7.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51431d0abbed3a868e967f8257c5faf283d41ec882f58413cf295a389bb22e58"}, - {file = "coverage-7.5.0-cp310-cp310-win32.whl", hash = "sha256:f609ebcb0242d84b7adeee2b06c11a2ddaec5464d21888b2c8255f5fd6a98ae4"}, - {file = "coverage-7.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6782cd6216fab5a83216cc39f13ebe30adfac2fa72688c5a4d8d180cd52e8f6a"}, - {file = "coverage-7.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e768d870801f68c74c2b669fc909839660180c366501d4cc4b87efd6b0eee375"}, - {file = "coverage-7.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84921b10aeb2dd453247fd10de22907984eaf80901b578a5cf0bb1e279a587cb"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:710c62b6e35a9a766b99b15cdc56d5aeda0914edae8bb467e9c355f75d14ee95"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c379cdd3efc0658e652a14112d51a7668f6bfca7445c5a10dee7eabecabba19d"}, - {file = "coverage-7.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fea9d3ca80bcf17edb2c08a4704259dadac196fe5e9274067e7a20511fad1743"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:41327143c5b1d715f5f98a397608f90ab9ebba606ae4e6f3389c2145410c52b1"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:565b2e82d0968c977e0b0f7cbf25fd06d78d4856289abc79694c8edcce6eb2de"}, - {file = "coverage-7.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cf3539007202ebfe03923128fedfdd245db5860a36810136ad95a564a2fdffff"}, - {file = "coverage-7.5.0-cp311-cp311-win32.whl", hash = "sha256:bf0b4b8d9caa8d64df838e0f8dcf68fb570c5733b726d1494b87f3da85db3a2d"}, - {file = "coverage-7.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c6384cc90e37cfb60435bbbe0488444e54b98700f727f16f64d8bfda0b84656"}, - {file = "coverage-7.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fed7a72d54bd52f4aeb6c6e951f363903bd7d70bc1cad64dd1f087980d309ab9"}, - {file = "coverage-7.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cbe6581fcff7c8e262eb574244f81f5faaea539e712a058e6707a9d272fe5b64"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad97ec0da94b378e593ef532b980c15e377df9b9608c7c6da3506953182398af"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd4bacd62aa2f1a1627352fe68885d6ee694bdaebb16038b6e680f2924a9b2cc"}, - {file = "coverage-7.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf032b6c105881f9d77fa17d9eebe0ad1f9bfb2ad25777811f97c5362aa07f2"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ba01d9ba112b55bfa4b24808ec431197bb34f09f66f7cb4fd0258ff9d3711b1"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:f0bfe42523893c188e9616d853c47685e1c575fe25f737adf473d0405dcfa7eb"}, - {file = "coverage-7.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a9a7ef30a1b02547c1b23fa9a5564f03c9982fc71eb2ecb7f98c96d7a0db5cf2"}, - {file = "coverage-7.5.0-cp312-cp312-win32.whl", hash = "sha256:3c2b77f295edb9fcdb6a250f83e6481c679335ca7e6e4a955e4290350f2d22a4"}, - {file = "coverage-7.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:427e1e627b0963ac02d7c8730ca6d935df10280d230508c0ba059505e9233475"}, - {file = "coverage-7.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9dd88fce54abbdbf4c42fb1fea0e498973d07816f24c0e27a1ecaf91883ce69e"}, - {file = "coverage-7.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a898c11dca8f8c97b467138004a30133974aacd572818c383596f8d5b2eb04a9"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07dfdd492d645eea1bd70fb1d6febdcf47db178b0d99161d8e4eed18e7f62fe7"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3d117890b6eee85887b1eed41eefe2e598ad6e40523d9f94c4c4b213258e4a4"}, - {file = "coverage-7.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6afd2e84e7da40fe23ca588379f815fb6dbbb1b757c883935ed11647205111cb"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9960dd1891b2ddf13a7fe45339cd59ecee3abb6b8326d8b932d0c5da208104f"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ced268e82af993d7801a9db2dbc1d2322e786c5dc76295d8e89473d46c6b84d4"}, - {file = "coverage-7.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7c211f25777746d468d76f11719e64acb40eed410d81c26cefac641975beb88"}, - {file = "coverage-7.5.0-cp38-cp38-win32.whl", hash = "sha256:262fffc1f6c1a26125d5d573e1ec379285a3723363f3bd9c83923c9593a2ac25"}, - {file = "coverage-7.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:eed462b4541c540d63ab57b3fc69e7d8c84d5957668854ee4e408b50e92ce26a"}, - {file = "coverage-7.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0194d654e360b3e6cc9b774e83235bae6b9b2cac3be09040880bb0e8a88f4a1"}, - {file = "coverage-7.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:33c020d3322662e74bc507fb11488773a96894aa82a622c35a5a28673c0c26f5"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbdf2cae14a06827bec50bd58e49249452d211d9caddd8bd80e35b53cb04631"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3235d7c781232e525b0761730e052388a01548bd7f67d0067a253887c6e8df46"}, - {file = "coverage-7.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2de4e546f0ec4b2787d625e0b16b78e99c3e21bc1722b4977c0dddf11ca84e"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0e206259b73af35c4ec1319fd04003776e11e859936658cb6ceffdeba0f5be"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2055c4fb9a6ff624253d432aa471a37202cd8f458c033d6d989be4499aed037b"}, - {file = "coverage-7.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075299460948cd12722a970c7eae43d25d37989da682997687b34ae6b87c0ef0"}, - {file = "coverage-7.5.0-cp39-cp39-win32.whl", hash = "sha256:280132aada3bc2f0fac939a5771db4fbb84f245cb35b94fae4994d4c1f80dae7"}, - {file = "coverage-7.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:c58536f6892559e030e6924896a44098bc1290663ea12532c78cef71d0df8493"}, - {file = "coverage-7.5.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:2b57780b51084d5223eee7b59f0d4911c31c16ee5aa12737c7a02455829ff067"}, - {file = "coverage-7.5.0.tar.gz", hash = "sha256:cf62d17310f34084c59c01e027259076479128d11e4661bb6c9acb38c5e19bb8"}, + {file = "coverage-7.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:554c7327bf0fd688050348e22db7c8e163fb7219f3ecdd4732d7ed606b417263"}, + {file = "coverage-7.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d0305e02e40c7cfea5d08d6368576537a74c0eea62b77633179748d3519d6705"}, + {file = "coverage-7.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:829fb55ad437d757c70d5b1c51cfda9377f31506a0a3f3ac282bc6a387d6a5f1"}, + {file = "coverage-7.5.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:894b1acded706f1407a662d08e026bfd0ff1e59e9bd32062fea9d862564cfb65"}, + {file = "coverage-7.5.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe76d6dee5e4febefa83998b17926df3a04e5089e3d2b1688c74a9157798d7a2"}, + {file = "coverage-7.5.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c7ebf2a37e4f5fea3c1a11e1f47cea7d75d0f2d8ef69635ddbd5c927083211fc"}, + {file = "coverage-7.5.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20e611fc36e1a0fc7bbf957ef9c635c8807d71fbe5643e51b2769b3cc0fb0b51"}, + {file = "coverage-7.5.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c5c5b7ae2763533152880d5b5b451acbc1089ade2336b710a24b2b0f5239d20"}, + {file = "coverage-7.5.2-cp310-cp310-win32.whl", hash = "sha256:1e4225990a87df898e40ca31c9e830c15c2c53b1d33df592bc8ef314d71f0281"}, + {file = "coverage-7.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:976cd92d9420e6e2aa6ce6a9d61f2b490e07cb468968adf371546b33b829284b"}, + {file = "coverage-7.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5997d418c219dcd4dcba64e50671cca849aaf0dac3d7a2eeeb7d651a5bd735b8"}, + {file = "coverage-7.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ec27e93bbf5976f0465e8936f02eb5add99bbe4e4e7b233607e4d7622912d68d"}, + {file = "coverage-7.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f11f98753800eb1ec872562a398081f6695f91cd01ce39819e36621003ec52a"}, + {file = "coverage-7.5.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e34680049eecb30b6498784c9637c1c74277dcb1db75649a152f8004fbd6646"}, + {file = "coverage-7.5.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e12536446ad4527ac8ed91d8a607813085683bcce27af69e3b31cd72b3c5960"}, + {file = "coverage-7.5.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3d3f7744b8a8079d69af69d512e5abed4fb473057625588ce126088e50d05493"}, + {file = "coverage-7.5.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:431a3917e32223fcdb90b79fe60185864a9109631ebc05f6c5aa03781a00b513"}, + {file = "coverage-7.5.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a7c6574225f34ce45466f04751d957b5c5e6b69fca9351db017c9249786172ce"}, + {file = "coverage-7.5.2-cp311-cp311-win32.whl", hash = "sha256:2b144d142ec9987276aeff1326edbc0df8ba4afbd7232f0ca10ad57a115e95b6"}, + {file = "coverage-7.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:900532713115ac58bc3491b9d2b52704a05ed408ba0918d57fd72c94bc47fba1"}, + {file = "coverage-7.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9a42970ce74c88bdf144df11c52c5cf4ad610d860de87c0883385a1c9d9fa4ab"}, + {file = "coverage-7.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26716a1118c6ce2188283b4b60a898c3be29b480acbd0a91446ced4fe4e780d8"}, + {file = "coverage-7.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60b66b0363c5a2a79fba3d1cd7430c25bbd92c923d031cae906bdcb6e054d9a2"}, + {file = "coverage-7.5.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d22eba19273b2069e4efeff88c897a26bdc64633cbe0357a198f92dca94268"}, + {file = "coverage-7.5.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bb5b92a0ab3d22dfdbfe845e2fef92717b067bdf41a5b68c7e3e857c0cff1a4"}, + {file = "coverage-7.5.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1aef719b6559b521ae913ddeb38f5048c6d1a3d366865e8b320270b7bc4693c2"}, + {file = "coverage-7.5.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8809c0ea0e8454f756e3bd5c36d04dddf222989216788a25bfd6724bfcee342c"}, + {file = "coverage-7.5.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1acc2e2ef098a1d4bf535758085f508097316d738101a97c3f996bccba963ea5"}, + {file = "coverage-7.5.2-cp312-cp312-win32.whl", hash = "sha256:97de509043d3f0f2b2cd171bdccf408f175c7f7a99d36d566b1ae4dd84107985"}, + {file = "coverage-7.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:8941e35a0e991a7a20a1fa3e3182f82abe357211f2c335a9e6007067c3392fcf"}, + {file = "coverage-7.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5662bf0f6fb6757f5c2d6279c541a5af55a39772c2362ed0920b27e3ce0e21f7"}, + {file = "coverage-7.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d9c62cff2ffb4c2a95328488fd7aa96a7a4b34873150650fe76b19c08c9c792"}, + {file = "coverage-7.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74eeaa13e8200ad72fca9c5f37395fb310915cec6f1682b21375e84fd9770e84"}, + {file = "coverage-7.5.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f29bf497d51a5077994b265e976d78b09d9d0dff6ca5763dbb4804534a5d380"}, + {file = "coverage-7.5.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f96aa94739593ae0707eda9813ce363a0a0374a810ae0eced383340fc4a1f73"}, + {file = "coverage-7.5.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:51b6cee539168a912b4b3b040e4042b9e2c9a7ad9c8546c09e4eaeff3eacba6b"}, + {file = "coverage-7.5.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:59a75e6aa5c25b50b5a1499f9718f2edff54257f545718c4fb100f48d570ead4"}, + {file = "coverage-7.5.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29da75ce20cb0a26d60e22658dd3230713c6c05a3465dd8ad040ffc991aea318"}, + {file = "coverage-7.5.2-cp38-cp38-win32.whl", hash = "sha256:23f2f16958b16152b43a39a5ecf4705757ddd284b3b17a77da3a62aef9c057ef"}, + {file = "coverage-7.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:9e41c94035e5cdb362beed681b58a707e8dc29ea446ea1713d92afeded9d1ddd"}, + {file = "coverage-7.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06d96b9b19bbe7f049c2be3c4f9e06737ec6d8ef8933c7c3a4c557ef07936e46"}, + {file = "coverage-7.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:878243e1206828908a6b4a9ca7b1aa8bee9eb129bf7186fc381d2646f4524ce9"}, + {file = "coverage-7.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:482df956b055d3009d10fce81af6ffab28215d7ed6ad4a15e5c8e67cb7c5251c"}, + {file = "coverage-7.5.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a35c97af60a5492e9e89f8b7153fe24eadfd61cb3a2fb600df1a25b5dab34b7e"}, + {file = "coverage-7.5.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24bb4c7859a3f757a116521d4d3a8a82befad56ea1bdacd17d6aafd113b0071e"}, + {file = "coverage-7.5.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e1046aab24c48c694f0793f669ac49ea68acde6a0798ac5388abe0a5615b5ec8"}, + {file = "coverage-7.5.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:448ec61ea9ea7916d5579939362509145caaecf03161f6f13e366aebb692a631"}, + {file = "coverage-7.5.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4a00bd5ba8f1a4114720bef283cf31583d6cb1c510ce890a6da6c4268f0070b7"}, + {file = "coverage-7.5.2-cp39-cp39-win32.whl", hash = "sha256:9f805481d5eff2a96bac4da1570ef662bf970f9a16580dc2c169c8c3183fa02b"}, + {file = "coverage-7.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:2c79f058e7bec26b5295d53b8c39ecb623448c74ccc8378631f5cb5c16a7e02c"}, + {file = "coverage-7.5.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:40dbb8e7727560fe8ab65efcddfec1ae25f30ef02e2f2e5d78cfb52a66781ec5"}, + {file = "coverage-7.5.2.tar.gz", hash = "sha256:13017a63b0e499c59b5ba94a8542fb62864ba3016127d1e4ef30d354fc2b00e9"}, ] [package.dependencies] @@ -196,13 +196,13 @@ files = [ [[package]] name = "platformdirs" -version = "4.2.1" +version = "4.2.2" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, - {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, + {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, + {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, ] [package.extras] @@ -227,22 +227,22 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "protobuf" -version = "5.26.1" +version = "5.27.0" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-5.26.1-cp310-abi3-win32.whl", hash = "sha256:3c388ea6ddfe735f8cf69e3f7dc7611e73107b60bdfcf5d0f024c3ccd3794e23"}, - {file = "protobuf-5.26.1-cp310-abi3-win_amd64.whl", hash = "sha256:e6039957449cb918f331d32ffafa8eb9255769c96aa0560d9a5bf0b4e00a2a33"}, - {file = "protobuf-5.26.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:38aa5f535721d5bb99861166c445c4105c4e285c765fbb2ac10f116e32dcd46d"}, - {file = "protobuf-5.26.1-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:fbfe61e7ee8c1860855696e3ac6cfd1b01af5498facc6834fcc345c9684fb2ca"}, - {file = "protobuf-5.26.1-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:f7417703f841167e5a27d48be13389d52ad705ec09eade63dfc3180a959215d7"}, - {file = "protobuf-5.26.1-cp38-cp38-win32.whl", hash = "sha256:d693d2504ca96750d92d9de8a103102dd648fda04540495535f0fec7577ed8fc"}, - {file = "protobuf-5.26.1-cp38-cp38-win_amd64.whl", hash = "sha256:9b557c317ebe6836835ec4ef74ec3e994ad0894ea424314ad3552bc6e8835b4e"}, - {file = "protobuf-5.26.1-cp39-cp39-win32.whl", hash = "sha256:b9ba3ca83c2e31219ffbeb9d76b63aad35a3eb1544170c55336993d7a18ae72c"}, - {file = "protobuf-5.26.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ee014c2c87582e101d6b54260af03b6596728505c79f17c8586e7523aaa8f8c"}, - {file = "protobuf-5.26.1-py3-none-any.whl", hash = "sha256:da612f2720c0183417194eeaa2523215c4fcc1a1949772dc65f05047e08d5932"}, - {file = "protobuf-5.26.1.tar.gz", hash = "sha256:8ca2a1d97c290ec7b16e4e5dff2e5ae150cc1582f55b5ab300d45cb0dfa90e51"}, + {file = "protobuf-5.27.0-cp310-abi3-win32.whl", hash = "sha256:2f83bf341d925650d550b8932b71763321d782529ac0eaf278f5242f513cc04e"}, + {file = "protobuf-5.27.0-cp310-abi3-win_amd64.whl", hash = "sha256:b276e3f477ea1eebff3c2e1515136cfcff5ac14519c45f9b4aa2f6a87ea627c4"}, + {file = "protobuf-5.27.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:744489f77c29174328d32f8921566fb0f7080a2f064c5137b9d6f4b790f9e0c1"}, + {file = "protobuf-5.27.0-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:f51f33d305e18646f03acfdb343aac15b8115235af98bc9f844bf9446573827b"}, + {file = "protobuf-5.27.0-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:56937f97ae0dcf4e220ff2abb1456c51a334144c9960b23597f044ce99c29c89"}, + {file = "protobuf-5.27.0-cp38-cp38-win32.whl", hash = "sha256:a17f4d664ea868102feaa30a674542255f9f4bf835d943d588440d1f49a3ed15"}, + {file = "protobuf-5.27.0-cp38-cp38-win_amd64.whl", hash = "sha256:aabbbcf794fbb4c692ff14ce06780a66d04758435717107c387f12fb477bf0d8"}, + {file = "protobuf-5.27.0-cp39-cp39-win32.whl", hash = "sha256:587be23f1212da7a14a6c65fd61995f8ef35779d4aea9e36aad81f5f3b80aec5"}, + {file = "protobuf-5.27.0-cp39-cp39-win_amd64.whl", hash = "sha256:7cb65fc8fba680b27cf7a07678084c6e68ee13cab7cace734954c25a43da6d0f"}, + {file = "protobuf-5.27.0-py3-none-any.whl", hash = "sha256:673ad60f1536b394b4fa0bcd3146a4130fcad85bfe3b60eaa86d6a0ace0fa374"}, + {file = "protobuf-5.27.0.tar.gz", hash = "sha256:07f2b9a15255e3cf3f137d884af7972407b556a7a220912b252f26dc3121e6bf"}, ] [[package]] @@ -453,13 +453,13 @@ testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-po [[package]] name = "virtualenv" -version = "20.26.1" +version = "20.26.2" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.1-py3-none-any.whl", hash = "sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75"}, - {file = "virtualenv-20.26.1.tar.gz", hash = "sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b"}, + {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, + {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index 838f097..2c0dd9b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,23 +45,20 @@ proj = ["pyproj"] tox = "^4.0.16" coverage = { version = "^7.0.0", extras = ["toml"] } -# Black -[tool.black] +[tool.ruff] line-length = 120 -fast = true -extend-exclude = ''' -( - .*_pb2.py -) -''' -target-version = ["py39", "py310", "py311", "py312"] +target-version = "py38" +show-fixes = true +extend-include = ["*.ipynb"] +extend-exclude = ["mapbox_vector_tile/Mapbox/vector_tile_pb2.py"] -# Isort -[tool.isort] -profile = "black" -line_length = 120 -force_alphabetical_sort_within_sections = true -case_sensitive = true +[tool.ruff.lint] +select = ["E", "F", "C90", "W", "B", "UP", "I", "RUF100", "TID", "SIM", "PIE", "N", "C4", "G", "PTH"] +unfixable = ["B"] +extend-ignore = ["B024", "G004", "UP038"] +flake8-tidy-imports.ban-relative-imports = "all" +mccabe.max-complexity = 15 +pep8-naming.extend-ignore-names = ["assertRoundTrip"] # Coverage [tool.coverage.run] diff --git a/tests/__init__.py b/tests/__init__.py index 1c6c979..d5880f6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,9 +1,7 @@ import doctest -import glob -import os +from pathlib import Path optionflags = doctest.REPORT_ONLY_FIRST_FAILURE | doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS -_basedir = os.path.dirname(__file__) -paths = glob.glob("%s/*.txt" % _basedir) -test_suite = doctest.DocFileSuite(*paths, **dict(module_relative=False, optionflags=optionflags)) +paths = Path(__file__).parent.glob("*.txt") +test_suite = doctest.DocFileSuite(*paths, module_relative=False, optionflags=optionflags) diff --git a/tests/test_decoder.py b/tests/test_decoder.py index 7e83b6b..8edc5c2 100644 --- a/tests/test_decoder.py +++ b/tests/test_decoder.py @@ -10,7 +10,11 @@ class BaseTestCase(unittest.TestCase): def test_decoder(self): - vector_tile = b'\x1aI\n\x05water\x12\x1a\x08\x01\x12\x06\x00\x00\x01\x01\x02\x02\x18\x03"\x0c\t\x00\x80@\x1a\x00\x01\x02\x00\x00\x02\x0f\x1a\x03foo\x1a\x03baz\x1a\x03uid"\x05\n\x03bar"\x05\n\x03foo"\x02 {(\x80 x\x02' # noqa + vector_tile = ( + b'\x1aI\n\x05water\x12\x1a\x08\x01\x12\x06\x00\x00\x01\x01\x02\x02\x18\x03"\x0c\t\x00\x80@\x1a' + b'\x00\x01\x02\x00\x00\x02\x0f\x1a\x03foo\x1a\x03baz\x1a\x03uid"\x05\n\x03bar"\x05\n\x03foo"\x02 {(\x80 ' + b"x\x02" + ) self.assertEqual( mapbox_vector_tile.decode(vector_tile), { @@ -31,7 +35,11 @@ def test_decoder(self): ) def test_decoder_geojson(self): - vector_tile = b'\x1aI\n\x05water\x12\x1a\x08\x01\x12\x06\x00\x00\x01\x01\x02\x02\x18\x03"\x0c\t\x00\x80@\x1a\x00\x01\x02\x00\x00\x02\x0f\x1a\x03foo\x1a\x03baz\x1a\x03uid"\x05\n\x03bar"\x05\n\x03foo"\x02 {(\x80 x\x02' # noqa + vector_tile = ( + b'\x1aI\n\x05water\x12\x1a\x08\x01\x12\x06\x00\x00\x01\x01\x02\x02\x18\x03"\x0c\t\x00\x80@\x1a' + b'\x00\x01\x02\x00\x00\x02\x0f\x1a\x03foo\x1a\x03baz\x1a\x03uid"\x05\n\x03bar"\x05\n\x03foo"\x02' + b" {(\x80 x\x02" + ) self.assertEqual( mapbox_vector_tile.decode(vector_tile, default_options={"geojson": False}), { @@ -55,7 +63,10 @@ def test_decode_polygon_no_cmd_seg_end(self): # CMD_SEG_END after the polygon parts # this tests that the decoder can detect that a new # CMD_MOVE_TO implicitly closes the previous polygon - vector_tile = b'\x1a+\n\x05water\x12\x1d\x18\x03"\x19\t\x00\x80@"\x08\x00\x00\x07\x07\x00\x00\x08\t\x02\x01"\x00\x03\x04\x00\x00\x04\x03\x00(\x80 x\x02' # noqa + vector_tile = ( + b'\x1a+\n\x05water\x12\x1d\x18\x03"\x19\t\x00\x80@"\x08\x00\x00\x07\x07\x00\x00\x08\t\x02\x01"' + b"\x00\x03\x04\x00\x00\x04\x03\x00(\x80 x\x02" + ) self.assertEqual( mapbox_vector_tile.decode(vector_tile), { @@ -82,7 +93,11 @@ def test_decode_polygon_no_cmd_seg_end(self): ) def test_nondefault_extent(self): - vector_tile = b'\x1aK\n\x05water\x12\x1c\x08\x01\x12\x06\x00\x00\x01\x01\x02\x02\x18\x02"\x0e\t\x80}\xd0\x12\x12\xbf>\xd86\xbf>\xd86\x1a\x03foo\x1a\x03baz\x1a\x03uid"\x05\n\x03bar"\x05\n\x03foo"\x02 {(\x80@x\x02' # noqa + vector_tile = ( + b'\x1aK\n\x05water\x12\x1c\x08\x01\x12\x06\x00\x00\x01\x01\x02\x02\x18\x02"\x0e\t\x80}\xd0\x12' + b'\x12\xbf>\xd86\xbf>\xd86\x1a\x03foo\x1a\x03baz\x1a\x03uid"\x05\n\x03bar"\x05\n\x03foo"\x02 ' + b"{(\x80@x\x02" + ) self.assertEqual( mapbox_vector_tile.decode(vector_tile), { diff --git a/tests/test_encoder.py b/tests/test_encoder.py index dd1161d..5d5f6c0 100644 --- a/tests/test_encoder.py +++ b/tests/test_encoder.py @@ -201,7 +201,11 @@ def test_with_wkt(self): def test_with_wkb(self): self.assertRoundTrip( - input_geometry=b"\001\003\000\000\000\001\000\000\000\005\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\360?\000\000\000\000\000\000\360?\000\000\000\000\000\000\360?\000\000\000\000\000\000\360?\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000", # noqa + input_geometry=b"\001\003\000\000\000\001\000\000\000\005\000\000\000\000\000\000\000\000\000\000\000\000" + b"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\360?" + b"\000\000\000\000\000\000\360?\000\000\000\000\000\000\360?\000\000\000\000\000\000\360?" + b"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" + b"\000\000\000", expected_geometry={"type": "Polygon", "coordinates": [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]]}, ) @@ -290,7 +294,10 @@ def test_encode_multilinestring(self): ) def test_encode_multipolygon_normal_winding_order(self): - geometry = "MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))" # noqa + geometry = ( + "MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), " + "(30 20, 20 15, 20 25, 30 20)))" + ) self.assertRoundTrip( input_geometry=geometry, expected_geometry={ @@ -307,7 +314,10 @@ def test_encode_multipolygon_normal_winding_order(self): ) def test_encode_multipolygon_normal_winding_order_zero_area(self): - geometry = "MULTIPOLYGON (((40 40, 40 20, 40 45, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))" # noqa + geometry = ( + "MULTIPOLYGON (((40 40, 40 20, 40 45, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), " + "(30 20, 20 15, 20 25, 30 20)))" + ) # NB there is only one resultant polygon here self.assertRoundTrip( input_geometry=geometry, @@ -346,7 +356,7 @@ def test_encode_property_bool(self): def test_encode_property_int(self): geometry = "POINT(0 0)" properties = { - "test_int": int(1), + "test_int": 1, } self.assertRoundTrip( input_geometry=geometry, expected_geometry={"type": "Point", "coordinates": [0, 0]}, properties=properties @@ -374,11 +384,11 @@ def test_encode_property_list(self): def test_encode_multiple_values_test(self): geometry = "POINT(0 0)" - properties1 = dict(foo="bar", baz="bar") - properties2 = dict(quux="morx", baz="bar") + properties1 = {"foo": "bar", "baz": "bar"} + properties2 = {"quux": "morx", "baz": "bar"} name = "foo" - feature1 = dict(geometry=geometry, properties=properties1) - feature2 = dict(geometry=geometry, properties=properties2) + feature1 = {"geometry": geometry, "properties": properties1} + feature2 = {"geometry": geometry, "properties": properties2} source = [{"name": name, "features": [feature1, feature2]}] encoded = encode(source) decoded = decode(encoded) @@ -404,7 +414,7 @@ def test_too_small_linestring(self): from mapbox_vector_tile.encoder import on_invalid_geometry_make_valid shape = shapely.wkt.loads("LINESTRING(-71.160281 42.258729,-71.160837 42.259113,-71.161144 42.25932)") - features = [dict(geometry=shape, properties={})] + features = [{"geometry": shape, "properties": {}}] pbf = encode( {"name": "foo", "features": features}, default_options={"on_invalid_geometry": on_invalid_geometry_make_valid}, @@ -413,7 +423,7 @@ def test_too_small_linestring(self): features = result["foo"]["features"] self.assertEqual(0, len(features)) - def test_encode_1_True_values(self): + def test_encode_1_true_values(self): geometry = "POINT(0 0)" properties = { "foo": True, @@ -498,11 +508,11 @@ class QuantizeTest(unittest.TestCase): def test_quantize(self): from mapbox_vector_tile import decode, encode - props = dict(foo="bar") + props = {"foo": "bar"} shape = "POINT(15 15)" - feature = dict(geometry=shape, properties=props) + feature = {"geometry": shape, "properties": props} features = [feature] - source = dict(name="layername", features=features) + source = {"name": "layername", "features": features} bounds = 10.0, 10.0, 20.0, 20.0 pbf = encode(source, default_options={"quantize_bounds": bounds}) result = decode(pbf) @@ -514,11 +524,11 @@ def test_quantize(self): def test_y_coord_down(self): from mapbox_vector_tile import decode, encode - props = dict(foo="bar") + props = {"foo": "bar"} shape = "POINT(10 10)" - feature = dict(geometry=shape, properties=props) + feature = {"geometry": shape, "properties": props} features = [feature] - source = dict(name="layername", features=features) + source = {"name": "layername", "features": features} pbf = encode(source, default_options={"y_coord_down": True}) result = decode(pbf, default_options={"y_coord_down": True}) act_feature = result["layername"]["features"][0] @@ -529,11 +539,11 @@ def test_y_coord_down(self): def test_quantize_and_y_coord_down(self): from mapbox_vector_tile import decode, encode - props = dict(foo="bar") + props = {"foo": "bar"} shape = "POINT(30 30)" - feature = dict(geometry=shape, properties=props) + feature = {"geometry": shape, "properties": props} features = [feature] - source = dict(name="layername", features=features) + source = {"name": "layername", "features": features} bounds = 0.0, 0.0, 50.0, 50.0 pbf = encode(source, default_options={"quantize_bounds": bounds, "y_coord_down": True}) @@ -554,11 +564,11 @@ class ExtentTest(unittest.TestCase): def test_custom_extent(self): from mapbox_vector_tile import decode, encode - props = dict(foo="bar") + props = {"foo": "bar"} shape = "POINT(10 10)" - feature = dict(geometry=shape, properties=props) + feature = {"geometry": shape, "properties": props} features = [feature] - source = dict(name="layername", features=features) + source = {"name": "layername", "features": features} bounds = 0.0, 0.0, 10.0, 10.0 pbf = encode(source, default_options={"quantize_bounds": bounds, "extents": 50}) result = decode(pbf) @@ -578,8 +588,8 @@ def test_invalid_geometry_ignore(self): geometry = "POLYGON ((10 10, 20 10, 20 20, 15 15, 15 5, 10 10))" shape = shapely.wkt.loads(geometry) self.assertFalse(shape.is_valid) - feature = dict(geometry=shape, properties={}) - source = dict(name="layername", features=[feature]) + feature = {"geometry": shape, "properties": {}} + source = {"name": "layername", "features": [feature]} pbf = encode(source, default_options={"on_invalid_geometry": on_invalid_geometry_ignore}) result = decode(pbf) self.assertEqual(0, len(result["layername"]["features"])) @@ -593,9 +603,9 @@ def test_invalid_geometry_raise(self): geometry = "POLYGON ((10 10, 20 10, 20 20, 15 15, 15 5, 10 10))" shape = shapely.wkt.loads(geometry) self.assertFalse(shape.is_valid) - feature = dict(geometry=shape, properties={}) - source = dict(name="layername", features=[feature]) - with self.assertRaises(Exception): + feature = {"geometry": shape, "properties": {}} + source = {"name": "layername", "features": [feature]} + with self.assertRaises(ValueError): encode(source, default_options={"on_invalid_geometry": on_invalid_geometry_raise}) def test_invalid_geometry_make_valid(self): @@ -607,8 +617,8 @@ def test_invalid_geometry_make_valid(self): geometry = "POLYGON ((10 10, 20 10, 20 20, 15 15, 15 5, 10 10))" shape = shapely.wkt.loads(geometry) self.assertFalse(shape.is_valid) - feature = dict(geometry=shape, properties={}) - source = dict(name="layername", features=[feature]) + feature = {"geometry": shape, "properties": {}} + source = {"name": "layername", "features": [feature]} pbf = encode(source, default_options={"on_invalid_geometry": on_invalid_geometry_make_valid}) result = decode(pbf) self.assertEqual(1, len(result["layername"]["features"])) @@ -631,8 +641,8 @@ def test_bowtie_self_touching(self): bowtie = "POLYGON ((0 0, 0 2, 1 1, 2 2, 2 0, 1 1, 0 0))" shape = shapely.wkt.loads(bowtie) self.assertFalse(shape.is_valid) - feature = dict(geometry=shape, properties={}) - source = dict(name="layername", features=[feature]) + feature = {"geometry": shape, "properties": {}} + source = {"name": "layername", "features": [feature]} pbf = encode(source, default_options={"on_invalid_geometry": on_invalid_geometry_make_valid}) result = decode(pbf) self.assertEqual(1, len(result["layername"]["features"])) @@ -655,8 +665,8 @@ def test_bowtie_self_crossing(self): bowtie = "POLYGON ((0 0, 2 2, 2 0, 0 2, 0 0))" shape = shapely.wkt.loads(bowtie) self.assertFalse(shape.is_valid) - feature = dict(geometry=shape, properties={}) - source = dict(name="layername", features=[feature]) + feature = {"geometry": shape, "properties": {}} + source = {"name": "layername", "features": [feature]} pbf = encode(source, default_options={"on_invalid_geometry": on_invalid_geometry_make_valid}) result = decode(pbf) self.assertEqual(1, len(result["layername"]["features"])) @@ -683,8 +693,8 @@ def test_make_valid_self_crossing(self): geometry = "POLYGON ((10 10, 20 10, 20 20, 15 15, 15 5, 10 10))" shape = shapely.wkt.loads(geometry) self.assertFalse(shape.is_valid) - feature = dict(geometry=shape, properties={}) - source = dict(name="layername", features=[feature]) + feature = {"geometry": shape, "properties": {}} + source = {"name": "layername", "features": [feature]} pbf = encode(source, default_options={"on_invalid_geometry": on_invalid_geometry_make_valid}) result = decode(pbf) self.assertEqual(1, len(result["layername"]["features"])) @@ -711,8 +721,8 @@ def test_validate_generates_rounding_error(self): bowtie = "POLYGON((0 0, 1 1, 0 1, 1 0, 0 0))" shape = shapely.wkt.loads(bowtie) self.assertFalse(shape.is_valid) - feature = dict(geometry=shape, properties={}) - source = dict(name="layername", features=[feature]) + feature = {"geometry": shape, "properties": {}} + source = {"name": "layername", "features": [feature]} pbf = encode(source, default_options={"on_invalid_geometry": on_invalid_geometry_make_valid}) result = decode(pbf) features = result["layername"]["features"] @@ -748,7 +758,7 @@ def test_quantize_makes_mutlipolygon_invalid(self): """657115.9120547654 5674684.979891453)))""" ) quantize_bounds = (645740.0149532147, 5674684.979891453, 665307.8941942193, 5694252.8591324575) - features = [dict(geometry=shape, properties={})] + features = [{"geometry": shape, "properties": {}}] pbf = encode( {"name": "foo", "features": features}, default_options={ @@ -775,7 +785,7 @@ def test_flipped_geometry_produces_multipolygon(self): """3644 2326, 3605 2251, 3566 2230, 3547 2122, 3482 2014, 3479 1966, 3455 1944, 3458 1910, 3449 1902, """ """3449 1939))""" ) - features = [dict(geometry=shape, properties={})] + features = [{"geometry": shape, "properties": {}}] pbf = encode( {"name": "foo", "features": features}, default_options={"on_invalid_geometry": on_invalid_geometry_make_valid}, @@ -791,20 +801,20 @@ def test_flipped_geometry_produces_multipolygon(self): self.assertTrue(poly.is_valid) def test_make_valid_can_return_multipolygon(self): - import os.path + from pathlib import Path import shapely.wkt from mapbox_vector_tile import encode from mapbox_vector_tile.encoder import on_invalid_geometry_make_valid - test_dir = os.path.dirname(os.path.realpath(__file__)) + test_dir = Path(__file__).resolve().parent file_name = "error_nested_multipolygon.wkt" - with open(os.path.join(test_dir, file_name)) as fh: + with (test_dir / file_name).open() as fh: shape = wkt.loads(fh.read()) - features = [dict(geometry=shape, properties={})] + features = [{"geometry": shape, "properties": {}}] pbf = encode( {"name": "foo", "features": features}, default_options={ @@ -835,7 +845,7 @@ def test_too_small_geometry(self): shape = shapely.wkt.loads( "LINESTRING (3065.656210384849 3629.831662879646, 3066.458953567231 3629.725941289478)" ) - features = [dict(geometry=shape, properties={})] + features = [{"geometry": shape, "properties": {}}] pbf = encode( {"name": "foo", "features": features}, default_options={"on_invalid_geometry": on_invalid_geometry_make_valid}, @@ -896,10 +906,7 @@ def test_example_multi_polygon(self): ] tile = VectorTile(default_options={"extents": 4096, "quantize_bounds": None, "y_coord_down": True}) - tile.add_layer( - name="example_layer", - features=[dict(geometry=input_geometry)], - ) + tile.add_layer(name="example_layer", features=[{"geometry": input_geometry}]) self.assertEqual(1, len(tile.layer.features)) f = tile.layer.features[0] self.assertEqual(expected_commands, list(f.geometry)) @@ -954,7 +961,7 @@ def test_example_multi_polygon_y_up(self): ] tile = VectorTile(default_options={"extents": 20, "quantize_bounds": None, "y_coord_down": False}) - tile.add_layer(name="example_layer", features=[dict(geometry=input_geometry)]) + tile.add_layer(name="example_layer", features=[{"geometry": input_geometry}]) self.assertEqual(1, len(tile.layer.features)) f = tile.layer.features[0] self.assertEqual(expected_commands, list(f.geometry)) @@ -978,7 +985,7 @@ def test_issue_57(self): ] tile = VectorTile(default_options={"extents": 4096, "quantize_bounds": None, "y_coord_down": True}) - tile.add_layer(name="example_layer", features=[dict(geometry=input_geometry)]) + tile.add_layer(name="example_layer", features=[{"geometry": input_geometry}]) self.assertEqual(1, len(tile.layer.features)) f = tile.layer.features[0] self.assertEqual(expected_commands, list(f.geometry)) @@ -988,11 +995,11 @@ class InvalidVectorTileTest(unittest.TestCase): def test_duplicate_layer_name(self): from mapbox_vector_tile import encode - props = dict(foo="bar") + props = {"foo": "bar"} shape = "POINT(10 10)" - feature = dict(geometry=shape, properties=props) + feature = {"geometry": shape, "properties": props} features = [feature] - source = [dict(name="layername", features=features), dict(name="layername", features=features)] + source = [{"name": "layername", "features": features}, {"name": "layername", "features": features}] with self.assertRaises(ValueError) as ex: encode(source, default_options={"extents": 4096}) self.assertEqual(str(ex.exception), "The layer name 'layername' already exists in the vector tile.") @@ -1000,16 +1007,16 @@ def test_duplicate_layer_name(self): def test_empty_layer_name(self): from mapbox_vector_tile import encode - props = dict(foo="bar") + props = {"foo": "bar"} shape = "POINT(10 10)" - feature = dict(geometry=shape, properties=props) + feature = {"geometry": shape, "properties": props} features = [feature] - source = [dict(name="", features=features)] + source = [{"name": "", "features": features}] with self.assertRaises(ValueError) as ex: encode(source, default_options={"extents": 4096}) self.assertEqual(str(ex.exception), "A layer name can not be empty. '' was provided.") - source = [dict(name=None, features=features)] + source = [{"name": None, "features": features}] with self.assertRaises(ValueError) as ex: encode(source, default_options={"extents": 4096}) self.assertEqual(str(ex.exception), "A layer name can not be empty. None was provided.") @@ -1022,17 +1029,17 @@ def test_empty_layer(self): self.assertEqual(res, b"") # Layer without feature - res = encode(dict(name="layer", features=[]), default_options={"extents": 4096}) + res = encode({"name": "layer", "features": []}, default_options={"extents": 4096}) self.assertEqual(res, b"\x1a\x0c\n\x05layer(\x80 x\x02") def test_invalid_extent(self): from mapbox_vector_tile import encode - props = dict(foo="bar") + props = {"foo": "bar"} shape = "POINT(10 10)" - feature = dict(geometry=shape, properties=props) + feature = {"geometry": shape, "properties": props} features = [feature] - source = [dict(name="layername", features=features)] + source = [{"name": "layername", "features": features}] with self.assertRaises(ValueError) as ex: encode(source, default_options={"extents": 0}) self.assertEqual(str(ex.exception), "The extents must be positive. 0 provided.") diff --git a/tests/test_polygon.py b/tests/test_polygon.py index 8b645df..1048b51 100644 --- a/tests/test_polygon.py +++ b/tests/test_polygon.py @@ -2,8 +2,8 @@ Tests for vector_tile/polygon.py """ -import os import unittest +from pathlib import Path from shapely import wkt @@ -12,8 +12,8 @@ class TestPolygonMakeValid(unittest.TestCase): def test_dev_errors(self): - test_dir = os.path.dirname(os.path.realpath(__file__)) - with open(os.path.join(test_dir, "errors.wkt")) as fh: + test_dir = Path(__file__).resolve().parent + with (test_dir / "errors.wkt").open() as fh: for line in fh: geom = wkt.loads(line) fixed = make_it_valid(geom)