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
@@ -0,0 +1,512 @@
1
+ import enum
2
+ from abc import abstractmethod
3
+ from collections.abc import Iterable
4
+ from typing import List, Optional, Union, Dict
5
+
6
+ from pandas import DataFrame, Series
7
+
8
+ from lets_plot.geo_data_internals.constants import DF_COLUMN_HIGHLIGHTS, DF_COLUMN_COUNTRY, DF_COLUMN_STATE, \
9
+ DF_COLUMN_COUNTY, DF_COLUMN_CITY, DF_COLUMN_ID, DF_COLUMN_FOUND_NAME, DF_COLUMN_POSITION, DF_COLUMN_LIMIT, \
10
+ DF_COLUMN_CENTROID
11
+ from .gis.geocoding_service import GeocodingService
12
+ from .gis.request import PayloadKind, RequestBuilder, RequestKind, MapRegion, RegionQuery
13
+ from .gis.response import Answer, GeocodedFeature, Namesake, AmbiguousFeature, LevelKind
14
+ from .gis.response import SuccessResponse, Response, AmbiguousResponse, ErrorResponse
15
+ from .type_assertion import assert_type, assert_list_type
16
+
17
+ NO_OBJECTS_FOUND_EXCEPTION_TEXT = 'No objects were found.'
18
+ MULTIPLE_OBJECTS_FOUND_EXCEPTION_TEXT = "Multiple objects were found. Use all_result=True to see them."
19
+
20
+
21
+ class Resolution(enum.Enum):
22
+ city_high = 15
23
+ city_medium = 14
24
+ city_low = 13
25
+ county_high = 12
26
+ county_medium = 11
27
+ county_low = 10
28
+ state_high = 9
29
+ state_medium = 8
30
+ state_low = 7
31
+ country_high = 6
32
+ country_medium = 5
33
+ country_low = 4
34
+ world_high = 3
35
+ world_medium = 2
36
+ world_low = 1
37
+
38
+
39
+ class PlacesDataFrameBuilder:
40
+ def __init__(self, level_kind: LevelKind):
41
+ self.level_kind: LevelKind = level_kind
42
+ self._request: List[str] = []
43
+ self._found_name: List[str] = []
44
+ self._county: List[Optional[str]] = []
45
+ self._state: List[Optional[str]] = []
46
+ self._country: List[Optional[str]] = []
47
+
48
+ def append_row(self, query: RegionQuery, feature: GeocodedFeature):
49
+ self._request.append(_select_request_string(query.request, feature.name))
50
+ self._found_name.append(feature.name)
51
+
52
+ if query is None:
53
+ self._county.append(MapRegion.name_or_none(None))
54
+ self._state.append(MapRegion.name_or_none(None))
55
+ self._country.append(MapRegion.name_or_none(None))
56
+ else:
57
+ self._county.append(MapRegion.name_or_none(query.county))
58
+ self._state.append(MapRegion.name_or_none(query.state))
59
+ self._country.append(MapRegion.name_or_none(query.country))
60
+
61
+ def build_dict(self):
62
+ def contains_values(column):
63
+ return any(v is not None for v in column)
64
+
65
+ data = {}
66
+
67
+ request_column = _level_to_column_name(self.level_kind)
68
+
69
+ data[request_column] = self._request
70
+ data[DF_COLUMN_FOUND_NAME] = self._found_name
71
+
72
+ if contains_values(self._county):
73
+ data[DF_COLUMN_COUNTY] = self._county
74
+
75
+ if contains_values(self._state):
76
+ data[DF_COLUMN_STATE] = self._state
77
+
78
+ if contains_values(self._country):
79
+ data[DF_COLUMN_COUNTRY] = self._country
80
+
81
+ return data
82
+
83
+ @abstractmethod
84
+ def to_data_frame(self, answers: List[Answer], queries: List[RegionQuery], level_kind: LevelKind) -> DataFrame:
85
+ raise ValueError('Not implemented')
86
+
87
+
88
+ class Geocodes:
89
+ def __init__(self, level_kind: LevelKind, answers: List[Answer], queries: List[RegionQuery], highlights: bool = False):
90
+ assert_list_type(answers, Answer)
91
+ assert_list_type(queries, RegionQuery)
92
+
93
+ if len(answers) == 0:
94
+ assert len(queries) == 1 and queries[0].request is None # select all
95
+ else:
96
+ assert len(queries) == len(answers) # regular request - should have same size
97
+
98
+ try:
99
+ import geopandas
100
+ except ImportError:
101
+ raise ValueError('Module \'geopandas\'is required for geocoding') from None
102
+
103
+ self._level_kind: LevelKind = level_kind
104
+ self._answers: List[Answer] = answers
105
+
106
+ features = []
107
+ for answer in answers:
108
+ features.extend(answer.features)
109
+
110
+ self._geocoded_features: List[GeocodedFeature] = features
111
+ self._highlights: bool = highlights
112
+ self._queries: List[RegionQuery] = queries
113
+
114
+ def __repr__(self):
115
+ return self.to_data_frame().to_string()
116
+
117
+ def __len__(self):
118
+ return len(self._geocoded_features)
119
+
120
+ def to_map_regions(self) -> List[MapRegion]:
121
+ regions: List[MapRegion] = []
122
+ for answer, query in _zip_answers(self._answers, self._queries):
123
+ for feature in answer.features:
124
+ regions.append(
125
+ MapRegion.place(feature.id, _select_request_string(query.request, feature.name), self._level_kind))
126
+ return regions
127
+
128
+ def as_list(self) -> List['Geocodes']:
129
+ if len(self._queries) == 0:
130
+ return [Geocodes(self._level_kind, [answer], [RegionQuery(request=None)], self._highlights) for answer in
131
+ self._answers]
132
+
133
+ assert len(self._queries) == len(self._answers)
134
+ return [Geocodes(self._level_kind, [answer], [query], self._highlights) for query, answer in
135
+ zip(self._queries, self._answers)]
136
+
137
+ def unique_ids(self) -> List[str]:
138
+ seen = set()
139
+ seen_add = seen.add
140
+ return [feature.id for feature in self._geocoded_features if not (feature.id in seen or seen_add(feature.id))]
141
+
142
+ def boundaries(self, resolution: Optional[Union[int, str, Resolution]] = None, inc_res: int = 0):
143
+ """
144
+ Return boundaries for given regions in form of GeoDataFrame.
145
+
146
+ Parameters
147
+ ----------
148
+ resolution: [str | int | None]
149
+ Boundaries resolution.
150
+
151
+ int: [1-15]
152
+ 15 - maximum quality, 1 - maximum performance:
153
+ - 1-3 for world scale view
154
+ - 4-6 for country scale view
155
+ - 7-9 for state scale view
156
+ - 10-12 for county scale view
157
+ - 13-15 for city scale view
158
+
159
+ str: ['world', 'country', 'state', 'county', 'city']
160
+ 'city' - maximum quality, 'world' - maximum performance.
161
+ Corresponding numeric resolutions:
162
+ - 'world' - 2
163
+ - 'country' - 5
164
+ - 'state' - 8
165
+ - 'county' - 11
166
+ - 'city' - 14
167
+
168
+ Kind of area expected to be displayed. Resolution depends on a number of objects - single state is a 'state'
169
+ scale view, while 50 states is a 'country' scale view.
170
+
171
+ It is allowed to use any kind of resolution for any regions, i.e. 'city' for state to see more detailed
172
+ boundary (when need to show zoomed part), or 'world' (when used for small preview).
173
+
174
+ None:
175
+ Autodetection. Uses level_kind that was used for geocoding this regions object and number of objects in it.
176
+ Prefers performance over qulity. It's expected to get pixelated geometries with autodetection.
177
+ Use explicit resolution for better quality.
178
+
179
+ Resolution for countries:
180
+ If n < 3 => 3
181
+ else => 1
182
+
183
+ Resolution for states:
184
+ If n < 3 => 7
185
+ If n < 10 => 4
186
+ else => 2
187
+
188
+ Resolution for counties:
189
+ If n < 5 => 10
190
+ If n < 20 => 8
191
+ else => 3
192
+
193
+ Resolution for cities:
194
+ If n < 5 => 13
195
+ If n < 50 => 4
196
+ else => 3
197
+
198
+ inc_res: int
199
+ Increase auto-detected resolution.
200
+
201
+ Examples
202
+ --------
203
+ .. jupyter-execute::
204
+
205
+ >>> from lets_plot.geo_data import *
206
+ >>> rb = regions_country(['germany', 'russia']).boundaries()
207
+ >>> rb
208
+ """
209
+ from lets_plot.geo_data.to_geo_data_frame import BoundariesGeoDataFrame
210
+
211
+ if resolution is None:
212
+ autodetected_resolution = _autodetect_resolution(self._level_kind, len(self._geocoded_features))
213
+ int_resolution = min(Resolution.city_high.value, autodetected_resolution + inc_res)
214
+ elif isinstance(resolution, int):
215
+ int_resolution = resolution
216
+ elif isinstance(resolution, Resolution):
217
+ int_resolution = resolution.value
218
+ elif isinstance(resolution, str):
219
+ int_resolution = _parse_resolution(resolution).value
220
+ else:
221
+ raise ValueError('Invalid resolution: ' + type(resolution).__name__)
222
+
223
+ if int_resolution < Resolution.world_low.value or int_resolution > Resolution.city_high.value:
224
+ raise ValueError(
225
+ "Resolution is out of range. Expected to be from ({}) to ({}), but was ({})."
226
+ .format(Resolution.world_low.value, Resolution.city_high.value, int_resolution)
227
+ )
228
+
229
+ return self._execute(
230
+ self._request_builder(PayloadKind.boundaries)
231
+ .set_resolution(int_resolution),
232
+ BoundariesGeoDataFrame()
233
+ )
234
+
235
+ def limits(self) -> 'GeoDataFrame':
236
+ """
237
+ Return bboxes (Polygon geometry) for given regions in form of GeoDataFrame. For regions intersecting
238
+ anti-meridian bbox will be divided into two and stored as two rows.
239
+
240
+ Examples
241
+ ---------
242
+ .. jupyter-execute::
243
+
244
+ >>> from lets_plot.geo_data import *
245
+ >>> rl = regions_country(['germany', 'russia']).limits()
246
+ >>> rl
247
+ """
248
+ from lets_plot.geo_data.to_geo_data_frame import LimitsGeoDataFrame
249
+ return self._execute(
250
+ self._request_builder(PayloadKind.limits),
251
+ LimitsGeoDataFrame()
252
+ )
253
+
254
+ def centroids(self):
255
+ """
256
+ Return centroids (Point geometry) for given regions in form of GeoDataFrame.
257
+
258
+ Examples
259
+ ---------
260
+ .. jupyter-execute::
261
+
262
+ >>> from lets_plot.geo_data import *
263
+ >>> rc = regions_country(['germany', 'russia']).centroids()
264
+ >>> rc
265
+ """
266
+ from lets_plot.geo_data.to_geo_data_frame import CentroidsGeoDataFrame
267
+ return self._execute(
268
+ self._request_builder(PayloadKind.centroids),
269
+ CentroidsGeoDataFrame()
270
+ )
271
+
272
+ def to_data_frame(self) -> DataFrame:
273
+ places = PlacesDataFrameBuilder(self._level_kind)
274
+
275
+ # for us-48 queries doesnt' count
276
+ for query, answer in _zip_answers(self._queries, self._answers):
277
+ for feature in answer.features:
278
+ places.append_row(query, feature)
279
+
280
+ def geo_rect_to_list(geo_rect: 'GeoRect') -> List:
281
+ return [geo_rect.start_lon, geo_rect.min_lat, geo_rect.end_lon, geo_rect.max_lat]
282
+
283
+ data = {
284
+ DF_COLUMN_ID: [feature.id for feature in self._geocoded_features],
285
+ **places.build_dict(),
286
+ DF_COLUMN_CENTROID: [[feature.centroid.lon, feature.centroid.lat] for feature in self._geocoded_features],
287
+ DF_COLUMN_POSITION: [geo_rect_to_list(feature.position) for feature in self._geocoded_features],
288
+ DF_COLUMN_LIMIT: [geo_rect_to_list(feature.limit) for feature in self._geocoded_features]
289
+ }
290
+
291
+ if self._highlights:
292
+ data[DF_COLUMN_HIGHLIGHTS] = [feature.highlights for feature in self._geocoded_features]
293
+
294
+ return DataFrame(data)
295
+
296
+ def _execute(self, request_builder: RequestBuilder, df_converter):
297
+ response = GeocodingService().do_request(request_builder.build())
298
+
299
+ if not isinstance(response, SuccessResponse):
300
+ _raise_exception(response)
301
+
302
+ features = []
303
+
304
+ for a in response.answers:
305
+ features.extend(a.features)
306
+
307
+ self._join_payload(features)
308
+
309
+ return df_converter.to_data_frame(self._answers, self._queries, self._level_kind)
310
+
311
+ def _request_builder(self, payload_kind: PayloadKind) -> RequestBuilder:
312
+ assert_type(payload_kind, PayloadKind)
313
+
314
+ return RequestBuilder() \
315
+ .set_request_kind(RequestKind.explicit) \
316
+ .set_ids(self.unique_ids()) \
317
+ .set_requested_payload([payload_kind])
318
+
319
+ def _join_payload(self, payloads: List[GeocodedFeature]):
320
+ for payload in payloads:
321
+ for feature in self._get_features(payload.id):
322
+
323
+ if payload.limit is not None:
324
+ feature.limit = payload.limit
325
+
326
+ if payload.boundary is not None:
327
+ feature.boundary = payload.boundary
328
+
329
+ if payload.centroid is not None:
330
+ feature.centroid = payload.centroid
331
+
332
+ if payload.position is not None:
333
+ feature.position = payload.position
334
+
335
+ def _get_features(self, feature_id: str) -> List[GeocodedFeature]:
336
+ return [feature for feature in self._geocoded_features if feature.id == feature_id]
337
+
338
+
339
+ request_types = Optional[Union[str, List[str], Series]]
340
+
341
+
342
+ def _raise_exception(response: Response):
343
+ msg = _format_error_message(response)
344
+ raise ValueError(msg)
345
+
346
+
347
+ def _format_error_message(response: Response) -> str:
348
+ if isinstance(response, AmbiguousResponse):
349
+ not_found_names: Dict = {}
350
+ multiple_objects: List[AmbiguousFeature] = []
351
+
352
+ for ambiguous_feature in response.features:
353
+ if ambiguous_feature.total_namesake_count == 0:
354
+ not_found_names[ambiguous_feature.query] = None
355
+
356
+ if ambiguous_feature.total_namesake_count > 0:
357
+ multiple_objects.append(ambiguous_feature)
358
+
359
+ if len(not_found_names) > 0:
360
+ display_limit = 10
361
+ msg_text = 'No objects were found for '
362
+ if len(not_found_names) > display_limit:
363
+ msg_text += ', '.join(list(not_found_names.keys())[:display_limit])
364
+ msg_text += ' and ({}) more'.format(len(not_found_names) - display_limit)
365
+ else:
366
+ msg_text += ', '.join(list(not_found_names.keys()))
367
+
368
+ return msg_text + '.\n'
369
+
370
+ if len(multiple_objects) > 0:
371
+ message = ''
372
+ for multiple_object in multiple_objects:
373
+ message += _create_multiple_error_message(
374
+ multiple_object.query,
375
+ multiple_object.namesake_examples,
376
+ multiple_object.total_namesake_count
377
+ ) + '\n'
378
+
379
+ return message
380
+
381
+ return 'Invalid bad feature'
382
+
383
+ if isinstance(response, ErrorResponse):
384
+ return response.message
385
+
386
+ return 'Unsupported error response status: ' + str(response.__class__)
387
+
388
+
389
+ def _create_multiple_error_message(request: str, namesakes: List[Namesake], total_namesake_count: int):
390
+ lines = []
391
+ for namesake in namesakes:
392
+ line = '- ' + namesake.name
393
+ if len(namesake.parents) > 0:
394
+ line += ' (' + ', '.join([o.name for o in namesake.parents]) + ')'
395
+ lines.append(line)
396
+
397
+ text = 'Multiple objects (' + str(total_namesake_count) + ') were found for ' + request
398
+ if not lines:
399
+ text += '.'
400
+ else:
401
+ text += ':\n' + '\n'.join(lines)
402
+ return text
403
+
404
+
405
+ def _to_level_kind(level_kind: Optional[Union[str, LevelKind]]) -> Optional[LevelKind]:
406
+ if level_kind is None:
407
+ return None
408
+
409
+ if isinstance(level_kind, LevelKind):
410
+ return level_kind
411
+
412
+ if isinstance(level_kind, str):
413
+ return LevelKind(level_kind)
414
+
415
+ raise ValueError('Invalid level kind')
416
+
417
+
418
+ def _parse_resolution(resolution: str) -> Resolution:
419
+ if isinstance(resolution, str):
420
+ if resolution == 'city':
421
+ return Resolution.city_medium
422
+
423
+ if resolution == 'county':
424
+ return Resolution.county_medium
425
+
426
+ if resolution == 'state':
427
+ return Resolution.state_medium
428
+
429
+ if resolution == 'country':
430
+ return Resolution.country_medium
431
+
432
+ if resolution == 'world':
433
+ return Resolution.world_medium
434
+
435
+ return Resolution[resolution]
436
+
437
+ raise ValueError('Invalid resolution type: ' + type(resolution).__name__)
438
+
439
+
440
+ def _ensure_is_list(obj) -> Optional[List[str]]:
441
+ if obj is None:
442
+ return None
443
+
444
+ if isinstance(obj, Iterable) and not isinstance(obj, str):
445
+ return [v for v in obj]
446
+
447
+ return [obj]
448
+
449
+
450
+ def _autodetect_resolution(level: LevelKind, count: int) -> int:
451
+ if level == LevelKind.country:
452
+ if count < 3:
453
+ return Resolution.world_high.value
454
+ else:
455
+ return Resolution.world_low.value
456
+
457
+ if level == LevelKind.state:
458
+ if count < 3:
459
+ return Resolution.state_low.value
460
+ if count < 10:
461
+ return Resolution.country_low.value
462
+ else:
463
+ return Resolution.world_medium.value
464
+
465
+ if level == LevelKind.county:
466
+ if count < 5:
467
+ return Resolution.county_low.value
468
+ elif count < 20:
469
+ return Resolution.state_medium.value
470
+ else:
471
+ return Resolution.world_high.value
472
+
473
+ if level == LevelKind.city:
474
+ if count < 5:
475
+ return Resolution.city_low.value
476
+ elif count < 50:
477
+ return Resolution.country_low.value
478
+ else:
479
+ return Resolution.world_high.value
480
+
481
+
482
+ def _select_request_string(request: Optional[str], name: str) -> str:
483
+ if request is None:
484
+ return name
485
+
486
+ if len(request) == 0:
487
+ return name
488
+
489
+ if 'us-48' == request.lower():
490
+ return name
491
+
492
+ return request
493
+
494
+
495
+ def _level_to_column_name(level_kind: LevelKind):
496
+ if level_kind == LevelKind.city:
497
+ return DF_COLUMN_CITY
498
+ elif level_kind == LevelKind.county:
499
+ return DF_COLUMN_COUNTY
500
+ elif level_kind == LevelKind.state:
501
+ return DF_COLUMN_STATE
502
+ elif level_kind == LevelKind.country:
503
+ return DF_COLUMN_COUNTRY
504
+ else:
505
+ raise ValueError('Unknown level kind: {}'.format(level_kind))
506
+
507
+
508
+ def _zip_answers(queries: List, answers: List):
509
+ if len(queries) > 0:
510
+ return zip(queries, answers)
511
+ else:
512
+ return zip([None] * len(answers), answers)
File without changes