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,247 @@
1
+ import enum
2
+ from typing import List, Optional, Dict, Union
3
+
4
+ from .geometry import GeometryBase, GeoPoint, GeoRect, Polygon, Multipolygon
5
+ from .request import LevelKind
6
+ from ..type_assertion import assert_list_type, assert_optional_type, assert_type, assert_optional_list_type
7
+
8
+
9
+ class Status(enum.Enum):
10
+ success = 'success'
11
+ ambiguous = 'ambiguous'
12
+ error = 'error'
13
+
14
+
15
+ class NamesakeParent:
16
+ def __init__(self, name: str, level: LevelKind):
17
+ assert_type(name, str)
18
+ assert_type(level, LevelKind)
19
+
20
+ self.name: str = name
21
+ self.level: LevelKind = level
22
+
23
+
24
+ class Namesake:
25
+ def __init__(self, name: str, parents: List[NamesakeParent]):
26
+ assert_type(name, str)
27
+ assert_list_type(parents, NamesakeParent)
28
+
29
+ self.name: str = name
30
+ self.parents: List[NamesakeParent] = parents
31
+
32
+
33
+ class Boundary(GeometryBase):
34
+ def __init__(self, geometry: Union[Multipolygon, Polygon, GeoPoint]):
35
+ self.geometry: Union[Multipolygon, Polygon, GeoPoint] = geometry
36
+
37
+
38
+ class GeocodedFeature:
39
+ def __init__(self,
40
+ id: str, name: str,
41
+ highlights: Optional[List[str]] = None,
42
+ boundary: Optional[Boundary] = None,
43
+ centroid: Optional[GeoPoint] = None,
44
+ limit: Optional[GeoRect] = None,
45
+ position: Optional[GeoRect] = None):
46
+ assert_type(id, str)
47
+ assert_type(name, str)
48
+ assert_optional_list_type(highlights, str)
49
+ assert_optional_type(boundary, Boundary)
50
+ assert_optional_type(centroid, GeoPoint)
51
+ assert_optional_type(limit, GeoRect)
52
+ assert_optional_type(position, GeoRect)
53
+
54
+ self.id: str = id
55
+ self.name: str = name
56
+ self.highlights: Optional[List[str]] = highlights
57
+ self.boundary: Optional[Boundary] = boundary
58
+ self.centroid: Optional[GeoPoint] = centroid
59
+ self.limit: Optional[GeoRect] = limit
60
+ self.position: Optional[GeoRect] = position
61
+
62
+
63
+ class AmbiguousFeature:
64
+ def __init__(self, query: str, total_namesake_count: int, namesake_examples: List[Namesake]):
65
+ assert_type(query, str)
66
+ assert_type(total_namesake_count, int)
67
+ assert_list_type(namesake_examples, Namesake)
68
+
69
+ self.query: str = query
70
+ self.total_namesake_count: int = total_namesake_count
71
+ self.namesake_examples: List[Namesake] = namesake_examples
72
+
73
+
74
+ class Response:
75
+ def __init__(self, message: str):
76
+ assert_type(message, str)
77
+ self.message: str = message
78
+
79
+
80
+ class Answer:
81
+ def __init__(self, features: List[GeocodedFeature]):
82
+ assert_list_type(features, GeocodedFeature)
83
+ self.features: List[GeocodedFeature] = features
84
+
85
+
86
+ class SuccessResponse(Response):
87
+ def __init__(self, message: str, level: LevelKind, answers: List[Answer]):
88
+ super().__init__(message)
89
+
90
+ assert_type(message, str)
91
+ assert_optional_type(level, LevelKind)
92
+ assert_list_type(answers, Answer)
93
+
94
+ self.level: LevelKind = level
95
+ self.answers: List[Answer] = answers
96
+
97
+ features = []
98
+ for answer in answers:
99
+ features.extend(answer.features)
100
+
101
+ self.features: List[GeocodedFeature] = features
102
+
103
+
104
+ class AmbiguousResponse(Response):
105
+ def __init__(self, message: str, level: LevelKind, features: List[AmbiguousFeature]):
106
+ super().__init__(message)
107
+
108
+ assert_type(message, str)
109
+ assert_optional_type(level, LevelKind)
110
+ assert_list_type(features, AmbiguousFeature)
111
+
112
+ self.level: LevelKind = level
113
+ self.features: List[AmbiguousFeature] = features
114
+
115
+
116
+ class ErrorResponse(Response):
117
+ def __init__(self, message: str):
118
+ super().__init__(message)
119
+
120
+
121
+ class FeatureBuilder:
122
+ def __init__(self):
123
+ self.query: Optional[str] = None
124
+ self.id: Optional[str] = None
125
+ self.name: Optional[str] = None
126
+ self.highlights: Optional[List[str]] = None
127
+ self.boundary: Optional[Boundary] = None
128
+ self.centroid: Optional[GeoPoint] = None
129
+ self.limit: Optional[GeoRect] = None
130
+ self.position: Optional[GeoRect] = None
131
+ self.total_namesake_count: Optional[int] = None
132
+ self.namesake_examples: List[Namesake] = []
133
+
134
+ def set_query(self, v: Optional[str]) -> 'FeatureBuilder':
135
+ assert_optional_type(v, str)
136
+ self.query = v
137
+ return self
138
+
139
+ def set_id(self, v: str) -> 'FeatureBuilder':
140
+ assert_type(v, str)
141
+ self.id = v
142
+ return self
143
+
144
+ def set_name(self, v: str) -> 'FeatureBuilder':
145
+ assert_type(v, str)
146
+ self.name = v
147
+ return self
148
+
149
+ def set_highlights(self, v: List[str]) -> 'FeatureBuilder':
150
+ assert_list_type(v, str)
151
+ self.highlights = v
152
+ return self
153
+
154
+ def set_boundary(self, v: Union[Multipolygon, Polygon, GeoPoint]) -> 'FeatureBuilder':
155
+ assert_type(v, (Multipolygon, Polygon, GeoPoint))
156
+ self.boundary = Boundary(v)
157
+ return self
158
+
159
+ def set_centroid(self, v: GeoPoint) -> 'FeatureBuilder':
160
+ assert_type(v, GeoPoint)
161
+ self.centroid = v
162
+ return self
163
+
164
+ def set_limit(self, v: GeoRect) -> 'FeatureBuilder':
165
+ assert_type(v, GeoRect)
166
+ self.limit = v
167
+ return self
168
+
169
+ def set_position(self, v: GeoRect) -> 'FeatureBuilder':
170
+ assert_type(v, GeoRect)
171
+ self.position = v
172
+ return self
173
+
174
+ def set_total_namesake_count(self, v: int) -> 'FeatureBuilder':
175
+ assert_type(v, int)
176
+ self.total_namesake_count = v
177
+ return self
178
+
179
+ def set_namesake_examples(self, v: List[Namesake]) -> 'FeatureBuilder':
180
+ assert_list_type(v, Namesake)
181
+ self.namesake_examples = v
182
+ return self
183
+
184
+ def add_namesake(self, namesake: Namesake) -> 'FeatureBuilder':
185
+ assert_type(namesake, Namesake)
186
+ self.namesake_examples.append(namesake)
187
+ return self
188
+
189
+ def build_ambiguous(self) -> AmbiguousFeature:
190
+ return AmbiguousFeature(self.query, self.total_namesake_count, self.namesake_examples)
191
+
192
+ def build_geocoded(self) -> GeocodedFeature:
193
+ return GeocodedFeature(self.id, self.name, self.highlights, self.boundary, self.centroid, self.limit,
194
+ self.position)
195
+
196
+
197
+ class ResponseBuilder:
198
+ def __init__(self):
199
+ self.status: Status = None
200
+ self.level: LevelKind = None
201
+ self.message: str = None
202
+ self.answers: List[Answer] = None
203
+ self.ambiguous_features: List[AmbiguousFeature] = None
204
+ self.data: Dict = None
205
+
206
+ def set_status(self, v: Status) -> 'ResponseBuilder':
207
+ assert_type(v, Status)
208
+ self.status = v
209
+ return self
210
+
211
+ def set_level(self, v: LevelKind) -> 'ResponseBuilder':
212
+ assert_type(v, LevelKind)
213
+ self.level = v
214
+ return self
215
+
216
+ def set_message(self, v: str) -> 'ResponseBuilder':
217
+ assert_type(v, str)
218
+ self.message = v
219
+ return self
220
+
221
+ def set_ambiguous_features(self, v: List[AmbiguousFeature]) -> 'ResponseBuilder':
222
+ assert_list_type(v, AmbiguousFeature)
223
+ self.ambiguous_features = v
224
+ return self
225
+
226
+ def set_answers(self, v: List[Answer]) -> 'ResponseBuilder':
227
+ assert_list_type(v, Answer)
228
+ self.answers = v
229
+ return self
230
+
231
+ def set_geocoded_features(self, v: List[GeocodedFeature]):
232
+ '''
233
+ Exactly matching non-exploding features, i.e. one feature per answer
234
+ '''
235
+ assert_list_type(v, GeocodedFeature)
236
+ self.answers = [Answer([f]) for f in v]
237
+ return self
238
+
239
+ def build(self) -> Response:
240
+ if self.status == Status.error:
241
+ return ErrorResponse(self.message)
242
+ elif self.status == Status.success:
243
+ return SuccessResponse(self.message, self.level, self.answers)
244
+ elif self.status == Status.ambiguous:
245
+ return AmbiguousResponse(self.message, self.level, self.ambiguous_features)
246
+ else:
247
+ raise ValueError('Unknown status: ' + str(self.status))
@@ -0,0 +1,65 @@
1
+ from enum import Enum
2
+ from typing import Union, Optional, List
3
+
4
+ from pandas import DataFrame
5
+
6
+ from .geocodes import Geocodes
7
+
8
+ LOCATION_COORDINATE_COLUMNS = {'lon', 'lat'}
9
+ LOCATION_RECTANGLE_COLUMNS = {'lonmin', 'latmin', 'lonmax', 'latmax'}
10
+ LOCATION_LIST_ERROR_MESSAGE = "Expected: location = [double lon1, double lat1, ... , double lonN, double latN]"
11
+ LOCATION_DATAFRAME_ERROR_MESSAGE = "Expected: location = DataFrame with [{}] or [{}] columns" \
12
+ .format(', '.join(LOCATION_COORDINATE_COLUMNS), ', '.join(LOCATION_RECTANGLE_COLUMNS))
13
+
14
+
15
+ class RegionKind(Enum):
16
+ region_ids = 'region_ids'
17
+ region_name = 'region_name'
18
+ coordinates = 'coordinates'
19
+ data_frame = 'data_frame'
20
+
21
+
22
+ def _prepare_parent(parent: Union[str, Geocodes]) -> Optional[dict]:
23
+ if not parent:
24
+ return None
25
+
26
+ if isinstance(parent, Geocodes):
27
+ kind = RegionKind.region_ids
28
+ value = parent.unique_ids()
29
+
30
+ elif isinstance(parent, str):
31
+ kind = RegionKind.region_name
32
+ value = parent
33
+
34
+ else:
35
+ raise ValueError('Wrong parent type: ' + parent.__str__())
36
+
37
+ return {'type': kind.value, 'data': value}
38
+
39
+
40
+ def _prepare_location(location: Union[str, Geocodes, List[float], DataFrame]) -> Optional[dict]:
41
+ if location is None:
42
+ return None
43
+
44
+ value = location
45
+ if isinstance(location, Geocodes):
46
+ kind = RegionKind.region_ids
47
+ value = location.unique_ids()
48
+
49
+ elif isinstance(location, str):
50
+ kind = RegionKind.region_name
51
+
52
+ elif isinstance(location, list):
53
+ if len(location) == 0 or len(location) % 2 != 0:
54
+ raise ValueError(LOCATION_LIST_ERROR_MESSAGE)
55
+ kind = RegionKind.coordinates
56
+
57
+ elif isinstance(location, DataFrame):
58
+ if not LOCATION_COORDINATE_COLUMNS.issubset(location.columns) and not LOCATION_RECTANGLE_COLUMNS.issubset(location.columns):
59
+ raise ValueError(LOCATION_DATAFRAME_ERROR_MESSAGE)
60
+ kind = RegionKind.data_frame
61
+
62
+ else:
63
+ raise ValueError('Wrong location type: ' + location.__str__())
64
+
65
+ return {'type': kind.value, 'data': value}
@@ -0,0 +1,141 @@
1
+ from typing import List
2
+
3
+ import shapely
4
+ from geopandas import GeoDataFrame
5
+ from pandas import DataFrame
6
+ from shapely.geometry import box
7
+
8
+ from lets_plot.geo_data import PlacesDataFrameBuilder, abstractmethod
9
+ from lets_plot.geo_data.geocodes import _zip_answers
10
+ from lets_plot.geo_data.gis.request import RegionQuery, LevelKind
11
+ from lets_plot.geo_data.gis.response import Answer, GeocodedFeature, GeoRect, Boundary, Multipolygon, Polygon, GeoPoint
12
+
13
+ ShapelyPoint = shapely.geometry.Point
14
+ ShapelyLinearRing = shapely.geometry.LinearRing
15
+ ShapelyPolygon = shapely.geometry.Polygon
16
+ ShapelyMultiPolygon = shapely.geometry.MultiPolygon
17
+
18
+
19
+ def _create_geo_data_frame(data, geometry) -> DataFrame:
20
+ return GeoDataFrame(
21
+ data,
22
+ crs='EPSG:4326',
23
+ geometry=geometry
24
+ )
25
+
26
+
27
+ class RectGeoDataFrame:
28
+ def __init__(self):
29
+ super().__init__()
30
+ self._lonmin: List[float] = []
31
+ self._latmin: List[float] = []
32
+ self._lonmax: List[float] = []
33
+ self._latmax: List[float] = []
34
+
35
+ def to_data_frame(self, answers: List[Answer], queries: List[RegionQuery], level_kind: LevelKind) -> DataFrame:
36
+ assert len(answers) == len(queries)
37
+ places = PlacesDataFrameBuilder(level_kind)
38
+
39
+ for query, answer in _zip_answers(queries, answers):
40
+ for feature in answer.features:
41
+ rects: List[GeoRect] = self._read_rect(feature)
42
+ for rect in rects:
43
+ places.append_row(query, feature)
44
+ self._lonmin.append(rect.start_lon)
45
+ self._latmin.append(rect.min_lat)
46
+ self._lonmax.append(rect.end_lon)
47
+ self._latmax.append(rect.max_lat)
48
+
49
+ geometry = [
50
+ box(lmt[0], lmt[1], lmt[2], lmt[3]) for lmt in zip(self._lonmin, self._latmin, self._lonmax, self._latmax)
51
+ ]
52
+ return _create_geo_data_frame(places.build_dict(), geometry=geometry)
53
+
54
+ def _read_rect(self, feature: GeocodedFeature) -> List[GeoRect]:
55
+ rect: GeoRect = self._select_rect(feature)
56
+ if rect.crosses_antimeridian():
57
+ return [
58
+ GeoRect(start_lon=rect.start_lon, end_lon=180., min_lat=rect.min_lat, max_lat=rect.max_lat),
59
+ GeoRect(start_lon=-180., end_lon=rect.end_lon, min_lat=rect.min_lat, max_lat=rect.max_lat)
60
+ ]
61
+ else:
62
+ return [rect]
63
+
64
+ @abstractmethod
65
+ def _select_rect(self, feature: GeocodedFeature) -> GeoRect:
66
+ pass
67
+
68
+
69
+ class CentroidsGeoDataFrame:
70
+ def __init__(self):
71
+ super().__init__()
72
+ self._lons: List[float] = []
73
+ self._lats: List[float] = []
74
+
75
+ def to_data_frame(self, answers: List[Answer], queries: List[RegionQuery], level_kind: LevelKind) -> DataFrame:
76
+ places = PlacesDataFrameBuilder(level_kind)
77
+
78
+ for query, answer in _zip_answers(queries, answers):
79
+ for feature in answer.features:
80
+ places.append_row(query, feature)
81
+ self._lons.append(feature.centroid.lon)
82
+ self._lats.append(feature.centroid.lat)
83
+
84
+ geometry = [ShapelyPoint(pnt[0], pnt[1]) for pnt in zip(self._lons, self._lats)]
85
+ return _create_geo_data_frame(places.build_dict(), geometry)
86
+
87
+
88
+ class BoundariesGeoDataFrame:
89
+ def __init__(self):
90
+ super().__init__()
91
+
92
+ def to_data_frame(self, answers: List[Answer], queries: List[RegionQuery], level_kind: LevelKind) -> DataFrame:
93
+ places = PlacesDataFrameBuilder(level_kind)
94
+
95
+ geometry = []
96
+ for query, answer in _zip_answers(queries, answers):
97
+ for feature in answer.features:
98
+ places.append_row(query, feature)
99
+ geometry.append(self._geo_parse_geometry(feature.boundary))
100
+
101
+ return _create_geo_data_frame(places.build_dict(), geometry=geometry)
102
+
103
+ def _geo_parse_geometry(self, boundary: Boundary):
104
+ geometry = boundary.geometry
105
+ if isinstance(geometry, GeoPoint):
106
+ return self._geo_parse_point(geometry)
107
+
108
+ if isinstance(geometry, Polygon):
109
+ return self._geo_parse_polygon(geometry)
110
+
111
+ if isinstance(geometry, Multipolygon):
112
+ return self._geo_parse_multipolygon(geometry)
113
+
114
+ raise ValueError('Invalid geometry type')
115
+
116
+ def _geo_parse_multipolygon(self, geometry: Multipolygon) -> ShapelyMultiPolygon:
117
+ geo_polygons: List[ShapelyPolygon] = [self._geo_parse_polygon(polygon) for polygon in geometry.polygons]
118
+ return ShapelyMultiPolygon(geo_polygons)
119
+
120
+ def _geo_parse_polygon(self, polygon: Polygon) -> ShapelyPolygon:
121
+ geo_rings: List[ShapelyLinearRing] = [
122
+ ShapelyLinearRing([(p.lon, p.lat) for p in ring.points]) for ring in polygon.rings
123
+ ]
124
+
125
+ if len(geo_rings) == 0:
126
+ return ShapelyPolygon()
127
+ else:
128
+ return ShapelyPolygon(shell=geo_rings[0], holes=geo_rings[1:])
129
+
130
+ def _geo_parse_point(self, geometry_data: GeoPoint) -> ShapelyPoint:
131
+ return ShapelyPoint((geometry_data.lon, geometry_data.lat))
132
+
133
+
134
+ class LimitsGeoDataFrame(RectGeoDataFrame):
135
+ def _select_rect(self, feature: GeocodedFeature) -> GeoRect:
136
+ return feature.limit
137
+
138
+
139
+ class PositionsGeoDataFrame(RectGeoDataFrame):
140
+ def _select_rect(self, feature: GeocodedFeature) -> GeoRect:
141
+ return feature.position
@@ -0,0 +1,34 @@
1
+ def assert_type(obj, *types):
2
+ assert isinstance(obj, types), 'Invalid type: \nActual: ' + str(type(obj)) + ' (' + str(obj) + ')\nExpected: ' + str(types)
3
+
4
+
5
+ def assert_optional_type(obj, *types):
6
+ if obj is None:
7
+ return
8
+ assert_type(obj, *types)
9
+
10
+
11
+ def assert_list_type(obj, *types):
12
+ assert_type(obj, list, tuple)
13
+ for v in obj:
14
+ assert_type(v, *types)
15
+
16
+
17
+ def assert_optional_list_type(obj, *types):
18
+ if obj is None:
19
+ return
20
+ assert_list_type(obj, *types)
21
+
22
+
23
+ def assert_optional_tuple_type(obj, *types):
24
+ if obj is None:
25
+ return
26
+ assert_tuple_type(obj, *types)
27
+
28
+
29
+ def assert_tuple_type(obj, *types):
30
+ assert_type(obj, tuple)
31
+ assert len(obj) == len(types)
32
+
33
+ for v, t in zip(obj, types):
34
+ assert_type(v, t)
@@ -0,0 +1,4 @@
1
+ # Copyright (c) 2021. 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
+ # This module is designed to be used from code as it won't cause the OSM attribution to appear
@@ -0,0 +1,13 @@
1
+ # Copyright (c) 2021. 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
+ DF_COLUMN_ID = 'id'
5
+ DF_COLUMN_POSITION = 'position'
6
+ DF_COLUMN_LIMIT = 'limit'
7
+ DF_COLUMN_CENTROID = 'centroid'
8
+ DF_COLUMN_FOUND_NAME = 'found name'
9
+ DF_COLUMN_HIGHLIGHTS = 'highlights'
10
+ DF_COLUMN_CITY = 'city'
11
+ DF_COLUMN_COUNTRY = 'country'
12
+ DF_COLUMN_STATE = 'state'
13
+ DF_COLUMN_COUNTY = 'county'
@@ -0,0 +1,33 @@
1
+ # Copyright (c) 2021. 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
+ from typing import List, Any
4
+
5
+ from lets_plot.geo_data_internals.constants import DF_COLUMN_CITY, DF_COLUMN_COUNTY, DF_COLUMN_STATE, DF_COLUMN_COUNTRY
6
+
7
+
8
+ def find_geo_names(obj) -> List[str]:
9
+ if is_geocoder(obj):
10
+ data = obj.get_geocodes()
11
+ else:
12
+ data = obj
13
+
14
+ names = []
15
+ if DF_COLUMN_CITY in data:
16
+ names.append(DF_COLUMN_CITY)
17
+ if DF_COLUMN_COUNTY in data:
18
+ names.append(DF_COLUMN_COUNTY)
19
+ if DF_COLUMN_STATE in data:
20
+ names.append(DF_COLUMN_STATE)
21
+ if DF_COLUMN_COUNTRY in data:
22
+ names.append(DF_COLUMN_COUNTRY)
23
+
24
+ return names
25
+
26
+
27
+ def is_geocoder(v: Any) -> bool:
28
+ # do not import Geocoder directly to suppress OSM attribution from geo_data package
29
+ if v is None:
30
+ return False
31
+
32
+ return any(base.__name__ == 'Geocoder' for base in type(v).mro())
33
+
lets_plot/mapping.py ADDED
@@ -0,0 +1,115 @@
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
+ __all__ = ['as_discrete']
5
+
6
+
7
+ class MappingMeta:
8
+ def __init__(self, variable, annotation, levels, **parameters):
9
+ if variable is None:
10
+ raise ValueError("variable can't be none")
11
+
12
+ if annotation is None:
13
+ raise ValueError("annotation can't be none")
14
+
15
+ self.variable = variable
16
+ self.annotation = annotation
17
+ self.levels = levels
18
+ self.parameters = parameters
19
+
20
+
21
+ def as_discrete(variable, label=None, order_by=None, order=None, levels=None):
22
+ """
23
+ The function converts a column to a discrete scale and allows you to specify the order of its values.
24
+
25
+ Parameters
26
+ ----------
27
+ variable : str
28
+ The name of the variable.
29
+ label : str
30
+ The name of the scale - used as the axis label or the legend title.
31
+ order_by : str
32
+ The variable name to order by.
33
+ order : int
34
+ The ordering direction. 1 for ascending, -1 for descending.
35
+ levels : list
36
+ The list of values that defines a specific order of categories.
37
+
38
+ Returns
39
+ -------
40
+ ``MappingMeta`` or list
41
+ Variable meta information.
42
+
43
+ Notes
44
+ -----
45
+ The plot will use a discrete scale for the aesthetic mapping.
46
+ It is similar to the ``factor()`` function from R but works differently - there is no data transformation.
47
+
48
+ To enable ordering mode, at least one ordering parameter (``order_by`` or ``order``) should be specified.
49
+ By the default, it will use descending direction and ordering by eigenvalues.
50
+ You cannot specify different order settings for the same variable.
51
+ But if these settings don't contradict each other, they will be combined.
52
+
53
+ Examples
54
+ --------
55
+ .. jupyter-execute::
56
+ :linenos:
57
+ :emphasize-lines: 12
58
+
59
+ import numpy as np
60
+ from lets_plot import *
61
+ LetsPlot.setup_html()
62
+ n = 100
63
+ np.random.seed(42)
64
+ data = {
65
+ 'x': np.random.normal(size=n),
66
+ 'y': np.random.normal(size=n),
67
+ 'c': np.random.randint(5, size=n),
68
+ }
69
+ ggplot(data, aes('x', 'y')) + \\
70
+ geom_point(aes(color=as_discrete('c')))
71
+
72
+ |
73
+
74
+ .. jupyter-execute::
75
+ :linenos:
76
+ :emphasize-lines: 11
77
+
78
+ import numpy as np
79
+ from lets_plot import *
80
+ LetsPlot.setup_html()
81
+ n = 100
82
+ np.random.seed(42)
83
+ data = {
84
+ 'x': np.random.uniform(size=100),
85
+ 'c': np.random.choice(list('abcde'), size=100),
86
+ }
87
+ ggplot(data) + \\
88
+ geom_boxplot(aes(as_discrete('c', label='class', order=1), 'x'))
89
+
90
+ |
91
+
92
+ .. jupyter-execute::
93
+ :linenos:
94
+ :emphasize-lines: 12-13
95
+
96
+ import numpy as np
97
+ from lets_plot import *
98
+ LetsPlot.setup_html()
99
+ n = 100
100
+ np.random.seed(42)
101
+ data = {
102
+ 'x': np.random.normal(size=n),
103
+ 'c': np.random.choice(list('abcde'), size=n),
104
+ 'i': np.random.randint(3, size=n),
105
+ }
106
+ ggplot(data) + \\
107
+ geom_bar(aes(as_discrete('c', order=1, order_by='..count..'), 'x', \\
108
+ fill=as_discrete('i', order=1, order_by='..count..')))
109
+
110
+ """
111
+ if isinstance(variable, str):
112
+ label = variable if label is None else label
113
+ return MappingMeta(variable, 'as_discrete', levels=levels, label=label, order_by=order_by, order=order)
114
+ # aes(x=as_discrete([1, 2, 3])) - pass as is
115
+ return variable