Commit 82654c2f authored by Bas Couwenberg's avatar Bas Couwenberg

Imported Upstream version 0.5.0+ds

parents
include README.md
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
from . import encoder
from . import decoder
def decode(tile, y_coord_down=False):
vector_tile = decoder.TileData()
message = vector_tile.getMessage(tile, y_coord_down)
return message
def encode(layers, quantize_bounds=None, y_coord_down=False, extents=4096,
on_invalid_geometry=None, round_fn=None):
vector_tile = encoder.VectorTile(extents, on_invalid_geometry,
round_fn=round_fn)
if (isinstance(layers, list)):
for layer in layers:
vector_tile.addFeatures(layer['features'], layer['name'],
quantize_bounds, y_coord_down)
else:
vector_tile.addFeatures(layers['features'], layers['name'],
quantize_bounds, y_coord_down)
return vector_tile.tile.SerializeToString()
import sys
from builtins import map
PY3 = sys.version_info[0] == 3
if PY3:
from .Mapbox import vector_tile_pb2_p3
vector_tile = vector_tile_pb2_p3
else:
from .Mapbox import vector_tile_pb2
vector_tile = vector_tile_pb2
def apply_map(fn, x):
return list(map(fn, x))
from past.builtins import xrange
from .compat import vector_tile
cmd_bits = 3
CMD_MOVE_TO = 1
CMD_LINE_TO = 2
CMD_SEG_END = 7
UNKNOWN = 0
POINT = 1
LINESTRING = 2
POLYGON = 3
class TileData:
"""
"""
def __init__(self):
self.tile = vector_tile.tile()
def getMessage(self, pbf_data, y_coord_down=False):
self.tile.ParseFromString(pbf_data)
tile = {}
for layer in self.tile.layers:
keys = layer.keys
vals = layer.values
features = []
for feature in layer.features:
tags = feature.tags
props = {}
assert len(tags) % 2 == 0, 'Unexpected number of tags'
for key_idx, val_idx in zip(tags[::2], tags[1::2]):
key = keys[key_idx]
val = vals[val_idx]
value = self.parse_value(val)
props[key] = value
geometry = self.parse_geometry(feature.geometry, feature.type,
layer.extent, y_coord_down)
new_feature = {
"geometry": geometry,
"properties": props,
"id": feature.id,
"type": feature.type
}
features.append(new_feature)
tile[layer.name] = {
"extent": layer.extent,
"version": layer.version,
"features": features,
}
return tile
def zero_pad(self, val):
return '0' + val if val[0] == 'b' else val
def parse_value(self, val):
for candidate in ('bool_value',
'double_value',
'float_value',
'int_value',
'sint_value',
'string_value',
'uint_value'):
if val.HasField(candidate):
return getattr(val, candidate)
raise ValueError('%s is an unknown value')
def zig_zag_decode(self, n):
return (n >> 1) ^ (-(n & 1))
def parse_geometry(self, geom, ftype, extent, y_coord_down):
# [9 0 8192 26 0 10 2 0 0 2 15]
i = 0
coords = []
dx = 0
dy = 0
parts = [] # for multi linestrings and polygons
while i != len(geom):
item = bin(geom[i])
ilen = len(item)
cmd = int(self.zero_pad(item[(ilen - cmd_bits):ilen]), 2)
cmd_len = int(self.zero_pad(item[:ilen - cmd_bits]), 2)
i = i + 1
def _ensure_polygon_closed(coords):
if coords and coords[0] != coords[-1]:
coords.append(coords[0])
if cmd == CMD_SEG_END:
if ftype == POLYGON:
_ensure_polygon_closed(coords)
parts.append(coords)
coords = []
elif cmd == CMD_MOVE_TO or cmd == CMD_LINE_TO:
if coords and cmd == CMD_MOVE_TO:
if ftype in (LINESTRING, POLYGON):
# multi line string or polygon
# our encoder includes CMD_SEG_END to denote
# the end of a polygon ring, but this path
# would also handle the case where we receive
# a move without a previous close on polygons
# for polygons, we want to ensure that it is
# closed
if ftype == POLYGON:
_ensure_polygon_closed(coords)
parts.append(coords)
coords = []
for point in xrange(0, cmd_len):
x = geom[i]
i = i + 1
y = geom[i]
i = i + 1
# zipzag decode
x = self.zig_zag_decode(x)
y = self.zig_zag_decode(y)
x = x + dx
y = y + dy
dx = x
dy = y
if not y_coord_down:
y = extent - y
coords.append([x, y])
if ftype == POINT:
return coords
elif ftype == LINESTRING:
if parts:
if coords:
parts.append(coords)
return parts[0] if len(parts) == 1 else parts
else:
return coords
elif ftype == POLYGON:
if coords:
parts.append(coords)
def _area_sign(ring):
a = sum(ring[i][0]*ring[i+1][1] - ring[i+1][0]*ring[i][1] for i in range(0, len(ring)-1)) # noqa
return -1 if a < 0 else 1 if a > 0 else 0
polygon = []
polygons = []
winding = 0
for ring in parts:
a = _area_sign(ring)
if a == 0:
continue
if winding == 0:
winding = a
if winding == a:
if polygon:
polygons.append(polygon)
polygon = [ring]
else:
polygon.append(ring)
if polygon:
polygons.append(polygon)
return polygons[0] if len(polygons) == 1 else polygons
else:
raise ValueError('Unknown geometry type: %s' % ftype)
This diff is collapsed.
[bdist_wheel]
universal = 1
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
import io
from setuptools import setup, find_packages
with io.open('README.md') as readme_file:
long_description = readme_file.read()
def test_suite():
try:
import unittest2 as unittest
except:
import unittest
suite = unittest.TestLoader().discover("tests")
return suite
setup(name='mapbox-vector-tile',
version='0.5.0',
description=u"Mapbox Vector Tile",
long_description=long_description,
classifiers=[],
keywords='',
author=u"Harish Krishna",
author_email='harish.krsn@gmail.com',
url='https://github.com/tilezen/mapbox-vector-tile',
license='MIT',
packages=find_packages(),
include_package_data=True,
zip_safe=False,
test_suite="setup.test_suite",
install_requires=["setuptools", "protobuf", "shapely", "future"]
)
import doctest
import glob
import os
optionflags = (doctest.REPORT_ONLY_FIRST_FAILURE |
doctest.NORMALIZE_WHITESPACE |
doctest.ELLIPSIS)
_basedir = os.path.dirname(__file__)
paths = glob.glob("%s/*.txt" % _basedir)
test_suite = doctest.DocFileSuite(*paths, **dict(module_relative=False,
optionflags=optionflags))
# -*- coding: utf-8 -*-
"""
Tests for vector_tile/decoder.py
"""
import unittest
import mapbox_vector_tile
from mapbox_vector_tile.compat import PY3
class BaseTestCase(unittest.TestCase):
def test_decoder(self):
if PY3:
vector_tile = b'\x1aI\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\x03baz\x1a\x03uid"\x05\n\x03bar"\x05\n\x03foo"\x02 {(\x80 x\x02' # noqa
else:
vector_tile = '\x1aI\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\x03baz\x1a\x03uid"\x05\n\x03bar"\x05\n\x03foo"\x02 {(\x80 x\x02' # noqa
self.assertEqual(mapbox_vector_tile.decode(vector_tile), {
'water': {
'version': 2,
'extent': 4096,
'features': [{
'geometry': [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]],
'properties': {
'foo': 'bar',
'baz': 'foo',
'uid': 123
},
'id': 1,
'type': 3
}],
},
})
def test_decode_polygon_no_cmd_seg_end(self):
# the binary here was generated without including the
# CMD_SEG_END after the polygon parts
# this tests that the decoder can detect that a new
# CMD_MOVE_TO implicitly closes the previous polygon
if PY3:
vector_tile = b'\x1a+\n\x05water\x12\x1d\x18\x03"\x19\t\x00\x80@"\x08\x00\x00\x07\x07\x00\x00\x08\t\x02\x01"\x00\x03\x04\x00\x00\x04\x03\x00(\x80 x\x02' # noqa
else:
vector_tile = '\x1a+\n\x05water\x12\x1d\x18\x03"\x19\t\x00\x80@"\x08\x00\x00\x07\x07\x00\x00\x08\t\x02\x01"\x00\x03\x04\x00\x00\x04\x03\x00(\x80 x\x02' # noqa
self.assertEqual(mapbox_vector_tile.decode(vector_tile), {
'water': {
'version': 2,
'extent': 4096,
'features': [{
'geometry': [
[[0, 0], [4, 0], [4, 4], [0, 4], [0, 0]],
[[1, 1], [1, 3], [3, 3], [3, 1], [1, 1]],
],
'properties': {},
'id': 0,
'type': 3
}],
},
})
def test_nondefault_extent(self):
if PY3:
vector_tile = b'\x1aK\n\x05water\x12\x1c\x08\x01\x12\x06\x00\x00\x01\x01\x02\x02\x18\x02"\x0e\t\x80}\xd0\x12\x12\xbf>\xd86\xbf>\xd86\x1a\x03foo\x1a\x03baz\x1a\x03uid"\x05\n\x03bar"\x05\n\x03foo"\x02 {(\x80@x\x02' # noqa
else:
vector_tile = '\x1aK\n\x05water\x12\x1c\x08\x01\x12\x06\x00\x00\x01\x01\x02\x02\x18\x02"\x0e\t\x80}\xd0\x12\x12\xbf>\xd86\xbf>\xd86\x1a\x03foo\x1a\x03baz\x1a\x03uid"\x05\n\x03bar"\x05\n\x03foo"\x02 {(\x80@x\x02' # noqa
self.assertEqual(mapbox_vector_tile.decode(vector_tile), {
'water': {
'version': 2,
'extent': 8192,
'features': [{
'geometry': [[8000, 7000], [4000, 3500], [0, 0]],
'id': 1,
'properties': {'baz': 'foo', 'foo': 'bar', 'uid': 123},
'type': 2
}],
}
})
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment