├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CHANGES.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── codecov.yml ├── pyproject.toml ├── requirements.txt ├── rio_tiler_mvt ├── __init__.py └── mvt.pyx ├── setup.cfg ├── setup.py └── tests ├── fixtures ├── cog_classes.tif └── test.tif └── test_mvt.py /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | # On every pull request, but only on push to master 4 | on: 5 | push: 6 | branches: 7 | - master 8 | tags: 9 | - '*' 10 | pull_request: 11 | env: 12 | LATEST_PY_VERSION: '3.13' 13 | 14 | jobs: 15 | tests: 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | python-version: 20 | - '3.9' 21 | - '3.10' 22 | - '3.11' 23 | - '3.12' 24 | - '3.13' 25 | 26 | steps: 27 | - uses: actions/checkout@v4 28 | - name: Set up Python ${{ matrix.python-version }} 29 | uses: actions/setup-python@v5 30 | with: 31 | python-version: ${{ matrix.python-version }} 32 | - name: Install dependencies 33 | run: | 34 | python -m pip install --upgrade pip 35 | python -m pip install -e ".[test]" 36 | python -m pip install "vector-tile-base@git+https://github.com/mapbox/vector-tile-base.git" 37 | 38 | - name: Run pre-commit 39 | if: ${{ matrix.python-version == env.LATEST_PY_VERSION }} 40 | run: | 41 | python -m pip install pre-commit 42 | pre-commit run --all-files 43 | 44 | - name: Run tests 45 | run: python -m pytest --cov rio_tiler_mvt --cov-report xml --cov-report term-missing -s -vv 46 | 47 | - name: Upload Results 48 | if: ${{ matrix.python-version == env.LATEST_PY_VERSION }} 49 | uses: codecov/codecov-action@v4 50 | with: 51 | file: ./coverage.xml 52 | flags: unittests 53 | name: ${{ matrix.python-version }} 54 | fail_ci_if_error: false 55 | 56 | publish: 57 | needs: [tests] 58 | runs-on: ubuntu-latest 59 | if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release' 60 | steps: 61 | - uses: actions/checkout@v4 62 | - name: Set up Python 63 | uses: actions/setup-python@v5 64 | with: 65 | python-version: ${{ env.LATEST_PY_VERSION }} 66 | 67 | - name: Install dependencies 68 | run: | 69 | python -m pip install --upgrade pip 70 | python -m pip install numpy cython wheel setuptools twine 71 | 72 | - name: Build sdist 73 | run: python setup.py sdist 74 | 75 | - name: Build and publish 76 | env: 77 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 78 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 79 | run: twine upload --skip-existing dist/* 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | rio_tiler_mvt/mvt.c -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/PyCQA/isort 3 | rev: 5.13.2 4 | hooks: 5 | - id: isort 6 | language_version: python 7 | 8 | - repo: https://github.com/astral-sh/ruff-pre-commit 9 | rev: v0.3.5 10 | hooks: 11 | - id: ruff 12 | args: ["--fix"] 13 | - id: ruff-format 14 | 15 | - repo: https://github.com/pre-commit/mirrors-mypy 16 | rev: v1.9.0 17 | hooks: 18 | - id: mypy 19 | language_version: python 20 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## 0.2.0 (2025-01-20) 2 | 3 | * update python versions support 4 | 5 | ## 0.1.0 (2022-08-08) 6 | 7 | * Fix polygon and point coordinates in `pixels_encoder` 8 | 9 | **breaking changes** 10 | 11 | * remove python 3.6 support 12 | 13 | ## 0.0.1dev2 (2021-04-06) 14 | 15 | * update tests for new vector-tile-base version 16 | * add `shapes` encoding (add shapely and rasterio in requirements). 17 | 18 | **breaking changes** 19 | 20 | * renamed `mvt.encoder` to `mvt.pixels_encoder` 21 | 22 | ## 0.0.1dev1 (2019-06-25) 23 | 24 | * Fix missing files in package 25 | 26 | ## 0.0.1dev0 (2019-06-24) 27 | 28 | * Initial release. 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vincent Sarago 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include rio_tiler_mvt/*.pyx 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rio-tiler-mvt 2 | 3 |

4 | rio-tiler 5 |

6 |

7 | A rio-tiler plugin to translate tile array to MVT (using python-vtzero). 8 |

9 |

10 | 11 | Test 12 | 13 | 14 | Coverage 15 | 16 | 17 | Package version 18 | 19 | 20 | Downloads 21 | 22 |

23 | 24 | 25 | More on [COG Talk](https://medium.com/devseed/search?q=cog%20talk) blog posts 26 | 27 | ## Install 28 | 29 | ### Requirements 30 | 31 | rio-tiler-mvt use [python-vtzero](https://github.com/tilery/python-vtzero) wrapper to encode point and polygons to MVT. Because VTZERO is a C++ library, python-vtzero is written in Cython, thus cython is required to compile this library. 32 | 33 | ```bash 34 | $ pip install cython # see https://github.com/tilery/python-vtzero#requirements 35 | 36 | $ pip install rio-tiler-mvt 37 | ``` 38 | Or 39 | ```bash 40 | $ git clone http://github.com/cogeotiff/rio-tiler-mvt 41 | $ cd rio-tiler-mvt 42 | $ pip install -e . 43 | ``` 44 | 45 | ## Rio-tiler + Mapbox Vector tiles 46 | 47 | ### API 48 | 49 | #### **pixel_encoder** 50 | 51 | pixels_encoder( 52 | data: numpy.ndarray, 53 | mask: numpy.ndarray, 54 | band_names: list = [], 55 | layer_name: str = "my_layer", 56 | feature_type: str = "point" 57 | ) 58 | 59 | Inputs: 60 | - data: raster tile data to encode 61 | - mask: mask data 62 | - band_names: Raster band's names 63 | - layer_name: Layer name 64 | - feature_type: Feature type (point or polygon) 65 | 66 | Returns: 67 | - mvt : Mapbox Vector Tile encoded data. 68 | 69 | Examples: 70 | 71 | ```python 72 | from rio_tiler.io import COGReader 73 | from rio_tiler_mvt import pixels_encoder 74 | 75 | with COGReader("fixtures/test.tif") as cog 76 | img = cog.tile(72, 63, 7, resampling_method="nearest") 77 | mvt = pixels_encoder(img.data, img.mask, layer_name="test", feature_type="point") 78 | ``` 79 | 80 | #### **shapes_encoder** 81 | 82 | shapes_encoder( 83 | data: numpy.ndarray, # 1D array (height, width) 84 | mask: numpy.ndarray, 85 | layer_name: str = "my_layer", 86 | colormap: dict = {}, 87 | class_names: dict = {} 88 | ) 89 | 90 | Inputs: 91 | - data: raster tile data to encode 92 | - mask: mask data 93 | - layer_name: Layer name 94 | - colormap: GDAL colormap. If provided a `color` value will be added to the feature properties 95 | - class_names: Dictionary mapping pixel value with class names. If provided a `name` value will be added to the feature properties. 96 | 97 | Returns: 98 | - mvt : Mapbox Vector Tile encoded data. 99 | 100 | 101 | ## Contribution & Development 102 | 103 | Issues and pull requests are more than welcome. 104 | 105 | **dev install** 106 | 107 | ```bash 108 | $ git clone https://github.com/cogeotiff/rio-tiler-mvt.git 109 | $ cd rio-tiler-mvt 110 | $ pip install -e .[dev] 111 | ``` 112 | 113 | **pre-commit** 114 | 115 | This repo is set to use `pre-commit` to run *isort*, *flake8*, *pydocstring*, *black* ("uncompromising Python code formatter") and mypy when committing new code. 116 | 117 | ```bash 118 | $ pre-commit install 119 | ``` 120 | 121 | ## Links 122 | 123 | - **satellite-3d**: http://github.com/developmentseed/satellite-3d.git 124 | - **titiler-mvt**: http://github.com/developmentseed/titiler-mvt.git 125 | - **rio-viz**: http://github.com/developmentseed/rio-viz.git 126 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: off 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 5 9 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=46.4", "wheel", "cython", "numpy"] 3 | 4 | [tool.mypy] 5 | no_strict_optional = true 6 | ignore_missing_imports = true 7 | 8 | [tool.isort] 9 | profile = "black" 10 | known_first_party = ["rio_tiler_mvt"] 11 | known_third_party = ["rasterio", "vtzero", "shapely"] 12 | default_section = "THIRDPARTY" 13 | 14 | [tool.ruff] 15 | line-length = 90 16 | 17 | [tool.ruff.lint] 18 | select = [ 19 | "D1", # pydocstyle errors 20 | "E", # pycodestyle errors 21 | "W", # pycodestyle warnings 22 | "F", # flake8 23 | "C", # flake8-comprehensions 24 | "B", # flake8-bugbear 25 | ] 26 | ignore = [ 27 | "E501", # line too long, handled by black 28 | "B008", # do not perform function calls in argument defaults 29 | "B905", # ignore zip() without an explicit strict= parameter, only support with python >3.10 30 | "B028", 31 | ] 32 | 33 | [tool.ruff.lint.mccabe] 34 | max-complexity = 12 35 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | vtzero 3 | rasterio 4 | shapely 5 | -------------------------------------------------------------------------------- /rio_tiler_mvt/__init__.py: -------------------------------------------------------------------------------- 1 | """rio-tiler-mvt: MVT encoder.""" 2 | 3 | from rio_tiler_mvt.mvt import pixels_encoder, shapes_encoder # noqa 4 | 5 | __version__ = "0.2.0" 6 | -------------------------------------------------------------------------------- /rio_tiler_mvt/mvt.pyx: -------------------------------------------------------------------------------- 1 | # cython: language_level=3 2 | 3 | import warnings 4 | 5 | import numpy 6 | 7 | cimport numpy 8 | 9 | from rasterio._features import _shapes 10 | from rasterio.rio.helpers import coords 11 | from rasterio.transform import IDENTITY 12 | from shapely import geometry 13 | from vtzero.tile import Layer, Point, Polygon, Tile 14 | 15 | 16 | cpdef bytes pixels_encoder( 17 | data, 18 | mask, 19 | list band_names = [], 20 | str layer_name = "my_layer", 21 | str feature_type = "point", 22 | ): 23 | cdef int sc = 4096 // data.shape[1] # cell resolution 24 | cdef tuple indexes = numpy.where(mask) # Index of non-masked data 25 | 26 | cdef tuple 27 | cdef int x, y, x_coord, y_coord 28 | 29 | if not band_names: 30 | band_names = [ 31 | f"band{ix}" for ix in range(1, data.shape[0] + 1) 32 | ] 33 | 34 | mvt = Tile() 35 | mvt_layer = Layer(mvt, layer_name.encode()) 36 | for (y, x) in zip(indexes[0], indexes[1]): 37 | x_coord = sc * x 38 | y_coord = sc * y 39 | 40 | if feature_type == 'point': 41 | feature = Point(mvt_layer) 42 | # Point is at the center of the pixel 43 | feature.add_point(x_coord + sc // 2, y_coord + sc // 2) 44 | 45 | elif feature_type == 'polygon': 46 | feature = Polygon(mvt_layer) 47 | feature.add_ring(5) 48 | feature.set_point(x_coord, y_coord) 49 | feature.set_point(x_coord + sc, y_coord) 50 | feature.set_point(x_coord + sc, y_coord + sc) 51 | feature.set_point(x_coord, y_coord + sc) 52 | feature.set_point(x_coord, y_coord) 53 | 54 | else: 55 | raise Exception(f"Invalid geometry type: {feature_type}") 56 | 57 | # TODO: fix https://github.com/tilery/python-vtzero/issues/3 58 | for bidx in range(data.shape[0]): 59 | feature.add_property( 60 | band_names[bidx].encode(), 61 | str(data[bidx, y, x]).encode() 62 | ) 63 | feature.commit() 64 | 65 | return mvt.serialize() 66 | 67 | 68 | cpdef bytes encoder( 69 | data, 70 | mask, 71 | list band_names = [], 72 | str layer_name = "my_layer", 73 | str feature_type = "point", 74 | ): 75 | """Compatibility with previous version.""" 76 | warnings.warn( 77 | "'mvt.encoder' function will be deprecated in next version, please use 'mvt.pixels_encoder'", 78 | DeprecationWarning, 79 | ) 80 | return pixels_encoder(data, mask, band_names, layer_name, feature_type) 81 | 82 | 83 | cpdef bytes shapes_encoder( 84 | data, 85 | mask, 86 | str layer_name = "my_layer", 87 | dict colormap = {}, 88 | dict class_names = {}, 89 | ): 90 | """Encode polygon extracted from GDAL polygonize. 91 | 92 | Args: 93 | data (numpy.ndarray): image data. 94 | mask (numpy.ndarray): mask array. 95 | layer_name (str): MVT layer name. 96 | colormap (dict): GDAL colormap. If provided a `color` value will be added 97 | to the feature properties. 98 | class_names (dict): Dictionary mapping pixel value with class names. If provided 99 | a `name` value will be added to the feature properties. 100 | 101 | Returns: 102 | bytes: Mapbox Vector Tile. 103 | 104 | """ 105 | cdef float x, y 106 | cdef int r, g, b 107 | 108 | mvt = Tile() 109 | mvt_layer = Layer(mvt, layer_name.encode()) 110 | 111 | # scaling factor 112 | cdef int sc = 4096 // max(data.shape) 113 | 114 | # Iterate through shapes and values 115 | for (p, v) in _shapes(data, mask, connectivity=4, transform=IDENTITY.scale(sc)): 116 | # make sure v is the same datatype as the input data 117 | v = data.dtype.type(v) 118 | feature = Polygon(mvt_layer) 119 | feature.set_id(v) 120 | 121 | polygon = geometry.shape(p) 122 | # Simple GEOS Validation 123 | if not polygon.is_valid: 124 | # If the polygon is not valid we use a little trick 125 | polygon = polygon.buffer(0) 126 | 127 | # add exterior ring 128 | feature.add_ring(len(polygon.exterior.coords)) 129 | for x, y in polygon.exterior.coords: 130 | feature.set_point(int(x), int(y)) 131 | 132 | # add interior rings 133 | for polygon_interior in list(polygon.interiors): 134 | feature.add_ring(len(polygon_interior.coords)) 135 | for x, y in polygon_interior.coords: 136 | feature.set_point(int(x), int(y)) 137 | 138 | if colormap: 139 | r, g, b, _ = colormap.get(v, (0, 0, 0, 0)) 140 | feature.add_property("color".encode(), '#{:02x}{:02x}{:02x}'.format(r, g, b).encode()) 141 | 142 | if class_names: 143 | feature.add_property("name".encode(), class_names.get(v, "").encode()) 144 | else: 145 | feature.add_property("value".encode(), str(v).encode()) 146 | 147 | feature.commit() 148 | 149 | return mvt.serialize() 150 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.2.0 3 | commit = True 4 | tag = True 5 | tag_name = {new_version} 6 | parse = 7 | (?P\d+)\.(?P\d+)\.(?P\d+) 8 | ((?P
a|b|rc|dev)(?P\d+))?
 9 | serialize =
10 | 	{major}.{minor}.{patch}{pre}{prenum}
11 | 	{major}.{minor}.{patch}
12 | 
13 | [bumpversion:file:rio_tiler_mvt/__init__.py]
14 | search = __version__ = "{current_version}"
15 | replace = __version__ = "{new_version}"
16 | 
17 | [metadata]
18 | version = attr: rio_tiler_mvt.__version__
19 | 


--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
 1 | """Setup for rio-tiler-mvt."""
 2 | 
 3 | import numpy
 4 | from Cython.Build import cythonize
 5 | from setuptools import find_packages, setup
 6 | from setuptools.extension import Extension
 7 | 
 8 | with open("README.md") as f:
 9 |     long_description = f.read()
10 | 
11 | inst_reqs = ["numpy", "vtzero", "rasterio", "shapely"]
12 | 
13 | extra_reqs = {
14 |     "test": [
15 |         # "vector-tile-base@git+https://github.com/mapbox/vector-tile-base.git",
16 |         "protobuf==3.20.1",
17 |         "rio-tiler>=2.0",
18 |         "pytest",
19 |         "pytest-cov",
20 |     ],
21 |     "dev": ["pre-commit"],
22 | }
23 | 
24 | ext_options = {"include_dirs": [numpy.get_include()]}
25 | ext_modules = cythonize(
26 |     [Extension("rio_tiler_mvt.mvt", ["rio_tiler_mvt/mvt.pyx"], **ext_options)]
27 | )
28 | 
29 | setup(
30 |     name="rio-tiler-mvt",
31 |     description="""A rio-tiler plugin to encode tile array to MVT""",
32 |     long_description=long_description,
33 |     long_description_content_type="text/markdown",
34 |     python_requires=">=3.9",
35 |     classifiers=[
36 |         "Intended Audience :: Information Technology",
37 |         "Intended Audience :: Science/Research",
38 |         "License :: OSI Approved :: MIT License",
39 |         "Programming Language :: Python :: 3.9",
40 |         "Programming Language :: Python :: 3.10",
41 |         "Programming Language :: Python :: 3.11",
42 |         "Programming Language :: Python :: 3.12",
43 |         "Programming Language :: Python :: 3.13",
44 |         "Topic :: Scientific/Engineering :: GIS",
45 |     ],
46 |     keywords="COG MVT mapbox vectortile GIS",
47 |     author="Vincent Sarago",
48 |     author_email="vincent@developmentseed.org",
49 |     url="https://github.com/cogeotiff/rio-tiler-mvt",
50 |     license="MIT",
51 |     packages=find_packages(exclude=["tests*"]),
52 |     include_package_data=True,
53 |     zip_safe=False,
54 |     install_requires=inst_reqs,
55 |     extras_require=extra_reqs,
56 |     ext_modules=ext_modules,
57 | )
58 | 


--------------------------------------------------------------------------------
/tests/fixtures/cog_classes.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cogeotiff/rio-tiler-mvt/a85a8b05548a445de78d501d4ea62b34737a1e44/tests/fixtures/cog_classes.tif


--------------------------------------------------------------------------------
/tests/fixtures/test.tif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cogeotiff/rio-tiler-mvt/a85a8b05548a445de78d501d4ea62b34737a1e44/tests/fixtures/test.tif


--------------------------------------------------------------------------------
/tests/test_mvt.py:
--------------------------------------------------------------------------------
  1 | """tests rio_tiler_mvt."""
  2 | 
  3 | import os
  4 | 
  5 | import pytest
  6 | import vector_tile_base
  7 | from rio_tiler.io import COGReader
  8 | 
  9 | from rio_tiler_mvt import pixels_encoder, shapes_encoder
 10 | from rio_tiler_mvt.mvt import encoder
 11 | 
 12 | 
 13 | def test_pixels_encoder():
 14 |     """Test MVT encoder."""
 15 |     asset = os.path.join(os.path.dirname(__file__), "fixtures", "test.tif")
 16 |     x = 72
 17 |     y = 63
 18 |     z = 7
 19 | 
 20 |     with COGReader(asset) as cog:
 21 |         tile, mask = cog.tile(x, y, z, resampling_method="nearest")
 22 | 
 23 |     # test with default
 24 |     vt = pixels_encoder(tile, mask)
 25 |     mvt = vector_tile_base.VectorTile(vt)
 26 |     assert len(mvt.layers) == 1
 27 |     layer = mvt.layers[0]
 28 |     assert layer.name == "my_layer"
 29 |     assert layer.extent == 4096
 30 |     assert layer.version == 2
 31 | 
 32 |     assert len(layer.features) == 75
 33 |     feat = layer.features[0]
 34 |     assert feat.type == "point"
 35 |     props = feat.attributes
 36 |     assert len(props) == 1
 37 |     assert props["band1"] == "21.08714485168457"
 38 | 
 39 |     # Test polygon
 40 |     vt = pixels_encoder(tile, mask, feature_type="polygon")
 41 |     mvt = vector_tile_base.VectorTile(vt)
 42 |     layer = mvt.layers[0]
 43 |     feat = layer.features[0]
 44 |     assert feat.type == "polygon"
 45 |     props = feat.attributes
 46 |     assert props["band1"] == "21.08714485168457"
 47 | 
 48 |     # Test band name
 49 |     vt = pixels_encoder(tile, mask, band_names=["pop"])
 50 |     mvt = vector_tile_base.VectorTile(vt)
 51 |     props = mvt.layers[0].features[0].attributes
 52 |     assert props["pop"] == "21.08714485168457"
 53 | 
 54 |     # Test layer name
 55 |     vt = pixels_encoder(tile, mask, layer_name="facebook")
 56 |     mvt = vector_tile_base.VectorTile(vt)
 57 |     assert len(mvt.layers) == 1
 58 |     layer = mvt.layers[0]
 59 |     assert layer.name == "facebook"
 60 | 
 61 |     with pytest.warns(DeprecationWarning):
 62 |         vt = encoder(tile, mask)
 63 |         mvt = vector_tile_base.VectorTile(vt)
 64 |         assert len(mvt.layers) == 1
 65 | 
 66 |     # Test bad feature type
 67 |     with pytest.raises(Exception):  # noqa: B017
 68 |         pixels_encoder(tile, mask, feature_type="somethingelse")
 69 | 
 70 | 
 71 | def test_shapes_encoder():
 72 |     """Test MVT encoder."""
 73 |     asset = os.path.join(os.path.dirname(__file__), "fixtures", "cog_classes.tif")
 74 |     x, y, z = 814, 1750, 12
 75 |     with COGReader(asset) as cog:
 76 |         tile, mask = cog.tile(x, y, z, resampling_method="nearest", tilesize=256)
 77 | 
 78 |     # test with default
 79 |     vt = shapes_encoder(tile[0], mask)
 80 |     mvt = vector_tile_base.VectorTile(vt)
 81 |     assert len(mvt.layers) == 1
 82 |     layer = mvt.layers[0]
 83 |     assert layer.name == "my_layer"
 84 |     assert layer.extent == 4096
 85 |     assert layer.version == 2
 86 | 
 87 |     with COGReader(asset) as cog:
 88 |         tile, mask = cog.tile(x, y, z, resampling_method="nearest", tilesize=4096)
 89 |         colormap = cog.colormap
 90 |         classes = {0: "none", 1: "grass", 2: "urban", 18: "something"}
 91 | 
 92 |     # test with default
 93 |     vt = shapes_encoder(tile[0], mask)
 94 |     mvt = vector_tile_base.VectorTile(vt)
 95 |     assert len(mvt.layers) == 1
 96 |     layer = mvt.layers[0]
 97 |     assert layer.name == "my_layer"
 98 |     assert layer.extent == 4096
 99 |     assert layer.version == 2
100 | 
101 |     feat = layer.features[0]
102 |     assert feat.type == "polygon"
103 |     props = feat.attributes
104 |     assert len(props) == 1
105 |     assert props["value"]
106 | 
107 |     vt = shapes_encoder(tile[0], mask, colormap=colormap, class_names=classes)
108 |     mvt = vector_tile_base.VectorTile(vt)
109 |     assert len(mvt.layers) == 1
110 |     layer = mvt.layers[0]
111 |     assert layer.name == "my_layer"
112 |     assert layer.extent == 4096
113 |     assert layer.version == 2
114 | 
115 |     feat = layer.features[0]
116 |     assert feat.type == "polygon"
117 | 
118 |     props = feat.attributes
119 |     assert len(props) == 2
120 |     assert props["color"] == "#4c70a3"
121 |     assert props["name"] == "something"
122 | 


--------------------------------------------------------------------------------