def test_points_pbf(self): ''' Create 3 points (2 on west, 1 on east hemisphere) and retrieve as pbf. 2 points should be returned in western hemisphere and 1 on eastern at zoom level 1 (clip on) ''' self.defineGeometry('POINT') point_sf = Point(-122.42, 37.78) point_berlin = Point(13.41, 52.52) point_lima = Point(-77.03, 12.04) self.insertTestRow(point_sf.wkt, 'San Francisco') self.insertTestRow(point_berlin.wkt, 'Berlin') self.insertTestRow(point_lima.wkt, 'Lima') ######## # northwest quadrant should return San Francisco and Lima tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_test", "pbf", 0, 0, 1) pbf_result = mapbox_vector_tile.decode(tile_content) self.assertTrue(tile_mimetype.endswith('/x-protobuf')) self.assertIn('vectile_test', pbf_result) layer_result = pbf_result['vectile_test'] self.assertEqual(len(layer_result['features']), 2) extent = tile_bounds_mercator(0, 0, 1) cities = [] # Make sure that the right cities have been returned and that the geometries match for feature in layer_result['features']: if feature['properties']['name'] == 'San Francisco': cities.append(feature['properties']['name']) self.assertTrue(point_sf.almost_equals(decoded_pbf_asshape(feature, extent), decimal=1)) elif feature['properties']['name'] == 'Lima': cities.append(feature['properties']['name']) self.assertTrue(point_lima.almost_equals(decoded_pbf_asshape(feature, extent), decimal=1)) self.assertTrue('San Francisco' in cities) self.assertTrue('Lima' in cities) ########## # northeast quadrant should return Berlin tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_test", "pbf", 0, 1, 1) pbf_result = mapbox_vector_tile.decode(tile_content) self.assertTrue(tile_mimetype.endswith('/x-protobuf')) self.assertIn('vectile_test', pbf_result) layer_result = pbf_result['vectile_test'] self.assertEqual(len(layer_result['features']), 1) self.assertTrue('Berlin' in layer_result['features'][0]['properties']['name'])
def test_points_pbf(self): """ Create 3 points (2 on west, 1 on east hemisphere) and retrieve as pbf. 2 points should be returned in western hemisphere and 1 on eastern at zoom level 1 (clip on) """ self.defineGeometry("POINT") point_sf = Point(-122.42, 37.78) point_berlin = Point(13.41, 52.52) point_lima = Point(-77.03, 12.04) self.insertTestRow(point_sf.wkt, "San Francisco") self.insertTestRow(point_berlin.wkt, "Berlin") self.insertTestRow(point_lima.wkt, "Lima") ######## # northwest quadrant should return San Francisco and Lima tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_test", "pbf", 0, 0, 1) pbf_result = mapbox_vector_tile.decode(tile_content) self.assertTrue(tile_mimetype.endswith("/x-protobuf")) self.assertIn("vectile_test", pbf_result) layer_result = pbf_result["vectile_test"] self.assertEqual(len(layer_result["features"]), 2) extent = tile_bounds_mercator(0, 0, 1) cities = [] # Make sure that the right cities have been returned and that the geometries match for feature in layer_result["features"]: if feature["properties"]["name"] == "San Francisco": cities.append(feature["properties"]["name"]) self.assertTrue(point_sf.almost_equals(decoded_pbf_asshape(feature, extent), decimal=1)) elif feature["properties"]["name"] == "Lima": cities.append(feature["properties"]["name"]) self.assertTrue(point_lima.almost_equals(decoded_pbf_asshape(feature, extent), decimal=1)) self.assertTrue("San Francisco" in cities) self.assertTrue("Lima" in cities) ########## # northeast quadrant should return Berlin tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_test", "pbf", 0, 1, 1) pbf_result = mapbox_vector_tile.decode(tile_content) self.assertTrue(tile_mimetype.endswith("/x-protobuf")) self.assertIn("vectile_test", pbf_result) layer_result = pbf_result["vectile_test"] self.assertEqual(len(layer_result["features"]), 1) self.assertTrue("Berlin" in layer_result["features"][0]["properties"]["name"])
def assertRoundTrip(self, input_geometry, expected_geometry, name=None, properties=None, id=None, expected_len=1, expected_properties=None): if input_geometry is None: input_geometry = self.feature_geometry if name is None: name = self.layer_name if properties is None: properties = self.feature_properties if expected_properties is None: expected_properties = properties source = [{ "name": name, "features": [{ "geometry": input_geometry, "properties": properties }] }] if id: source[0]['features'][0]['id'] = id encoded = encode(source) decoded = decode(encoded) self.assertIn(name, decoded) layer = decoded[name] features = layer['features'] self.assertEqual(expected_len, len(features)) self.assertEqual(features[0]['properties'], expected_properties) self.assertEqual(features[0]['geometry'], expected_geometry) if id: self.assertEqual(features[0]['id'], id)
def test_make_valid_can_return_multipolygon(self): from mapbox_vector_tile import encode from mapbox_vector_tile.encoder import on_invalid_geometry_make_valid import shapely.wkt import os.path test_dir = os.path.dirname(os.path.realpath(__file__)) file_name = 'error_nested_multipolygon.wkt' with open(os.path.join(test_dir, file_name)) as fh: shape = wkt.loads(fh.read()) features = [dict(geometry=shape, properties={})] pbf = encode({'name': 'foo', 'features': features}, quantize_bounds=(-10018754.1713946, 11271098.44281893, -8766409.899970269, 12523442.714243261), on_invalid_geometry=on_invalid_geometry_make_valid) result = decode(pbf) features = result['foo']['features'] self.assertEqual(1, len(features)) geom = features[0]['geometry'] self.assertEquals(geom['type'], 'MultiPolygon') multipolygon = shapely.geometry.shape(geom) self.assertTrue(multipolygon.is_valid) area = 0 for p in multipolygon.geoms: self.assertTrue(p.is_valid) area += p.area self.assertEquals(4339852.5, area)
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': { 'type': 'Polygon', 'coordinates': [ [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]] ] }, 'properties': { 'foo': 'bar', 'baz': 'foo', 'uid': 123 }, 'id': 1, 'type': 3 }], }, })
def test_bowtie_self_crossing(self): from mapbox_vector_tile import encode from mapbox_vector_tile.encoder import on_invalid_geometry_make_valid import shapely.geometry import shapely.wkt bowtie = ('POLYGON ((0 0, 2 2, 2 0, 0 2, 0 0))') shape = shapely.wkt.loads(bowtie) self.assertFalse(shape.is_valid) feature = dict(geometry=shape, properties={}) source = dict(name='layername', features=[feature]) pbf = encode(source, on_invalid_geometry=on_invalid_geometry_make_valid) result = decode(pbf) self.assertEqual(1, len(result['layername']['features'])) valid_geometries = result['layername']['features'][0]['geometry'] multipolygon = shapely.geometry.shape(valid_geometries) self.assertEqual(multipolygon.geom_type, 'MultiPolygon') self.assertTrue(multipolygon.is_valid) total_area = 0 for p in multipolygon.geoms: self.assertEqual(p.geom_type, 'Polygon') self.assertTrue(p.is_valid) self.assertGreater(p.area, 0) total_area += p.area self.assertEquals(2, total_area)
def test_make_valid_self_crossing(self): from mapbox_vector_tile import encode from mapbox_vector_tile.encoder import on_invalid_geometry_make_valid import shapely.geometry import shapely.wkt geometry = 'POLYGON ((10 10, 20 10, 20 20, 15 15, 15 5, 10 10))' shape = shapely.wkt.loads(geometry) self.assertFalse(shape.is_valid) feature = dict(geometry=shape, properties={}) source = dict(name='layername', features=[feature]) pbf = encode(source, on_invalid_geometry=on_invalid_geometry_make_valid) result = decode(pbf) self.assertEqual(1, len(result['layername']['features'])) valid_geometries = result['layername']['features'][0]['geometry'] geom_type = result['layername']['features'][0]['type'] self.assertEqual(3, geom_type) # 3 means POLYGON self.assertEqual(valid_geometries['type'], 'MultiPolygon') multipolygon = shapely.geometry.shape(valid_geometries) self.assertTrue(multipolygon.is_valid) total_area = 0 for p in multipolygon.geoms: self.assertTrue(p.is_valid) self.assertGreater(p.area, 0) total_area += p.area self.assertEquals(50, total_area) self.assertEquals(50, multipolygon.area)
def copy_dir(root_src_dir, root_dst_dir): """ Copy vector tiles from source to destination directory with overlap handling""" for src_dir, dirs, files in os.walk(root_src_dir): dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1) if not os.path.exists(dst_dir): os.makedirs(dst_dir) for file_ in files: src_file = os.path.join(src_dir, file_) dst_file = os.path.join(dst_dir, file_) if os.path.exists(dst_file): # If we have .pbf's that clash, we want to merge them # together and write them to the new directory as dst_dir if file_ == "metadata.json": os.remove(dst_file) else: print "\n Merging tiles to... " + dst_file with open(src_file, 'rb') as f: data = f.read() decoded_data1 = mapbox_vector_tile.decode(data) with open(dst_file, 'rb') as f: data = f.read() decoded_data2 = mapbox_vector_tile.decode(data) for k, v in decoded_data2.items(): if k in decoded_data1: decoded_data1[k]["features"] += decoded_data2[k]["features"] else: decoded_data1[k] = decoded_data2[k] listofdict = [] for k, v in decoded_data1.items(): dic = { 'name': k, 'features': decoded_data1[k]["features"] } listofdict.append(dic) encoded_data = mapbox_vector_tile.encode(listofdict) with open(dst_file, 'w') as f: f.write(encoded_data) else: shutil.copy(src_file, dst_dir)
def parse_data(self, x, y, zoom, data): self.x = x self.y = y self.zoom = zoom tile = mapbox_vector_tile.decode(data) self.extent = tile["osm"]["extent"] for feature in tile["osm"]["features"]: self._handle_feature(feature)
def test_retrieve_valid_tiles(self): point = sample_point() url = mvt_url(0, 0, 0) res = self.client.get(url) decoded_data = mapbox_vector_tile.decode(res.content) self.assertEqual(res.status_code, status.HTTP_200_OK) self.assertIsNotNone(decoded_data)
def _decode_binary_tile_data(self, data): try: # The offset of 32 signals to the zlib header that the gzip header is expected but skipped. file_content = zlib.decompress(data, 32 + zlib.MAX_WBITS) decoded_data = mapbox_vector_tile.decode(file_content) except: print "decoding data with mapbox_vector_tile failed", sys.exc_info( ) return return decoded_data
def getTile(self, zoom_level, tile_col, tile_row): import mapbox_vector_tile con = sqlite3.connect(self.source.resolve()) tile_data = con.execute(''' SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?''', (zoom_level, tile_col, tile_row)).fetchone()[0] return mapbox_vector_tile.decode(gzip.decompress(tile_data))
def test_encode_multipolygon(self): geometry = 'MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))' expected_result = '\x1a9\n\x05water\x12\x0e\x18\x03"\n\tP\xb0?\x12\'\t2\x1e\x0f\x12\x1b\x18\x03"\x17\t(\xba?"\x13\n\x00((\n\x1e\x1d\x0f\t\x1d\x00\x12\x13\n\x00\x13\x0f(\x80 x\x02' result = mapbox_vector_tile.encode([ dict(name='water', features=[dict(geometry=geometry, properties={})])]) self.assertEqual(expected_result, result) decoded = mapbox_vector_tile.decode(result) features = decoded['water'] self.assertEqual(2, len(features))
def test_merge_tile_directories_with_overlap(self): config_path_1 = "/tiler-data/test-data/configs/mergeOverlap1.tiler.json" self.assertTrue(os.path.isfile(config_path_1)) tiles_from_config(config_path_1) output_path_1 = "/tiler-data/tiles/test_merge_overlap_1" self.assertTrue(os.path.isdir(output_path_1)) config_path_2 = "/tiler-data/test-data/configs/mergeOverlap2.tiler.json" self.assertTrue(os.path.isfile(config_path_2)) tiles_from_config(config_path_2) output_path_2 = "/tiler-data/tiles/test_merge_overlap_2" self.assertTrue(os.path.isdir(output_path_2)) out_dir = "/tiler-data/tiles/merge-test-overlap/" merge_tile_directories(output_path_1, output_path_2, out_dir) with open("/tiler-data/tiles/merge-test-overlap/2/0/1.pbf", 'rb') as f: data = f.read() overlap_merge = mapbox_vector_tile.decode(data) with open("/tiler-data/tiles/test_merge_overlap_1/2/0/1.pbf", 'rb') as f: data = f.read() overlap_1 = mapbox_vector_tile.decode(data) with open("/tiler-data/tiles/test_merge_overlap_2/2/0/1.pbf", 'rb') as f: data = f.read() overlap_2 = mapbox_vector_tile.decode(data) self.assertTrue(os.path.isdir(out_dir)) self.assertTrue(get_num_pbfs(out_dir) > 0) self.assertTrue( str(overlap_merge).find("Pizazz") and str(overlap_1).find("Pizazz")) self.assertEqual(str(overlap_2).find("Pizazz"), -1) self.assertTrue( str(overlap_merge).find("Hungry") and str(overlap_2).find("Hungry")) self.assertEqual(str(overlap_1).find("Hungry"), -1)
def test_encode_feature_with_id(self): geometry = 'POINT(1 1)' expected_result = '\x1a\x18\n\x05water\x12\n\x08*\x18\x01"\x04\t\x02\xfe?(\x80 x\x02' result = mapbox_vector_tile.encode([ dict(name='water', features=[dict(geometry=geometry, properties={}, id=42)])]) self.assertEqual(expected_result, result) decoded = mapbox_vector_tile.decode(result) features = decoded['water'] self.assertEqual(1, len(features)) feature = features[0] self.assertEqual(42, feature['id'])
def test_too_small_linestring(self): from mapbox_vector_tile import encode from mapbox_vector_tile.encoder import on_invalid_geometry_make_valid import shapely.wkt shape = shapely.wkt.loads( 'LINESTRING(-71.160281 42.258729,-71.160837 42.259113,-71.161144 42.25932)') # noqa features = [dict(geometry=shape, properties={})] pbf = encode({'name': 'foo', 'features': features}, on_invalid_geometry=on_invalid_geometry_make_valid) result = decode(pbf) features = result['foo']['features'] self.assertEqual(0, len(features))
def test_invalid_geometry_ignore(self): from mapbox_vector_tile import encode from mapbox_vector_tile.encoder import on_invalid_geometry_ignore import shapely.wkt geometry = 'POLYGON ((10 10, 20 10, 20 20, 15 15, 15 5, 10 10))' shape = shapely.wkt.loads(geometry) self.assertFalse(shape.is_valid) feature = dict(geometry=shape, properties={}) source = dict(name='layername', features=[feature]) pbf = encode(source, on_invalid_geometry=on_invalid_geometry_ignore) result = decode(pbf) self.assertEqual(0, len(result['layername']['features']))
def test_too_small_geometry(self): from mapbox_vector_tile import encode from mapbox_vector_tile.encoder import on_invalid_geometry_make_valid import shapely.wkt shape = shapely.wkt.loads( 'LINESTRING (3065.656210384849 3629.831662879646, 3066.458953567231 3629.725941289478)') # noqa features = [dict(geometry=shape, properties={})] pbf = encode({'name': 'foo', 'features': features}, on_invalid_geometry=on_invalid_geometry_make_valid) result = decode(pbf) features = result['foo']['features'] self.assertEqual(0, len(features))
def test_quantize_makes_mutlipolygon_invalid(self): from mapbox_vector_tile import encode from mapbox_vector_tile.encoder import on_invalid_geometry_make_valid import shapely.wkt shape = shapely.wkt.loads('MULTIPOLYGON (((656510.8206577231 5674684.979891453, 656511.16 5674685.9, 656514.1758819892 5674684.979891453, 656510.8206577231 5674684.979891453)), ((657115.9120547654 5674684.979891453, 657118.85 5674690, 657118.0689111941 5674684.979891453, 657115.9120547654 5674684.979891453)))') # noqa quantize_bounds = (645740.0149532147, 5674684.979891453, 665307.8941942193, 5694252.8591324575) # noqa features = [dict(geometry=shape, properties={})] pbf = encode({'name': 'foo', 'features': features}, quantize_bounds=quantize_bounds, on_invalid_geometry=on_invalid_geometry_make_valid) result = decode(pbf) features = result['foo']['features'] self.assertEqual(1, len(features))
def test_quantize_and_y_coord_down(self): from mapbox_vector_tile import decode from mapbox_vector_tile import encode props = dict(foo='bar') shape = 'POINT(30 30)' feature = dict(geometry=shape, properties=props) features = [feature] source = dict(name='layername', features=features) bounds = 0.0, 0.0, 50.0, 50.0 pbf = encode(source, quantize_bounds=bounds, y_coord_down=True) result_decode_no_flip = decode(pbf, y_coord_down=True) act_feature = result_decode_no_flip['layername']['features'][0] act_geom = act_feature['geometry'] exp_geom = [[2458, 2458]] self.assertEqual(exp_geom, act_geom) result_decode_flip = decode(pbf) act_feature = result_decode_flip['layername']['features'][0] act_geom = act_feature['geometry'] exp_geom = [[2458, 1638]] self.assertEqual(exp_geom, act_geom)
def mapper(self, x, y, zoom, data): if data is None: return 0 tile = mapbox_vector_tile.decode(data) count = 0 if tile.get('buildings'): count += len(tile['buildings']) if tile.get('roads'): count += len(tile['roads']) return count
def test_y_coord_down(self): from mapbox_vector_tile import decode from mapbox_vector_tile import encode props = dict(foo='bar') shape = 'POINT(10 10)' feature = dict(geometry=shape, properties=props) features = [feature] source = dict(name='layername', features=features) pbf = encode(source, y_coord_down=True) result = decode(pbf, y_coord_down=True) act_feature = result['layername']['features'][0] act_geom = act_feature['geometry'] exp_geom = [[10, 10]] self.assertEqual(exp_geom, act_geom)
def test_decoder(self): 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' self.assertEqual(mapbox_vector_tile.decode(vector_tile), { 'water': [{ 'geometry': [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]], 'properties': { 'foo': 'bar', 'baz': 'foo', 'uid': 123 }, 'id': 1 }] } )
def test_quantize(self): from mapbox_vector_tile import decode from mapbox_vector_tile import encode props = dict(foo='bar') shape = 'POINT(15 15)' feature = dict(geometry=shape, properties=props) features = [feature] source = dict(name='layername', features=features) bounds = 10.0, 10.0, 20.0, 20.0 pbf = encode(source, quantize_bounds=bounds) result = decode(pbf) act_feature = result['layername']['features'][0] act_geom = act_feature['geometry'] exp_geom = {'type': 'Point', 'coordinates': [2048, 2048]} self.assertEqual(exp_geom, act_geom)
def test_custom_extent(self): from mapbox_vector_tile import decode from mapbox_vector_tile import encode props = dict(foo='bar') shape = 'POINT(10 10)' feature = dict(geometry=shape, properties=props) features = [feature] source = dict(name='layername', features=features) bounds = 0.0, 0.0, 10.0, 10.0 pbf = encode(source, quantize_bounds=bounds, extents=50) result = decode(pbf) act_feature = result['layername']['features'][0] act_geom = act_feature['geometry'] exp_geom = [[50, 50]] self.assertEqual(exp_geom, act_geom)
def test_MVT_MRF_generation(self): # Process config file test_artifact_path = os.path.join(self.main_artifact_path, 'mvt_mrf') config = self.parse_vector_config(self.mrf_test_config, test_artifact_path) # Run vectorgen os.chdir(test_artifact_path) if os.path.isfile("../../tilematrixsets.xml") == False: # Check for tilematrixsets.xml if os.path.isfile("/etc/onearth/config/conf/tilematrixsets.xml"): os.symlink("/etc/onearth/config/conf/tilematrixsets.xml", test_artifact_path+"/tilematrixsets.xml") else: self.fail("Cannot find tilematrixsets.xml. Aborting test.") else: os.symlink("../../tilematrixsets.xml", test_artifact_path+"/tilematrixsets.xml") cmd = 'oe_vectorgen -c ' + self.mrf_test_config run_command(cmd, ignore_warnings=True) # Get index of first, second-to-last, and last tile in MRF with open(os.path.join(config['output_dir'], config['prefix'] + '.idx'), 'rb') as idx: first_byte = idx.read(16) idx.seek(-32, 2) penultimate_byte = idx.read(16) last_byte = idx.read(16) top_tile_feature_count = 0 for byte in (first_byte, penultimate_byte, last_byte): tile_buffer = StringIO.StringIO() offset = struct.unpack('>q', byte[0:8])[0] size = struct.unpack('>q', byte[8:16])[0] with open(os.path.join(config['output_dir'], config['prefix'] + '.pvt'), 'rb') as pvt: pvt.seek(offset) tile_buffer.write(pvt.read(size)) tile_buffer.seek(0) # Check to see if extracted files are valid zip files and valid MVT tiles try: unzipped_tile = gzip.GzipFile(fileobj=tile_buffer) tile_data = unzipped_tile.read() except IOError: self.fail("Invalid tile found in MRF -- can't be unzipped.") try: tile = mapbox_vector_tile.decode(tile_data) except: self.fail("Can't decode MVT tile -- bad protobuffer or wrong MVT structure") # Check the top 2 tiles to see if they have any features (they should) if byte != first_byte: top_tile_feature_count += len(tile[tile.keys()[0]]['features']) self.assertTrue(top_tile_feature_count, "Top two files contain no features -- MRF likely was not created correctly.")
def test_custom_rounding_function(self): from mapbox_vector_tile import decode from mapbox_vector_tile import encode props = dict(foo='bar') shape = 'POINT(10 10)' feature = dict(geometry=shape, properties=props) features = [feature] source = dict(name='layername', features=features) bounds = 0.0, 0.0, 10.0, 10.0 # A really bad, custom "rounding" function pbf = encode(source, quantize_bounds=bounds, round_fn=lambda x: 5) result = decode(pbf) act_feature = result['layername']['features'][0] act_geom = act_feature['geometry'] exp_geom = [[5, 5]] self.assertEqual(exp_geom, act_geom)
def test_flipped_geometry_produces_multipolygon(self): from mapbox_vector_tile import encode from mapbox_vector_tile.encoder import on_invalid_geometry_make_valid import shapely.wkt shape = shapely.wkt.loads('POLYGON ((3449 1939, 3476 1967, 3473 1996, 3483 2027, 3542 2119, 3538 2160, 3563 2233, 3602 2255, 3639 2326, 3629 2388, 3573 2455, 3594 2493, 3558 2533, 3573 2549, 3518 2572, 3502 2592, 3505 2607, 3513 2614, 3535 2616, 3537 2610, 3535 2602, 3537 2599, 3548 2607, 3551 2636, 3528 2634, 3537 2668, 3549 2670, 3528 2711, 3550 2667, 3532 2635, 3550 2641, 3553 2613, 3549 2602, 3540 2596, 3512 2610, 3506 2589, 3576 2552, 3576 2543, 3563 2535, 3596 2506, 3597 2494, 3587 2469, 3589 2451, 3636 2385, 3644 2326, 3605 2251, 3566 2230, 3547 2122, 3482 2014, 3479 1966, 3455 1944, 3458 1910, 3449 1902, 3449 1939))') # noqa features = [dict(geometry=shape, properties={})] pbf = encode({'name': 'foo', 'features': features}, on_invalid_geometry=on_invalid_geometry_make_valid) result = decode(pbf) features = result['foo']['features'] self.assertEqual(1, len(features)) geom = shapely.geometry.shape(features[0]['geometry']) self.assertEqual(features[0]['geometry']['type'], 'MultiPolygon') self.assertEqual(geom.geom_type, 'MultiPolygon') self.assertTrue(geom.is_valid) for poly in geom.geoms: self.assertTrue(poly.is_valid)
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 }], } })
def test_validate_generates_rounding_error(self): from mapbox_vector_tile import encode from mapbox_vector_tile.encoder import on_invalid_geometry_make_valid import shapely.geometry import shapely.wkt bowtie = ('POLYGON((0 0, 1 1, 0 1, 1 0, 0 0))') shape = shapely.wkt.loads(bowtie) self.assertFalse(shape.is_valid) feature = dict(geometry=shape, properties={}) source = dict(name='layername', features=[feature]) pbf = encode(source, on_invalid_geometry=on_invalid_geometry_make_valid) result = decode(pbf) features = result['layername']['features'] self.assertEqual(1, len(features)) shape = shapely.geometry.Polygon(features[0]['geometry'][0]) self.assertTrue(shape.is_valid) self.assertGreater(shape.area, 0)
def test_polygon_pbf(self): ''' Create a polygon to cover the world and make sure it is "similar" (clip on) (pbf) ''' self.defineGeometry('POLYGON') geom = Polygon([(-180, -85.05), (180, -85.05), (180, 85.05), (-180, 85.05), (-180, -85.05)]) self.insertTestRow(geom.wkt) tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_test", "pbf", 0, 0, 0) self.assertTrue(tile_mimetype.endswith('/x-protobuf')) pbf_result = mapbox_vector_tile.decode(tile_content) layer_result = pbf_result['vectile_test'] extent = tile_bounds_mercator(0, 0, 0) result_geom = decoded_pbf_asshape(layer_result['features'][0], extent) expected_geom = Polygon([(-180, -85.05), (180, -85.05), (180, 85.05), (-180, 85.05), (-180, -85.05)]) # What is going on here is a bit unorthodox, but let me explain. The clipping # code inside TileStache relies on GEOS Intersection alongside some TileStache code # that creates a clipping geometry based on the tile perimeter. The tile perimeter # is made out of 17 (x,y) coordinates and not a box. Hence, the GEOS::Intersection # os that perimeter with the geometry of the vector we get back from the data provider # can end with extra vertices. Although it is the right shape, we cannot do a straight # comparisson because the expected geometry and the returned geometry *may* have extra # vertices. Simplify() will not do much because the distance of the vertices can clearly # be bigger than the tolerance. # # To add to this, because of double precision, the vertices may not be exact. # An optional way to find out if two shapes are close enough, is to buffer the two features # by just a little bit and then subtract each other like so: # # geometry1.difference(geometry2) == empty set? # geometry2.difference(geometry1) == empty set? # # If both geometries are empty, then they are similar. Hence what you see below self.assertTrue(result_geom.difference(expected_geom.buffer(0.01)).is_empty) self.assertTrue(expected_geom.difference(result_geom.buffer(0.01)).is_empty)
def test_vector_tile_endpoint_pbf(self): # Get url for a tile. self.url = reverse('vectortiles-list', kwargs={'aggregationlayer': self.agglayer.id, 'z': 11, 'x': 552, 'y': 859, 'frmt': 'pbf'}) # Setup request with fromula that will multiply the rasterlayer by itself response = self.client.get(self.url) self.assertEqual(response.status_code, 200) result = mapbox_vector_tile.decode(response.content) self.assertEqual( 'St Petersburg', result['My Aggregation Layer']['features'][0]['properties']['name'], ) self.assertEqual( 'Coverall', result['My Aggregation Layer']['features'][1]['properties']['name'], ) self.assertEqual( [3267, 3986], result['My Aggregation Layer']['features'][0]['geometry']['coordinates'][0][0][0], )
def test_encode_multiple_values_test(self): geometry = 'POINT(0 0)' properties1 = dict(foo='bar', baz='bar') properties2 = dict(quux='morx', baz='bar') name = 'foo' feature1 = dict(geometry=geometry, properties=properties1) feature2 = dict(geometry=geometry, properties=properties2) source = [{ "name": name, "features": [feature1, feature2] }] encoded = encode(source) decoded = decode(encoded) self.assertIn(name, decoded) layer = decoded[name] features = layer['features'] self.assertEqual(2, len(features)) self.assertEqual(features[0]['properties'], properties1) self.assertEqual(features[1]['properties'], properties2)
def check_valid_mvt(file, warn_if_empty=False): tile_buffer = StringIO.StringIO() tile_buffer.write(file.read()) tile_buffer.seek(0) try: unzipped_tile = gzip.GzipFile(fileobj=tile_buffer) tile_data = unzipped_tile.read() except IOError: return False try: tile = mapbox_vector_tile.decode(tile_data) except: return False if warn_if_empty: try: num_features = len(tile[tile.keys()[0]]['features']) except IndexError: return False return True
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': [{ '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 }] })