
    aclT                        d Z ddlmZ ddlZddlZddlZddlmZm	Z	m
Z
 ddlmZ e                    d dD                       Z G d d	e          Z G d
 de          Zd ZdZdS )z"
Utilities to support XYZservices
    )annotationsN)OptionalCallableUnion)quotec                    i | ]}|d S )  ).0xs     /lib/python3.11/site-packages/xyzservices/lib.py
<dictcomp>r      s    '@'@'@!2'@'@'@    z., -_/c                  H    e Zd ZdZd Zd ZddZddZ	 	 	 	 dddZddZ	d	S )Buncha  A dict with attribute-access

    :class:`Bunch` is used to store :class:`TileProvider` objects.

    Examples
    --------
    >>> black_and_white = TileProvider(
    ...     name="My black and white tiles",
    ...     url="https://myserver.com/bw/{z}/{x}/{y}",
    ...     attribution="(C) xyzservices",
    ... )
    >>> colorful = TileProvider(
    ...     name="My colorful tiles",
    ...     url="https://myserver.com/color/{z}/{x}/{y}",
    ...     attribution="(C) xyzservices",
    ... )
    >>> MyTiles = Bunch(BlackAndWhite=black_and_white, Colorful=colorful)
    >>> MyTiles
    {'BlackAndWhite': {'name': 'My black and white tiles', 'url': 'https://myserver.com/bw/{z}/{x}/{y}', 'attribution': '(C) xyzservices'}, 'Colorful': {'name': 'My colorful tiles', 'url': 'https://myserver.com/color/{z}/{x}/{y}', 'attribution': '(C) xyzservices'}}
    >>> MyTiles.BlackAndWhite.url
    'https://myserver.com/bw/{z}/{x}/{y}'
    c                h    	 |                      |          S # t          $ r t          |          w xY wN)__getitem__KeyErrorAttributeError)selfkeys     r   __getattr__zBunch.__getattr__*   sD    	&##C((( 	& 	& 	& %%%	&s    1c                *    |                                  S r   keysr   s    r   __dir__zBunch.__dir__0   s    yy{{r   Fc                f   d}|                                  D ]q}t          | |         t                    rd}nd}t          t	          j                              }|d| d| d| d| d| |                             d	
           dz  }r|rdn
dt           d}d| dt          |            d| d}|S )Nr	   zxyzservices.TileProviderzxyzservices.BunchzO
            <li class="xyz-child">
                <input type="checkbox" id="z5" class="xyz-checkbox"/>
                <label for="z">z <span>zM</span></label>
                <div class="xyz-inside">
                    T)insidez6
                </div>
            </li>
            <style></style>
        <div>
        z
            <div class="xyz-wrap">
                <div class="xyz-header">
                    <div class="xyz-obj">xyzservices.Bunch</div>
                    <div class="xyz-name">z items</div>
                </div>
                <div class="xyz-details">
                    <ul class="xyz-collapsible">
                        z\
                    </ul>
                </div>
            </div>
        </div>
        )	r   
isinstanceTileProviderstruuiduuid4_repr_html_	CSS_STYLElen)r   r    childrenr   objuidstylehtmls           r   r)   zBunch._repr_html_3   s'   99;; 	 	C$s)\22 *0)djll##C ,/  !  %(  14 
 #Y**$*77   HH ?"?I"?"?"?	  ,/t99  "  " r   returndictc                0    i fd |            S )a  Return the nested :class:`Bunch` collapsed into the one level dictionary.

        Dictionary keys are :class:`TileProvider` names (e.g. ``OpenStreetMap.Mapnik``)
        and its values are :class:`TileProvider` objects.

        Returns
        -------
        flattened : dict
            dictionary of :class:`TileProvider` objects

        Examples
        --------
        >>> import xyzservices.providers as xyz
        >>> len(xyz)
        36

        >>> flat = xyz.flatten()
        >>> len(xyz)
        207

        c                    t          | t                    r| | j        <   d S |                                 D ]} |           d S r   )r$   r%   namevalues)providerprov_get_providersflats     r   r9   z%Bunch.flatten.<locals>._get_providerss   s]    (L11 )&.X]###$OO-- ) )D"N4(((() )r   r
   )r   r9   r:   s    @@r   flattenzBunch.flattenZ   s@    . 	) 	) 	) 	) 	) 	) 	tr   NkeywordOptional[str]r5   requires_tokenOptional[bool]functionCallable[[TileProvider], bool]c                8    d fd | ||||          S )a		  Return a subset of the :class:`Bunch` matching the filter conditions

        Each :class:`TileProvider` within a :class:`Bunch` is checked against one or
        more specified conditions and kept if they are satisfied or removed if at least
        one condition is not met.

        Parameters
        ----------
        keyword : str (optional)
            Condition returns ``True`` if ``keyword`` string is present in any string
            value in a :class:`TileProvider` object.
            The comparison is not case sensitive.
        name : str (optional)
            Condition returns ``True`` if ``name`` string is present in
            the name attribute of :class:`TileProvider` object.
            The comparison is not case sensitive.
        requires_token : bool (optional)
            Condition returns ``True`` if :meth:`TileProvider.requires_token` returns
            ``True`` (i.e. if the object requires specification of API token).
        function : callable (optional)
            Custom function taking :class:`TileProvider` as an argument and returns
            bool. If ``function`` is given, other parameters are ignored.

        Returns
        -------
        filtered : Bunch

        Examples
        --------
        >>> import xyzservices.providers as xyz

        You can filter all free providers (not requiring API token):

        >>> free_providers = xyz.filter(requires_token=False)

        Or all providers with ``open`` in the name:

        >>> open_providers = xyz.filter(name="open")

        You can use keyword search to find all providers based on OpenStreetMap data:

        >>> osm_providers = xyz.filter(keyword="openstreetmap")

        You can combine multiple conditions to find providers based on OpenStreetMap
        data that require API token:

        >>> osm_locked = xyz.filter(keyword="openstreetmap", requires_token=True)

        You can also pass custom function that takes :class:`TileProvider` and returns
        boolean value. You can then find all providers with ``max_zoom`` smaller than
        18:

        >>> def zoom18(provider):
        ...    if hasattr(provider, "max_zoom") and provider.max_zoom < 18:
        ...        return True
        ...    return False
        >>> small_zoom = xyz.filter(function=zoom18)
        c                   g }|od}|                                  D ]C}t          |t                    r,|                                |                                v rd} nD|                    |           |Fd}|                                | j                                        v rd}|                    |           |/d}|                                 |u rd}|                    |           t          |          S )NFT)r6   r$   r&   lowerappendr5   r>   all)	r7   r<   r5   r>   condkeyword_matchv
name_matchtoken_matchs	            r   	_validatezBunch.filter.<locals>._validate   s	   D + %!** " "A!!S)) ""==??aggii7 ",0M!EM*** ("
::<<8=#6#6#8#88 &!%JJ''' )#**,,> '"&KK(((t99r   c                    t                      }|                                 D ]Y\  }}t          |t                    r(| 
||||          r|||<   1 ||          r|||<   B 	|||||          }|r|||<   Z|S )N)r<   r5   r>   r<   r5   r>   r@   )r   itemsr$   r%   )bunchr<   r5   r>   r@   newr   valuefiltered_filter_bunchrL   s            r   rT   z#Bunch.filter.<locals>._filter_bunch   s    ''C#kkmm , ,
Ue\22 , 
-$9!$+!%+9	   - (-CH#8E?? -',CH  -} '!'5!)     H   ,#+CJr   rN   r
   )r   r<   r5   r>   r@   rT   rL   s        @@r   filterzBunch.filter~   s\    D	 	 	6	 	 	 	 	 	< })
 
 
 	
r   r&   r%   c                    d |                                                                  D             }|                    t                                                    }||v r||         S t          d| d          )a  Return :class:`TileProvider` based on the name query

        Returns a matching :class:`TileProvider` from the :class:`Bunch` if the ``name``
        contains the same letters in the same order as the provider's name irrespective
        of the letter case, spaces, dashes and other characters.
        See examples for details.

        Parameters
        ----------
        name : str
            Name of the tile provider. Formatting does not matter.

        Returns
        -------
        match: TileProvider

        Examples
        --------
        >>> import xyzservices.providers as xyz

        All these queries return the same ``CartoDB.Positron`` TileProvider:

        >>> xyz.query_name("CartoDB Positron")
        >>> xyz.query_name("cartodbpositron")
        >>> xyz.query_name("cartodb-positron")
        >>> xyz.query_name("carto db/positron")
        >>> xyz.query_name("CARTO_DB_POSITRON")
        >>> xyz.query_name("CartoDB.Positron")

        c                n    i | ]2\  }}|                     t                                                    |3S r
   )	translateQUERY_NAME_TRANSLATIONrD   )r   krI   s      r   r   z$Bunch.query_name.<locals>.<dictcomp>   sG     
 
 
1 KK.//5577
 
 
r   z*No matching provider found for the query 'z'.)r;   rO   rX   rY   rD   
ValueError)r   r5   xyz_flat_lower
name_cleans       r   
query_namezBunch.query_name  s    >
 
,,..
 
 
 ^^$:;;AACC
' 	.!*--NdNNNOOOr   F)r1   r2   )NNNN)
r<   r=   r5   r=   r>   r?   r@   rA   r1   r   r5   r&   r1   r%   )
__name__
__module____qualname____doc__r   r   r)   r;   rU   r^   r
   r   r   r   r      s         4& & &  % % % %N" " " "L "&")-37A
 A
 A
 A
 A
F'P 'P 'P 'P 'P 'Pr   r   c                       e Zd ZdZ fdZddZddZ	 	 	 	 	 dddZddZe	d             Z
ddZedd            Z xZS )r%   a$  
    A dict with attribute-access and that
    can be called to update keys


    Examples
    --------

    You can create custom :class:`TileProvider` by passing your attributes to the object
    as it would have been a ``dict()``. It is required to always specify ``name``,
    ``url``, and ``attribution``.

    >>> public_provider = TileProvider(
    ...     name="My public tiles",
    ...     url="https://myserver.com/tiles/{z}/{x}/{y}.png",
    ...     attribution="(C) xyzservices",
    ... )

    Alternatively, you can create it from a dictionary of attributes. When specifying a
    placeholder for the access token, please use the ``"<insert your access token
    here>"`` string to ensure that :meth:`~xyzservices.TileProvider.requires_token`
    method works properly.

    >>> private_provider = TileProvider(
    ...    {
    ...        "url": "https://myserver.com/tiles/{z}/{x}/{y}.png?apikey={accessToken}",
    ...        "attribution": "(C) xyzservices",
    ...        "accessToken": "<insert your access token here>",
    ...        "name": "my_private_provider",
    ...    }
    ... )

    It is customary to include ``html_attribution`` attribute containing HTML string as
    ``'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>
    contributors'`` alongisde a plain-text ``attribution``.

    You can then fetch all information as attributes:

    >>> public_provider.url
    'https://myserver.com/tiles/{z}/{x}/{y}.png'

    >>> public_provider.attribution
    '(C) xyzservices'

    To ensure you will be able to use the tiles, you can check if the
    :class:`TileProvider` requires a token or API key.

    >>> public_provider.requires_token()
    False
    >>> private_provider.requires_token()
    True

    You can also generate URL in the required format with or without placeholders:

    >>> public_provider.build_url()
    'https://myserver.com/tiles/{z}/{x}/{y}.png'
    >>> private_provider.build_url(x=12, y=21, z=11, accessToken="my_token")
    'https://myserver.com/tiles/11/12/21.png?access_token=my_token'

    c                    t                      j        |i | g }dD ]-}||                                 vr|                    |           .t	          |          dk    r(dd                    |           d}t          |          d S )N)r5   urlattributionr   zyThe attributes `name`, `url`, and `attribution` are required to initialise a `TileProvider`. Please provide values for: `z`, ``)super__init__r   rE   r+   joinr   )r   argskwargsmissingelmsg	__class__s         r   rk   zTileProvider.__init__i  s    $)&)))0 	# 	#B$ #r"""w<<! 	&, KK((, , ,  !%%%	& 	&r   r1   c                N    t          |           }|                    |           |S r   )r%   updater   rn   rQ   s      r   __call__zTileProvider.__call__x  s&    4  

6
r   c                $    t          |           }|S r   r%   ru   s      r   copyzTileProvider.copy}  s    4  
r   NTr   Optional[Union[int, str]]yzscale_factorr=   fill_subdomainr?   r&   c           	        |                                  }|d}|d}|d}|                    |           |                                rt          d          |                    d          }|r|}	|                    dd           n|                    dd          }	|r|                    d	d
          }
|
d         }nd} |j        d|||||	d|S )a  
        Build the URL of tiles from the :class:`TileProvider` object

        Can return URL with placeholders or the final tile URL.

        Parameters
        ----------

        x, y, z : int (optional)
            tile number
        scale_factor : str (optional)
            Scale factor (where supported). For example, you can get double resolution
            (512 x 512) instead of standard one (256 x 256) with ``"@2x"``. If you want
            to keep a placeholder, pass `"{r}"`.
        fill_subdomain : bool (optional, default True)
            Fill subdomain placeholder with the first available subdomain. If False, the
            URL will contain ``{s}`` placeholder for subdomain.

        **kwargs
            Other potential attributes updating the :class:`TileProvider`.

        Returns
        -------

        url : str
            Formatted URL

        Examples
        --------
        >>> import xyzservices.providers as xyz

        >>> xyz.CartoDB.DarkMatter.build_url()
        'https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png'

        >>> xyz.CartoDB.DarkMatter.build_url(x=9, y=11, z=5)
        'https://a.basemaps.cartocdn.com/dark_all/5/9/11.png'

        >>> xyz.CartoDB.DarkMatter.build_url(x=9, y=11, z=5, scale_factor="@2x")
        'https://a.basemaps.cartocdn.com/dark_all/5/9/11@2x.png'

        >>> xyz.MapBox.build_url(accessToken="my_token")
        'https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token=my_token'

        Nz{x}z{y}z{z}zToken is required for this provider, but not provided. You can either update TileProvider or pass respective keywords to build_url().rg   rr	   
subdomainsabcr   z{s})r   r{   r|   sr   r
   )ry   rt   r>   r[   popformat)r   r   r{   r|   r}   r~   rn   r7   rg   r   r   r   s               r   	build_urlzTileProvider.build_url  s   j 99;; 	A 	A 	A""$$ 	"   ll5!! 	&ALLd####S"%%A 	!lE::J1AAAsz>Aa1>>X>>>r   boolc                    |                                  D ]*\  }}t          |t                    rd|v r|| j        v r dS +dS )al  
        Returns ``True`` if the TileProvider requires access token to fetch tiles.

        The token attribute name vary and some :class:`TileProvider` objects may require
        more than one token (e.g. ``HERE``). The information is deduced from the
        presence of `'<insert your...'` string in one or more of attributes. When
        specifying a placeholder for the access token, please use the ``"<insert your
        access token here>"`` string to ensure that
        :meth:`~xyzservices.TileProvider.requires_token` method works properly.

        Returns
        -------
        bool

        Examples
        --------
        >>> import xyzservices.providers as xyz
        >>> xyz.MapBox.requires_token()
        True

        >>> xyz.CartoDB.Positron
        False

        We can specify this API key by calling the object or overriding the attribute.
        Overriding the attribute will alter existing object:

        >>> xyz.OpenWeatherMap.Clouds["apiKey"] = "my-private-api-key"

        Calling the object will return a copy:

        >>> xyz.OpenWeatherMap.Clouds(apiKey="my-private-api-key")


        z<insert yourTF)rO   r$   r&   rg   )r   r   vals      r   r>   zTileProvider.requires_token  sY    H 

 	  	 HC#s##  #(=  $(?  44ur   c                N    d|                                  v r| d         S | d         S )Nhtml_attributionrh   r   r   s    r   r   zTileProvider.html_attribution  s.    , 	,*++M""r   Fc                    d}|                                  D ]\  }}|dk    r|d| d| dz  }|rdn
dt           d}d| d	| j         d
| d}|S )Nr	   r5   z
<dt><span>z</span></dt><dd>z</dd>r!   r"   r#   z
            <div class="xyz-wrap">
                <div class="xyz-header">
                    <div class="xyz-obj">xyzservices.TileProvider</div>
                    <div class="xyz-name">z</div>
                </div>
                <div class="xyz-details">
                    <dl class="xyz-attrs">
                        z\
                    </dl>
                </div>
            </div>
        </div>
        )rO   r*   r5   )r   r    provider_infor   r   r/   r0   s          r   r)   zTileProvider._repr_html_  s    

 	N 	NHCf} N!Mc!M!M3!M!M!MM?"?I"?"?"?	  ,09  '  " r   r5   c           	         d}t          j        t          j                            | dt          |           d                    }|D ]}|d         |k    r nt          d| d          |d         }t          j        t          j                            | d|                     } | |d         |d	         |                    d
          |                    d          |                    d                    S )a  
        Creates a :class:`TileProvider` object based on the definition from
        the `Quick Map Services <https://qms.nextgis.com/>`__ open catalog.

        Parameters
        ----------
        name : str
            Service name

        Returns
        -------
        :class:`TileProvider`

        Examples
        --------
        >>> from xyzservices.lib import TileProvider
        >>> provider = TileProvider.from_qms("OpenTopoMap")
        z*https://qms.nextgis.com/api/v1/geoservicesz	/?search=z	&type=tmsr5   z	Service 'z' not found.id/rg   z_minz_maxcopyright_text)r5   rg   min_zoommax_zoomrh   )jsonloadurllibrequesturlopenr   r[   get)clsr5   QMS_API_URLservicesservice
service_idservice_detailss          r   from_qmszTileProvider.from_qms"  s'   ( C9N""k#R#RE$KK#R#R#RSS
 
   	= 	=Gv$&  ;;;;<<<T]
)N""k#@#@J#@#@AA
 
 s (&$((11$((11'++,<==
 
 
 	
r   )r1   r%   )NNNNT)r   rz   r{   rz   r|   rz   r}   r=   r~   r?   r1   r&   )r1   r   r_   r`   )ra   rb   rc   rd   rk   rv   ry   r   r>   propertyr   r)   classmethodr   __classcell__)rr   s   @r   r%   r%   +  s        ; ;z& & & & &   
    (,'+'+&*)-U? U? U? U? U?n( ( ( (T # # X#
   4 *
 *
 *
 [*
 *
 *
 *
 *
r   r%   c                   t          j        |           }t                      }|                                D ]Q}||         d                                v rt	                    ||<   3t          fdD                       ||<   R|S )Nrg   c                <    i | ]}|t          |                   S r
   rx   )r   ir7   s     r   r   z_load_json.<locals>.<dictcomp>^  s'    @@@!L!--@@@r   )r   loadsr   r   r%   )fdata	providersprovider_namer7   s       @r   
_load_jsonr   P  s    :a==DI 	 	&HMMOO# 	'3H'='=Im$$ (-@@@@x@@@( (Im$$ r   u8  
/* CSS stylesheet for displaying xyzservices objects in Jupyter.*/
.xyz-wrap {
    --xyz-border-color: var(--jp-border-color2, #ddd);
    --xyz-font-color2: var(--jp-content-font-color2, rgba(128, 128, 128, 1));
    --xyz-background-color-white: var(--jp-layout-color1, white);
    --xyz-background-color: var(--jp-layout-color2, rgba(128, 128, 128, 0.1));
}

html[theme=dark] .xyz-wrap,
body.vscode-dark .xyz-wrap,
body.vscode-high-contrast .xyz-wrap {
    --xyz-border-color: #222;
    --xyz-font-color2: rgba(255, 255, 255, 0.54);
    --xyz-background-color-white: rgba(255, 255, 255, 1);
    --xyz-background-color: rgba(255, 255, 255, 0.05);

}

.xyz-header {
    padding-top: 6px;
    padding-bottom: 6px;
    margin-bottom: 4px;
    border-bottom: solid 1px var(--xyz-border-color);
}

.xyz-header>div {
    display: inline;
    margin-top: 0;
    margin-bottom: 0;
}

.xyz-obj,
.xyz-name {
    margin-left: 2px;
    margin-right: 10px;
}

.xyz-obj {
    color: var(--xyz-font-color2);
}

.xyz-attrs {
    grid-column: 1 / -1;
}

dl.xyz-attrs {
    padding: 0 5px 0 5px;
    margin: 0;
    display: grid;
    grid-template-columns: 135px auto;
    background-color: var(--xyz-background-color);
}

.xyz-attrs dt,
dd {
    padding: 0;
    margin: 0;
    float: left;
    padding-right: 10px;
    width: auto;
}

.xyz-attrs dt {
    font-weight: normal;
    grid-column: 1;
}

.xyz-attrs dd {
    grid-column: 2;
    white-space: pre-wrap;
    word-break: break-all;
}

.xyz-details ul>li>label>span {
    color: var(--xyz-font-color2);
    padding-left: 10px;
}

.xyz-inside {
    display: none;
}

.xyz-checkbox:checked~.xyz-inside {
    display: contents;
}

.xyz-collapsible li>input {
    display: none;
}

.xyz-collapsible>li>label {
    cursor: pointer;
}

.xyz-collapsible>li>label:hover {
    color: var(--xyz-font-color2);
}

ul.xyz-collapsible {
    list-style: none!important;
    padding-left: 20px!important;
}

.xyz-checkbox+label:before {
    content: '►';
    font-size: 11px;
}

.xyz-checkbox:checked+label:before {
    content: '▼';
}

.xyz-wrap {
    margin-bottom: 10px;
}
)rd   
__future__r   r   r'   urllib.requestr   typingr   r   r   urllib.parser   r&   	maketransrY   r2   r   r%   r   r*   r
   r   r   <module>r      s    # " " " " "       , , , , , , , , , ,      '@'@x'@'@'@AA YP YP YP YP YPD YP YP YPxb
 b
 b
 b
 b
5 b
 b
 b
J	  (t			r   