from urllib.error import URLError import pytest import xyzservices.providers as xyz from xyzservices import Bunch, TileProvider @pytest.fixture def basic_provider(): return TileProvider( url="https://myserver.com/tiles/{z}/{x}/{y}.png", attribution="(C) xyzservices", name="my_public_provider", ) @pytest.fixture def retina_provider(): return TileProvider( url="https://myserver.com/tiles/{z}/{x}/{y}{r}.png", attribution="(C) xyzservices", name="my_public_provider2", r="@2x", ) @pytest.fixture def silent_retina_provider(): return TileProvider( url="https://myserver.com/tiles/{z}/{x}/{y}{r}.png", attribution="(C) xyzservices", name="my_public_retina_provider3", ) @pytest.fixture def private_provider(): return TileProvider( url="https://myserver.com/tiles/{z}/{x}/{y}?access_token={accessToken}", attribution="(C) xyzservices", accessToken="", name="my_private_provider", ) @pytest.fixture def html_attr_provider(): return TileProvider( url="https://myserver.com/tiles/{z}/{x}/{y}.png", attribution="(C) xyzservices", html_attribution='© xyzservices', # noqa name="my_public_provider_html", ) @pytest.fixture def subdomain_provider(): return TileProvider( url="https://{s}.myserver.com/tiles/{z}/{x}/{y}.png", attribution="(C) xyzservices", subdomains="abcd", name="my_subdomain_provider", ) @pytest.fixture def test_bunch( basic_provider, retina_provider, silent_retina_provider, private_provider, html_attr_provider, subdomain_provider, ): return Bunch( basic_provider=basic_provider, retina_provider=retina_provider, silent_retina_provider=silent_retina_provider, private_provider=private_provider, bunched=Bunch( html_attr_provider=html_attr_provider, subdomain_provider=subdomain_provider ), ) def test_expect_name_url_attribution(): msg = ( "The attributes `name`, `url`, and `attribution` are " "required to initialise a `TileProvider`. Please provide " "values for: " ) with pytest.raises(AttributeError, match=msg + "`name`, `url`, `attribution`"): TileProvider({}) with pytest.raises(AttributeError, match=msg + "`url`, `attribution`"): TileProvider({"name": "myname"}) with pytest.raises(AttributeError, match=msg + "`attribution`"): TileProvider({"url": "my_url", "name": "my_name"}) with pytest.raises(AttributeError, match=msg + "`attribution`"): TileProvider(url="my_url", name="my_name") def test_build_url( basic_provider, retina_provider, silent_retina_provider, private_provider, subdomain_provider, ): expected = "https://myserver.com/tiles/{z}/{x}/{y}.png" assert basic_provider.build_url() == expected expected = "https://myserver.com/tiles/3/1/2.png" assert basic_provider.build_url(1, 2, 3) == expected assert basic_provider.build_url(1, 2, 3, scale_factor="@2x") == expected assert silent_retina_provider.build_url(1, 2, 3) == expected expected = "https://myserver.com/tiles/3/1/2@2x.png" assert retina_provider.build_url(1, 2, 3) == expected assert silent_retina_provider.build_url(1, 2, 3, scale_factor="@2x") == expected expected = "https://myserver.com/tiles/3/1/2@5x.png" assert retina_provider.build_url(1, 2, 3, scale_factor="@5x") == expected expected = "https://myserver.com/tiles/{z}/{x}/{y}?access_token=my_token" assert private_provider.build_url(accessToken="my_token") == expected with pytest.raises(ValueError, match="Token is required for this provider"): private_provider.build_url() expected = "https://{s}.myserver.com/tiles/{z}/{x}/{y}.png" assert subdomain_provider.build_url(fill_subdomain=False) expected = "https://a.myserver.com/tiles/{z}/{x}/{y}.png" assert subdomain_provider.build_url() def test_requires_token(private_provider, basic_provider): assert private_provider.requires_token() is True assert basic_provider.requires_token() is False def test_html_repr(basic_provider, retina_provider): provider_strings = [ '
', '
', '
xyzservices.TileProvider
', '
my_public_provider
', '
', '
', "
url
https://myserver.com/tiles/{z}/{x}/{y}.png
", "
attribution
(C) xyzservices
", ] for html_string in provider_strings: assert html_string in basic_provider._repr_html_() bunch = Bunch({"first": basic_provider, "second": retina_provider}) bunch_strings = [ '
xyzservices.Bunch
', '
2 items
', '
    ', '
  • ', "xyzservices.TileProvider", '
    ', ] bunch_repr = bunch._repr_html_() for html_string in provider_strings + bunch_strings: assert html_string in bunch_repr assert bunch_repr.count('
  • ') == 2 assert bunch_repr.count('
    ') == 3 assert bunch_repr.count('
    ') == 3 def test_copy(basic_provider): basic2 = basic_provider.copy() assert isinstance(basic2, TileProvider) def test_callable(): # only testing the callable functionality to override a keyword, as we # cannot test the actual providers that need an API key updated_provider = xyz.GeoportailFrance.plan(apikey="mykey") assert isinstance(updated_provider, TileProvider) assert "url" in updated_provider assert updated_provider["apikey"] == "mykey" # check that original provider dict is not modified assert xyz.GeoportailFrance.plan["apikey"] == "essentiels" def test_html_attribution_fallback(basic_provider, html_attr_provider): # TileProvider.html_attribution falls back to .attribution if the former not present assert basic_provider.html_attribution == basic_provider.attribution assert ( html_attr_provider.html_attribution == '© xyzservices' ) @pytest.mark.xfail(reason="timeout error", raises=URLError) def test_from_qms(): provider = TileProvider.from_qms("OpenStreetMap Standard aka Mapnik") assert isinstance(provider, TileProvider) @pytest.mark.xfail(reason="timeout error", raises=URLError) def test_from_qms_not_found_error(): with pytest.raises(ValueError): TileProvider.from_qms("LolWut") def test_flatten( basic_provider, retina_provider, silent_retina_provider, private_provider ): nested_bunch = Bunch( first_bunch=Bunch(first=basic_provider, second=retina_provider), second_bunch=Bunch(first=silent_retina_provider, second=private_provider), ) assert len(nested_bunch) == 2 assert len(nested_bunch.flatten()) == 4 def test_filter(test_bunch): assert len(test_bunch.filter(keyword="private").flatten()) == 1 assert len(test_bunch.filter(keyword="public").flatten()) == 4 assert len(test_bunch.filter(keyword="{s}").flatten()) == 1 assert len(test_bunch.filter(name="retina").flatten()) == 1 assert len(test_bunch.filter(requires_token=True).flatten()) == 1 assert len(test_bunch.filter(requires_token=False).flatten()) == 5 assert len(test_bunch.filter(requires_token=False)) == 4 # check nested structure assert len(test_bunch.filter(keyword="{s}", requires_token=False).flatten()) == 1 assert len(test_bunch.filter(name="nonsense").flatten()) == 0 def custom(provider): if hasattr(provider, "subdomains") and provider.subdomains == "abcd": return True if hasattr(provider, "r"): return True return False assert len(test_bunch.filter(function=custom).flatten()) == 2 def test_query_name(): options = [ "CartoDB Positron", "cartodbpositron", "cartodb-positron", "carto db/positron", "CARTO_DB_POSITRON", "CartoDB.Positron", "Carto,db,positron", ] for option in options: queried = xyz.query_name(option) assert isinstance(queried, TileProvider) assert queried.name == "CartoDB.Positron" with pytest.raises(ValueError, match="No matching provider found"): xyz.query_name("i don't exist") # Name with underscore GH124 option_with_underscore = "NASAGIBS.ASTER_GDEM_Greyscale_Shaded_Relief" queried = xyz.query_name(option_with_underscore) assert isinstance(queried, TileProvider) assert queried.name == option_with_underscore