├── .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 |
5 |
7 | A rio-tiler plugin to translate tile array to MVT (using python-vtzero). 8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
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 | --------------------------------------------------------------------------------