From 70b4b62fecc0caa3163e2945038ffd9d40b2c08e Mon Sep 17 00:00:00 2001 From: thuydotm Date: Wed, 10 Aug 2022 17:29:43 +0700 Subject: [PATCH 1/5] test list tiles on vector source --- mapshader/tests/test_tile_utils.py | 66 +++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/mapshader/tests/test_tile_utils.py b/mapshader/tests/test_tile_utils.py index b0d3fb3..7f565d3 100644 --- a/mapshader/tests/test_tile_utils.py +++ b/mapshader/tests/test_tile_utils.py @@ -1,13 +1,65 @@ +import pytest + +import geopandas as gpd +import spatialpandas +from shapely.geometry import Polygon + +from mapshader.sources import MapSource +from mapshader.sources import VectorSource from mapshader.tile_utils import ( get_tile, render_tiles_by_extent, - get_tiles_by_extent + get_tiles_by_extent, + list_tiles ) +MIN_ZOOM = 0 +MAX_ZOOM = 5 +ALL_ZOOM_LEVELS = range(MIN_ZOOM, MAX_ZOOM) TEMPLATE = ('https://c.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png') +@pytest.fixture +def full_map_polygon_vector_source(min_zoom, max_zoom): + + if min_zoom > max_zoom: + polygon_source = None + else: + # create a polygon that fully covers the whole map + lat_point_list = [85.05112878, -85.05112878, -85.05112878, 85.05112878, 85.05112878] + lon_point_list = [-180, -180, 180, 180, -180] + polygon_geom = Polygon(zip(lon_point_list, lat_point_list)) + crs = {'init': 'epsg:4326'} + polygon_gdf = gpd.GeoDataFrame(index=[0], crs=crs, geometry=[polygon_geom]) + + # construct transforms + buffered_extent_transform = dict(name='add_projected_buffered_extent', + args=dict(crs='4326', + buffer_distance=.01, + geometry_field='geometry')) + transforms = [buffered_extent_transform] + # construct value obj + source_obj = dict() + source_obj['geometry_type'] = 'polygon' + source_obj['data'] = polygon_gdf + source_obj['transforms'] = transforms + source_obj['tiling'] = dict( + min_zoom=min_zoom, + max_zoom=max_zoom, + xmin_field='buffer_0_4326_xmin', + xmax_field='buffer_0_4326_xmax', + ymin_field='buffer_0_4326_ymin', + ymax_field='buffer_0_4326_ymax', + ) + + # VectorSource from source object we created above + polygon_source = VectorSource.from_obj(source_obj) + polygon_source.load() + + return polygon_source, min_zoom, max_zoom + + def test_get_tile(): lng = -90.283741 lat = 29.890626 @@ -37,3 +89,15 @@ def test_get_tiles_by_extent(): tiles = get_tiles_by_extent(xmin, ymin, xmax, ymax, level) tile_list = list(tiles) assert len(tile_list) == 6 + + +@pytest.mark.parametrize("min_zoom", ALL_ZOOM_LEVELS) +@pytest.mark.parametrize("max_zoom", ALL_ZOOM_LEVELS) +def test_list_tiles(full_map_polygon_vector_source): + polygon_source, minz, maxz = full_map_polygon_vector_source + if polygon_source is not None: + tiles_ddf = list_tiles(polygon_source) + # the polygon fully covers the whole map, + # thus at each zoom level, all possible tiles will be generated + num_all_possible_tiles = sum([2**(2*i) for i in range(minz, maxz + 1)]) + assert len(tiles_ddf) == num_all_possible_tiles From ca4994c58dd2c16b76282639d25303b673f7a199 Mon Sep 17 00:00:00 2001 From: thuydotm Date: Wed, 10 Aug 2022 19:31:50 +0700 Subject: [PATCH 2/5] test point vector source --- mapshader/tests/test_tile_utils.py | 65 ++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/mapshader/tests/test_tile_utils.py b/mapshader/tests/test_tile_utils.py index 7f565d3..d2c71a1 100644 --- a/mapshader/tests/test_tile_utils.py +++ b/mapshader/tests/test_tile_utils.py @@ -2,9 +2,8 @@ import geopandas as gpd import spatialpandas -from shapely.geometry import Polygon +from shapely.geometry import Polygon, Point -from mapshader.sources import MapSource from mapshader.sources import VectorSource from mapshader.tile_utils import ( get_tile, @@ -13,9 +12,8 @@ list_tiles ) -MIN_ZOOM = 0 -MAX_ZOOM = 5 -ALL_ZOOM_LEVELS = range(MIN_ZOOM, MAX_ZOOM) +ZOOM_LEVELS_1_5 = range(1, 5) +ZOOM_LEVELS_1_24 = range(1, 24) TEMPLATE = ('https://c.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png') @@ -60,6 +58,37 @@ def full_map_polygon_vector_source(min_zoom, max_zoom): return polygon_source, min_zoom, max_zoom +@pytest.fixture +def point_vector_source(min_zoom, max_zoom): + if min_zoom > max_zoom: + point_source = None + else: + # create a point at (0, 0) + point = Point(0, 0) + crs = {'init': 'epsg:4326'} + point_gdf = gpd.GeoDataFrame(index=[0], crs=crs, geometry=[point]) + point_gdf['x'] = [point.x] + point_gdf['y'] = [point.y] + + # construct value obj + source_obj = dict() + source_obj['geometry_type'] = 'point' + source_obj['data'] = point_gdf + source_obj['tiling'] = dict( + min_zoom=min_zoom, + max_zoom=max_zoom, + xmin_field='x', + xmax_field='x', + ymin_field='y', + ymax_field='y', + ) + + # VectorSource from source object we created above + point_source = VectorSource.from_obj(source_obj) + + return point_source, min_zoom, max_zoom + + def test_get_tile(): lng = -90.283741 lat = 29.890626 @@ -91,9 +120,9 @@ def test_get_tiles_by_extent(): assert len(tile_list) == 6 -@pytest.mark.parametrize("min_zoom", ALL_ZOOM_LEVELS) -@pytest.mark.parametrize("max_zoom", ALL_ZOOM_LEVELS) -def test_list_tiles(full_map_polygon_vector_source): +@pytest.mark.parametrize("min_zoom", ZOOM_LEVELS_1_5) +@pytest.mark.parametrize("max_zoom", [5]) +def test_list_tiles_polygon(full_map_polygon_vector_source): polygon_source, minz, maxz = full_map_polygon_vector_source if polygon_source is not None: tiles_ddf = list_tiles(polygon_source) @@ -101,3 +130,23 @@ def test_list_tiles(full_map_polygon_vector_source): # thus at each zoom level, all possible tiles will be generated num_all_possible_tiles = sum([2**(2*i) for i in range(minz, maxz + 1)]) assert len(tiles_ddf) == num_all_possible_tiles + + +@pytest.mark.parametrize("min_zoom", ZOOM_LEVELS_1_24) +@pytest.mark.parametrize("max_zoom", [24]) +def test_list_tiles_point(point_vector_source): + + point_source, minz, maxz = point_vector_source + + if point_source is not None: + tiles_ddf = list_tiles(point_source).compute() + # there is only a single point at (0, 0) in the vector point source, + # thus at each zoom level, only one tile will be generated + assert len(tiles_ddf) == maxz - minz + 1 + + # at each zoom level, the generated tile is at the center of the map + for i, row in tiles_ddf.iterrows(): + x = row['x'] + y = row['y'] + z = row['z'] + assert x == y == 2**(z-1) From 3735b576b26125aba2b067e706940ec1d9859d70 Mon Sep 17 00:00:00 2001 From: thuydotm Date: Thu, 11 Aug 2022 10:35:19 +0700 Subject: [PATCH 3/5] line --- mapshader/tests/test_tile_utils.py | 63 +++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/mapshader/tests/test_tile_utils.py b/mapshader/tests/test_tile_utils.py index d2c71a1..78da29d 100644 --- a/mapshader/tests/test_tile_utils.py +++ b/mapshader/tests/test_tile_utils.py @@ -1,8 +1,7 @@ import pytest import geopandas as gpd -import spatialpandas -from shapely.geometry import Polygon, Point +from shapely.geometry import Polygon, Point, LineString from mapshader.sources import VectorSource from mapshader.tile_utils import ( @@ -12,7 +11,7 @@ list_tiles ) -ZOOM_LEVELS_1_5 = range(1, 5) +ZOOM_LEVELS_1_8 = range(1, 8) ZOOM_LEVELS_1_24 = range(1, 24) TEMPLATE = ('https://c.tile.openstreetmap.org/{{z}}/{{x}}/{{y}}.png') @@ -89,6 +88,40 @@ def point_vector_source(min_zoom, max_zoom): return point_source, min_zoom, max_zoom +@pytest.fixture +def line_vector_source(min_zoom, max_zoom): + if min_zoom > max_zoom: + line_source = None + else: + # create a horizontal line y=0 crossing 2 points (-180, 0), and (180, 0) + p1 = Point(-180, 0) + p2 = Point(180, 0) + line = LineString([p1, p2]) + line_gdf = gpd.GeoDataFrame(index=[0], crs={'init': 'epsg:4326'}, geometry=[line]) + line_gdf['xmin'] = [p1.x] + line_gdf['ymin'] = [p1.y] + line_gdf['xmax'] = [p2.x] + line_gdf['ymax'] = [p2.y] + + # construct value obj + source_obj = dict() + source_obj['geometry_type'] = 'line' + source_obj['data'] = line_gdf + source_obj['tiling'] = dict( + min_zoom=min_zoom, + max_zoom=max_zoom, + xmin_field='xmin', + xmax_field='xmax', + ymin_field='ymin', + ymax_field='ymax', + ) + + # VectorSource from source object we created above + line_source = VectorSource.from_obj(source_obj) + + return line_source, min_zoom, max_zoom + + def test_get_tile(): lng = -90.283741 lat = 29.890626 @@ -120,10 +153,12 @@ def test_get_tiles_by_extent(): assert len(tile_list) == 6 -@pytest.mark.parametrize("min_zoom", ZOOM_LEVELS_1_5) -@pytest.mark.parametrize("max_zoom", [5]) +@pytest.mark.parametrize("min_zoom", ZOOM_LEVELS_1_8) +@pytest.mark.parametrize("max_zoom", [8]) def test_list_tiles_polygon(full_map_polygon_vector_source): + polygon_source, minz, maxz = full_map_polygon_vector_source + if polygon_source is not None: tiles_ddf = list_tiles(polygon_source) # the polygon fully covers the whole map, @@ -150,3 +185,21 @@ def test_list_tiles_point(point_vector_source): y = row['y'] z = row['z'] assert x == y == 2**(z-1) + + +@pytest.mark.parametrize("min_zoom", ZOOM_LEVELS_1_8) +@pytest.mark.parametrize("max_zoom", [8]) +def test_list_tiles_line(line_vector_source): + + line_source, minz, maxz = line_vector_source + + if line_source is not None: + tiles_ddf = list_tiles(line_source) + assert len(tiles_ddf) == sum([2**z for z in range(minz, maxz+1)]) + + # # at each zoom level, the generated tile is at the center of the map + # for i, row in tiles_ddf.iterrows(): + # x = row['x'] + # y = row['y'] + # z = row['z'] + # assert x == y == 2**(z-1) From 09f50535b54461789ebc3a50c057a8d6d60aade2 Mon Sep 17 00:00:00 2001 From: thuydotm Date: Thu, 11 Aug 2022 10:48:48 +0700 Subject: [PATCH 4/5] more tests --- mapshader/tests/test_tile_utils.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mapshader/tests/test_tile_utils.py b/mapshader/tests/test_tile_utils.py index 78da29d..5771abf 100644 --- a/mapshader/tests/test_tile_utils.py +++ b/mapshader/tests/test_tile_utils.py @@ -196,10 +196,11 @@ def test_list_tiles_line(line_vector_source): if line_source is not None: tiles_ddf = list_tiles(line_source) assert len(tiles_ddf) == sum([2**z for z in range(minz, maxz+1)]) - - # # at each zoom level, the generated tile is at the center of the map - # for i, row in tiles_ddf.iterrows(): - # x = row['x'] - # y = row['y'] - # z = row['z'] - # assert x == y == 2**(z-1) + tiles_ddf = tiles_ddf.compute() + # at each zoom level, the generated tiles intersect with line y=0 + for i, row in tiles_ddf.iterrows(): + x = row['x'] + y = row['y'] + z = row['z'] + assert y == 2**(z-1) + assert x < 2**z From b115534341f278617ee1cb4ca5b626a898e0c559 Mon Sep 17 00:00:00 2001 From: thuydotm Date: Thu, 11 Aug 2022 11:39:39 +0700 Subject: [PATCH 5/5] more tests --- mapshader/tests/test_tile_utils.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/mapshader/tests/test_tile_utils.py b/mapshader/tests/test_tile_utils.py index 5771abf..432f467 100644 --- a/mapshader/tests/test_tile_utils.py +++ b/mapshader/tests/test_tile_utils.py @@ -1,5 +1,6 @@ import pytest +import numpy as np import geopandas as gpd from shapely.geometry import Polygon, Point, LineString @@ -194,13 +195,13 @@ def test_list_tiles_line(line_vector_source): line_source, minz, maxz = line_vector_source if line_source is not None: + tiles_ddf = list_tiles(line_source) - assert len(tiles_ddf) == sum([2**z for z in range(minz, maxz+1)]) + assert len(tiles_ddf) == sum([2 ** z for z in range(minz, maxz + 1)]) tiles_ddf = tiles_ddf.compute() - # at each zoom level, the generated tiles intersect with line y=0 - for i, row in tiles_ddf.iterrows(): - x = row['x'] - y = row['y'] - z = row['z'] - assert y == 2**(z-1) - assert x < 2**z + tiles_ddf_by_zoom = tiles_ddf.groupby('z') + for z, tiles_z in tiles_ddf_by_zoom: + # at each zoom level, the generated tiles intersect with line y=0 + assert len(tiles_z) == 2**z + assert np.all(np.unique(tiles_z['y']) == [2**(z - 1)]) + assert np.all(np.sort(np.unique(tiles_z['x'])) == range(2**z))