ssb-sgis 1.0.2__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.
- sgis/__init__.py +10 -6
- sgis/exceptions.py +2 -2
- sgis/geopandas_tools/bounds.py +17 -15
- sgis/geopandas_tools/buffer_dissolve_explode.py +24 -5
- sgis/geopandas_tools/conversion.py +15 -6
- sgis/geopandas_tools/duplicates.py +2 -2
- sgis/geopandas_tools/general.py +9 -5
- sgis/geopandas_tools/geometry_types.py +3 -3
- sgis/geopandas_tools/neighbors.py +3 -3
- sgis/geopandas_tools/point_operations.py +2 -2
- sgis/geopandas_tools/polygon_operations.py +5 -5
- sgis/geopandas_tools/sfilter.py +3 -3
- sgis/helpers.py +3 -3
- sgis/io/read_parquet.py +1 -1
- sgis/maps/examine.py +16 -2
- sgis/maps/explore.py +370 -57
- sgis/maps/legend.py +164 -72
- sgis/maps/map.py +184 -90
- sgis/maps/maps.py +92 -90
- sgis/maps/thematicmap.py +236 -83
- sgis/networkanalysis/closing_network_holes.py +2 -2
- sgis/networkanalysis/cutting_lines.py +3 -3
- sgis/networkanalysis/directednetwork.py +1 -1
- sgis/networkanalysis/finding_isolated_networks.py +2 -2
- sgis/networkanalysis/networkanalysis.py +7 -7
- sgis/networkanalysis/networkanalysisrules.py +1 -1
- sgis/networkanalysis/traveling_salesman.py +1 -1
- sgis/parallel/parallel.py +39 -19
- sgis/raster/__init__.py +0 -6
- sgis/raster/cube.py +51 -5
- sgis/raster/image_collection.py +2560 -0
- sgis/raster/indices.py +14 -5
- sgis/raster/raster.py +131 -236
- sgis/raster/sentinel_config.py +104 -0
- sgis/raster/zonal.py +0 -1
- {ssb_sgis-1.0.2.dist-info → ssb_sgis-1.0.3.dist-info}/METADATA +1 -1
- ssb_sgis-1.0.3.dist-info/RECORD +61 -0
- sgis/raster/methods_as_functions.py +0 -0
- sgis/raster/torchgeo.py +0 -171
- ssb_sgis-1.0.2.dist-info/RECORD +0 -61
- {ssb_sgis-1.0.2.dist-info → ssb_sgis-1.0.3.dist-info}/LICENSE +0 -0
- {ssb_sgis-1.0.2.dist-info → ssb_sgis-1.0.3.dist-info}/WHEEL +0 -0
sgis/maps/map.py
CHANGED
|
@@ -4,6 +4,8 @@ This module holds the Map class, which is the basis for the Explore class.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import warnings
|
|
7
|
+
from collections.abc import Sequence
|
|
8
|
+
from statistics import mean
|
|
7
9
|
from typing import Any
|
|
8
10
|
|
|
9
11
|
import matplotlib
|
|
@@ -22,6 +24,10 @@ from ..geopandas_tools.general import clean_geoms
|
|
|
22
24
|
from ..geopandas_tools.general import drop_inactive_geometry_columns
|
|
23
25
|
from ..geopandas_tools.general import get_common_crs
|
|
24
26
|
from ..helpers import get_object_name
|
|
27
|
+
from ..helpers import unit_is_meters
|
|
28
|
+
from ..raster.image_collection import Band
|
|
29
|
+
from ..raster.image_collection import Image
|
|
30
|
+
from ..raster.image_collection import ImageCollection
|
|
25
31
|
|
|
26
32
|
try:
|
|
27
33
|
from torchgeo.datasets.geo import RasterDataset
|
|
@@ -91,12 +97,12 @@ class Map:
|
|
|
91
97
|
self,
|
|
92
98
|
*gdfs: GeoDataFrame,
|
|
93
99
|
column: str | None = None,
|
|
94
|
-
labels: tuple[str] | None = None,
|
|
95
100
|
k: int = 5,
|
|
96
101
|
bins: tuple[float] | None = None,
|
|
97
102
|
nan_label: str = "Missing",
|
|
98
103
|
nan_color="#c2c2c2",
|
|
99
104
|
scheme: str = DEFAULT_SCHEME,
|
|
105
|
+
cmap: str | None = None,
|
|
100
106
|
**kwargs,
|
|
101
107
|
) -> None:
|
|
102
108
|
"""Initialiser.
|
|
@@ -104,12 +110,13 @@ class Map:
|
|
|
104
110
|
Args:
|
|
105
111
|
*gdfs: Variable length GeoDataFrame list.
|
|
106
112
|
column: The column name to work with.
|
|
107
|
-
labels: Tuple of labels for each GeoDataFrame.
|
|
108
113
|
k: Number of bins or classes for classification (default: 5).
|
|
109
114
|
bins: Predefined bins for data classification.
|
|
110
115
|
nan_label: Label for missing data.
|
|
111
116
|
nan_color: Color for missing data.
|
|
112
117
|
scheme: Classification scheme to be used.
|
|
118
|
+
cmap (str): Colormap of the plot. See:
|
|
119
|
+
https://matplotlib.org/stable/tutorials/colors/colormaps.html
|
|
113
120
|
**kwargs: Arbitrary keyword arguments.
|
|
114
121
|
"""
|
|
115
122
|
gdfs, column, kwargs = self._separate_args(gdfs, column, kwargs)
|
|
@@ -119,27 +126,14 @@ class Map:
|
|
|
119
126
|
self._k = k
|
|
120
127
|
self.nan_label = nan_label
|
|
121
128
|
self.nan_color = nan_color
|
|
122
|
-
self._cmap =
|
|
129
|
+
self._cmap = cmap
|
|
123
130
|
self.scheme = scheme
|
|
124
131
|
|
|
125
|
-
if not all(isinstance(gdf, GeoDataFrame) for gdf in gdfs):
|
|
126
|
-
gdfs = [
|
|
127
|
-
to_gdf(gdf) if not isinstance(gdf, GeoDataFrame) else gdf
|
|
128
|
-
for gdf in gdfs
|
|
129
|
-
]
|
|
130
|
-
if not all(isinstance(gdf, GeoDataFrame) for gdf in gdfs):
|
|
131
|
-
raise ValueError("gdfs must be GeoDataFrames.")
|
|
132
|
-
|
|
133
|
-
if "namedict" in kwargs:
|
|
134
|
-
for i, gdf in enumerate(gdfs):
|
|
135
|
-
gdf.name = kwargs["namedict"][i]
|
|
136
|
-
kwargs.pop("namedict")
|
|
137
|
-
|
|
138
132
|
# need to get the object names of the gdfs before copying. Only getting,
|
|
139
133
|
# not setting, labels. So the original gdfs don't get the label column.
|
|
140
|
-
self.labels =
|
|
141
|
-
|
|
142
|
-
|
|
134
|
+
self.labels: list[str] = [
|
|
135
|
+
_determine_best_name(gdf, column, i) for i, gdf in enumerate(gdfs)
|
|
136
|
+
]
|
|
143
137
|
|
|
144
138
|
show = kwargs.pop("show", True)
|
|
145
139
|
if isinstance(show, (int, bool)):
|
|
@@ -165,31 +159,18 @@ class Map:
|
|
|
165
159
|
if not len(gdf):
|
|
166
160
|
continue
|
|
167
161
|
|
|
168
|
-
self._gdfs.append(gdf)
|
|
162
|
+
self._gdfs.append(to_gdf(gdf))
|
|
169
163
|
new_labels.append(label)
|
|
170
164
|
self.show.append(show)
|
|
171
165
|
self.labels = new_labels
|
|
172
166
|
|
|
173
|
-
# if len(self._gdfs):
|
|
174
|
-
# last_show = self.show[-1]
|
|
175
|
-
# else:
|
|
176
|
-
# last_show = show
|
|
177
|
-
|
|
178
167
|
# pop all geometry-like items from kwargs into self._gdfs
|
|
179
168
|
self.kwargs = {}
|
|
180
169
|
i = 0
|
|
181
170
|
for key, value in kwargs.items():
|
|
182
|
-
# if isinstance(value, GeoDataFrame):
|
|
183
|
-
# self._gdfs.append(value)
|
|
184
|
-
# self.labels.append(key)
|
|
185
|
-
# try:
|
|
186
|
-
# show = show_kwargs[i]
|
|
187
|
-
# except IndexError:
|
|
188
|
-
# pass
|
|
189
|
-
# self.show.append(show)
|
|
190
|
-
# i += 1
|
|
191
|
-
# continue
|
|
192
171
|
try:
|
|
172
|
+
if not len(value):
|
|
173
|
+
continue
|
|
193
174
|
self._gdfs.append(to_gdf(value))
|
|
194
175
|
self.labels.append(key)
|
|
195
176
|
try:
|
|
@@ -208,10 +189,10 @@ class Map:
|
|
|
208
189
|
)
|
|
209
190
|
|
|
210
191
|
if not any(len(gdf) for gdf in self._gdfs):
|
|
211
|
-
|
|
212
|
-
self._gdfs = None
|
|
192
|
+
self._gdfs = []
|
|
213
193
|
self._is_categorical = True
|
|
214
194
|
self._unique_values = []
|
|
195
|
+
self._nan_idx = []
|
|
215
196
|
return
|
|
216
197
|
|
|
217
198
|
if not self.labels:
|
|
@@ -262,7 +243,7 @@ class Map:
|
|
|
262
243
|
Because floats don't always equal each other. This will make very
|
|
263
244
|
similar values count as the same value in the color classification.
|
|
264
245
|
"""
|
|
265
|
-
array = self._gdf.loc[~self._nan_idx, self._column]
|
|
246
|
+
array = self._gdf.loc[list(~self._nan_idx), self._column]
|
|
266
247
|
self._min = np.min(array)
|
|
267
248
|
self._max = np.max(array)
|
|
268
249
|
self._get_multiplier(array)
|
|
@@ -313,29 +294,70 @@ class Map:
|
|
|
313
294
|
# make sure they are lists
|
|
314
295
|
bins = [bin_ for bin_ in bins]
|
|
315
296
|
|
|
316
|
-
if min(bins) > 0 and min(
|
|
317
|
-
|
|
318
|
-
):
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
297
|
+
if min(bins) > 0 and min(
|
|
298
|
+
self._gdf.loc[list(~self._nan_idx), self._column]
|
|
299
|
+
) < min(bins):
|
|
300
|
+
num = min(self._gdf.loc[list(~self._nan_idx), self._column])
|
|
301
|
+
# if isinstance(num, float):
|
|
302
|
+
# num -= (
|
|
303
|
+
# float(f"1e-{abs(self.legend.rounding)}")
|
|
304
|
+
# if self.legend and self.legend.rounding
|
|
305
|
+
# else 0
|
|
306
|
+
# )
|
|
307
|
+
bins = [num] + bins
|
|
308
|
+
|
|
309
|
+
if min(bins) < 0 and min(
|
|
310
|
+
self._gdf.loc[list(~self._nan_idx), self._column]
|
|
311
|
+
) < min(bins):
|
|
312
|
+
num = min(self._gdf.loc[list(~self._nan_idx), self._column])
|
|
313
|
+
# if isinstance(num, float):
|
|
314
|
+
# num -= (
|
|
315
|
+
# float(f"1e-{abs(self.legend.rounding)}")
|
|
316
|
+
# if self.legend and self.legend.rounding
|
|
317
|
+
# else 0
|
|
318
|
+
# )
|
|
319
|
+
bins = [num] + bins
|
|
325
320
|
|
|
326
321
|
if max(bins) > 0 and max(
|
|
327
322
|
self._gdf.loc[self._gdf[self._column].notna(), self._column]
|
|
328
323
|
) > max(bins):
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
324
|
+
num = max(self._gdf.loc[self._gdf[self._column].notna(), self._column])
|
|
325
|
+
# if isinstance(num, float):
|
|
326
|
+
# num += (
|
|
327
|
+
# float(f"1e-{abs(self.legend.rounding)}")
|
|
328
|
+
# if self.legend and self.legend.rounding
|
|
329
|
+
# else 0
|
|
330
|
+
# )
|
|
331
|
+
bins = bins + [num]
|
|
332
332
|
|
|
333
333
|
if max(bins) < 0 and max(
|
|
334
334
|
self._gdf.loc[self._gdf[self._column].notna(), self._column]
|
|
335
335
|
) < max(bins):
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
336
|
+
num = max(self._gdf.loc[self._gdf[self._column].notna(), self._column])
|
|
337
|
+
# if isinstance(num, float):
|
|
338
|
+
# num += (
|
|
339
|
+
# float(f"1e-{abs(self.legend.rounding)}")
|
|
340
|
+
# if self.legend and self.legend.rounding
|
|
341
|
+
# else 0
|
|
342
|
+
# )
|
|
343
|
+
|
|
344
|
+
bins = bins + [num]
|
|
345
|
+
|
|
346
|
+
def adjust_bin(num: int | float, i: int) -> int | float:
|
|
347
|
+
if isinstance(num, int):
|
|
348
|
+
return num
|
|
349
|
+
adjuster = (
|
|
350
|
+
float(f"1e-{abs(self.legend.rounding)}")
|
|
351
|
+
if self.legend and self.legend.rounding
|
|
352
|
+
else 0
|
|
353
|
+
)
|
|
354
|
+
if i == 0:
|
|
355
|
+
return num - adjuster
|
|
356
|
+
elif i == len(bins) - 1:
|
|
357
|
+
return num + adjuster
|
|
358
|
+
return num
|
|
359
|
+
|
|
360
|
+
bins = [adjust_bin(x, i) for i, x in enumerate(bins)]
|
|
339
361
|
|
|
340
362
|
return bins
|
|
341
363
|
|
|
@@ -347,16 +369,26 @@ class Map:
|
|
|
347
369
|
) -> tuple[tuple[GeoDataFrame], str]:
|
|
348
370
|
"""Separate GeoDataFrames from string (column argument)."""
|
|
349
371
|
|
|
350
|
-
def as_dict(obj):
|
|
372
|
+
def as_dict(obj) -> dict:
|
|
351
373
|
if hasattr(obj, "__dict__"):
|
|
352
374
|
return obj.__dict__
|
|
353
375
|
elif isinstance(obj, dict):
|
|
354
376
|
return obj
|
|
355
|
-
raise TypeError
|
|
356
|
-
|
|
357
|
-
allowed_types = (
|
|
377
|
+
raise TypeError(type(obj))
|
|
378
|
+
|
|
379
|
+
allowed_types = (
|
|
380
|
+
GeoDataFrame,
|
|
381
|
+
GeoSeries,
|
|
382
|
+
Geometry,
|
|
383
|
+
RasterDataset,
|
|
384
|
+
ImageCollection,
|
|
385
|
+
Image,
|
|
386
|
+
Band,
|
|
387
|
+
)
|
|
358
388
|
|
|
359
|
-
gdfs
|
|
389
|
+
gdfs = ()
|
|
390
|
+
more_gdfs = {}
|
|
391
|
+
i = 0
|
|
360
392
|
for arg in args:
|
|
361
393
|
if isinstance(arg, str):
|
|
362
394
|
if column is None:
|
|
@@ -367,12 +399,31 @@ class Map:
|
|
|
367
399
|
)
|
|
368
400
|
elif isinstance(arg, allowed_types):
|
|
369
401
|
gdfs = gdfs + (arg,)
|
|
402
|
+
# elif isinstance(arg, Sequence) and not isinstance(arg, str):
|
|
370
403
|
elif isinstance(arg, dict) or hasattr(arg, "__dict__"):
|
|
371
404
|
# add dicts or classes with GeoDataFrames to kwargs
|
|
372
|
-
more_gdfs = {}
|
|
373
405
|
for key, value in as_dict(arg).items():
|
|
374
406
|
if isinstance(value, allowed_types):
|
|
375
407
|
more_gdfs[key] = value
|
|
408
|
+
elif isinstance(value, dict) or hasattr(value, "__dict__"):
|
|
409
|
+
# elif isinstance(value, Sequence) and not isinstance(value, str):
|
|
410
|
+
try:
|
|
411
|
+
# same as above, one level down
|
|
412
|
+
more_gdfs |= {
|
|
413
|
+
k: v
|
|
414
|
+
for k, v in as_dict(value).items()
|
|
415
|
+
if isinstance(v, allowed_types)
|
|
416
|
+
}
|
|
417
|
+
except Exception:
|
|
418
|
+
# ignore all exceptions
|
|
419
|
+
pass
|
|
420
|
+
|
|
421
|
+
elif isinstance(arg, Sequence) and not isinstance(arg, str):
|
|
422
|
+
# add dicts or classes with GeoDataFrames to kwargs
|
|
423
|
+
for value in arg:
|
|
424
|
+
if isinstance(value, allowed_types):
|
|
425
|
+
name = _determine_best_name(value, column, i)
|
|
426
|
+
more_gdfs[name] = value
|
|
376
427
|
elif isinstance(value, dict) or hasattr(value, "__dict__"):
|
|
377
428
|
try:
|
|
378
429
|
# same as above, one level down
|
|
@@ -384,8 +435,15 @@ class Map:
|
|
|
384
435
|
except Exception:
|
|
385
436
|
# no need to raise here
|
|
386
437
|
pass
|
|
438
|
+
elif isinstance(value, Sequence) and not isinstance(value, str):
|
|
439
|
+
for x in value:
|
|
440
|
+
if not isinstance(x, allowed_types):
|
|
441
|
+
continue
|
|
442
|
+
name = _determine_best_name(value, column, i)
|
|
443
|
+
more_gdfs[name] = x
|
|
444
|
+
i += 1
|
|
387
445
|
|
|
388
|
-
|
|
446
|
+
kwargs |= more_gdfs
|
|
389
447
|
|
|
390
448
|
return gdfs, column, kwargs
|
|
391
449
|
|
|
@@ -394,7 +452,7 @@ class Map:
|
|
|
394
452
|
if self.scheme is None:
|
|
395
453
|
return
|
|
396
454
|
|
|
397
|
-
if
|
|
455
|
+
if self.bins is None:
|
|
398
456
|
self.bins = self._create_bins(self._gdf, self._column)
|
|
399
457
|
if len(self.bins) <= self._k and len(self.bins) != len(self._unique_values):
|
|
400
458
|
self._k = len(self.bins)
|
|
@@ -406,17 +464,6 @@ class Map:
|
|
|
406
464
|
self._unique_values = self.nan_label
|
|
407
465
|
self._k = 1
|
|
408
466
|
|
|
409
|
-
def _get_labels(self, gdfs: tuple[GeoDataFrame]) -> None:
|
|
410
|
-
"""Putting the labels/names in a list before copying the gdfs."""
|
|
411
|
-
self.labels: list[str] = []
|
|
412
|
-
for i, gdf in enumerate(gdfs):
|
|
413
|
-
if hasattr(gdf, "name") and isinstance(gdf.name, str):
|
|
414
|
-
name = gdf.name
|
|
415
|
-
else:
|
|
416
|
-
name = get_object_name(gdf)
|
|
417
|
-
name = name or str(i)
|
|
418
|
-
self.labels.append(name)
|
|
419
|
-
|
|
420
467
|
def _set_labels(self) -> None:
|
|
421
468
|
"""Setting the labels after copying the gdfs."""
|
|
422
469
|
gdfs = []
|
|
@@ -470,7 +517,18 @@ class Map:
|
|
|
470
517
|
if not self._column:
|
|
471
518
|
return True
|
|
472
519
|
|
|
520
|
+
def is_maybe_km2():
|
|
521
|
+
if "area" in self._column and (
|
|
522
|
+
"km2" in self._column
|
|
523
|
+
or "kilomet" in self._column
|
|
524
|
+
and ("sq" in self._column or "2" in self._column)
|
|
525
|
+
):
|
|
526
|
+
return True
|
|
527
|
+
else:
|
|
528
|
+
return False
|
|
529
|
+
|
|
473
530
|
maybe_area = 1 if "area" in self._column else 0
|
|
531
|
+
maybe_area_km2 = 1 if is_maybe_km2() else 0
|
|
474
532
|
maybe_length = (
|
|
475
533
|
1 if any(x in self._column for x in ["meter", "metre", "leng"]) else 0
|
|
476
534
|
)
|
|
@@ -479,7 +537,10 @@ class Map:
|
|
|
479
537
|
col_not_present = 0
|
|
480
538
|
for gdf in self._gdfs:
|
|
481
539
|
if self._column not in gdf:
|
|
482
|
-
if
|
|
540
|
+
if maybe_area_km2 and unit_is_meters(gdf):
|
|
541
|
+
gdf["area_km2"] = gdf.area / 1_000_000
|
|
542
|
+
maybe_area_km2 += 1
|
|
543
|
+
elif maybe_area:
|
|
483
544
|
gdf["area"] = gdf.area
|
|
484
545
|
maybe_area += 1
|
|
485
546
|
elif maybe_length:
|
|
@@ -492,6 +553,9 @@ class Map:
|
|
|
492
553
|
all_nan += 1
|
|
493
554
|
return True
|
|
494
555
|
|
|
556
|
+
if maybe_area_km2 > 1:
|
|
557
|
+
self._column = "area_km2"
|
|
558
|
+
return False
|
|
495
559
|
if maybe_area > 1:
|
|
496
560
|
self._column = "area"
|
|
497
561
|
return False
|
|
@@ -507,7 +571,7 @@ class Map:
|
|
|
507
571
|
|
|
508
572
|
return False
|
|
509
573
|
|
|
510
|
-
def
|
|
574
|
+
def _make_categories_colors_dict(self) -> None:
|
|
511
575
|
# custom categorical cmap
|
|
512
576
|
if not self._cmap and len(self._unique_values) <= len(_CATEGORICAL_CMAP):
|
|
513
577
|
self._categories_colors_dict = {
|
|
@@ -529,6 +593,7 @@ class Map:
|
|
|
529
593
|
for i, category in enumerate(self._unique_values)
|
|
530
594
|
}
|
|
531
595
|
|
|
596
|
+
def _fix_nans(self) -> None:
|
|
532
597
|
if any(self._nan_idx):
|
|
533
598
|
self._gdf[self._column] = self._gdf[self._column].fillna(self.nan_label)
|
|
534
599
|
self._categories_colors_dict[self.nan_label] = self.nan_color
|
|
@@ -549,7 +614,7 @@ class Map:
|
|
|
549
614
|
If 'scheme' is not specified, the jenks_breaks function is used, which is
|
|
550
615
|
much faster than the one from Mapclassifier.
|
|
551
616
|
"""
|
|
552
|
-
if not len(gdf.loc[~self._nan_idx, column]):
|
|
617
|
+
if not len(gdf.loc[list(~self._nan_idx), column]):
|
|
553
618
|
return np.array([0])
|
|
554
619
|
|
|
555
620
|
n_classes = (
|
|
@@ -565,29 +630,26 @@ class Map:
|
|
|
565
630
|
n_classes = len(self._unique_values)
|
|
566
631
|
|
|
567
632
|
if self.scheme == "jenks":
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
)
|
|
572
|
-
bins = self._add_minmax_to_bins(bins)
|
|
573
|
-
except Exception:
|
|
574
|
-
pass
|
|
633
|
+
bins = jenks_breaks(
|
|
634
|
+
gdf.loc[list(~self._nan_idx), column], n_classes=n_classes
|
|
635
|
+
)
|
|
575
636
|
else:
|
|
576
637
|
binning = classify(
|
|
577
|
-
np.asarray(gdf.loc[~self._nan_idx, column]),
|
|
638
|
+
np.asarray(gdf.loc[list(~self._nan_idx), column]),
|
|
578
639
|
scheme=self.scheme,
|
|
579
|
-
k=self._k,
|
|
640
|
+
# k=self._k,
|
|
641
|
+
k=n_classes,
|
|
580
642
|
)
|
|
581
643
|
bins = binning.bins
|
|
582
|
-
|
|
644
|
+
|
|
645
|
+
bins = self._add_minmax_to_bins(bins)
|
|
583
646
|
|
|
584
647
|
unique_bins = list({round(bin_, 5) for bin_ in bins})
|
|
585
648
|
unique_bins.sort()
|
|
586
649
|
|
|
587
|
-
if self._k == len(self._unique_values) - 1
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
if len(unique_bins) == len(self._unique_values):
|
|
650
|
+
if self._k == len(self._unique_values) - 1 or len(unique_bins) == len(
|
|
651
|
+
self._unique_values
|
|
652
|
+
):
|
|
591
653
|
return np.array(unique_bins)
|
|
592
654
|
|
|
593
655
|
if len(unique_bins) == len(bins) - 1:
|
|
@@ -624,6 +686,8 @@ class Map:
|
|
|
624
686
|
|
|
625
687
|
def _classify_from_bins(self, gdf: GeoDataFrame, bins: np.ndarray) -> np.ndarray:
|
|
626
688
|
"""Place the column values into groups."""
|
|
689
|
+
bins = bins.copy()
|
|
690
|
+
|
|
627
691
|
# if equal lenght, convert to integer and check for equality
|
|
628
692
|
if len(bins) == len(self._unique_values):
|
|
629
693
|
if gdf[self._column].isna().all():
|
|
@@ -638,6 +702,14 @@ class Map:
|
|
|
638
702
|
if len(bins) == self._k + 1:
|
|
639
703
|
bins = bins[1:]
|
|
640
704
|
|
|
705
|
+
if (
|
|
706
|
+
self.legend
|
|
707
|
+
and self.legend.rounding
|
|
708
|
+
and (self.legend.rounding or 1) <= 0
|
|
709
|
+
):
|
|
710
|
+
bins[0] = bins[0] - 1
|
|
711
|
+
bins[-1] = bins[-1] + 1
|
|
712
|
+
|
|
641
713
|
if gdf[self._column].isna().all():
|
|
642
714
|
return np.repeat(len(bins), len(gdf))
|
|
643
715
|
|
|
@@ -686,7 +758,8 @@ class Map:
|
|
|
686
758
|
@cmap.setter
|
|
687
759
|
def cmap(self, new_value: str) -> None:
|
|
688
760
|
self._cmap = new_value
|
|
689
|
-
|
|
761
|
+
if not self._is_categorical:
|
|
762
|
+
self.change_cmap(cmap=new_value, start=self.cmap_start, stop=self.cmap_stop)
|
|
690
763
|
|
|
691
764
|
@property
|
|
692
765
|
def gdf(self) -> GeoDataFrame:
|
|
@@ -738,3 +811,24 @@ class Map:
|
|
|
738
811
|
return self[key]
|
|
739
812
|
except (KeyError, ValueError, IndexError, AttributeError):
|
|
740
813
|
return default
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
def _determine_best_name(obj: Any, column: str | None, i: int) -> str:
|
|
817
|
+
try:
|
|
818
|
+
# Frame 3: actual object name Frame 2: maps.py:explore(). Frame 1: __init__. Frame 0: this function.
|
|
819
|
+
return str(get_object_name(obj, start=3))
|
|
820
|
+
except ValueError:
|
|
821
|
+
if isinstance(obj, GeoSeries) and obj.name:
|
|
822
|
+
return str(obj.name)
|
|
823
|
+
elif isinstance(obj, GeoDataFrame) and len(obj.columns) == 2 and not column:
|
|
824
|
+
series = obj.drop(columns=obj._geometry_column_name).iloc[:, 0]
|
|
825
|
+
if (
|
|
826
|
+
len(series.unique()) == 1
|
|
827
|
+
and mean(isinstance(x, str) for x in series) > 0.5
|
|
828
|
+
):
|
|
829
|
+
return str(next(iter(series)))
|
|
830
|
+
elif series.name:
|
|
831
|
+
return str(series.name)
|
|
832
|
+
else:
|
|
833
|
+
# generic label e.g. Image(1)
|
|
834
|
+
return f"{obj.__class__.__name__}({i})"
|