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/thematicmap.py CHANGED
@@ -1,86 +1,153 @@
1
1
  """Make static maps with geopandas and matplotlib."""
2
+
2
3
  import warnings
4
+ from typing import Any
3
5
 
4
6
  import matplotlib
7
+ import matplotlib.figure
5
8
  import matplotlib.pyplot as plt
6
9
  import numpy as np
7
10
  import pandas as pd
8
11
  from geopandas import GeoDataFrame
9
12
 
10
- from .legend import ContinousLegend, Legend
13
+ from .legend import LEGEND_KWARGS
14
+ from .legend import ContinousLegend
15
+ from .legend import Legend
16
+ from .legend import prettify_bins
11
17
  from .map import Map
12
18
 
13
-
14
19
  # the geopandas._explore raises a deprication warning. Ignoring for now.
15
20
  warnings.filterwarnings(
16
21
  action="ignore", category=matplotlib.MatplotlibDeprecationWarning
17
22
  )
18
23
  pd.options.mode.chained_assignment = None
19
24
 
25
+ MAP_KWARGS = {
26
+ "bins",
27
+ "title",
28
+ "title_fontsize",
29
+ "size",
30
+ "cmap",
31
+ "cmap_start",
32
+ "cmap_stop",
33
+ "scheme",
34
+ "k",
35
+ "column",
36
+ "title_color",
37
+ "facecolor",
38
+ "labelcolor",
39
+ "nan_color",
40
+ "title_kwargs",
41
+ "bg_gdf_color",
42
+ "title_position",
43
+ }
20
44
 
21
- class ThematicMap(Map):
22
- """Class for creating static maps with geopandas and matplotlib.
23
45
 
24
- The class takes one or more GeoDataFrames and a column name. The class attributes
25
- can then be set to customise the map before plotting.
46
+ class ThematicMap(Map):
47
+ """Class for making static maps.
26
48
 
27
49
  Args:
28
50
  *gdfs: One or more GeoDataFrames.
29
51
  column: The name of the column to plot.
52
+ title: Title of the plot.
53
+ title_position: Title position. Either "center" (default), "left" or "right".
30
54
  size: Width and height of the plot in inches. Fontsize of title and legend is
31
55
  adjusted accordingly. Defaults to 25.
32
- black: If False (default), the background will be white and the text black. If
56
+ dark: If False (default), the background will be white and the text black. If
33
57
  True, the background will be black and the text white. When True, the
34
58
  default cmap is "viridis", and when False, the default is red to purple
35
59
  (RdPu).
36
-
37
- Attributes:
38
- size (int): Width and height of the plot in inches.
39
- k (int): Number of color groups.
40
- legend (Legend): The legend object of the map. The legend holds its own set of
41
- attributes. See the Legend class for details.
42
- title (str): Title of the plot.
43
- title_color (str): Color of the title font.
44
- title_fontsize (int): Color of the title font.
45
- bins (list[int | float]): For numeric columns. List of numbers that define the
46
- maximum value for the color groups.
47
- cmap (str): Colormap of the plot. See:
60
+ cmap: Colormap of the plot. See:
48
61
  https://matplotlib.org/stable/tutorials/colors/colormaps.html
49
- cmap_start (int): Start position for the color palette.
50
- cmap_stop (int): End position for the color palette.
51
- facecolor (str): Background color.
52
-
53
- Examples
62
+ scheme: How to devide numeric values into categories. Defaults to
63
+ "naturalbreaks".
64
+ k: Number of color groups.
65
+ bins: For numeric columns. List of numbers that define the
66
+ maximum value for the color groups.
67
+ nan_label: Label for missing data.
68
+ legend_kwargs: dictionary with attributes for the legend. E.g.:
69
+ title: Legend title. Defaults to the column name.
70
+ rounding: If positive number, it will round floats to n decimals.
71
+ If negative, eg. -2, the number 3429 is rounded to 3400.
72
+ By default, the rounding depends on the column's maximum value
73
+ and standard deviation.
74
+ position: The legend's x and y position in the plot. By default, it's
75
+ decided dynamically by finding the space with most distance to
76
+ the geometries. To be specified as a tuple of
77
+ x and y position between 0 and 1. E.g. position=(0.8, 0.2) for a position
78
+ in the bottom right corner, (0.2, 0.8) for the upper left corner.
79
+ pretty_labels: Whether to capitalize words in text categories.
80
+ label_suffix: For numeric columns. The text to put after each number
81
+ in the legend labels. Defaults to None.
82
+ label_sep: For numeric columns. Text to put in between the two numbers
83
+ in each color group in the legend. Defaults to '-'.
84
+ thousand_sep: For numeric columns. Separator between each thousand for
85
+ large numbers. Defaults to None, meaning no separator.
86
+ decimal_mark: For numeric columns. Text to use as decimal point.
87
+ Defaults to None, meaning '.' (dot) unless 'thousand_sep' is
88
+ '.'. In this case, ',' (comma) will be used as decimal mark.
89
+ **kwargs: Additional attributes for the map. E.g.:
90
+ title_color (str): Color of the title font.
91
+ title_fontsize (int): Color of the title font.
92
+ cmap_start (int): Start position for the color palette.
93
+ cmap_stop (int): End position for the color palette.
94
+ facecolor (str): Background color.
95
+ labelcolor (str): Color for the labels.
96
+ nan_color: Color for missing data.
97
+
98
+ Examples:
54
99
  --------
55
100
  >>> import sgis as sg
56
- >>> points = sg.random_points(100).pipe(sg.buff, np.random.rand(100))
57
- >>> points2 = sg.random_points(100).pipe(sg.buff, np.random.rand(100))
101
+ >>> points = sg.random_points(100, loc=1000).pipe(sg.buff, np.random.rand(100) * 100)
102
+ >>> points2 = sg.random_points(100, loc=1000).pipe(sg.buff, np.random.rand(100) * 100)
103
+
58
104
 
59
105
  Simple plot with legend and title.
60
106
 
61
- >>> m = sg.ThematicMap(points, points2, "area")
62
- >>> m.title = "Area of random circles"
107
+ >>> m = sg.ThematicMap(points, points2, column="area", title="Area of random circles")
63
108
  >>> m.plot()
64
109
 
65
- Plot with custom legend units (label_suffix) and separator (label_sep).
66
-
67
- >>> m = sg.ThematicMap(points, points2, "area")
68
- >>> m.title = "Area of random circles"
69
- >>> m.legend.label_suffix = "m2"
70
- >>> m.legend.label_sep = "to"
110
+ Plot with custom legend units (label_suffix) and thousand separator.
111
+ And with rounding set to -2, meaning e.g. 3429 is rounded to 3400.
112
+ If rounding was set to positive 2, 3429 would be rounded to 3429.00.
113
+
114
+ >>> m = sg.ThematicMap(
115
+ ... points,
116
+ ... points2,
117
+ ... column="area",
118
+ ... title = "Area of random circles",
119
+ ... legend_kwargs=dict(
120
+ ... rounding=-2,
121
+ ... thousand_sep=" ",
122
+ ... label_sep="to",
123
+ ... ),
124
+ ... )
71
125
  >>> m.plot()
72
126
 
73
- With custom bins and legend labels.
74
-
75
- >>> m = sg.ThematicMap(points, points2, "area")
76
- >>> m.title = "Area of random circles"
77
- >>> m.bins = [1, 2, 3]
78
- >>> m.legend.labels = [
79
- ... f"{int(round(min(points.length),0))} to 1",
80
- ... "1 to 2",
81
- ... "2 to 3",
82
- ... f"3 to {int(round(max(points.length),0))}",
83
- ... ]
127
+ With custom bins for the categories, and other customizations.
128
+
129
+ >>> m = sg.ThematicMap(
130
+ ... points,
131
+ ... points2,
132
+ ... column="area",
133
+ ... cmap="Greens",
134
+ ... cmap_start=50,
135
+ ... cmap_stop=255,
136
+ ... nan_label="Missing",
137
+ ... title = "Area of random circles",
138
+ ... bins = [5000, 10000, 15000, 20000],
139
+ ... title_kwargs=dict(
140
+ ... loc="left",
141
+ ... y=0.93,
142
+ ... x=0.025,
143
+ ... ),
144
+ ... legend_kwargs=dict(
145
+ ... thousand_sep=" ",
146
+ ... label_sep="to",
147
+ ... decimal_mark=".",
148
+ ... label_suffix="m2",
149
+ ... ),
150
+ ... )
84
151
  >>> m.plot()
85
152
  """
86
153
 
@@ -88,25 +155,101 @@ class ThematicMap(Map):
88
155
  self,
89
156
  *gdfs: GeoDataFrame,
90
157
  column: str | None = None,
158
+ title: str | None = None,
159
+ title_position: tuple[float, float] | None = None,
91
160
  size: int = 25,
92
- black: bool = False,
93
- ):
94
- super().__init__(*gdfs, column=column)
161
+ dark: bool = False,
162
+ cmap: str | None = None,
163
+ scheme: str = "naturalbreaks",
164
+ k: int = 5,
165
+ bins: tuple[float] | None = None,
166
+ nan_label: str = "Missing",
167
+ legend_kwargs: dict | None = None,
168
+ title_kwargs: dict | None = None,
169
+ **kwargs,
170
+ ) -> None:
171
+ """Initialiser."""
172
+ super().__init__(
173
+ *gdfs,
174
+ column=column,
175
+ scheme=scheme,
176
+ k=k,
177
+ bins=bins,
178
+ nan_label=nan_label,
179
+ )
95
180
 
181
+ self.title = title
96
182
  self._size = size
97
- self._black = black
183
+ self._dark = dark
184
+ self.title_kwargs = title_kwargs or {}
185
+ if title_position and "position" in self.title_kwargs:
186
+ raise TypeError(
187
+ "Specify either 'title_position' or title_kwargs position, not both."
188
+ )
189
+ if title_position or "position" in self.title_kwargs:
190
+ position = self.title_kwargs.pop("position", title_position)
191
+ error_mess = (
192
+ "legend_kwargs position should be a two length tuple/list with two numbers between "
193
+ "0 and 1 (x, y position)"
194
+ )
195
+ if not hasattr(position, "__len__"):
196
+ raise TypeError(error_mess)
197
+ if len(position) != 2:
198
+ raise ValueError(error_mess)
199
+ x, y = position
200
+ if "loc" not in self.title_kwargs:
201
+ if x < 0.4:
202
+ self.title_kwargs["loc"] = "left"
203
+ elif x > 0.6:
204
+ self.title_kwargs["loc"] = "right"
205
+ else:
206
+ self.title_kwargs["loc"] = "center"
207
+
208
+ self.title_kwargs["x"], self.title_kwargs["y"] = x, y
98
209
  self.background_gdfs = []
99
210
 
100
- self._title_fontsize = self._size * 2
211
+ legend_kwargs = legend_kwargs or {}
101
212
 
102
- self.black = black
213
+ self._title_fontsize = self._size * 1.9
103
214
 
104
- if not self._is_categorical:
215
+ black = kwargs.pop("black", None)
216
+ self._dark = self._dark or black
217
+
218
+ if not self.cmap and not self._is_categorical:
105
219
  self._choose_cmap()
106
220
 
221
+ self._dark_or_light()
107
222
  self._create_legend()
108
223
 
109
- def change_cmap(self, cmap: str, start: int = 0, stop: int = 256):
224
+ if cmap:
225
+ self._cmap = cmap
226
+
227
+ for key, value in kwargs.items():
228
+ if key not in MAP_KWARGS:
229
+ raise TypeError(
230
+ f"{self.__class__.__name__} got an unexpected keyword argument {key}"
231
+ )
232
+ try:
233
+ setattr(self, key, value)
234
+ except Exception:
235
+ setattr(self, f"_{key}", value)
236
+
237
+ for key, value in legend_kwargs.items():
238
+ if key not in LEGEND_KWARGS:
239
+ raise TypeError(
240
+ f"{self.__class__.__name__} legend_kwargs got an unexpected key {key}"
241
+ )
242
+ try:
243
+ setattr(self.legend, key, value)
244
+ except Exception:
245
+ setattr(self.legend, f"_{key}", value)
246
+
247
+ @property
248
+ def valid_keywords(self) -> set[str]:
249
+ """List all valid keywords for the class initialiser."""
250
+ return MAP_KWARGS
251
+
252
+ def change_cmap(self, cmap: str, start: int = 0, stop: int = 256) -> "ThematicMap":
110
253
  """Change the color palette of the plot.
111
254
 
112
255
  Args:
@@ -119,7 +262,9 @@ class ThematicMap(Map):
119
262
  super().change_cmap(cmap, start, stop)
120
263
  return self
121
264
 
122
- def add_background(self, gdf, color: str | None = None):
265
+ def add_background(
266
+ self, gdf: GeoDataFrame, color: str | None = None
267
+ ) -> "ThematicMap":
123
268
  """Add a GeoDataFrame as a background layer.
124
269
 
125
270
  Args:
@@ -145,7 +290,6 @@ class ThematicMap(Map):
145
290
 
146
291
  This method should be run after customising the map, but before saving.
147
292
  """
148
-
149
293
  __test = kwargs.pop("__test", False)
150
294
  include_legend = bool(kwargs.pop("legend", self.legend))
151
295
 
@@ -170,7 +314,7 @@ class ThematicMap(Map):
170
314
  else:
171
315
  kwargs = self._prepare_continous_plot(kwargs)
172
316
  if self.legend:
173
- if not self.legend._rounding_has_been_set:
317
+ if not self.legend.rounding:
174
318
  self.legend._rounding = self.legend._get_rounding(
175
319
  array=self._gdf.loc[~self._nan_idx, self._column]
176
320
  )
@@ -187,15 +331,15 @@ class ThematicMap(Map):
187
331
  self._gdf, k=self._k + bool(len(self._nan_idx))
188
332
  )
189
333
 
190
- if __test:
191
- return
192
-
193
334
  self._prepare_plot(**kwargs)
194
335
 
195
336
  if self.legend:
196
337
  self.ax = self.legend._actually_add_legend(ax=self.ax)
197
338
 
198
- self._gdf.plot(legend=include_legend, ax=self.ax, **kwargs)
339
+ self.ax = self._gdf.plot(legend=include_legend, ax=self.ax, **kwargs)
340
+
341
+ if __test:
342
+ return self
199
343
 
200
344
  def save(self, path: str) -> None:
201
345
  """Save figure as image file.
@@ -214,7 +358,7 @@ class ThematicMap(Map):
214
358
  with fs.open(path, "wb") as file:
215
359
  plt.savefig(file)
216
360
 
217
- def _prepare_plot(self, **kwargs):
361
+ def _prepare_plot(self, **kwargs) -> None:
218
362
  """Add figure and axis, title and background gdf."""
219
363
  for attr in self.__dict__.keys():
220
364
  if attr in self.kwargs:
@@ -231,12 +375,16 @@ class ThematicMap(Map):
231
375
  if hasattr(self, "_background_gdfs"):
232
376
  self._actually_add_background()
233
377
 
234
- if hasattr(self, "title") and self.title:
378
+ if self.title:
235
379
  self.ax.set_title(
236
- self.title, fontsize=self.title_fontsize, color=self.title_color
380
+ self.title,
381
+ **(
382
+ dict(fontsize=self.title_fontsize, color=self.title_color)
383
+ | self.title_kwargs
384
+ ),
237
385
  )
238
386
 
239
- def _prepare_continous_plot(self, kwargs) -> dict:
387
+ def _prepare_continous_plot(self, kwargs: dict) -> dict:
240
388
  """Create bins and colors."""
241
389
  self._prepare_continous_map()
242
390
 
@@ -250,6 +398,13 @@ class ThematicMap(Map):
250
398
  return kwargs
251
399
 
252
400
  else:
401
+ if self.legend.rounding and self.legend.rounding < 0:
402
+ self.bins = prettify_bins(self.bins, self.legend.rounding)
403
+ self.bins = list({round(bin_, 5) for bin_ in self.bins})
404
+ self.bins.sort()
405
+ # self.legend._rounding_was = self.legend.rounding
406
+ # self.legend.rounding = None
407
+
253
408
  classified = self._classify_from_bins(self._gdf, bins=self.bins)
254
409
  classified_sequential = self._push_classification(classified)
255
410
  n_colors = len(np.unique(classified_sequential)) - any(self._nan_idx)
@@ -257,10 +412,13 @@ class ThematicMap(Map):
257
412
  self._bins_unique_values = self._make_bin_value_dict(
258
413
  self._gdf, classified_sequential
259
414
  )
415
+
260
416
  colorarray = self._unique_colors[classified_sequential]
261
417
  kwargs["color"] = colorarray
262
418
 
263
- if self.legend and not self.legend._rounding_has_been_set:
419
+ if (
420
+ self.legend and self.legend.rounding
421
+ ): # not self.legend._rounding_has_been_set:
264
422
  self.bins = self.legend._set_rounding(
265
423
  bins=self.bins, rounding=self.legend._rounding
266
424
  )
@@ -270,12 +428,15 @@ class ThematicMap(Map):
270
428
 
271
429
  return kwargs
272
430
 
273
- def _prepare_categorical_plot(self, kwargs) -> dict:
431
+ def _prepare_categorical_plot(self, kwargs: dict) -> dict:
274
432
  """Map values to colors."""
275
- self._get_categorical_colors()
276
- colorarray = self._gdf["color"]
433
+ self._make_categories_colors_dict()
434
+ if self._gdf is not None and len(self._gdf):
435
+ self._fix_nans()
277
436
 
278
- kwargs["color"] = colorarray
437
+ if self._gdf is not None:
438
+ colorarray = self._gdf["color"]
439
+ kwargs["color"] = colorarray
279
440
  return kwargs
280
441
 
281
442
  def _actually_add_legend(self) -> None:
@@ -300,10 +461,10 @@ class ThematicMap(Map):
300
461
  bin_values=self._bins_unique_values,
301
462
  )
302
463
 
303
- def _create_legend(self):
464
+ def _create_legend(self) -> None:
304
465
  """Instantiate the Legend class."""
305
466
  kwargs = {}
306
- if self._black:
467
+ if self._dark:
307
468
  kwargs["facecolor"] = "#0f0f0f"
308
469
  kwargs["labelcolor"] = "#fefefe"
309
470
  kwargs["title_color"] = "#fefefe"
@@ -313,9 +474,9 @@ class ThematicMap(Map):
313
474
  else:
314
475
  self.legend = ContinousLegend(title=self._column, size=self._size, **kwargs)
315
476
 
316
- def _choose_cmap(self):
317
- """kwargs is to catch start and stop points for the cmap in __init__."""
318
- if self._black:
477
+ def _choose_cmap(self) -> None:
478
+ """Kwargs is to catch start and stop points for the cmap in __init__."""
479
+ if self._dark:
319
480
  self._cmap = "viridis"
320
481
  self.cmap_start = 0
321
482
  self.cmap_stop = 256
@@ -324,7 +485,7 @@ class ThematicMap(Map):
324
485
  self.cmap_start = 23
325
486
  self.cmap_stop = 256
326
487
 
327
- def _make_bin_value_dict(self, gdf, classified) -> dict:
488
+ def _make_bin_value_dict(self, gdf: GeoDataFrame, classified: np.ndarray) -> dict:
328
489
  """Dict with unique values of all bins. Used in labels in ContinousLegend."""
329
490
  bins_unique_values = {
330
491
  i: list(set(gdf.loc[classified == i, self._column]))
@@ -332,19 +493,21 @@ class ThematicMap(Map):
332
493
  }
333
494
  return bins_unique_values
334
495
 
335
- def _actually_add_background(self):
496
+ def _actually_add_background(self) -> None:
336
497
  self.ax.set_xlim([self.minx - self.diffx * 0.03, self.maxx + self.diffx * 0.03])
337
498
  self.ax.set_ylim([self.miny - self.diffy * 0.03, self.maxy + self.diffy * 0.03])
338
499
  self._background_gdfs.plot(ax=self.ax, color=self.bg_gdf_color)
339
500
 
340
501
  @staticmethod
341
- def _get_matplotlib_figure_and_axix(figsize: tuple[int, int]):
502
+ def _get_matplotlib_figure_and_axix(
503
+ figsize: tuple[int, int]
504
+ ) -> tuple[matplotlib.figure.Figure, matplotlib.axes.Axes]:
342
505
  fig = plt.figure(figsize=figsize)
343
506
  ax = fig.add_subplot(1, 1, 1)
344
507
  return fig, ax
345
508
 
346
- def _black_or_white(self):
347
- if self._black:
509
+ def _dark_or_light(self) -> None:
510
+ if self._dark:
348
511
  self.facecolor, self.title_color, self.bg_gdf_color = (
349
512
  "#0f0f0f",
350
513
  "#fefefe",
@@ -358,38 +521,40 @@ class ThematicMap(Map):
358
521
  self.facecolor, self.title_color, self.bg_gdf_color = (
359
522
  "#fefefe",
360
523
  "#0f0f0f",
361
- "#ebebeb",
524
+ "#dbdbdb",
362
525
  )
363
526
  self.nan_color = "#c2c2c2"
364
527
  if not self._is_categorical:
365
528
  self.change_cmap("RdPu", start=23)
366
529
 
367
- self._create_legend()
368
-
369
530
  @property
370
- def black(self):
371
- return self._black
372
-
373
- @black.setter
374
- def black(self, new_value: bool):
375
- self._black = new_value
376
- self._black_or_white()
531
+ def dark(self) -> bool:
532
+ """Whether to use dark background and light text colors."""
533
+ return self._dark
534
+
535
+ @dark.setter
536
+ def dark(self, new_value: bool):
537
+ self._dark = new_value
538
+ self._dark_or_light()
539
+ self._create_legend()
377
540
 
378
541
  @property
379
- def title_fontsize(self):
542
+ def title_fontsize(self) -> int:
543
+ """Title fontsize, not to be confused with legend.title_fontsize."""
380
544
  return self._title_fontsize
381
545
 
382
546
  @title_fontsize.setter
383
- def title_fontsize(self, new_value: bool):
547
+ def title_fontsize(self, new_value: int) -> None:
384
548
  self._title_fontsize = new_value
385
549
  self._title_fontsize_has_been_set = True
386
550
 
387
551
  @property
388
- def size(self):
552
+ def size(self) -> int:
553
+ """Size of the image."""
389
554
  return self._size
390
555
 
391
556
  @size.setter
392
- def size(self, new_value: bool):
557
+ def size(self, new_value: bool) -> None:
393
558
  """Adjust font and marker size if not actively set."""
394
559
  self._size = new_value
395
560
  if not hasattr(self, "_title_fontsize_has_been_set"):
@@ -403,7 +568,8 @@ class ThematicMap(Map):
403
568
  if not hasattr(self.legend, "_markersize_has_been_set"):
404
569
  self.legend._markersize = self._size
405
570
 
406
- def __setattr__(self, __name: str, __value) -> None:
571
+ def __setattr__(self, __name: str, __value: Any) -> None:
572
+ """Set an attribute with square brackets."""
407
573
  if "legend_" in __name:
408
574
  last_part = __name.split("legend_")[-1]
409
575
  raise AttributeError(
sgis/maps/tilesources.py CHANGED
@@ -1,4 +1,6 @@
1
- from xyzservices import TileProvider, Bunch, providers
1
+ from xyzservices import Bunch
2
+ from xyzservices import TileProvider
3
+ from xyzservices import providers
2
4
 
3
5
  kartverket = Bunch(
4
6
  norgeskart=TileProvider(
@@ -7,49 +9,42 @@ kartverket = Bunch(
7
9
  attribution="© Kartverket",
8
10
  html_attribution='&copy; <a href="https://kartverket.no">Kartverket</a>',
9
11
  ),
10
-
11
12
  bakgrunnskart_forenklet=TileProvider(
12
13
  name="Norgeskart forenklet",
13
14
  url="https://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=bakgrunnskart_forenklet&zoom={z}&x={x}&y={y}",
14
15
  attribution="© Kartverket",
15
16
  html_attribution='&copy; <a href="https://kartverket.no">Kartverket</a>',
16
17
  ),
17
-
18
18
  norges_grunnkart=TileProvider(
19
19
  name="Norges grunnkart",
20
20
  url="https://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=norges_grunnkart&zoom={z}&x={x}&y={y}",
21
21
  attribution="© Kartverket",
22
22
  html_attribution='&copy; <a href="https://kartverket.no">Kartverket</a>',
23
23
  ),
24
-
25
24
  norges_grunnkart_gråtone=TileProvider(
26
25
  name="Norges grunnkart gråtone",
27
26
  url="https://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=norges_grunnkart_graatone&zoom={z}&x={x}&y={y}",
28
27
  attribution="© Kartverket",
29
28
  html_attribution='&copy; <a href="https://kartverket.no">Kartverket</a>',
30
29
  ),
31
-
32
30
  n50=TileProvider(
33
31
  name="N5 til N50 kartdata",
34
32
  url="https://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=kartdata3&zoom={z}&x={x}&y={y}",
35
33
  attribution="© Kartverket",
36
34
  html_attribution='&copy; <a href="https://kartverket.no">Kartverket</a>',
37
35
  ),
38
-
39
36
  topogråtone=TileProvider(
40
37
  name="Topografisk norgeskart gråtone",
41
38
  url="https://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=topo4graatone&zoom={z}&x={x}&y={y}",
42
39
  attribution="© Kartverket",
43
40
  html_attribution='&copy; <a href="https://kartverket.no">Kartverket</a>',
44
41
  ),
45
-
46
42
  toporaster=TileProvider(
47
43
  name="Topografisk raster",
48
44
  url="https://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=toporaster4&zoom={z}&x={x}&y={y}",
49
45
  attribution="© Kartverket",
50
46
  html_attribution='&copy; <a href="https://kartverket.no">Kartverket</a>',
51
47
  ),
52
-
53
48
  norge_i_bilder=TileProvider(
54
49
  name="Norge i bilder",
55
50
  url="https://opencache.statkart.no/gatekeeper/gk/gk.open_nib_web_mercator_wmts_v2?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=Nibcache_web_mercator_v2&STYLE=default&FORMAT=image/jpgpng&tileMatrixSet=default028mm&tileMatrix={z}&tileRow={y}&tileCol={x}",
@@ -7,7 +7,7 @@ from pandas import DataFrame
7
7
 
8
8
 
9
9
  def _get_route_frequencies(
10
- graph,
10
+ graph: Graph,
11
11
  roads: GeoDataFrame,
12
12
  weight_df: DataFrame,
13
13
  ) -> GeoDataFrame:
@@ -60,7 +60,6 @@ def _get_route(
60
60
  od_pairs: pd.MultiIndex,
61
61
  ) -> GeoDataFrame:
62
62
  """Function used in the get_route method of NetworkAnalysis."""
63
-
64
63
  warnings.filterwarnings("ignore", category=RuntimeWarning)
65
64
 
66
65
  resultlist: list[DataFrame] = []
@@ -86,7 +85,8 @@ def _get_route(
86
85
  if not resultlist:
87
86
  warnings.warn(
88
87
  "No paths were found. Try larger search_tolerance or search_factor. "
89
- "Or close_network_holes() or remove_isolated()."
88
+ "Or close_network_holes() or remove_isolated().",
89
+ stacklevel=1,
90
90
  )
91
91
  return pd.DataFrame(columns=["origin", "destination", weight, "geometry"])
92
92
 
@@ -121,7 +121,8 @@ def _get_k_routes(
121
121
  if not resultlist:
122
122
  warnings.warn(
123
123
  "No paths were found. Try larger search_tolerance or search_factor. "
124
- "Or close_network_holes() or remove_isolated()."
124
+ "Or close_network_holes() or remove_isolated().",
125
+ stacklevel=1,
125
126
  )
126
127
  return pd.DataFrame(columns=["origin", "destination", weight, "geometry"])
127
128