U
    </eS                     @  s   d Z ddlmZ ddlZddlZddlZddlmZ ddl	m
Z
 edd dD ZG d	d
 d
eZG dd deZdd ZdZdS )z"
Utilities to support XYZservices
    )annotationsN)Callable)quotec                 C  s   i | ]
}|d qS )  ).0xr   r   3/tmp/pip-unpacked-wheel-eve_l6qg/xyzservices/lib.py
<dictcomp>   s      r
   z., -_/c                   @  s`   e Zd ZdZdd Zdd ZdddZd	d
ddZdddddd dddZddd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              
   C  s>   z|  |W S  tk
r8 } zt||W 5 d }~X Y nX d S N)__getitem__KeyErrorAttributeError)selfkeyerrr   r   r	   __getattr__*   s    zBunch.__getattr__c                 C  s   |   S r   )keysr   r   r   r	   __dir__0   s    zBunch.__dir__Fc                 C  s   d}| D ]\}t | | tr d}nd}tt }|d| d| d| d| d| | jd	d
 d7 }q|rnd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>
        )
isinstanceTileProviderstruuidZuuid4_repr_html_	CSS_STYLElen)r   r   childrenr   objuidstylehtmlr   r   r	   r   3   s4    

zBunch._repr_html_dictreturnc                   s   i  fd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                   s0   t | tr| | j< n|  D ]} | qd S r   )r   r   namevalues)providerZprov_get_providersZflatr   r	   r.   r   s    
z%Bunch.flatten.<locals>._get_providersr   r   r   r-   r	   flattenY   s    zBunch.flattenN
str | Nonebool | NonezCallable[[TileProvider], bool])keywordr*   requires_tokenfunctionr)   c                   s(   dd  fdd  | ||||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           	      S  s   g }|d k	rJd}|   D ]&}t|tr| | krd} q@q|| |d k	rvd}| | j krld}|| |d k	rd}|  |krd}|| t|S )NFT)r+   r   r   lowerappendr*   r3   all)	r,   r2   r*   r3   ZcondZkeyword_matchvZ
name_matchZtoken_matchr   r   r	   	_validate   s&    


zBunch.filter.<locals>._validatec           	        sx   t  }|  D ]d\}}t|trT|d krB||||drR|||< qr||rr|||< q |||||d}|r|||< q|S )N)r2   r*   r3   r2   r*   r3   r4   )r   itemsr   r   )	Zbunchr2   r*   r3   r4   newr   valuefiltered_filter_bunchr9   r   r	   r@      s.    



z#Bunch.filter.<locals>._filter_bunchr:   r   )r   r2   r*   r3   r4   r   r?   r	   filter}   s    BzBunch.filterr   r   r*   r)   c                 C  sH   dd |    D }|t }||kr4|| S td| ddS )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                 S  s    i | ]\}}| t |qS r   )	translateQUERY_NAME_TRANSLATIONr5   )r   kr8   r   r   r	   r
     s    z$Bunch.query_name.<locals>.<dictcomp>z*No matching provider found for the query 'z'.N)r/   r;   rC   rD   r5   
ValueError)r   r*   Zxyz_flat_lowerZ
name_cleanr   r   r	   
query_name   s    
zBunch.query_name)F)NNNN)
__name__
__module____qualname____doc__r   r   r   r/   rA   rG   r   r   r   r	   r      s   
&&     r   c                      s   e Zd ZdZ fddZd dddZd dddZddddddddddZddddZe	dd Z
dddZedd dddZ  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                   s\   t  j|| g }dD ]}||  kr|| qt|dkrXdd| d}t|d S )N)r*   urlattributionr   zyThe attributes `name`, `url`, and `attribution` are required to initialise a `TileProvider`. Please provide values for: `z`, ``)super__init__r   r6   r!   joinr   )r   argskwargsmissingelmsg	__class__r   r	   rP   e  s    zTileProvider.__init__r(   c                 K  s   t | }|| |S r   )r   update)r   rS   r<   r   r   r	   __call__t  s    
zTileProvider.__call__c                 C  s   t | }|S r   r   )r   r<   r   r   r	   copyy  s    zTileProvider.copyNTzint | str | Noner0   r1   r   )r   yzscale_factorfill_subdomainr)   c                 K  s   |   }|dkrd}|dkr d}|dkr,d}|| | rFtd|d}|rf|}	|dd n|dd}	|r|d	d
}
|
d }nd}|jf |||||	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().rL   rr   
subdomainsabcr   z{s})r   r]   r^   sra   )r\   rY   r3   rF   popformat)r   r   r]   r^   r_   r`   rS   r,   rL   ra   rb   rd   r   r   r	   	build_url}  s,    5


zTileProvider.build_urlboolc                 C  s8   |   D ]*\}}t|trd|kr|| jkr dS q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)r;   r   r   rL   )r   r   valr   r   r	   r3     s    $zTileProvider.requires_tokenc                 C  s   d| kr| d S | d S )Nhtml_attributionrM   r   r   r   r   r	   rj     s    zTileProvider.html_attributionFc                 C  sf   d}|   D ]&\}}|dkr|d| d| d7 }q|r<dn
dt d}d| d	| j d
| d}|S )Nr   r*   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>
        )r;   r    r*   )r   r   Zprovider_infor   ri   r%   r&   r   r   r	   r     s    
zTileProvider._repr_html_rB   c                 C  s   d}t tj| dt| d}|D ]}|d |kr* qPq*td| d|d }t tj| d| }| |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=tmsr*   z	Service 'z' not found.id/rL   Zz_minZz_maxZcopyright_text)r*   rL   Zmin_zoomZmax_zoomrM   )jsonloadurllibrequesturlopenr   rF   get)clsr*   Zqms_api_urlZservicesZserviceZ
service_idZservice_detailsr   r   r	   from_qms  s&    zTileProvider.from_qms)NNNNT)F)rH   rI   rJ   rK   rP   rZ   r\   rg   r3   propertyrj   r   classmethodrt   __classcell__r   r   rW   r	   r   '  s    =     W)

r   c                   sV   t | }t }|D ]<}||  d kr6t ||< qt fdd D ||< q|S )NrL   c                   s   i | ]}|t  | qS r   r[   )r   ir,   r   r	   r
   X  s      z_load_json.<locals>.<dictcomp>)rm   loadsr   r   )fdataZ	providersZprovider_namer   ry   r	   
_load_jsonK  s    

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;
}
)rK   
__future__r   rm   urllib.requestro   r   typingr   urllib.parser   r   	maketransrD   r'   r   r   r}   r    r   r   r   r	   <module>   s       &