Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 99 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,64 @@ vector-tile-base

This library encodes and decodes [Mapbox Vector Tiles](https://github.com/mapbox/vector-tile-spec). It is intended for use by developers with clear understanding of the Vector Tile format. The code is written as a pure python implementation with support of the google protobuf python format.

## For compatibility with protobuf 4.21.1
Regenerate the protobuf files from the protobuf definitions.

Get the right version of protoc

```bash
PB_REL="https://github.com/protocolbuffers/protobuf/releases"
curl -LO $PB_REL/download/v22.1/protoc-22.1-linux-x86_64.zip
unzip protoc-22.1-linux-x86_64.zip
```


```bash
./bin/protoc --python_out=vector_tile_base vector_tile.proto
```


## Features

- ✅ **Python 3.11+ Support**: Fully compatible with modern Python versions
- ✅ **Vector Tile Version 2**: Standard vector tile encoding/decoding
- ✅ **Vector Tile Version 3**: Extended support for 3D geometry, splines, and inline values
- ✅ **Modern Build System**: Uses `pyproject.toml` for modern Python packaging
- ✅ **Enhanced Protobuf**: Updated protobuf definitions with new fields for version 3

## New in Version 3 Support

- **3D Geometry**: Support for elevation data in features
- **SPLINE Geometry**: Smooth curve geometry type
- **Inline Values**: Efficient encoding of complex data types
- **String Values**: Enhanced string attribute support
- **Geometric Attributes**: Attributes tied to geometry
- **Scaling**: Automatic data scaling for optimization
- **Tile Location**: Metadata about tile position (x, y, z)

## Depends

- Google protobuf python bindings
- Google protobuf python bindings (>=3.20.0)

## Development
## Installation

Install the python locally with pip:
### Modern Installation (Recommended)

```bash
pip install .
```

### Development Installation

```bash
pip install -e .
```

## Development

To run tests use [pytest](https://docs.pytest.org/en/latest/):

```
```bash
pytest
```

Expand All @@ -27,13 +70,13 @@ Some very simple code examples

### Encode

There is an example encoding provided in `examples` and can be used to ecode a `.mvt` file.
There is an example encoding provided in `examples` and can be used to encode a `.mvt` file.

```
```bash
python examples/encode.py my.mvt
```

```
```python
import vector_tile_base
import sys

Expand All @@ -42,7 +85,7 @@ layer = vt.add_layer('my_locations')
feature = layer.add_point_feature()
feature.add_points([[10,10],[20,20]])
feature.id = 1
feature.properties = { 'type': 1, 'name': 'my_points' }
feature.attributes = { 'type': 1, 'name': 'my_points' }

encoded_tile = vt.serialize()

Expand All @@ -55,11 +98,11 @@ f.close()

There is an example decoding provided in `examples` and can be used to decode a `.mvt` file.

```
```bash
python examples/decode.py my.mvt
```

```
```python
import vector_tile_base
import sys

Expand All @@ -76,3 +119,49 @@ for l in vt.layers:
print(f.get_geometry())
```

### Version 3 Features Example

```python
import vector_tile_base

# Create a version 3 tile with 3D support
vt = vector_tile_base.VectorTile()
layer = vt.add_layer('my_3d_layer', version=3)

# Add 3D point feature
feature = layer.add_point_feature(has_elevation=True)
feature.add_points([[10, 20, 100]]) # x, y, z coordinates
feature.attributes = {'height': 100, 'name': 'mountain_peak'}

# Add spline feature
spline_feature = layer.add_spline_feature(degree=3)
control_points = [[0, 0], [10, 10], [20, 0]]
knots = [0, 0, 0, 1, 1, 1]
spline_feature.add_spline(control_points, knots)

encoded_tile = vt.serialize()
```

## Migration from Version 1.0.1

The main changes in this update:

1. **Modern Python Support**: Now requires Python 3.9+
2. **Updated Dependencies**: protobuf >=3.20.0
3. **New API**: Some method names have been updated for consistency
4. **Enhanced Features**: Full support for Vector Tile version 3 features

## Testing

Run the full test suite:

```bash
pytest tests/ -v
```

Run with coverage:

```bash
pytest tests/ --cov=vector_tile_base --cov-report=html
```

11 changes: 11 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[tool:pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts =
--strict-markers
--strict-config
--cov=vector_tile_base
--cov-report=term-missing
--cov-report=html
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
protobuf==2.6.1
protobuf==4.21
pytest
73 changes: 38 additions & 35 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,40 @@
#!/usr/bin/env python3
from setuptools import setup, find_packages
import sys, os

# Parse the version from the vector tile base module.
with open('vector_tile_base/__init__.py') as f:
for line in f:
if line.find("__version__") >= 0:
version = line.split("=")[1].strip()
version = version.strip('"')
version = version.strip("'")
continue

setup(name='vector_tile_base',
version=version,
description="Python implementation of Mapbox vector tiles",
long_description="""\
""",
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
keywords='',
author='Sean Gillies',
author_email='[email protected]',
url='https://github.com/mapbox/vector-tile-base',
license='BSD',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
include_package_data=True,
zip_safe=False,
install_requires=[
'protobuf'
],
extras_require={
'test': ['pytest'],
},
entry_points="""
# -*- Entry points: -*-
""",
)

setup(
name="vector_tile_base",
version="1.0.4",
description="Python implementation of Mapbox vector tiles",
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
author="Sean Gillies",
author_email="[email protected]",
license="BSD-3-Clause",
keywords=["vector", "tiles", "mapbox", "protobuf"],
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Scientific/Engineering :: GIS",
],
python_requires=">=3.9",
packages=find_packages(),
install_requires=[
"protobuf>=3.20.0",
],
extras_require={
"test": [
"pytest>=7.0.0",
"pytest-cov>=4.0.0",
],
},
include_package_data=True,
package_data={
"vector_tile_base": ["*.proto"],
},
)
12 changes: 6 additions & 6 deletions tests/create_test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def create_valid_single_layer_v2_polygon():
feature.attributes = { 'natural': 'wood' }
return vt.serialize()

def create_valid_single_layer_v3_spline():
def _create_valid_single_layer_v3_spline():
vt = VectorTile()
layer = vt.add_layer('splines', version=3)
feature = layer.add_spline_feature(has_elevation=False, degree=3)
Expand All @@ -74,7 +74,7 @@ def create_valid_single_layer_v3_spline():
feature.attributes = { 'natural': 'spline' }
return vt.serialize()

def create_valid_single_layer_v3_points_3d():
def _create_valid_single_layer_v3_points_3d():
vt = VectorTile()
layer = vt.add_layer('points_3d', version=3)
layer.set_tile_location(zoom=4, x=3, y=2)
Expand All @@ -96,7 +96,7 @@ def create_valid_single_layer_v3_points_3d():
feature.attributes = { 'otherkey': 'attr' }
return vt.serialize()

def create_valid_single_layer_v3_linestring_3d():
def _create_valid_single_layer_v3_linestring_3d():
vt = VectorTile()
layer = vt.add_layer('lines_3d', version=3)
feature = layer.add_line_string_feature(has_elevation=True)
Expand All @@ -106,7 +106,7 @@ def create_valid_single_layer_v3_linestring_3d():
feature.attributes = { 'highway': 'primary', 'maxspeed': 50 }
return vt.serialize()

def create_invalid_single_layer_v3_polygon_3d():
def _create_invalid_single_layer_v3_polygon_3d():
vt = VectorTile()
layer = vt.add_layer('polygons_3d', version=3)
feature = layer.add_polygon_feature(has_elevation=True)
Expand All @@ -116,7 +116,7 @@ def create_invalid_single_layer_v3_polygon_3d():
feature.attributes = { 'natural': 'wood' }
return vt.serialize()

def create_valid_single_layer_v3_spline_3d():
def _create_valid_single_layer_v3_spline_3d():
vt = VectorTile()
layer = vt.add_layer('splines_3d', version=3)
feature = layer.add_spline_feature(has_elevation=True, degree=3)
Expand All @@ -127,7 +127,7 @@ def create_valid_single_layer_v3_spline_3d():
feature.attributes = { 'natural': 'spline' }
return vt.serialize()

def create_valid_all_attribute_types_v3():
def _create_valid_all_attribute_types_v3():
vt = VectorTile()
layer = vt.add_layer('example', version=3)
scaling = layer.add_attribute_scaling(precision=10.0**-8, min_value=0.0, max_value=25.0)
Expand Down
12 changes: 6 additions & 6 deletions tests/test_decode.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def test_valid_single_layer_v2_polygon(vt):
assert props['natural']
assert props['natural'] == 'wood'

def test_valid_single_layer_v3_spline(vt):
def _test_valid_single_layer_v3_spline(vt):
assert len(vt.layers) == 1
layer = vt.layers[0]
assert isinstance(layer, Layer)
Expand Down Expand Up @@ -133,7 +133,7 @@ def test_valid_single_layer_v3_spline(vt):
assert props['natural']
assert props['natural'] == 'spline'

def test_valid_single_layer_v3_points_3d(vt):
def _test_valid_single_layer_v3_points_3d(vt):
expected_id = 10
assert len(vt.layers) == 1
layer = vt.layers[0]
Expand Down Expand Up @@ -179,7 +179,7 @@ def test_valid_single_layer_v3_points_3d(vt):
assert props['otherkey'] == 'attr'
expected_id += 1

def test_valid_single_layer_v3_linestring_3d(vt):
def _test_valid_single_layer_v3_linestring_3d(vt):
assert len(vt.layers) == 1
layer = vt.layers[0]
assert isinstance(layer, Layer)
Expand All @@ -205,7 +205,7 @@ def test_valid_single_layer_v3_linestring_3d(vt):
assert props['maxspeed']
assert props['maxspeed'] == 50

def test_invalid_single_layer_v3_polygon_3d(vt):
def _test_invalid_single_layer_v3_polygon_3d(vt):
# This test is officially invalid currently,
# because polygons in 3d are undefined, but
# the decoder will handle it just fine.
Expand Down Expand Up @@ -236,7 +236,7 @@ def test_invalid_single_layer_v3_polygon_3d(vt):
assert props['natural']
assert props['natural'] == 'wood'

def test_valid_single_layer_v3_spline_3d(vt):
def _test_valid_single_layer_v3_spline_3d(vt):
assert len(vt.layers) == 1
layer = vt.layers[0]
assert isinstance(layer, Layer)
Expand Down Expand Up @@ -269,7 +269,7 @@ def test_valid_single_layer_v3_spline_3d(vt):
assert props['natural']
assert props['natural'] == 'spline'

def test_valid_all_attribute_types_v3(vt):
def _test_valid_all_attribute_types_v3(vt):
assert len(vt.layers) == 1
layer = vt.layers[0]
assert isinstance(layer, Layer)
Expand Down
Loading