lets-plot 4.8.1rc1__cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. lets_plot/__init__.py +382 -0
  2. lets_plot/_global_settings.py +192 -0
  3. lets_plot/_kbridge.py +197 -0
  4. lets_plot/_type_utils.py +133 -0
  5. lets_plot/_version.py +6 -0
  6. lets_plot/bistro/__init__.py +16 -0
  7. lets_plot/bistro/_plot2d_common.py +106 -0
  8. lets_plot/bistro/corr.py +448 -0
  9. lets_plot/bistro/im.py +196 -0
  10. lets_plot/bistro/joint.py +192 -0
  11. lets_plot/bistro/qq.py +207 -0
  12. lets_plot/bistro/residual.py +341 -0
  13. lets_plot/bistro/waterfall.py +332 -0
  14. lets_plot/export/__init__.py +6 -0
  15. lets_plot/export/ggsave_.py +172 -0
  16. lets_plot/frontend_context/__init__.py +8 -0
  17. lets_plot/frontend_context/_configuration.py +140 -0
  18. lets_plot/frontend_context/_dynamic_configure_html.py +115 -0
  19. lets_plot/frontend_context/_frontend_ctx.py +16 -0
  20. lets_plot/frontend_context/_html_contexts.py +223 -0
  21. lets_plot/frontend_context/_intellij_python_json_ctx.py +38 -0
  22. lets_plot/frontend_context/_isolated_webview_panel_ctx.py +81 -0
  23. lets_plot/frontend_context/_json_contexts.py +39 -0
  24. lets_plot/frontend_context/_jupyter_notebook_ctx.py +82 -0
  25. lets_plot/frontend_context/_mime_types.py +7 -0
  26. lets_plot/frontend_context/_static_html_page_ctx.py +76 -0
  27. lets_plot/frontend_context/_static_svg_ctx.py +26 -0
  28. lets_plot/frontend_context/_webbr_html_page_ctx.py +29 -0
  29. lets_plot/frontend_context/sandbox.py +5 -0
  30. lets_plot/geo_data/__init__.py +19 -0
  31. lets_plot/geo_data/core.py +335 -0
  32. lets_plot/geo_data/geocoder.py +988 -0
  33. lets_plot/geo_data/geocodes.py +512 -0
  34. lets_plot/geo_data/gis/__init__.py +0 -0
  35. lets_plot/geo_data/gis/fluent_dict.py +201 -0
  36. lets_plot/geo_data/gis/geocoding_service.py +42 -0
  37. lets_plot/geo_data/gis/geometry.py +91 -0
  38. lets_plot/geo_data/gis/json_request.py +232 -0
  39. lets_plot/geo_data/gis/json_response.py +308 -0
  40. lets_plot/geo_data/gis/request.py +492 -0
  41. lets_plot/geo_data/gis/response.py +247 -0
  42. lets_plot/geo_data/livemap_helper.py +65 -0
  43. lets_plot/geo_data/to_geo_data_frame.py +141 -0
  44. lets_plot/geo_data/type_assertion.py +34 -0
  45. lets_plot/geo_data_internals/__init__.py +4 -0
  46. lets_plot/geo_data_internals/constants.py +13 -0
  47. lets_plot/geo_data_internals/utils.py +33 -0
  48. lets_plot/mapping.py +115 -0
  49. lets_plot/package_data/lets-plot.min.js +3 -0
  50. lets_plot/plot/__init__.py +64 -0
  51. lets_plot/plot/_global_theme.py +14 -0
  52. lets_plot/plot/annotation.py +290 -0
  53. lets_plot/plot/coord.py +242 -0
  54. lets_plot/plot/core.py +1071 -0
  55. lets_plot/plot/expand_limits_.py +78 -0
  56. lets_plot/plot/facet.py +210 -0
  57. lets_plot/plot/font_features.py +71 -0
  58. lets_plot/plot/geom.py +9146 -0
  59. lets_plot/plot/geom_extras.py +53 -0
  60. lets_plot/plot/geom_function_.py +219 -0
  61. lets_plot/plot/geom_imshow_.py +393 -0
  62. lets_plot/plot/geom_livemap_.py +343 -0
  63. lets_plot/plot/ggbunch_.py +96 -0
  64. lets_plot/plot/gggrid_.py +139 -0
  65. lets_plot/plot/ggtb_.py +81 -0
  66. lets_plot/plot/guide.py +231 -0
  67. lets_plot/plot/label.py +187 -0
  68. lets_plot/plot/marginal_layer.py +181 -0
  69. lets_plot/plot/plot.py +245 -0
  70. lets_plot/plot/pos.py +344 -0
  71. lets_plot/plot/sampling.py +338 -0
  72. lets_plot/plot/sandbox_.py +26 -0
  73. lets_plot/plot/scale.py +3580 -0
  74. lets_plot/plot/scale_colormap_mpl.py +300 -0
  75. lets_plot/plot/scale_convenience.py +155 -0
  76. lets_plot/plot/scale_identity_.py +653 -0
  77. lets_plot/plot/scale_position.py +1342 -0
  78. lets_plot/plot/series_meta.py +209 -0
  79. lets_plot/plot/stat.py +585 -0
  80. lets_plot/plot/subplots.py +331 -0
  81. lets_plot/plot/subplots_util.py +24 -0
  82. lets_plot/plot/theme_.py +790 -0
  83. lets_plot/plot/theme_set.py +418 -0
  84. lets_plot/plot/tooltip.py +486 -0
  85. lets_plot/plot/util.py +267 -0
  86. lets_plot/settings_utils.py +244 -0
  87. lets_plot/tilesets.py +429 -0
  88. lets_plot-4.8.1rc1.dist-info/METADATA +221 -0
  89. lets_plot-4.8.1rc1.dist-info/RECORD +97 -0
  90. lets_plot-4.8.1rc1.dist-info/WHEEL +6 -0
  91. lets_plot-4.8.1rc1.dist-info/licenses/LICENSE +21 -0
  92. lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.FreeType +166 -0
  93. lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.ImageMagick +106 -0
  94. lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.expat +21 -0
  95. lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.fontconfig +200 -0
  96. lets_plot-4.8.1rc1.dist-info/top_level.txt +2 -0
  97. lets_plot_kotlin_bridge.cpython-311-x86_64-linux-gnu.so +0 -0
lets_plot/plot/util.py ADDED
@@ -0,0 +1,267 @@
1
+ #
2
+ # Copyright (c) 2019. JetBrains s.r.o.
3
+ # Use of this source code is governed by the MIT license that can be found in the LICENSE file.
4
+ #
5
+ from typing import Any, Tuple, Sequence, Optional, Dict, List
6
+
7
+ from lets_plot._type_utils import is_pandas_data_frame, is_polars_dataframe
8
+ from lets_plot.geo_data_internals.utils import find_geo_names
9
+ from lets_plot.mapping import MappingMeta
10
+ from lets_plot.plot.core import aes, FeatureSpec, PlotSpec
11
+ from lets_plot.plot.series_meta import _infer_type, TYPE_UNKNOWN, TYPE_DATE_TIME, _detect_time_zone
12
+
13
+
14
+ def as_boolean(val, *, default):
15
+ if val is None:
16
+ return default
17
+
18
+ return bool(val) and val != 'False'
19
+
20
+
21
+ def update_plot_aes_mapping(plot: PlotSpec, add_mapping: FeatureSpec):
22
+ existing_spec = plot.props().get('mapping', aes())
23
+ merged_mapping = {**existing_spec.as_dict(), **add_mapping.as_dict()}
24
+
25
+ # Re-annotate the data with the merged mapping.
26
+ data = plot.props().get('data', None)
27
+ data, processed_mapping, data_meta = as_annotated_data(data, aes(**merged_mapping))
28
+ plot.props()['data'] = data
29
+ plot.props()['mapping'] = processed_mapping
30
+
31
+ # Add data_meta to plot properties
32
+ for key, value in data_meta.items():
33
+ plot.props()[key] = value
34
+
35
+
36
+ def as_annotated_data(data: Any, mapping_spec: FeatureSpec) -> Tuple:
37
+ data_type_by_var: Dict[str, str] = {} # VarName to Type
38
+ mapping_meta_by_var: Dict[str, Dict[str, MappingMeta]] = {} # VarName to Dict[Aes, MappingMeta]
39
+ mappings = {} # Aes to VarName
40
+
41
+ # fill mapping_meta_by_var, mappings and data_type_by_var.
42
+ if mapping_spec is not None:
43
+ for key, spec in mapping_spec.props().items():
44
+ # the key is either an aesthetic name or 'name' (FeatureSpec.name property)
45
+ if key == 'name': # ignore FeatureSpec.name property
46
+ continue
47
+
48
+ if isinstance(spec, MappingMeta):
49
+ mappings[key] = spec.variable
50
+ mapping_meta_by_var.setdefault(spec.variable, {})[key] = spec
51
+ data_type_by_var[spec.variable] = TYPE_UNKNOWN
52
+ else:
53
+ mappings[key] = spec # spec is a variable name
54
+
55
+ data_type_by_var.update(_infer_type(data))
56
+
57
+ # Detect the tome zone - one for the entire data set.
58
+ time_zone_by_var_name = {}
59
+ for var_name, data_type in data_type_by_var.items():
60
+ if data_type == TYPE_DATE_TIME:
61
+ time_zone = _detect_time_zone(var_name, data)
62
+ if time_zone is not None:
63
+ time_zone_by_var_name[var_name] = time_zone
64
+
65
+ # fill series annotations
66
+ series_annotations = {} # var to series_annotation
67
+ for var_name, data_type in data_type_by_var.items():
68
+ series_annotation = {}
69
+
70
+ if data_type != TYPE_UNKNOWN:
71
+ series_annotation['type'] = data_type
72
+
73
+ if var_name in time_zone_by_var_name:
74
+ series_annotation['time_zone'] = time_zone_by_var_name[var_name]
75
+
76
+ if is_pandas_data_frame(data) and data[var_name].dtype.name == 'category' and data[var_name].dtype.ordered:
77
+ series_annotation['factor_levels'] = data[var_name].cat.categories.to_list()
78
+
79
+ elif is_polars_dataframe(data):
80
+ import polars
81
+
82
+ col_dtype = data[var_name].dtype
83
+ if isinstance(col_dtype, polars.datatypes.Enum):
84
+ series_annotation['factor_levels'] = list(col_dtype.categories)
85
+ elif isinstance(col_dtype, polars.datatypes.Categorical):
86
+ # # It does not seem possible to get categories in correct order from the Categorical dtype.
87
+ # categories_series = data[var_name].cat.get_categories()
88
+ # indises = [col_dtype.categories[cat] for cat in categories_series]
89
+ # cats = [col_dtype.categories[i] for i in indises]
90
+ # series_annotation['factor_levels'] = categories_series.to_list()
91
+ pass
92
+
93
+ elif var_name in mapping_meta_by_var:
94
+ levels = last_not_none(list(map(lambda mm: mm.levels, mapping_meta_by_var[var_name].values())))
95
+ if levels is not None:
96
+ series_annotation['factor_levels'] = levels
97
+
98
+ if 'factor_levels' in series_annotation and var_name in mapping_meta_by_var:
99
+ order = last_not_none(list(map(lambda mm: mm.parameters['order'], mapping_meta_by_var[var_name].values())))
100
+ if order is not None:
101
+ series_annotation['order'] = order
102
+
103
+ if len(series_annotation) > 0:
104
+ series_annotation['column'] = var_name
105
+ series_annotations[var_name] = series_annotation
106
+
107
+ # fill mapping annotations
108
+ mapping_annotations = []
109
+ for var_name, meta_data in mapping_meta_by_var.items():
110
+ for aesthetic, mapping_meta in meta_data.items():
111
+ if mapping_meta.annotation == 'as_discrete':
112
+ if 'factor_levels' in series_annotations.get(var_name, {}):
113
+ # there is a bug - if label is set then levels are not applied
114
+ continue
115
+
116
+ mapping_annotation = {}
117
+
118
+ # Note that the label is always set; otherwise, the scale title will appear as 'color.cyl'
119
+ label = mapping_meta.parameters.get('label')
120
+ if label is not None:
121
+ mapping_annotation.setdefault('parameters', {})['label'] = label
122
+
123
+ if mapping_meta.levels is not None:
124
+ mapping_annotation['levels'] = mapping_meta.levels
125
+
126
+ order_by = mapping_meta.parameters.get('order_by')
127
+ if order_by is not None:
128
+ mapping_annotation.setdefault('parameters', {})['order_by'] = order_by
129
+
130
+ order = mapping_meta.parameters.get('order')
131
+ if order is not None:
132
+ mapping_annotation.setdefault('parameters', {})['order'] = order
133
+
134
+ # add mapping meta if a custom label is set or if series annotation for var doesn't contain order options
135
+ # otherwise don't add mapping meta - it's redundant, nothing unique compared to series annotation
136
+ if len(mapping_annotation):
137
+ mapping_annotation['aes'] = aesthetic
138
+ mapping_annotation['annotation'] = 'as_discrete'
139
+ mapping_annotations.append(mapping_annotation)
140
+
141
+ data_meta = {}
142
+
143
+ if len(series_annotations) > 0:
144
+ data_meta.update({'series_annotations': list(series_annotations.values())})
145
+
146
+ if len(mapping_annotations) > 0:
147
+ data_meta.update({'mapping_annotations': mapping_annotations})
148
+
149
+ return data, aes(**mappings), {'data_meta': data_meta}
150
+
151
+
152
+ def is_data_pub_stream(data: Any) -> bool:
153
+ # try:
154
+ # from lets_plot.display import DataPubStream
155
+ # return isinstance(data, DataPubStream)
156
+ # except ImportError:
157
+ # return False # no pub-sub in standalone deployment
158
+ return False
159
+
160
+
161
+ def normalize_map_join(map_join):
162
+ if map_join is None:
163
+ return None
164
+
165
+ def invalid_map_join_format():
166
+ return ValueError("map_join must be a str, list[str] or pair of list[str]")
167
+
168
+ if isinstance(map_join, str): # 'foo' -> [['foo'], None]
169
+ data_names = [map_join]
170
+ map_names = None
171
+ elif isinstance(map_join, Sequence):
172
+ if all(isinstance(v, str) for v in map_join): # all items are strings
173
+ if len(map_join) == 1: # ['foo'] -> [['foo'], None]
174
+ data_names = map_join
175
+ map_names = None
176
+ elif len(map_join) == 2: # ['foo', 'bar'] -> [['foo'], ['bar']]
177
+ data_names = [map_join[0]]
178
+ map_names = [map_join[1]]
179
+ elif len(map_join) > 2: # ['foo', 'bar', 'baz'] -> error
180
+ raise ValueError(
181
+ "map_join of type list[str] expected to have 1 or 2 items, but was {}".format(len(map_join)))
182
+ else:
183
+ raise invalid_map_join_format()
184
+ elif all(isinstance(v, Sequence) and not isinstance(v, str) for v in map_join): # all items are lists
185
+ if len(map_join) == 1: # [['foo', 'bar']] -> [['foo', 'bar'], None]
186
+ data_names = map_join[0]
187
+ map_names = None
188
+ elif len(map_join) == 2: # [['foo', 'bar'], ['baz', 'qux']] -> [['foo', 'bar'], ['baz', 'qux']]
189
+ data_names = map_join[0]
190
+ map_names = map_join[1]
191
+ else:
192
+ raise invalid_map_join_format()
193
+ else:
194
+ raise invalid_map_join_format()
195
+
196
+ else:
197
+ raise invalid_map_join_format()
198
+
199
+ return [data_names, map_names]
200
+
201
+
202
+ def auto_join_geo_names(map_join: Any, gdf):
203
+ if map_join is None:
204
+ return None
205
+
206
+ data_names = map_join[0]
207
+ map_names = map_join[1]
208
+
209
+ if map_names is None:
210
+ map_names = find_geo_names(gdf)
211
+ if len(map_names) == 0:
212
+ raise ValueError(
213
+ "Can't deduce joining keys.\n"
214
+ "Define both data and map key columns in map_join "
215
+ "explicitly: map_join=[['data_column'], ['map_column']]."
216
+ )
217
+
218
+ if len(data_names) > len(map_names):
219
+ raise ValueError(
220
+ "Data key columns count exceeds map key columns count: {} > {}".format(len(data_names), len(map_names))
221
+ )
222
+
223
+ map_names = map_names[:len(data_names)] # use same number of key columns
224
+
225
+ return [data_names, map_names]
226
+
227
+
228
+ def is_geo_data_frame(data: Any) -> bool:
229
+ try:
230
+ from geopandas import GeoDataFrame
231
+ return isinstance(data, GeoDataFrame)
232
+ except ImportError:
233
+ return False
234
+
235
+
236
+ def get_geo_data_frame_meta(geo_data_frame) -> dict:
237
+ return {
238
+ 'geodataframe': {
239
+ 'geometry': geo_data_frame.geometry.name
240
+ }
241
+ }
242
+
243
+
244
+ def geo_data_frame_to_crs(gdf: 'GeoDataFrame', use_crs: Optional[str]):
245
+ if gdf.crs is None:
246
+ return gdf
247
+
248
+ return gdf.to_crs('EPSG:4326' if use_crs is None else use_crs)
249
+
250
+
251
+ def key_int2str(data):
252
+ if is_pandas_data_frame(data):
253
+ if data.columns.inferred_type == 'integer' or data.columns.inferred_type == 'mixed-integer':
254
+ data.columns = data.columns.astype(str)
255
+ return data
256
+
257
+ if isinstance(data, dict):
258
+ return {(str(k) if isinstance(k, int) else k): v for k, v in data.items()}
259
+
260
+ return data
261
+
262
+
263
+ def last_not_none(lst: List) -> Optional[Any]:
264
+ for i in reversed(lst):
265
+ if i is not None:
266
+ return i
267
+ return None
@@ -0,0 +1,244 @@
1
+ # Copyright (c) 2020. JetBrains s.r.o.
2
+ # Use of this source code is governed by the MIT license that can be found in the LICENSE file.
3
+
4
+ from ._global_settings import GEOCODING_PROVIDER_URL, MAPTILES_SOLID_FILL_COLOR, TILES_CHESSBOARD, \
5
+ _DATALORE_TILES_SERVICE
6
+ from ._global_settings import MAPTILES_KIND, MAPTILES_URL, MAPTILES_THEME, MAPTILES_ATTRIBUTION, MAPTILES_MIN_ZOOM, \
7
+ MAPTILES_MAX_ZOOM, TILES_VECTOR_LETS_PLOT, TILES_RASTER_ZXY, TILES_SOLID, _DATALORE_TILES_ATTRIBUTION
8
+ from ._global_settings import has_global_value, get_global_val, _DATALORE_TILES_MIN_ZOOM, _DATALORE_TILES_MAX_ZOOM
9
+
10
+ __all__ = ['maptiles_zxy', 'maptiles_lets_plot', 'maptiles_solid']
11
+
12
+
13
+ def maptiles_lets_plot(url: str = None, theme: str = None) -> dict:
14
+ """
15
+ Make vector tiles config. Can be used individually in `geom_livemap() <https://lets-plot.org/python/pages/api/lets_plot.geom_livemap.html>`__
16
+ or in every livemap via `LetsPlot.set() <https://lets-plot.org/python/pages/api/lets_plot.LetsPlot.html#lets_plot.LetsPlot.set>`__.
17
+
18
+ Parameters
19
+ ----------
20
+ url : str
21
+ Address of the tile server. Can be omitted if URL is already set in global settings.
22
+
23
+ theme : {'color', 'light', 'dark', 'bw'}
24
+ Tiles theme.
25
+
26
+ Returns
27
+ -------
28
+ dict
29
+ Tile provider settings.
30
+
31
+ Notes
32
+ -----
33
+ If you are using Safari and having trouble loading tiles, try disabling the NSURLSession Websocket feature.
34
+ Go to `Develop -> Experimental Features -> NSURLSession Websocket` to turn it off.
35
+
36
+ Also, you could use raster tiles from ``lets_plot.tilesets``, e.g.
37
+ ``ggplot() + geom_livemap(tiles=tilesets.OPEN_TOPO_MAP)``.
38
+
39
+ Examples
40
+ --------
41
+ .. jupyter-execute::
42
+ :linenos:
43
+ :emphasize-lines: 3
44
+
45
+ from lets_plot import *
46
+ LetsPlot.setup_html()
47
+ tiles = maptiles_lets_plot(url='wss://tiles.datalore.jetbrains.com', theme='light')
48
+ ggplot() + geom_livemap(tiles=tiles)
49
+
50
+ """
51
+ assert isinstance(url, (str, type(None))), "'url' argument is not str: {}".format(type(url))
52
+ assert isinstance(theme, (str, type(None))), "'theme' argument is not str: {}".format(type(theme))
53
+
54
+ if url is None:
55
+ global_maptiles_kind = get_global_val(MAPTILES_KIND) if has_global_value(MAPTILES_KIND) else None
56
+ global_maptiles_url = get_global_val(MAPTILES_URL) if has_global_value(MAPTILES_URL) else None
57
+
58
+ # try to read url from global settings
59
+ if global_maptiles_kind == TILES_VECTOR_LETS_PLOT:
60
+ if global_maptiles_url is None:
61
+ # global URL is somehow broken - use default URL
62
+ url = _DATALORE_TILES_SERVICE
63
+ else:
64
+ url = global_maptiles_url
65
+ else:
66
+ # User input:
67
+ # LetsPlot.set(maptiles_zxy(...))
68
+ # LetsPlot.set(maptiles_lets_plot(...))
69
+ # In this case global_maptiles_url will contain not-applicable raster tile URL.
70
+ # Use hardcoded lets_plot tiles URL.
71
+ url = _DATALORE_TILES_SERVICE
72
+
73
+ if url is None:
74
+ raise ValueError('lets_plot tiles service URL is not defined')
75
+
76
+ return {
77
+ MAPTILES_KIND: TILES_VECTOR_LETS_PLOT,
78
+ MAPTILES_URL: url,
79
+ MAPTILES_THEME: theme,
80
+ MAPTILES_ATTRIBUTION: _DATALORE_TILES_ATTRIBUTION,
81
+ MAPTILES_MIN_ZOOM: _DATALORE_TILES_MIN_ZOOM,
82
+ MAPTILES_MAX_ZOOM: _DATALORE_TILES_MAX_ZOOM,
83
+ }
84
+
85
+
86
+ def maptiles_zxy(url: str, attribution: str = None, min_zoom: int = None, max_zoom: int = None, subdomains: str = None,
87
+ **other_args) -> dict:
88
+ """
89
+ Make raster tiles config. Can be used individually in `geom_livemap() <https://lets-plot.org/python/pages/api/lets_plot.geom_livemap.html>`__
90
+ or in every livemap via `LetsPlot.set() <https://lets-plot.org/python/pages/api/lets_plot.LetsPlot.html#lets_plot.LetsPlot.set>`__.
91
+
92
+ Parameters
93
+ ----------
94
+ url : str
95
+ Template for a standard raster ZXY tile provider with {z}, {x}, {y} and {s} placeholders,
96
+ e.g. ``"https://{s}.tile.com/{z}/{x}/{y}.png"``. Where {z} means zoom, {x} and {y} means
97
+ tile coordinate, {s} means subdomains.
98
+ attribution : str
99
+ An attribution or a copyright notice to display on the map as required by the tile license.
100
+ Supports HTML links: ``'<a href="http://www.example.com">Example</a>'``.
101
+ min_zoom : int
102
+ Minimal zoom limit, an integer from 1 to 15. Should be less than or equal to ``max_zoom``.
103
+ max_zoom : int
104
+ Maximal zoom limit, an integer from 1 to 15. Should be greater than or equal to ``min_zoom``.
105
+ subdomains : str
106
+ Each character of this list is interpreted as standalone tile servers, so an interactive map
107
+ can request tiles from any of these servers independently for better load balance. If url
108
+ contains {s} placeholder and subdomains parameter is not set default string 'abc' will be used.
109
+ other_args
110
+ Any key-value pairs that can be substituted into the URL template, e.g.
111
+ ``maptiles_zxy(url='http://maps.example.com/{z}/{x}/{y}.png?access_key={key}', key='MY_ACCESS_KEY')``.
112
+
113
+ Returns
114
+ -------
115
+ dict
116
+ Tile provider settings.
117
+
118
+ Examples
119
+ --------
120
+ .. jupyter-execute::
121
+ :linenos:
122
+ :emphasize-lines: 3-7
123
+
124
+ from lets_plot import *
125
+ LetsPlot.setup_html()
126
+ tiles = maptiles_zxy(
127
+ url="https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/VIIRS_CityLights_2012/default/GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpg",
128
+ attribution='<a href="https://earthdata.nasa.gov/eosdis/science-system-description/eosdis-components/gibs">© NASA Global Imagery Browse Services (GIBS)</a>',
129
+ max_zoom=8
130
+ )
131
+ ggplot() + geom_livemap(tiles=tiles)
132
+
133
+ """
134
+ assert isinstance(url, str), "'url' argument is not str: {}".format(type(url))
135
+ assert isinstance(attribution, (str, type(None))), "'attribution' argument is not str: {}".format(type(url))
136
+ if subdomains is not None and "{s}" not in url:
137
+ raise ValueError("Subdomains are set but {s} placeholder is not found in url: " + subdomains)
138
+
139
+ for k, v in other_args.items():
140
+ assert k not in ["x", "y", "z", "s"], "other_args can't contain keys x, y, z and s"
141
+ url = url.replace("{" + k + "}", v)
142
+
143
+ if subdomains is not None and "{s}" in url:
144
+ url = url.replace("{s}", '[' + subdomains + ']')
145
+ elif subdomains is None and "{s}" in url:
146
+ url = url.replace("{s}", '[abc]')
147
+
148
+ return {
149
+ MAPTILES_KIND: TILES_RASTER_ZXY,
150
+ MAPTILES_URL: url,
151
+ MAPTILES_ATTRIBUTION: _build_attribution(attribution),
152
+ MAPTILES_MIN_ZOOM: min_zoom,
153
+ MAPTILES_MAX_ZOOM: max_zoom
154
+ }
155
+
156
+
157
+ def maptiles_solid(color: str):
158
+ """
159
+ Make solid color tiles config. Can be used individually in `geom_livemap() <https://lets-plot.org/python/pages/api/lets_plot.geom_livemap.html>`__
160
+ or in every livemap via `LetsPlot.set() <https://lets-plot.org/python/pages/api/lets_plot.LetsPlot.html#lets_plot.LetsPlot.set>`__.
161
+
162
+ Parameters
163
+ ----------
164
+ color : str
165
+ Color in HEX format.
166
+
167
+ Returns
168
+ -------
169
+ dict
170
+ Tile provider settings.
171
+
172
+ Examples
173
+ --------
174
+ .. jupyter-execute::
175
+ :linenos:
176
+ :emphasize-lines: 5
177
+
178
+ from lets_plot import *
179
+ from lets_plot.geo_data import *
180
+ LetsPlot.setup_html()
181
+ nyc = geocode_cities('New York').get_boundaries()
182
+ tiles = maptiles_solid(color='#d3d3d3')
183
+ ggplot() + geom_livemap(tiles=tiles) + geom_map(data=nyc)
184
+
185
+ """
186
+ return {
187
+ MAPTILES_KIND: TILES_SOLID,
188
+ MAPTILES_SOLID_FILL_COLOR: color
189
+ }
190
+
191
+
192
+ def maptiles_chessboard():
193
+ """
194
+ Make solid color tiles with chessboard pattern. Can be used individually in `geom_livemap() <https://lets-plot.org/python/pages/api/lets_plot.geom_livemap.html>`__
195
+ or in every livemap via `LetsPlot.set() <https://lets-plot.org/python/pages/api/lets_plot.LetsPlot.html#lets_plot.LetsPlot.set>`__.
196
+
197
+ Returns
198
+ -------
199
+ dict
200
+ Tile provider settings.
201
+
202
+ Examples
203
+ --------
204
+ .. jupyter-execute::
205
+ :linenos:
206
+ :emphasize-lines: 3
207
+
208
+ from lets_plot.settings_utils import maptiles_chessboard
209
+ LetsPlot.setup_html()
210
+ ggplot() + geom_livemap(tiles=maptiles_chessboard())
211
+
212
+ """
213
+ return {
214
+ MAPTILES_KIND: TILES_CHESSBOARD
215
+ }
216
+
217
+
218
+ def geocoding_service(url: str):
219
+ """
220
+ Make geocoding service config.
221
+ Can be applied via LetsPlot.set(...)
222
+
223
+ Parameters
224
+ ----------
225
+ url : string
226
+ Address of the geocoding server
227
+
228
+ Returns
229
+ -------
230
+ Geocoding service settings
231
+ """
232
+ assert isinstance(url, str), "'url' argument is not str: {}".format(type(url))
233
+
234
+ return {
235
+ GEOCODING_PROVIDER_URL: url
236
+ }
237
+
238
+
239
+ def _build_attribution(other_attributions):
240
+ map_attribution = '<a href="https://lets-plot.org">\u00a9 Lets-Plot</a>'
241
+ if other_attributions is None:
242
+ return map_attribution
243
+ else:
244
+ return map_attribution + ', ' + other_attributions