├── MANIFEST.in ├── requirements.txt ├── tests ├── fixtures │ ├── test.tif │ └── cog_classes.tif └── test_mvt.py ├── rio_tiler_mvt ├── __init__.py └── mvt.pyx ├── codecov.yml ├── .pre-commit-config.yaml ├── setup.cfg ├── CHANGES.md ├── pyproject.toml ├── LICENSE ├── .gitignore ├── setup.py ├── .github └── workflows │ └── ci.yml └── README.md /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include rio_tiler_mvt/*.pyx 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | vtzero 3 | rasterio 4 | shapely 5 | -------------------------------------------------------------------------------- /tests/fixtures/test.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cogeotiff/rio-tiler-mvt/HEAD/tests/fixtures/test.tif -------------------------------------------------------------------------------- /tests/fixtures/cog_classes.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cogeotiff/rio-tiler-mvt/HEAD/tests/fixtures/cog_classes.tif -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: off 2 | 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: auto 8 | threshold: 5 9 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | 


--------------------------------------------------------------------------------
/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 | 


--------------------------------------------------------------------------------
/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 | 


--------------------------------------------------------------------------------
/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 | 


--------------------------------------------------------------------------------
/.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


--------------------------------------------------------------------------------
/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 | 


--------------------------------------------------------------------------------
/.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 | 


--------------------------------------------------------------------------------
/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 | 


--------------------------------------------------------------------------------
/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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------