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.
- lets_plot/__init__.py +382 -0
- lets_plot/_global_settings.py +192 -0
- lets_plot/_kbridge.py +197 -0
- lets_plot/_type_utils.py +133 -0
- lets_plot/_version.py +6 -0
- lets_plot/bistro/__init__.py +16 -0
- lets_plot/bistro/_plot2d_common.py +106 -0
- lets_plot/bistro/corr.py +448 -0
- lets_plot/bistro/im.py +196 -0
- lets_plot/bistro/joint.py +192 -0
- lets_plot/bistro/qq.py +207 -0
- lets_plot/bistro/residual.py +341 -0
- lets_plot/bistro/waterfall.py +332 -0
- lets_plot/export/__init__.py +6 -0
- lets_plot/export/ggsave_.py +172 -0
- lets_plot/frontend_context/__init__.py +8 -0
- lets_plot/frontend_context/_configuration.py +140 -0
- lets_plot/frontend_context/_dynamic_configure_html.py +115 -0
- lets_plot/frontend_context/_frontend_ctx.py +16 -0
- lets_plot/frontend_context/_html_contexts.py +223 -0
- lets_plot/frontend_context/_intellij_python_json_ctx.py +38 -0
- lets_plot/frontend_context/_isolated_webview_panel_ctx.py +81 -0
- lets_plot/frontend_context/_json_contexts.py +39 -0
- lets_plot/frontend_context/_jupyter_notebook_ctx.py +82 -0
- lets_plot/frontend_context/_mime_types.py +7 -0
- lets_plot/frontend_context/_static_html_page_ctx.py +76 -0
- lets_plot/frontend_context/_static_svg_ctx.py +26 -0
- lets_plot/frontend_context/_webbr_html_page_ctx.py +29 -0
- lets_plot/frontend_context/sandbox.py +5 -0
- lets_plot/geo_data/__init__.py +19 -0
- lets_plot/geo_data/core.py +335 -0
- lets_plot/geo_data/geocoder.py +988 -0
- lets_plot/geo_data/geocodes.py +512 -0
- lets_plot/geo_data/gis/__init__.py +0 -0
- lets_plot/geo_data/gis/fluent_dict.py +201 -0
- lets_plot/geo_data/gis/geocoding_service.py +42 -0
- lets_plot/geo_data/gis/geometry.py +91 -0
- lets_plot/geo_data/gis/json_request.py +232 -0
- lets_plot/geo_data/gis/json_response.py +308 -0
- lets_plot/geo_data/gis/request.py +492 -0
- lets_plot/geo_data/gis/response.py +247 -0
- lets_plot/geo_data/livemap_helper.py +65 -0
- lets_plot/geo_data/to_geo_data_frame.py +141 -0
- lets_plot/geo_data/type_assertion.py +34 -0
- lets_plot/geo_data_internals/__init__.py +4 -0
- lets_plot/geo_data_internals/constants.py +13 -0
- lets_plot/geo_data_internals/utils.py +33 -0
- lets_plot/mapping.py +115 -0
- lets_plot/package_data/lets-plot.min.js +3 -0
- lets_plot/plot/__init__.py +64 -0
- lets_plot/plot/_global_theme.py +14 -0
- lets_plot/plot/annotation.py +290 -0
- lets_plot/plot/coord.py +242 -0
- lets_plot/plot/core.py +1071 -0
- lets_plot/plot/expand_limits_.py +78 -0
- lets_plot/plot/facet.py +210 -0
- lets_plot/plot/font_features.py +71 -0
- lets_plot/plot/geom.py +9146 -0
- lets_plot/plot/geom_extras.py +53 -0
- lets_plot/plot/geom_function_.py +219 -0
- lets_plot/plot/geom_imshow_.py +393 -0
- lets_plot/plot/geom_livemap_.py +343 -0
- lets_plot/plot/ggbunch_.py +96 -0
- lets_plot/plot/gggrid_.py +139 -0
- lets_plot/plot/ggtb_.py +81 -0
- lets_plot/plot/guide.py +231 -0
- lets_plot/plot/label.py +187 -0
- lets_plot/plot/marginal_layer.py +181 -0
- lets_plot/plot/plot.py +245 -0
- lets_plot/plot/pos.py +344 -0
- lets_plot/plot/sampling.py +338 -0
- lets_plot/plot/sandbox_.py +26 -0
- lets_plot/plot/scale.py +3580 -0
- lets_plot/plot/scale_colormap_mpl.py +300 -0
- lets_plot/plot/scale_convenience.py +155 -0
- lets_plot/plot/scale_identity_.py +653 -0
- lets_plot/plot/scale_position.py +1342 -0
- lets_plot/plot/series_meta.py +209 -0
- lets_plot/plot/stat.py +585 -0
- lets_plot/plot/subplots.py +331 -0
- lets_plot/plot/subplots_util.py +24 -0
- lets_plot/plot/theme_.py +790 -0
- lets_plot/plot/theme_set.py +418 -0
- lets_plot/plot/tooltip.py +486 -0
- lets_plot/plot/util.py +267 -0
- lets_plot/settings_utils.py +244 -0
- lets_plot/tilesets.py +429 -0
- lets_plot-4.8.1rc1.dist-info/METADATA +221 -0
- lets_plot-4.8.1rc1.dist-info/RECORD +97 -0
- lets_plot-4.8.1rc1.dist-info/WHEEL +6 -0
- lets_plot-4.8.1rc1.dist-info/licenses/LICENSE +21 -0
- lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.FreeType +166 -0
- lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.ImageMagick +106 -0
- lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.expat +21 -0
- lets_plot-4.8.1rc1.dist-info/licenses/licenses/LICENSE.fontconfig +200 -0
- lets_plot-4.8.1rc1.dist-info/top_level.txt +2 -0
- 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
|