"""Tests for `basemaps` module."""
import os
import unittest
from unittest import mock
import folium
import requests
import xyzservices
from geemap.basemaps import (
custom_tiles,
get_qms,
get_xyz_dict,
search_qms,
xyz_to_folium,
xyz_to_leaflet,
xyz_to_plotly,
)
from geemap import common
class TestCustomTiles(unittest.TestCase):
def test_custom_tiles_types(self):
"""Tests that custom_tiles is a dict and contains expected keys."""
self.assertIsInstance(custom_tiles, dict)
expected_keys = ["xyz", "wms"]
for key in custom_tiles:
self.assertIn(key, expected_keys)
def test_custom_tiles_xyz(self):
"""Tests that custom_tiles["xyz"] is a dict w/ expected keys/values."""
tiles = custom_tiles["xyz"]
self.assertIsInstance(tiles, dict)
for _, value in tiles.items():
self.assertIn("url", value)
self.assertIn("attribution", value)
self.assertIn("name", value)
self.assertTrue(value["url"].startswith("http"))
def test_custom_tiles_wms(self):
"""Tests that custom_tiles["wms"] is a dict w/ expected keys/values."""
tiles = custom_tiles["wms"]
self.assertIsInstance(tiles, dict)
for _, value in tiles.items():
self.assertIsInstance(value["url"], str)
self.assertIsInstance(value["attribution"], str)
self.assertIsInstance(value["name"], str)
self.assertTrue(value["url"].startswith("http"))
class TestXyzToLeaflet(unittest.TestCase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.tiles = xyz_to_leaflet()
def test_xyz_to_leaflet_is_dictionary(self):
"""Tests that xyz_to_leaflet returns a dict."""
self.assertIsInstance(self.tiles, dict)
def test_xyz_to_leaflet_sources(self):
"""Tests that xyz_to_leaflet has custom xyz, wms, and xyzservices."""
expected_keys = {
"custom_xyz": "OpenStreetMap",
"custom_wms": "USGS NAIP Imagery",
"xyzservices_xyz": "Esri.WorldTopoMap",
}
for _, expected_name in expected_keys.items():
self.assertIn(expected_name, self.tiles)
class TestXyzToFolium(unittest.TestCase):
def test_xyz_to_folium_sources(self):
"""Tests that xyz_to_folium has custom xyz, wms, and xyzservices."""
tiles = xyz_to_folium()
self.assertIn("OpenStreetMap", tiles)
self.assertIn("FWS NWI Wetlands", tiles)
self.assertIn("CartoDB.Positron", tiles)
for name, tile_layer in tiles.items():
if name in custom_tiles["wms"]:
self.assertIsInstance(tile_layer, folium.WmsTileLayer)
else:
self.assertIsInstance(tile_layer, folium.TileLayer)
@mock.patch.dict(os.environ, {"PLANET_API_KEY": "fake_api_key"})
@mock.patch.object(common, "planet_tiles")
def test_xyz_to_folium_with_planet(self, mock_planet_tiles):
"""Tests that xyz_to_folium includes planet tiles if API key is set."""
mock_planet_tiles.return_value = {
"Planet Basemap": folium.TileLayer(
tiles="https://fake-planet.com/tiles/{z}/{x}/{y}",
attr="Planet",
name="Planet Basemap",
)
}
tiles = xyz_to_folium()
self.assertIn("Planet Basemap", tiles)
mock_planet_tiles.assert_called_once_with(tile_format="folium")
class FakeProvider(xyzservices.TileProvider):
def __init__(self, name: str, url: str):
super().__init__(
name=name,
url=url,
attribution="© Fake Provider",
accessToken="<insert your access token here>",
)
class FakeXyz(xyzservices.Bunch):
def __init__(self):
token = "https://fake-server.com/tiles/{z}/{x}/{y}?apikey={accessToken}"
no_token = "https://fake-server.com/tiles/{z}/{x}/{y}"
self.fake_tile_a = FakeProvider("a", no_token)
self.fake_tile_b = FakeProvider("b", token)
self.fake_tile_c_1 = FakeProvider("c_1", no_token)
self.fake_tile_c_France = FakeProvider("c_France", no_token)
self.providers = xyzservices.Bunch(
a=self.fake_tile_a,
b=self.fake_tile_b,
c=xyzservices.Bunch(c_1=self.fake_tile_c_1, c_2=self.fake_tile_c_France),
)
@mock.patch.object(xyzservices, "providers", FakeXyz().providers)
class TestGetXyzDict(unittest.TestCase):
def test_get_xyz_dict_structure(self):
"""Tests that get_xyz_dict returns correct object structure."""
xyz_dict = get_xyz_dict()
tile_a = xyz_dict["a"]
tile_a_keys = tile_a.keys()
self.assertEqual("c_1", list(xyz_dict.keys())[1])
self.assertIsInstance(tile_a, xyzservices.lib.TileProvider)
self.assertIn("name", tile_a_keys)
self.assertIn("url", tile_a_keys)
self.assertIn("attribution", tile_a_keys)
self.assertIn("accessToken", tile_a_keys)
self.assertEqual("xyz", tile_a["type"])
self.assertEqual("a", tile_a.name)
self.assertEqual(
"https://fake-server.com/tiles/{z}/{x}/{y}", tile_a.build_url()
)
def test_get_xyz_dict_free_tiles(self):
"""Tests that get_xyz_dict correctly filters for free tile layers."""
xyz_dict_free = get_xyz_dict(free_only=True)
xyz_dict_all = get_xyz_dict(free_only=False)
self.assertNotIn("b", xyz_dict_free.keys())
self.assertIn("b", xyz_dict_all.keys())
def test_get_xyz_dict_france_tiles(self):
"""Tests that get_xyz_dict correctly filters for France tile layers."""
xyz_dict_all = get_xyz_dict(france=True)
xyz_dict_no_france = get_xyz_dict(france=False)
self.assertNotIn("c_France", xyz_dict_no_france.keys())
self.assertIn("c_France", xyz_dict_all.keys())
class TestXyzToPlotly(unittest.TestCase):
def test_xyz_to_plotly_sources(self):
"""Tests that xyz_to_plotly has custom xyz and xyzservices."""
tiles = xyz_to_plotly()
self.assertIn("OpenStreetMap", tiles)
self.assertIn("CartoDB.Positron", tiles)
for _, tile_layer in tiles.items():
self.assertIn("below", tile_layer)
self.assertIn("sourcetype", tile_layer)
self.assertEqual("raster", tile_layer["sourcetype"])
self.assertIn("source", tile_layer)
self.assertIsInstance(tile_layer["source"], list)
class TestQms(unittest.TestCase):
@mock.patch.object(requests, "get")
def test_search_qms_no_results(self, mock_get):
"""Tests search_qms with no results."""
mock_response = mock_get.return_value
mock_response.json.return_value = {"count": 0, "results": []}
result = search_qms("no results")
self.assertIsNone(result)
mock_get.assert_called_once_with(
"https://qms.nextgis.com/api/v1/geoservices/?search=no results&type=tms&epsg=3857&limit=10"
)
@mock.patch.object(requests, "get")
def test_search_qms_with_results(self, mock_get):
"""Tests search_qms with results."""
mock_response = mock_get.return_value
mock_response.json.return_value = {
"count": 1,
"results": [{"id": 1, "name": "result1"}],
}
result = search_qms("results")
self.assertEqual(result, [{"id": 1, "name": "result1"}])
mock_get.assert_called_once_with(
"https://qms.nextgis.com/api/v1/geoservices/?search=results&type=tms&epsg=3857&limit=10"
)
@mock.patch.object(requests, "get")
def test_search_qms_with_limit(self, mock_get):
"""Tests search_qms with limit."""
mock_response = mock_get.return_value
mock_response.json.return_value = {
"count": 1,
"results": [{"id": 1, "name": "result1"}],
}
result = search_qms("results", limit=5)
self.assertEqual(result, [{"id": 1, "name": "result1"}])
mock_get.assert_called_once_with(
"https://qms.nextgis.com/api/v1/geoservices/?search=results&type=tms&epsg=3857&limit=5"
)
@mock.patch.object(requests, "get")
def test_search_qms_count_greater_than_limit(self, mock_get):
"""Tests search_qms with count > limit."""
mock_response = mock_get.return_value
mock_response.json.return_value = {
"count": 2,
"results": [{"id": 1, "name": "result1"}, {"id": 2, "name": "result2"}],
}
result = search_qms("results", limit=1)
self.assertEqual(result, [{"id": 1, "name": "result1"}])
mock_get.assert_called_once_with(
"https://qms.nextgis.com/api/v1/geoservices/?search=results&type=tms&epsg=3857&limit=1"
)
@mock.patch.object(requests, "get")
def test_get_qms(self, mock_get):
"""Tests get_qms."""
mock_response = mock_get.return_value
mock_response.json.return_value = {"id": 123, "name": "service1"}
result = get_qms("123")
self.assertEqual(result, {"id": 123, "name": "service1"})
mock_get.assert_called_once_with(
"https://qms.nextgis.com/api/v1/geoservices/123"
)
if __name__ == "__main__":
unittest.main()