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,308 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from functools import partial
|
|
4
|
+
from typing import Dict, List, Optional, Union
|
|
5
|
+
|
|
6
|
+
from .fluent_dict import FluentDict
|
|
7
|
+
from .geometry import Ring
|
|
8
|
+
from .response import Multipolygon, GeoPoint, GeoRect, Boundary, Polygon
|
|
9
|
+
from .response import Response, ResponseBuilder, SuccessResponse, AmbiguousResponse
|
|
10
|
+
from .response import Status, LevelKind, Answer, GeocodedFeature, AmbiguousFeature, Namesake, NamesakeParent, \
|
|
11
|
+
FeatureBuilder
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ResponseField(Enum):
|
|
15
|
+
status = 'status'
|
|
16
|
+
message = 'message'
|
|
17
|
+
data = 'data'
|
|
18
|
+
answers = 'answers'
|
|
19
|
+
features = 'features'
|
|
20
|
+
geocoded_data = 'good_features'
|
|
21
|
+
incorrect_data = 'bad_features'
|
|
22
|
+
level = 'level'
|
|
23
|
+
position = 'position'
|
|
24
|
+
query = 'query'
|
|
25
|
+
name = 'name'
|
|
26
|
+
highlights = 'highlights'
|
|
27
|
+
limit = 'limit'
|
|
28
|
+
centroid = 'centroid'
|
|
29
|
+
boundary = 'boundary'
|
|
30
|
+
boundary_type = 'type'
|
|
31
|
+
geo_object_id = 'id'
|
|
32
|
+
boundary_lon = 'lon'
|
|
33
|
+
boundary_lat = 'lat'
|
|
34
|
+
boundary_coordinates = 'coordinates'
|
|
35
|
+
centroid_lon = 'lon'
|
|
36
|
+
centroid_lat = 'lat'
|
|
37
|
+
min_lon = 'min_lon'
|
|
38
|
+
min_lat = 'min_lat'
|
|
39
|
+
max_lon = 'max_lon'
|
|
40
|
+
max_lat = 'max_lat'
|
|
41
|
+
total_namesake_count = 'total_namesake_count'
|
|
42
|
+
namesake_examples = 'namesake_examples'
|
|
43
|
+
namesake_name = 'name'
|
|
44
|
+
namesake_parents = 'parents'
|
|
45
|
+
namesake_parent_name = 'name'
|
|
46
|
+
namesake_parent_level = 'level'
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class GeometryKind(Enum):
|
|
50
|
+
point = 'Point'
|
|
51
|
+
polygon = 'Polygon'
|
|
52
|
+
multipolygon = 'MultiPolygon'
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ResponseParser:
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def parse(response_json: Dict) -> Response:
|
|
59
|
+
response = ResponseBuilder()
|
|
60
|
+
response_dict = FluentDict(response_json) \
|
|
61
|
+
.visit_enum(ResponseField.status, Status, response.set_status) \
|
|
62
|
+
.visit_str(ResponseField.message, response.set_message)
|
|
63
|
+
|
|
64
|
+
if response.status == Status.error:
|
|
65
|
+
return response.build()
|
|
66
|
+
|
|
67
|
+
data_dict = FluentDict(response_dict.get(ResponseField.data)) \
|
|
68
|
+
.visit_enum_existing(ResponseField.level, LevelKind, response.set_level)
|
|
69
|
+
|
|
70
|
+
if response.status == Status.success:
|
|
71
|
+
data_dict.visit(ResponseField.answers, partial(ResponseParser._parse_answers, response=response))
|
|
72
|
+
elif response.status == Status.ambiguous:
|
|
73
|
+
data_dict.visit(ResponseField.features, partial(ResponseParser._parse_ambiguous_features, response=response))
|
|
74
|
+
else:
|
|
75
|
+
raise ValueError('Unknown response kind')
|
|
76
|
+
|
|
77
|
+
return response.build()
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
def _parse_answers(answers_json: List[Dict], response: ResponseBuilder):
|
|
81
|
+
answers: List[Answer] = []
|
|
82
|
+
for answer_json in answers_json:
|
|
83
|
+
features_json = answer_json.get(ResponseField.features.value, [])
|
|
84
|
+
geocoded_features: List[GeocodedFeature] = []
|
|
85
|
+
for feature_json in features_json:
|
|
86
|
+
feature = FeatureBuilder()
|
|
87
|
+
|
|
88
|
+
FluentDict(feature_json) \
|
|
89
|
+
.visit_str(ResponseField.geo_object_id, feature.set_id) \
|
|
90
|
+
.visit_str(ResponseField.name, feature.set_name) \
|
|
91
|
+
.visit_str_list_optional(ResponseField.highlights, feature.set_highlights) \
|
|
92
|
+
.visit_str_existing(ResponseField.boundary, lambda json: feature.set_boundary(GeoJson().parse_geometry(json))) \
|
|
93
|
+
.visit_object_optional(ResponseField.centroid, lambda json: feature.set_centroid(ResponseParser._parse_point(json))) \
|
|
94
|
+
.visit_object_optional(ResponseField.limit, lambda json: feature.set_limit(ResponseParser._parse_rect(json))) \
|
|
95
|
+
.visit_object_optional(ResponseField.position, lambda json: feature.set_position(ResponseParser._parse_rect(json)))
|
|
96
|
+
|
|
97
|
+
geocoded_features.append(feature.build_geocoded())
|
|
98
|
+
answers.append(Answer(geocoded_features))
|
|
99
|
+
|
|
100
|
+
response.set_answers(answers)
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def _parse_ambiguous_features(features_json: List[Dict], response: ResponseBuilder):
|
|
104
|
+
ambiguous_features: List[AmbiguousFeature] = []
|
|
105
|
+
for feature_json in features_json:
|
|
106
|
+
feature = FeatureBuilder()
|
|
107
|
+
FluentDict(feature_json) \
|
|
108
|
+
.visit_str(ResponseField.query, feature.set_query) \
|
|
109
|
+
.visit_int(ResponseField.total_namesake_count, feature.set_total_namesake_count) \
|
|
110
|
+
.visit_objects(ResponseField.namesake_examples, lambda json: feature.add_namesake(ResponseParser._parse_namesake(json)))
|
|
111
|
+
|
|
112
|
+
ambiguous_features.append(feature.build_ambiguous())
|
|
113
|
+
|
|
114
|
+
response.set_ambiguous_features(ambiguous_features)
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def _parse_point(centroid_dict: FluentDict) -> GeoPoint:
|
|
118
|
+
return GeoPoint(
|
|
119
|
+
centroid_dict.get(ResponseField.centroid_lon),
|
|
120
|
+
centroid_dict.get(ResponseField.centroid_lat)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def _parse_rect(rect_dict: FluentDict) -> GeoRect:
|
|
125
|
+
return GeoRect(
|
|
126
|
+
rect_dict.get(ResponseField.min_lon),
|
|
127
|
+
rect_dict.get(ResponseField.min_lat),
|
|
128
|
+
rect_dict.get(ResponseField.max_lon),
|
|
129
|
+
rect_dict.get(ResponseField.max_lat),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def _parse_namesake(namesake_dict: FluentDict):
|
|
134
|
+
return Namesake(
|
|
135
|
+
namesake_dict.get(ResponseField.namesake_name),
|
|
136
|
+
namesake_dict.get_objects(ResponseField.namesake_parents)
|
|
137
|
+
.map(
|
|
138
|
+
lambda parent: NamesakeParent(
|
|
139
|
+
parent.get(ResponseField.namesake_parent_name),
|
|
140
|
+
parent.get_enum(ResponseField.namesake_parent_level, LevelKind)))
|
|
141
|
+
.list()
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class ResponseFormatter:
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
def format(response: Response) -> Dict:
|
|
149
|
+
if isinstance(response, SuccessResponse):
|
|
150
|
+
return FluentDict() \
|
|
151
|
+
.put(ResponseField.status, Status.success.value) \
|
|
152
|
+
.put(ResponseField.message, response.message) \
|
|
153
|
+
.put(ResponseField.data, FluentDict()
|
|
154
|
+
.put(ResponseField.level, response.level.value)
|
|
155
|
+
.put(ResponseField.answers, list(map(ResponseFormatter._format_answer, response.answers)))) \
|
|
156
|
+
.to_dict()
|
|
157
|
+
elif isinstance(response, AmbiguousResponse):
|
|
158
|
+
return FluentDict() \
|
|
159
|
+
.put(ResponseField.status, Status.ambiguous.value) \
|
|
160
|
+
.put(ResponseField.message, response.message) \
|
|
161
|
+
.put(ResponseField.data, FluentDict()
|
|
162
|
+
.put(ResponseField.level, response.level.value)
|
|
163
|
+
.put(ResponseField.features, list(map(ResponseFormatter._format_ambiguous_feature, response.features)))) \
|
|
164
|
+
.to_dict()
|
|
165
|
+
|
|
166
|
+
@staticmethod
|
|
167
|
+
def _format_answer(answer: Answer) -> Dict:
|
|
168
|
+
features = []
|
|
169
|
+
for feature in answer.features:
|
|
170
|
+
features.append(
|
|
171
|
+
FluentDict() \
|
|
172
|
+
.put(ResponseField.geo_object_id, feature.id) \
|
|
173
|
+
.put(ResponseField.name, feature.name) \
|
|
174
|
+
.put(ResponseField.boundary, ResponseFormatter._format_boundary(feature.boundary)) \
|
|
175
|
+
.put(ResponseField.centroid, ResponseFormatter._format_centroid(feature.centroid)) \
|
|
176
|
+
.put(ResponseField.limit, ResponseFormatter._format_rect(feature.limit)) \
|
|
177
|
+
.put(ResponseField.position, ResponseFormatter._format_rect(feature.position)) \
|
|
178
|
+
.to_dict()
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
return FluentDict() \
|
|
182
|
+
.put(ResponseField.features, features) \
|
|
183
|
+
.to_dict()
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@staticmethod
|
|
187
|
+
def _format_centroid(point: Optional[GeoPoint]) -> Optional[Dict]:
|
|
188
|
+
if point is None:
|
|
189
|
+
return None
|
|
190
|
+
|
|
191
|
+
return FluentDict() \
|
|
192
|
+
.put(ResponseField.centroid_lon, point.lon) \
|
|
193
|
+
.put(ResponseField.centroid_lat, point.lat) \
|
|
194
|
+
.to_dict()
|
|
195
|
+
|
|
196
|
+
@staticmethod
|
|
197
|
+
def _format_rect(rect: Optional[GeoRect]) -> Optional[Dict]:
|
|
198
|
+
if rect is None:
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
return FluentDict() \
|
|
202
|
+
.put(ResponseField.min_lon, rect.start_lon) \
|
|
203
|
+
.put(ResponseField.min_lat, rect.min_lat) \
|
|
204
|
+
.put(ResponseField.max_lon, rect.end_lon) \
|
|
205
|
+
.put(ResponseField.max_lat, rect.max_lat) \
|
|
206
|
+
.to_dict()
|
|
207
|
+
|
|
208
|
+
@staticmethod
|
|
209
|
+
def _format_boundary(boundary: Optional[Boundary]) -> Optional[str]:
|
|
210
|
+
if boundary is None:
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
return GeoJson.format_geometry(boundary)
|
|
214
|
+
|
|
215
|
+
@staticmethod
|
|
216
|
+
def _format_ambiguous_feature(feaure: AmbiguousFeature) -> Dict:
|
|
217
|
+
return FluentDict() \
|
|
218
|
+
.put(ResponseField.query, feaure.query) \
|
|
219
|
+
.put(ResponseField.total_namesake_count, feaure.total_namesake_count) \
|
|
220
|
+
.put(ResponseField.namesake_examples, list(map(ResponseFormatter._format_namesake, feaure.namesake_examples))) \
|
|
221
|
+
.to_dict()
|
|
222
|
+
|
|
223
|
+
@staticmethod
|
|
224
|
+
def _format_namesake(namesake: Namesake) -> Dict:
|
|
225
|
+
return FluentDict() \
|
|
226
|
+
.put(ResponseField.namesake_name, namesake.name) \
|
|
227
|
+
.put(ResponseField.namesake_parents, list(map(ResponseFormatter._format_namesake_parent, namesake.parents))) \
|
|
228
|
+
.to_dict()
|
|
229
|
+
|
|
230
|
+
@staticmethod
|
|
231
|
+
def _format_namesake_parent(parent: NamesakeParent) -> Dict:
|
|
232
|
+
return FluentDict() \
|
|
233
|
+
.put(ResponseField.namesake_parent_name, parent.name) \
|
|
234
|
+
.put(ResponseField.namesake_parent_level, parent.level) \
|
|
235
|
+
.to_dict()
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class GeoJson:
|
|
239
|
+
|
|
240
|
+
def __init__(self):
|
|
241
|
+
self.lon_list: List[float] = []
|
|
242
|
+
self.lat_list: List[float] = []
|
|
243
|
+
|
|
244
|
+
@staticmethod
|
|
245
|
+
def parse_geometry(geometry_line: str) -> Union[Multipolygon, Polygon, GeoPoint]:
|
|
246
|
+
geoJson = GeoJson()
|
|
247
|
+
return geoJson._do_parse(geometry_line)
|
|
248
|
+
|
|
249
|
+
@staticmethod
|
|
250
|
+
def format_geometry(boundary: Boundary) -> str:
|
|
251
|
+
if isinstance(boundary.geometry, GeoPoint):
|
|
252
|
+
return json.dumps({
|
|
253
|
+
ResponseField.boundary_type.value: GeometryKind.point.value,
|
|
254
|
+
ResponseField.boundary_coordinates.value: [boundary.geometry.lon, boundary.geometry.lat]
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
if isinstance(boundary.geometry, Polygon):
|
|
258
|
+
return json.dumps({
|
|
259
|
+
ResponseField.boundary_type.value: GeometryKind.polygon.value,
|
|
260
|
+
ResponseField.boundary_coordinates.value: GeoJson._format_polygon(boundary.geometry)
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
if isinstance(boundary.geometry, Multipolygon):
|
|
264
|
+
return json.dumps({
|
|
265
|
+
ResponseField.boundary_type.value: GeometryKind.polygon.value,
|
|
266
|
+
ResponseField.boundary_coordinates.value: [GeoJson._format_polygon(poly) for poly in boundary.geometry.polygons]
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
@staticmethod
|
|
270
|
+
def _format_polygon(polygon: Polygon):
|
|
271
|
+
poly = []
|
|
272
|
+
for ring in polygon.rings:
|
|
273
|
+
poly.append([[p.lon, p.lat] for p in ring.points])
|
|
274
|
+
return poly
|
|
275
|
+
|
|
276
|
+
def _do_parse(self, geometry_line: str) -> Union[Multipolygon, Polygon, GeoPoint]:
|
|
277
|
+
geometry_data: dict = json.loads(geometry_line)
|
|
278
|
+
geometry_type: GeometryKind = GeometryKind(geometry_data[ResponseField.boundary_type.value])
|
|
279
|
+
|
|
280
|
+
if geometry_type == GeometryKind.point:
|
|
281
|
+
return self._parse_point(geometry_data)
|
|
282
|
+
|
|
283
|
+
if geometry_type == GeometryKind.polygon:
|
|
284
|
+
return self._parse_polygon(geometry_data[ResponseField.boundary_coordinates.value])
|
|
285
|
+
|
|
286
|
+
if geometry_type == GeometryKind.multipolygon:
|
|
287
|
+
return self._parse_multipolygon(geometry_data[ResponseField.boundary_coordinates.value])
|
|
288
|
+
|
|
289
|
+
raise ValueError('Invalid geometry type')
|
|
290
|
+
|
|
291
|
+
def _parse_multipolygon(self, geometry_data: List[List[List[List[float]]]]) -> Multipolygon:
|
|
292
|
+
return Multipolygon([self._parse_polygon(p) for p in geometry_data])
|
|
293
|
+
|
|
294
|
+
def _parse_polygon(self, geometry_data: List[List[List[float]]]) -> Polygon:
|
|
295
|
+
rings: List[Ring] = []
|
|
296
|
+
for ring in geometry_data:
|
|
297
|
+
rings.append(Ring([GeoPoint(p[0], p[1]) for p in ring]))
|
|
298
|
+
return Polygon(rings)
|
|
299
|
+
|
|
300
|
+
def _parse_point(self, geometry_data: dict) -> GeoPoint:
|
|
301
|
+
return GeoPoint(
|
|
302
|
+
lon=geometry_data[ResponseField.boundary_lon.value],
|
|
303
|
+
lat=geometry_data[ResponseField.boundary_lat.value]
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
def _add_point(self, lon: float, lat: float) -> None:
|
|
307
|
+
self.lon_list.append(lon)
|
|
308
|
+
self.lat_list.append(lat)
|