ssb-sgis 1.0.1__py3-none-any.whl → 1.0.3__py3-none-any.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 (60) hide show
  1. sgis/__init__.py +107 -121
  2. sgis/exceptions.py +5 -3
  3. sgis/geopandas_tools/__init__.py +1 -0
  4. sgis/geopandas_tools/bounds.py +86 -47
  5. sgis/geopandas_tools/buffer_dissolve_explode.py +62 -39
  6. sgis/geopandas_tools/centerlines.py +53 -44
  7. sgis/geopandas_tools/cleaning.py +87 -104
  8. sgis/geopandas_tools/conversion.py +164 -107
  9. sgis/geopandas_tools/duplicates.py +33 -19
  10. sgis/geopandas_tools/general.py +84 -52
  11. sgis/geopandas_tools/geometry_types.py +24 -10
  12. sgis/geopandas_tools/neighbors.py +23 -11
  13. sgis/geopandas_tools/overlay.py +136 -53
  14. sgis/geopandas_tools/point_operations.py +11 -10
  15. sgis/geopandas_tools/polygon_operations.py +53 -61
  16. sgis/geopandas_tools/polygons_as_rings.py +121 -78
  17. sgis/geopandas_tools/sfilter.py +17 -17
  18. sgis/helpers.py +116 -58
  19. sgis/io/dapla_functions.py +32 -23
  20. sgis/io/opener.py +13 -6
  21. sgis/io/read_parquet.py +2 -2
  22. sgis/maps/examine.py +55 -28
  23. sgis/maps/explore.py +471 -112
  24. sgis/maps/httpserver.py +12 -12
  25. sgis/maps/legend.py +285 -134
  26. sgis/maps/map.py +248 -129
  27. sgis/maps/maps.py +123 -119
  28. sgis/maps/thematicmap.py +260 -94
  29. sgis/maps/tilesources.py +3 -8
  30. sgis/networkanalysis/_get_route.py +5 -4
  31. sgis/networkanalysis/_od_cost_matrix.py +44 -1
  32. sgis/networkanalysis/_points.py +10 -4
  33. sgis/networkanalysis/_service_area.py +5 -2
  34. sgis/networkanalysis/closing_network_holes.py +22 -64
  35. sgis/networkanalysis/cutting_lines.py +58 -46
  36. sgis/networkanalysis/directednetwork.py +16 -8
  37. sgis/networkanalysis/finding_isolated_networks.py +6 -5
  38. sgis/networkanalysis/network.py +15 -13
  39. sgis/networkanalysis/networkanalysis.py +79 -61
  40. sgis/networkanalysis/networkanalysisrules.py +21 -17
  41. sgis/networkanalysis/nodes.py +2 -3
  42. sgis/networkanalysis/traveling_salesman.py +6 -3
  43. sgis/parallel/parallel.py +372 -142
  44. sgis/raster/base.py +9 -3
  45. sgis/raster/cube.py +331 -213
  46. sgis/raster/cubebase.py +15 -29
  47. sgis/raster/image_collection.py +2560 -0
  48. sgis/raster/indices.py +17 -12
  49. sgis/raster/raster.py +356 -275
  50. sgis/raster/sentinel_config.py +104 -0
  51. sgis/raster/zonal.py +38 -14
  52. {ssb_sgis-1.0.1.dist-info → ssb_sgis-1.0.3.dist-info}/LICENSE +1 -1
  53. {ssb_sgis-1.0.1.dist-info → ssb_sgis-1.0.3.dist-info}/METADATA +87 -16
  54. ssb_sgis-1.0.3.dist-info/RECORD +61 -0
  55. {ssb_sgis-1.0.1.dist-info → ssb_sgis-1.0.3.dist-info}/WHEEL +1 -1
  56. sgis/raster/bands.py +0 -48
  57. sgis/raster/gradient.py +0 -78
  58. sgis/raster/methods_as_functions.py +0 -124
  59. sgis/raster/torchgeo.py +0 -150
  60. ssb_sgis-1.0.1.dist-info/RECORD +0 -63
sgis/maps/maps.py CHANGED
@@ -8,37 +8,45 @@ The 'qtm' function shows a simple static map of one or more GeoDataFrames.
8
8
  """
9
9
 
10
10
  import inspect
11
+ import random
11
12
  from numbers import Number
12
13
  from typing import Any
13
14
 
14
- from geopandas import GeoDataFrame, GeoSeries
15
- from pyproj import CRS
15
+ import pyproj
16
+ from geopandas import GeoDataFrame
17
+ from geopandas import GeoSeries
16
18
  from shapely import Geometry
17
-
18
- from ..geopandas_tools.conversion import to_gdf as to_gdf_func
19
- from ..geopandas_tools.general import clean_geoms, get_common_crs, is_wkt
19
+ from shapely import box
20
+ from shapely.geometry import Polygon
21
+
22
+ from ..geopandas_tools.bounds import get_total_bounds
23
+ from ..geopandas_tools.conversion import to_bbox
24
+ from ..geopandas_tools.conversion import to_gdf
25
+ from ..geopandas_tools.conversion import to_shapely
26
+ from ..geopandas_tools.general import clean_geoms
27
+ from ..geopandas_tools.general import get_common_crs
28
+ from ..geopandas_tools.general import is_wkt
20
29
  from ..geopandas_tools.geocoding import address_to_gdf
21
30
  from ..geopandas_tools.geometry_types import get_geom_type
22
31
  from .explore import Explore
23
32
  from .map import Map
24
33
  from .thematicmap import ThematicMap
25
34
 
26
-
27
35
  try:
28
36
  from torchgeo.datasets.geo import RasterDataset
29
37
  except ImportError:
30
38
 
31
39
  class RasterDataset:
32
- """Placeholder"""
40
+ """Placeholder."""
33
41
 
34
42
 
35
43
  def _get_location_mask(kwargs: dict, gdfs) -> tuple[GeoDataFrame | None, dict]:
36
44
  try:
37
45
  crs = get_common_crs(gdfs)
38
- except IndexError:
46
+ except (IndexError, pyproj.exceptions.CRSError):
39
47
  for x in kwargs.values():
40
48
  try:
41
- crs = CRS(x.crs) if hasattr(x, "crs") else CRS(x["crs"])
49
+ crs = pyproj.CRS(x.crs) if hasattr(x, "crs") else pyproj.CRS(x["crs"])
42
50
  break
43
51
  except Exception:
44
52
  crs = None
@@ -62,7 +70,7 @@ def _get_location_mask(kwargs: dict, gdfs) -> tuple[GeoDataFrame | None, dict]:
62
70
  kwargs.pop(key)
63
71
  if isinstance(value, Number) and value > 1:
64
72
  size = value
65
- the_mask = to_gdf_func([mask], crs=4326).to_crs(crs).buffer(size)
73
+ the_mask = to_gdf([mask], crs=4326).to_crs(crs).buffer(size)
66
74
  return the_mask, kwargs
67
75
 
68
76
  return None, kwargs
@@ -72,14 +80,13 @@ def explore(
72
80
  *gdfs: GeoDataFrame | dict[str, GeoDataFrame],
73
81
  column: str | None = None,
74
82
  center: Any | None = None,
75
- center_4326: Any | None = None,
76
- labels: tuple[str] | None = None,
77
83
  max_zoom: int = 40,
78
84
  browser: bool = False,
79
85
  smooth_factor: int | float = 1.5,
80
86
  size: int | None = None,
87
+ max_images: int = 15,
81
88
  **kwargs,
82
- ) -> None:
89
+ ) -> Explore:
83
90
  """Interactive map of GeoDataFrames with layers that can be toggled on/off.
84
91
 
85
92
  It takes all the given GeoDataFrames and displays them together in an
@@ -94,13 +101,9 @@ def explore(
94
101
  *gdfs: one or more GeoDataFrames.
95
102
  column: The column to color the geometries by. Defaults to None, which means
96
103
  each GeoDataFrame will get a unique color.
97
- center: Either an address string to be geocoded or a geometry like object
98
- (coordinate pair (x, y), GeoDataFrame, bbox, etc.). If the geometry is a
99
- point (or line), it will be buffered by 1000 (can be changed with the)
100
- size parameter. If a polygon is given, it will not be buffered.
101
- labels: By default, the GeoDataFrames will be labeled by their object names.
102
- Alternatively, labels can be specified as a tuple of strings with the same
103
- length as the number of gdfs.
104
+ center: Geometry-like object to center the map on. If a three-length tuple
105
+ is given, the first two should be x and y coordinates and the third
106
+ should be a number of meters to buffer the centerpoint by.
104
107
  max_zoom: The maximum allowed level of zoom. Higher number means more zoom
105
108
  allowed. Defaults to 30, which is higher than the geopandas default.
106
109
  browser: If False (default), the maps will be shown in Jupyter.
@@ -109,17 +112,19 @@ def explore(
109
112
  5 is quite a lot of simplification.
110
113
  size: The buffer distance. Only used when center is given. It then defaults to
111
114
  1000.
115
+ max_images: Maximum number of images (Image, ImageCollection, Band) to show per
116
+ map. Defaults to 15.
112
117
  **kwargs: Keyword arguments to pass to geopandas.GeoDataFrame.explore, for
113
118
  instance 'cmap' to change the colors, 'scheme' to change how the data
114
119
  is grouped. This defaults to 'fisherjenkssampled' for numeric data.
115
120
 
116
- See also
117
- --------
121
+ See Also:
122
+ ---------
118
123
  samplemap: same functionality, but shows only a random area of a given size.
119
124
  clipmap: same functionality, but shows only the areas clipped by a given mask.
120
125
 
121
- Examples
122
- --------
126
+ Examples:
127
+ ---------
123
128
  >>> import sgis as sg
124
129
  >>> roads = sg.read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet")
125
130
  >>> points = sg.read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/points_oslo.parquet")
@@ -138,7 +143,6 @@ def explore(
138
143
  >>> points["meters"] = points.length
139
144
  >>> sg.explore(roads, points, column="meters", cmap="plasma", max_zoom=60, center_4326=(10.7463, 59.92, 500))
140
145
  """
141
-
142
146
  gdfs, column, kwargs = Map._separate_args(gdfs, column, kwargs)
143
147
 
144
148
  loc_mask, kwargs = _get_location_mask(kwargs | {"size": size}, gdfs)
@@ -152,7 +156,6 @@ def explore(
152
156
  *gdfs,
153
157
  column=column,
154
158
  mask=mask,
155
- labels=labels,
156
159
  browser=browser,
157
160
  max_zoom=max_zoom,
158
161
  **kwargs,
@@ -160,16 +163,13 @@ def explore(
160
163
 
161
164
  try:
162
165
  to_crs = gdfs[0].crs
163
- except IndexError:
166
+ except (IndexError, AttributeError):
164
167
  try:
165
- to_crs = [x for x in kwargs.values() if hasattr(x, "crs")][0].crs
166
- except IndexError:
168
+ to_crs = next(x for x in kwargs.values() if hasattr(x, "crs")).crs
169
+ except (IndexError, StopIteration):
167
170
  to_crs = None
168
171
 
169
- if center_4326 is not None:
170
- from_crs = 4326
171
- center = center_4326
172
- elif "crs" in kwargs:
172
+ if "crs" in kwargs:
173
173
  from_crs = kwargs.pop("crs")
174
174
  else:
175
175
  from_crs = to_crs
@@ -183,7 +183,11 @@ def explore(
183
183
  else:
184
184
  if isinstance(center, (tuple, list)) and len(center) == 3:
185
185
  *center, size = center
186
- mask = to_gdf_func(center, crs=from_crs)
186
+ mask = to_gdf(center, crs=from_crs)
187
+
188
+ bounds: Polygon = box(*get_total_bounds(*gdfs, *list(kwargs.values())))
189
+ if not mask.intersects(bounds).any():
190
+ mask = mask.set_crs(4326, allow_override=True)
187
191
 
188
192
  try:
189
193
  mask = mask.to_crs(to_crs)
@@ -197,7 +201,6 @@ def explore(
197
201
  *gdfs,
198
202
  column=column,
199
203
  mask=mask,
200
- labels=labels,
201
204
  browser=browser,
202
205
  max_zoom=max_zoom,
203
206
  **kwargs,
@@ -206,34 +209,36 @@ def explore(
206
209
  m = Explore(
207
210
  *gdfs,
208
211
  column=column,
209
- labels=labels,
210
212
  browser=browser,
211
213
  max_zoom=max_zoom,
212
214
  smooth_factor=smooth_factor,
215
+ max_images=max_images,
213
216
  **kwargs,
214
217
  )
215
218
 
216
- if m.gdfs is None and not len(m.raster_datasets):
217
- return
219
+ if m.gdfs is None and not len(m.rasters):
220
+ return m
218
221
 
219
222
  if not kwargs.pop("explore", True):
220
223
  return qtm(m._gdf, column=m.column, cmap=m._cmap, k=m.k)
221
224
 
222
225
  m.explore()
223
226
 
227
+ return m
228
+
224
229
 
225
230
  def samplemap(
226
231
  *gdfs: GeoDataFrame,
227
232
  column: str | None = None,
228
233
  size: int = 1000,
229
234
  sample_from_first: bool = True,
230
- labels: tuple[str] | None = None,
231
235
  max_zoom: int = 40,
232
236
  smooth_factor: int = 1.5,
233
237
  explore: bool = True,
234
238
  browser: bool = False,
239
+ max_images: int = 15,
235
240
  **kwargs,
236
- ) -> None:
241
+ ) -> Explore:
237
242
  """Shows an interactive map of a random area of GeoDataFrames.
238
243
 
239
244
  It takes all the GeoDataFrames specified, takes a random sample point from the
@@ -255,9 +260,6 @@ def samplemap(
255
260
  Defaults to 1000 (meters).
256
261
  sample_from_first: If True (default), the sample point is taken form the
257
262
  first specified GeoDataFrame. If False, all GeoDataFrames are considered.
258
- labels: By default, the GeoDataFrames will be labeled by their object names.
259
- Alternatively, labels can be specified as a tuple of strings the same
260
- length as the number of gdfs.
261
263
  max_zoom: The maximum allowed level of zoom. Higher number means more zoom
262
264
  allowed. Defaults to 30, which is higher than the geopandas default.
263
265
  smooth_factor: How much to simplify the geometries. 1 is the minimum,
@@ -266,17 +268,19 @@ def samplemap(
266
268
  or not in Jupyter, a static plot will be shown.
267
269
  browser: If False (default), the maps will be shown in Jupyter.
268
270
  If True the maps will be opened in a browser folder.
271
+ max_images: Maximum number of images (Image, ImageCollection, Band) to show per
272
+ map. Defaults to 15.
269
273
  **kwargs: Keyword arguments to pass to geopandas.GeoDataFrame.explore, for
270
274
  instance 'cmap' to change the colors, 'scheme' to change how the data
271
275
  is grouped. This defaults to 'fisherjenkssampled' for numeric data.
272
276
 
273
- See also
274
- --------
277
+ See Also:
278
+ ---------
275
279
  explore: Same functionality, but shows the entire area of the geometries.
276
280
  clipmap: Same functionality, but shows only the areas clipped by a given mask.
277
281
 
278
- Examples
279
- --------
282
+ Examples:
283
+ ---------
280
284
  >>> from sgis import read_parquet_url, samplemap
281
285
  >>> roads = read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_eidskog_2022.parquet")
282
286
  >>> points = read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/points_eidskog.parquet")
@@ -290,78 +294,72 @@ def samplemap(
290
294
  >>> samplemap(roads, points, size=5_000, column="meters")
291
295
 
292
296
  """
293
-
294
- if gdfs and isinstance(gdfs[-1], (float, int)):
297
+ if gdfs and len(gdfs) > 1 and isinstance(gdfs[-1], (float, int)):
295
298
  *gdfs, size = gdfs
296
299
 
297
300
  gdfs, column, kwargs = Map._separate_args(gdfs, column, kwargs)
298
301
 
299
- mask, kwargs = _get_location_mask(kwargs | {"size": size}, gdfs)
302
+ loc_mask, kwargs = _get_location_mask(kwargs | {"size": size}, gdfs)
300
303
  kwargs.pop("size")
304
+ mask = kwargs.pop("mask", loc_mask)
301
305
 
302
- if explore:
303
- m = Explore(
304
- *gdfs,
305
- column=column,
306
- labels=labels,
307
- browser=browser,
308
- max_zoom=max_zoom,
309
- smooth_factor=smooth_factor,
310
- **kwargs,
311
- )
312
- if m.gdfs is None and not len(m.raster_datasets):
313
- return
314
- if mask is not None:
315
- m._gdfs = [gdf.clip(mask) for gdf in m._gdfs]
316
- m._gdf = m._gdf.clip(mask)
317
- m._nan_idx = m._gdf[m._column].isna()
318
- m._get_unique_values()
319
-
320
- m.samplemap(size, sample_from_first=sample_from_first)
306
+ i = 0 if sample_from_first else random.choice(list(range(len(gdfs))))
307
+ try:
308
+ sample = gdfs[i]
309
+ except IndexError:
310
+ sample = list(kwargs.values())[i]
321
311
 
312
+ if mask is None:
313
+ try:
314
+ sample = sample.geometry.dropna().sample(1)
315
+ except Exception:
316
+ try:
317
+ sample = sample.sample(1)
318
+ except Exception:
319
+ pass
320
+ try:
321
+ sample = to_gdf(to_shapely(sample)).explode(ignore_index=True)
322
+ except Exception:
323
+ sample = to_gdf(to_shapely(to_bbox(sample))).explode(ignore_index=True)
322
324
  else:
323
- m = Map(
324
- *gdfs,
325
- column=column,
326
- labels=labels,
327
- **kwargs,
328
- )
329
-
330
- if sample_from_first:
331
- sample = m._gdfs[0].sample(1)
332
- else:
333
- sample = m._gdf.sample(1)
325
+ try:
326
+ sample = to_gdf(to_shapely(sample)).explode(ignore_index=True)
327
+ except Exception:
328
+ sample = to_gdf(to_shapely(to_bbox(sample))).explode(ignore_index=True)
334
329
 
335
- # convert lines to polygons
336
- if get_geom_type(sample) == "line":
337
- sample["geometry"] = sample.buffer(1)
330
+ sample = sample.clip(mask).sample(1)
338
331
 
339
- if get_geom_type(sample) == "polygon":
340
- random_point = sample.sample_points(size=1)
332
+ random_point = sample.sample_points(size=1)
341
333
 
342
- # if point or mixed geometries
343
- else:
344
- random_point = sample.centroid
334
+ center = (random_point.geometry.iloc[0].x, random_point.geometry.iloc[0].y)
335
+ print(f"center={center}, size={size}")
345
336
 
346
- center = (random_point.geometry.iloc[0].x, random_point.geometry.iloc[0].y)
347
- print(f"center={center}, size={size}")
337
+ mask = random_point.buffer(size)
348
338
 
349
- m._gdf = m._gdf.clip(random_point.buffer(size))
350
-
351
- qtm(m._gdf, column=m.column, cmap=m._cmap, k=m.k)
339
+ return clipmap(
340
+ *gdfs,
341
+ column=column,
342
+ mask=mask,
343
+ browser=browser,
344
+ max_zoom=max_zoom,
345
+ explore=explore,
346
+ smooth_factor=smooth_factor,
347
+ max_images=max_images,
348
+ **kwargs,
349
+ )
352
350
 
353
351
 
354
352
  def clipmap(
355
353
  *gdfs: GeoDataFrame,
356
354
  column: str | None = None,
357
355
  mask: GeoDataFrame | GeoSeries | Geometry = None,
358
- labels: tuple[str] | None = None,
359
356
  explore: bool = True,
360
357
  max_zoom: int = 40,
361
358
  smooth_factor: int | float = 1.5,
362
359
  browser: bool = False,
360
+ max_images: int = 15,
363
361
  **kwargs,
364
- ) -> None:
362
+ ) -> Explore | Map:
365
363
  """Shows an interactive map of a of GeoDataFrames clipped to the mask extent.
366
364
 
367
365
  It takes all the GeoDataFrames specified, clips them to the extent of the mask,
@@ -378,9 +376,6 @@ def clipmap(
378
376
  mask: the geometry to clip the data by.
379
377
  column: The column to color the geometries by. Defaults to None, which means
380
378
  each GeoDataFrame will get a unique color.
381
- labels: By default, the GeoDataFrames will be labeled by their object names.
382
- Alternatively, labels can be specified as a tuple of strings the same
383
- length as the number of gdfs.
384
379
  max_zoom: The maximum allowed level of zoom. Higher number means more zoom
385
380
  allowed. Defaults to 30, which is higher than the geopandas default.
386
381
  smooth_factor: How much to simplify the geometries. 1 is the minimum,
@@ -389,22 +384,29 @@ def clipmap(
389
384
  or not in Jupyter, a static plot will be shown.
390
385
  browser: If False (default), the maps will be shown in Jupyter.
391
386
  If True the maps will be opened in a browser folder.
387
+ max_images: Maximum number of images (Image, ImageCollection, Band) to show per
388
+ map. Defaults to 15.
392
389
  **kwargs: Keyword arguments to pass to geopandas.GeoDataFrame.explore, for
393
390
  instance 'cmap' to change the colors, 'scheme' to change how the data
394
391
  is grouped. This defaults to 'fisherjenkssampled' for numeric data.
395
392
 
396
- See also
397
- --------
393
+ See Also:
394
+ ---------
398
395
  explore: same functionality, but shows the entire area of the geometries.
399
396
  samplemap: same functionality, but shows only a random area of a given size.
400
397
  """
401
-
402
398
  gdfs, column, kwargs = Map._separate_args(gdfs, column, kwargs)
403
399
 
404
400
  if mask is None and len(gdfs) > 1:
405
401
  mask = gdfs[-1]
406
402
  gdfs = gdfs[:-1]
407
403
 
404
+ if not isinstance(mask, (GeoDataFrame | GeoSeries | Geometry | tuple)):
405
+ try:
406
+ mask = to_gdf(mask)
407
+ except Exception:
408
+ mask = to_shapely(to_bbox(mask))
409
+
408
410
  center = kwargs.pop("center", None)
409
411
  size = kwargs.pop("size", None)
410
412
 
@@ -412,29 +414,31 @@ def clipmap(
412
414
  m = Explore(
413
415
  *gdfs,
414
416
  column=column,
415
- labels=labels,
416
417
  browser=browser,
417
418
  max_zoom=max_zoom,
418
419
  smooth_factor=smooth_factor,
420
+ max_images=max_images,
419
421
  **kwargs,
420
422
  )
421
- if m.gdfs is None and not len(m.raster_datasets):
422
- return
423
+ m.mask = mask
424
+
425
+ if m.gdfs is None and not len(m.rasters):
426
+ return m
423
427
 
424
428
  m._gdfs = [gdf.clip(mask) for gdf in m._gdfs]
425
429
  m._gdf = m._gdf.clip(mask)
426
430
  m._nan_idx = m._gdf[m._column].isna()
427
431
  m._get_unique_values()
428
432
  m.explore(center=center, size=size)
433
+ return m
429
434
  else:
430
435
  m = Map(
431
436
  *gdfs,
432
437
  column=column,
433
- labels=labels,
434
438
  **kwargs,
435
439
  )
436
440
  if m.gdfs is None:
437
- return
441
+ return m
438
442
 
439
443
  m._gdfs = [gdf.clip(mask) for gdf in m._gdfs]
440
444
  m._gdf = m._gdf.clip(mask)
@@ -443,8 +447,10 @@ def clipmap(
443
447
 
444
448
  qtm(m._gdf, column=m.column, cmap=m._cmap, k=m.k)
445
449
 
450
+ return m
451
+
446
452
 
447
- def explore_locals(*gdfs, convert: bool = True, **kwargs):
453
+ def explore_locals(*gdfs: GeoDataFrame, convert: bool = True, **kwargs) -> None:
448
454
  """Displays all local variables with geometries (GeoDataFrame etc.).
449
455
 
450
456
  Local means inside a function or file/notebook.
@@ -478,17 +484,17 @@ def explore_locals(*gdfs, convert: bool = True, **kwargs):
478
484
 
479
485
  if isinstance(value, dict) or hasattr(value, "__dict__"):
480
486
  # add dicts or classes with GeoDataFrames to kwargs
481
- for key, value in as_dict(value).items():
482
- if isinstance(value, allowed_types):
483
- gdf = clean_geoms(to_gdf_func(value))
487
+ for key, val in as_dict(value).items():
488
+ if isinstance(val, allowed_types):
489
+ gdf = clean_geoms(to_gdf(val))
484
490
  if len(gdf):
485
491
  local_gdfs[key] = gdf
486
492
 
487
- elif isinstance(value, dict) or hasattr(value, "__dict__"):
493
+ elif isinstance(val, dict) or hasattr(val, "__dict__"):
488
494
  try:
489
- for k, v in value.items():
495
+ for k, v in val.items():
490
496
  if isinstance(v, allowed_types):
491
- gdf = clean_geoms(to_gdf_func(v))
497
+ gdf = clean_geoms(to_gdf(v))
492
498
  if len(gdf):
493
499
  local_gdfs[k] = gdf
494
500
  except Exception:
@@ -497,7 +503,7 @@ def explore_locals(*gdfs, convert: bool = True, **kwargs):
497
503
 
498
504
  continue
499
505
  try:
500
- gdf = clean_geoms(to_gdf_func(value))
506
+ gdf = clean_geoms(to_gdf(value))
501
507
  if len(gdf):
502
508
  local_gdfs[name] = gdf
503
509
  continue
@@ -512,7 +518,7 @@ def explore_locals(*gdfs, convert: bool = True, **kwargs):
512
518
  if not frame:
513
519
  break
514
520
 
515
- explore(*gdfs, **local_gdfs, **kwargs)
521
+ return explore(*gdfs, **local_gdfs, **kwargs)
516
522
 
517
523
 
518
524
  def qtm(
@@ -543,12 +549,13 @@ def qtm(
543
549
  'viridis' when black, and 'RdPu' when white.
544
550
  size: Size of the plot. Defaults to 10.
545
551
  title_fontsize: Size of the title.
552
+ legend: Whether to add legend. Defaults to True.
546
553
  cmap: Color palette of the map. See:
547
554
  https://matplotlib.org/stable/tutorials/colors/colormaps.html
548
555
  k: Number of color groups.
549
556
  **kwargs: Additional keyword arguments taken by the geopandas plot method.
550
557
 
551
- See also:
558
+ See Also:
552
559
  ThematicMap: Class with more options for customising the plot.
553
560
  """
554
561
  gdfs, column, kwargs = Map._separate_args(gdfs, column, kwargs)
@@ -561,9 +568,6 @@ def qtm(
561
568
  else:
562
569
  new_kwargs[key] = value
563
570
 
564
- # self.labels.append(key)
565
- # self.show.append(last_show)
566
-
567
571
  m = ThematicMap(*gdfs, column=column, size=size, black=black)
568
572
 
569
573
  if m._gdfs is None: