Skip to content

Commit

Permalink
Merge branch 'master' into test_optimise
Browse files Browse the repository at this point in the history
  • Loading branch information
rmarianski authored Jan 11, 2023
2 parents eda6b8e + f4ef1b0 commit e291a7a
Show file tree
Hide file tree
Showing 14 changed files with 814 additions and 391 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ jobs:
strategy:
matrix:
python-version:
- '3.7'
- '3.8'
- '3.9'
- '3.10'
Expand All @@ -33,7 +32,7 @@ jobs:
- name: Install dependencies
run: |
poetry env use "${{ matrix.python-version }}"
poetry install
poetry install --all-extras
- name: Run tox targets for ${{ matrix.python-version }}
run: |
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ repos:
rev: v3.3.1
hooks:
- id: pyupgrade
args: [ --py37-plus ]
args: [ --py38-plus ]
- repo: https://github.com/PyCQA/isort
rev: v5.11.3
rev: 5.11.4
hooks:
- id: isort
- repo: https://github.com/psf/black
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ Version 2.0.0
* 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
-------------
Expand Down
161 changes: 121 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@ Mapbox Vector Tile
Installation
------------

mapbox-vector-tile is compatible with Python 3.7 or newer. It is listed on PyPi as `mapbox-vector-tile`. The
mapbox-vector-tile is compatible with Python 3.8 or newer. It is listed on PyPi as `mapbox-vector-tile`. The
recommended way to install is via `pip`:

```shell
pip install mapbox-vector-tile
```

Note that `mapbox-vector-tile` depends on [Shapely](https://pypi.python.org/pypi/Shapely), a Python library for computational geometry which requires a library called [GEOS](https://trac.osgeo.org/geos/). Please see [Shapely's instructions](https://pypi.python.org/pypi/Shapely#installing-shapely) for information on how to install its prerequisites.
An extra dependency has been defined to install [`pyproj`](https://pyproj4.github.io/pyproj/stable/). This is useful
when changing the Coordinate Reference System when encoding or decoding tiles.

```shell
pip install mapbox-vector-tile[proj]
```

Encoding
--------
Expand All @@ -29,6 +34,12 @@ 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.

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
Expand Down Expand Up @@ -63,7 +74,7 @@ following keys
}
])

'\x1aH\n\x05water\x12\x18\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\x03uid\x1a\x03cat"\x05\n\x03bar"\x02 {"\x06\n\x04flew(\x80 x\x02\x1aD\n\x03air\x12\x15\x12\x06\x00\x00\x01\x01\x02\x02\x18\x02"\t\t\xbe\x02\xb6\x03\n\x81\x1b\x00\x1a\x03foo\x1a\x03uid\x1a\x03cat"\x05\n\x03bar"\x03 \xd2\t"\x06\n\x04flew(\x80 x\x02'
b'\x1aH\n\x05water\x12\x18\x12\x06\x00\x00\x01\x01\x02\x02\x18\x03"\x0c\t\x00\x80@\x1a\x00\x01\x02\x00\x00\x02\x0f\x1a\x03uid\x1a\x03foo\x1a\x03cat"\x02 {"\x05\n\x03bar"\x06\n\x04flew(\x80 x\x02\x1aD\n\x03air\x12\x15\x12\x06\x00\x00\x01\x01\x02\x02\x18\x02"\t\t\xbe\x02\xb6\x03\n\x81\x1b\x00\x1a\x03uid\x1a\x03foo\x1a\x03cat"\x03 \xd2\t"\x05\n\x03bar"\x06\n\x04flew(\x80 x\x02'


# Using WKB
Expand All @@ -72,7 +83,7 @@ following keys
"name": "water",
"features": [
{
"geometry":"\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",
"geometry":b"\x01\x03\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
"properties":{
"uid":123,
"foo":"bar",
Expand All @@ -85,7 +96,7 @@ following keys
"name": "air",
"features": [
{
"geometry":"\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",
"geometry":b"\x01\x03\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
"properties":{
"uid":1234,
"foo":"bar",
Expand All @@ -96,7 +107,7 @@ following keys
}
])

'\x1aJ\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\x03uid\x1a\x03cat"\x05\n\x03bar"\x02 {"\x06\n\x04flew(\x80 x\x02\x1aY\n\x03air\x12\x1c\x08\x01\x12\x08\x00\x00\x01\x01\x02\x02\x03\x03\x18\x03"\x0c\t\x00\x80@\x1a\x00\x01\x02\x00\x00\x02\x0f\x1a\x03foo\x1a\x03uid\x1a\x05balls\x1a\x03cat"\x05\n\x03bar"\x03 \xd2\t"\x05\n\x03foo"\x06\n\x04flew(\x80 x\x02'
b'\x1aH\n\x05water\x12\x18\x12\x06\x00\x00\x01\x01\x02\x02\x18\x03"\x0c\t\x00\x80@\x1a\x00\x01\x02\x00\x00\x02\x0f\x1a\x03uid\x1a\x03foo\x1a\x03cat"\x02 {"\x05\n\x03bar"\x06\n\x04flew(\x80 x\x02\x1aG\n\x03air\x12\x18\x12\x06\x00\x00\x01\x01\x02\x02\x18\x03"\x0c\t\x00\x80@\x1a\x00\x01\x02\x00\x00\x02\x0f\x1a\x03uid\x1a\x03foo\x1a\x03cat"\x03 \xd2\t"\x05\n\x03bar"\x06\n\x04flew(\x80 x\x02'
```

### Coordinate transformations for encoding
Expand Down Expand Up @@ -169,6 +180,56 @@ Note that this example may not have anything visible within the tile when render

Also note that the spec allows the extents to be modified, even though they are often set to 4096 by convention. `mapbox-vector-tile` assumes an extent of 4096.

```python
import mapbox_vector_tile
from pyproj import Transformer
from shapely.geometry import LineString

SRID_LNGLAT = 4326
SRID_SPHERICAL_MERCATOR = 3857
direct_transformer = Transformer.from_crs(crs_from=SRID_LNGLAT, crs_to=SRID_SPHERICAL_MERCATOR, always_xy=True)

lnglat_line = LineString(((-122.1, 45.1), (-122.2, 45.2)))

# Encode the tile
tile_pbf = mapbox_vector_tile.encode({
"name": "my-layer",
"features": [{
"geometry": lnglat_line.wkt,
"properties": {"stuff": "things"},
}]
},
default_options={"transformer": direct_transformer.transform})

# Decode the tile
reverse_transformer = Transformer.from_crs(crs_from=SRID_SPHERICAL_MERCATOR, crs_to=SRID_LNGLAT, always_xy=True)
mapbox_vector_tile.decode(tile=tile_pbf, default_options={"transformer": reverse_transformer.transform})

{
"my-layer": {
"extent": 4096,
"version": 1,
"features": [
{
"geometry": {
"type": "LineString",
"coordinates": [
[-122.10000156433787, 45.09999871982179],
[-122.20000202176608, 45.20000292038091]
]
},
"properties": {
"stuff": "things"
},
"id": 0,
"type": "Feature"
}
],
"type": "FeatureCollection"
}
}
```

### Quantization

The encoder also has options to quantize the data for you via the `quantize_bounds` option. When encoding, pass in the bounds in the form (minx, miny, maxx, maxy) and the coordinates will be scaled appropriately during encoding.
Expand All @@ -186,7 +247,7 @@ mapbox_vector_tile.encode([
}
]
}
], quantize_bounds=(10.0, 10.0, 20.0, 20.0))
], default_options={"quantize_bounds": (10.0, 10.0, 20.0, 20.0)})
```

In this example, the coordinate that would get encoded would be (2048, 2048)
Expand All @@ -211,7 +272,7 @@ mapbox_vector_tile.encode([
}
]
}
], quantize_bounds=(0.0, 0.0, 10.0, 10.0), extents=50)
], default_options={"quantize_bounds": (0.0, 0.0, 10.0, 10.0), "extents":50})
```

Decoding
Expand All @@ -237,44 +298,60 @@ Decode method takes in a valid google.protobuf.message Tile and returns decoded
}
```

The decoding 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 `decode` method for more
details about the available options and their global default values.

```python
>>> import mapbox_vector_tile

>>> mapbox_vector_tile.decode('\x1aJ\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\x03uid\x1a\x03cat"\x05\n\x03bar"\x02 {"\x06\n\x04flew(\x80 x\x02\x1aY\n\x03air\x12\x1c\x08\x01\x12\x08\x00\x00\x01\x01\x02\x02\x03\x03\x18\x03"\x0c\t\x00\x80@\x1a\x00\x01\x02\x00\x00\x02\x0f\x1a\x03foo\x1a\x03uid\x1a\x05balls\x1a\x03cat"\x05\n\x03bar"\x03 \xd2\t"\x05\n\x03foo"\x06\n\x04flew(\x80 x\x02')
>>> mapbox_vector_tile.decode(b'\x1aJ\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\x03uid\x1a\x03cat"\x05\n\x03bar"\x02 {"\x06\n\x04flew(\x80 x\x02\x1aY\n\x03air\x12\x1c\x08\x01\x12\x08\x00\x00\x01\x01\x02\x02\x03\x03\x18\x03"\x0c\t\x00\x80@\x1a\x00\x01\x02\x00\x00\x02\x0f\x1a\x03foo\x1a\x03uid\x1a\x05balls\x1a\x03cat"\x05\n\x03bar"\x03 \xd2\t"\x05\n\x03foo"\x06\n\x04flew(\x80 x\x02')

{
'water': {
'extent': 4096,
'version': 2,
'features': [{
'geometry': {'type': 'Polygon', 'coordinates': [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]},
'properties': {
'foo': 'bar',
'uid': 123,
'cat': 'flew'
},
'type': 3,
'id': 1
}
]
},
'air': {
'extent': 4096,
'version': 2,
'features': [{
'geometry': {'type': 'Polygon', 'coordinates': [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]},
'properties': {
'foo': 'bar',
'uid': 1234,
'balls': 'foo',
'cat': 'flew'
},
'type': 3,
'id': 1
}
]
}
"water": {
"extent": 4096,
"version": 2,
"features": [
{
"geometry": {
"type": "Polygon",
"coordinates": [[[0,0],[0,1],[1,1],[1,0],[0,0]]]
},
"properties": {
"foo": "bar",
"uid": 123,
"cat": "flew"
},
"id": 1,
"type": "Feature"
}
],
"type": "FeatureCollection"
},
"air": {
"extent": 4096,
"version": 2,
"features": [
{
"geometry": {
"type": "Polygon",
"coordinates": [[[0,0],[0,1],[1,1],[1,0],[0,0]]]
},
"properties": {
"foo": "bar",
"uid": 1234,
"balls": "foo",
"cat": "flew"
},
"id": 1,
"type": "Feature"
}
],
"type": "FeatureCollection"
}
}

```

Here's how you might decode a tile from a file.
Expand All @@ -288,6 +365,10 @@ Here's how you might decode a tile from a file.
>>> f.write(repr(decoded_data))
```

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

Expand Down
2 changes: 1 addition & 1 deletion bench/bench_encode.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def run_test(layers):
for layer in layers:
layer_description = {"features": layer, "name": "bar"}
profiler.enable()
encode(layer_description, on_invalid_geometry=on_invalid_geometry_ignore)
encode(layer_description, default_options={"on_invalid_geometry": on_invalid_geometry_ignore})
profiler.disable()
if i % 100 == 0:
print(f"{i} tiles produced")
Expand Down
Loading

0 comments on commit e291a7a

Please sign in to comment.