ssb-sgis 0.1.4__py3-none-any.whl → 0.1.6__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/maps/legend.py CHANGED
@@ -5,18 +5,15 @@ class.
5
5
 
6
6
  """
7
7
  import warnings
8
- from statistics import mean
9
8
 
10
9
  import matplotlib
11
10
  import matplotlib.pyplot as plt
12
11
  import numpy as np
13
12
  import pandas as pd
14
- from geopandas import GeoDataFrame
15
13
  from matplotlib.lines import Line2D
16
14
  from pandas import Series
17
15
 
18
- from ..geopandas_tools.general import points_in_bounds, to_gdf
19
- from ..geopandas_tools.point_operations import snap_all
16
+ from ..geopandas_tools.general import points_in_bounds
20
17
 
21
18
 
22
19
  # the geopandas._explore raises a deprication warning. Ignoring for now.
@@ -27,24 +24,18 @@ pd.options.mode.chained_assignment = None
27
24
 
28
25
 
29
26
  class Legend:
30
- """Holds the attributes of the legend in the ThematicMap class.
27
+ """Holds the general attributes of the legend in the ThematicMap class.
31
28
 
32
- This class is stored in the 'legend' attribute of the ThematicMap class.
29
+ This class holds attributes of the 'legend' attribute of the ThematicMap class.
33
30
  The fontsize, title_fontsize and markersize attributes are adjusted
34
31
  according to the size attribute of the ThematicMap.
35
32
 
36
- The attributes 'label_suffix', 'label_sep' and 'rounding' only apply to plots
37
- of numeric columns.
38
-
39
- The 'labels' attribute can be used to set labels manually. By default, the
40
- maximum and minimum values of each color group is used as label for numeric
41
- columns. For categorical columns, the column values are used.
33
+ If a numeric column is used, additional attributes can be found in the
34
+ ContinousLegend class.
42
35
 
43
36
  Attributes:
44
37
  title: Legend title. Defaults to the column name if used in the
45
38
  ThematicMap class.
46
- labels: To manually set labels for the color groups. Must be a list/tuple of
47
- same length as the number of color groups (k).
48
39
  position: The legend's x and y position in the plot, specified as a tuple of
49
40
  x and y position between 0 and 1. E.g. position=(0.8, 0.2) for a position
50
41
  in the bottom right corner, (0.2, 0.8) for the upper left corner.
@@ -56,13 +47,6 @@ class Legend:
56
47
  the ThematicMap class.
57
48
  framealpha: Transparency of the legend background.
58
49
  edgecolor: Color of the legend border. Defaults to #0f0f0f (almost black).
59
- label_suffix: For numeric columns. The text to put after each number in the
60
- legend labels.
61
- label_sep: For numeric columns. Text to put in between the two numbers in each
62
- color group in the legend.
63
- rounding: For numeric columns. Number of decimals in the legend labels. By
64
- default the rounding depends on the column's maximum value and standard
65
- deviation.
66
50
  kwargs: Stores additional keyword arguments taken by the matplotlib legend
67
51
  method. Specify this as e.g. m.legend.kwargs["labelcolor"] = "red", where
68
52
  'm' is the name of the ThematicMap instance. See here:
@@ -88,9 +72,12 @@ class Legend:
88
72
  8 POINT (0.12215 0.96588) 8
89
73
  9 POINT (0.02938 0.93467) 9
90
74
 
91
- Creating the ThematicMap instance.
75
+ Creating the ThematicMap instance will also create the legend. Since we
76
+ specify a numeric column, a ContinousLegend instance is created.
92
77
 
93
78
  >>> m = sg.ThematicMap(points, column="number")
79
+ >>> m.legend
80
+ <sgis.maps.legend.ContinousLegend object at 0x00000222206738D0>
94
81
 
95
82
  Changing the attributes that apply to both numeric and categorical columns.
96
83
 
@@ -99,34 +86,26 @@ class Legend:
99
86
  >>> m.legend.fontsize = 9
100
87
  >>> m.legend.markersize = 7.5
101
88
  >>> m.legend.position = (0.35, 0.28)
102
- >>> m.legend.kwargs["labelcolor"] = "red"
103
89
  >>> m.plot()
104
90
 
105
- Changing the additional attributes that only apply only to numeric columns.
91
+ Additional matplotlib keyword arguments can be specified as kwargs.
92
+
93
+ >>> m.legend.kwargs["labelcolor"] = "red"
94
+
95
+ Since we are using a numeric column, the legend is of type ContinousLegend.
96
+ We can therefore also access the attributes that only apply to numeric columns.
106
97
 
107
- >>> m = sg.ThematicMap(points, column="number")
108
98
  >>> m.label_sep = "to"
109
99
  >>> m.label_suffix = "num"
110
100
  >>> m.rounding = 2
111
101
  >>> m.plot()
112
102
 
113
- The final attribute, labels, should be changed along with the bins attribute
114
- of the ThematicMap class. The following bins will create a plot with the color
115
- groups 0-2, 3-5, 6-7 and 8-9. The legend labels can then be set accordingly.
116
-
117
- >>> m.bins = [2, 5, 7]
118
- >>> m.legend.labels = ["0 to 2 num", "3 to 5 num", "6 to 7 num", "8 to 9 num"]
119
- >>> m.plot()
120
-
121
103
  """
122
104
 
123
105
  def __init__(
124
106
  self,
125
107
  title: str | None = None,
126
108
  labels: list[str] | None = None,
127
- label_suffix: str = "",
128
- label_sep: str = "-",
129
- rounding: int | None = None,
130
109
  position: tuple[float] | None = None,
131
110
  markersize: int | None = None,
132
111
  fontsize: int | None = None,
@@ -152,14 +131,10 @@ class Legend:
152
131
  self.title_color = kwargs.pop("title_color", None)
153
132
  self.labelspacing = kwargs.pop("labelspacing", 0.8)
154
133
 
155
- self.label_suffix = label_suffix
156
- self.label_sep = label_sep
157
134
  self.labels = labels
158
- self._rounding = rounding
159
135
  self._position = position
160
136
  self.kwargs = kwargs
161
137
  self._position_has_been_set = True if position else False
162
- self._rounding_has_been_set = True if rounding else False
163
138
 
164
139
  def _get_legend_sizes(self, size, kwargs):
165
140
  """Adjust fontsize and markersize to size kwarg."""
@@ -182,128 +157,17 @@ class Legend:
182
157
  else:
183
158
  self._markersize = size
184
159
 
185
- def _get_rounding(self, array: Series | np.ndarray) -> int:
186
- def isinteger(x):
187
- return np.equal(np.mod(x, 1), 0)
188
-
189
- if np.all(isinteger(array)):
190
- return 0
191
- if np.max(array) > 30 and np.std(array) > 5:
192
- return 0
193
- if np.max(array) > 5 and np.std(array) > 1:
194
- return 1
195
- if np.max(array) > 1 and np.std(array) > 0.1:
196
- return 2
197
- return int(abs(np.log10(np.std(array)))) + 1
198
-
199
- @staticmethod
200
- def _set_rounding(bins, rounding: int | float):
201
- if rounding == 0:
202
- return [int(round(bin, 0)) for bin in bins]
203
- else:
204
- return [round(bin, rounding) for bin in bins]
205
-
206
- def _remove_max_legend_value(self):
207
- if not self._legend:
208
- raise ValueError("Cannot modify legend before it is created.")
209
-
210
- def _actually_add_continous_legend(
211
- self,
212
- ax,
213
- bins: list[float],
214
- colors: list[str],
215
- nan_label: str,
216
- bin_values: dict,
217
- ):
218
- for attr in self.__dict__.keys():
219
- if attr in self.kwargs:
220
- self[attr] = self.kwargs.pop(attr)
221
-
222
- self._patches, self._categories = [], []
223
-
224
- for color in colors:
225
- self._patches.append(
226
- Line2D(
227
- [0],
228
- [0],
229
- linestyle="none",
230
- marker="o",
231
- alpha=self.kwargs.get("alpha", 1),
232
- markersize=self._markersize,
233
- markerfacecolor=color,
234
- markeredgewidth=0,
235
- )
236
- )
237
-
238
- if self.labels:
239
- if len(self.labels) != len(colors):
240
- raise ValueError(
241
- f"Label list must be same length as 'k'. Got k={len(colors)} and "
242
- f"labels={len(self.labels)}"
243
- )
244
- self._categories = self.labels
245
-
246
- elif len(bins) == len(colors):
247
- for i, _ in enumerate(bins):
248
- min_ = np.min(bin_values[i])
249
- max_ = np.max(bin_values[i])
250
- min_rounded = self._set_rounding([min_], self._rounding)[0]
251
- max_rounded = self._set_rounding([max_], self._rounding)[0]
252
- if min_ == max_:
253
- self._categories.append(f"{min_rounded} {self.label_suffix}")
254
- else:
255
- self._categories.append(
256
- f"{min_rounded} {self.label_suffix} {self.label_sep} "
257
- f"{max_rounded} {self.label_suffix}"
258
- )
259
-
260
- else:
261
- for i, (cat1, cat2) in enumerate(zip(bins[:-1], bins[1:], strict=True)):
262
- if nan_label in str(cat1) or nan_label in str(cat2):
263
- self._categories.append(nan_label)
264
- else:
265
- min_ = np.min(bin_values[i])
266
- max_ = np.max(bin_values[i])
267
- min_rounded = self._set_rounding([min_], self._rounding)[0]
268
- max_rounded = self._set_rounding([max_], self._rounding)[0]
269
- if min_ == max_:
270
- self._categories.append(f"{min_rounded} {self.label_suffix}")
271
- else:
272
- self._categories.append(
273
- f"{min_rounded} {self.label_suffix} {self.label_sep} "
274
- f"{max_rounded} {self.label_suffix}"
275
- )
276
-
277
- legend = ax.legend(
278
- self._patches,
279
- self._categories,
280
- fontsize=self._fontsize,
281
- title=self.title,
282
- title_fontsize=self._title_fontsize,
283
- bbox_to_anchor=self._position + (self.width, self.height),
284
- fancybox=False,
285
- framealpha=self.framealpha,
286
- edgecolor=self.edgecolor,
287
- labelspacing=self.labelspacing,
288
- **self.kwargs,
289
- )
290
-
291
- if self.title_color:
292
- plt.setp(legend.get_title(), color=self.title_color)
293
-
294
- return ax
295
-
296
- def _actually_add_categorical_legend(
297
- self, ax, categories_colors: dict, nan_label: str
298
- ):
160
+ def _prepare_categorical_legend(self, categories_colors: dict, nan_label: str):
299
161
  for attr in self.__dict__.keys():
300
162
  if attr in self.kwargs:
301
163
  self[attr] = self.kwargs.pop(attr)
302
164
 
165
+ # swap column values with label values if labels is dict
303
166
  if self.labels and isinstance(self.labels, dict):
304
167
  categories_colors = {
305
168
  self.labels[cat]: color for cat, color in categories_colors.items()
306
169
  }
170
+ # swap column values with label list and hope it's in the correct order
307
171
  elif self.labels:
308
172
  categories_colors = {
309
173
  label: color
@@ -330,6 +194,8 @@ class Legend:
330
194
  markeredgewidth=0,
331
195
  )
332
196
  )
197
+
198
+ def _actually_add_legend(self, ax):
333
199
  legend = ax.legend(
334
200
  self._patches,
335
201
  self._categories,
@@ -380,6 +246,9 @@ class Legend:
380
246
  def __getitem__(self, item):
381
247
  return getattr(self, item)
382
248
 
249
+ def __setitem__(self, key, value):
250
+ setattr(self, key, value)
251
+
383
252
  def get(self, key, default=None):
384
253
  try:
385
254
  return self[key]
@@ -395,15 +264,6 @@ class Legend:
395
264
  self._position = new_value
396
265
  self._position_has_been_set = True
397
266
 
398
- @property
399
- def rounding(self):
400
- return self._rounding
401
-
402
- @rounding.setter
403
- def rounding(self, new_value: bool):
404
- self._rounding = new_value
405
- self._rounding_has_been_set = True
406
-
407
267
  @property
408
268
  def title_fontsize(self):
409
269
  return self._title_fontsize
@@ -432,58 +292,295 @@ class Legend:
432
292
  self._markersize_has_been_set = True
433
293
 
434
294
 
435
- class _ContinousLegend(Legend):
295
+ class ContinousLegend(Legend):
436
296
  """Holds the legend attributes specific to numeric columns.
437
297
 
438
- This class is stored in the 'legend' attribute of the ThematicMap class.
439
- The fontsize, title_fontsize and markersize attributes are adjusted
440
- according to the size attribute of the ThematicMap.
441
-
442
- The 'labels' attribute can be used to set labels manually. By default, the
443
- maximum and minimum values of each color group is used as label.
298
+ The attributes consern the labeling of the groups in the legend.
299
+ Labels can be set manually with the 'labels' attribute, or the format
300
+ of the labels can be changed with the remaining attributes.
444
301
 
445
302
  Attributes:
446
- labels: To manually set labels for the color groups. Must be a list/tuple of
447
- same length as the number of color groups (k).
448
- label_suffix: For numeric columns. The text to put after each number in the
449
- legend labels.
450
- label_sep: For numeric columns. Text to put in between the two numbers in each
451
- color group in the legend.
452
- rounding: Number of decimals for numeric columns. By default the rounding
453
- depends on the column's
303
+ labels: To manually set labels. If set, all other labeling attributes are
304
+ ignored. Should be given as a list of strings with the same length as
305
+ the number of color groups.
306
+ pretty_labels: If False (default), the minimum and maximum values of each
307
+ color group will be used as legend labels. If True, the labels will end
308
+ with the maximum value, but start at 1 + the maximum value of the previous
309
+ group. The labels will be correct but inaccurate.
310
+ label_suffix: The text to put after each number in the legend labels.
311
+ Defaults to None.
312
+ label_sep: Text to put in between the two numbers in each color group in
313
+ the legend. Defaults to '-'.
314
+ rounding: Number of decimals in the labels. By default, the rounding
315
+ depends on the column's maximum value and standard deviation.
316
+ OBS: The bins will not be rounded, meaning the labels might be wrong
317
+ if not bins are set manually.
318
+ thousand_sep: Separator between each thousand for large numbers. Defaults to
319
+ None, meaning no separator.
320
+ decimal_mark: Text to use as decimal point. Defaults to None, meaning '.' (dot)
321
+ unless 'thousand_sep' is '.'. In this case, ',' (comma) will be used as
322
+ decimal mark.
454
323
 
455
324
  Examples
456
325
  --------
457
- """
326
+ Create ten random points with a numeric column from 0 to 9.
458
327
 
459
- pass
328
+ >>> import sgis as sg
329
+ >>> points = sg.random_points(10)
330
+ >>> points["number"] = range(10)
331
+ >>> points
332
+ geometry number
333
+ 0 POINT (0.59780 0.50425) 0
334
+ 1 POINT (0.07019 0.26167) 1
335
+ 2 POINT (0.56475 0.15422) 2
336
+ 3 POINT (0.87293 0.60316) 3
337
+ 4 POINT (0.47373 0.20040) 4
338
+ 5 POINT (0.98661 0.15614) 5
339
+ 6 POINT (0.30951 0.77057) 6
340
+ 7 POINT (0.47802 0.52824) 7
341
+ 8 POINT (0.12215 0.96588) 8
342
+ 9 POINT (0.02938 0.93467) 9
460
343
 
344
+ Creating the ThematicMap instance with a numeric column.
461
345
 
462
- class _CategoricalLegend(Legend):
463
- """Holds the attributes of the legend in the ThematicMap class.
346
+ >>> m = sg.ThematicMap(points, column="number")
464
347
 
465
- This class is stored in the 'legend' attribute of the ThematicMap class.
466
- The fontsize, title_fontsize and markersize attributes are adjusted
467
- according to the size attribute of the ThematicMap.
348
+ Changing the attributes that apply to both numeric and categorical columns.
468
349
 
469
- Attributes:
470
- title: Legend title. Defaults to the column name if used in the
471
- ThematicMap class.
472
- position: The legend's x and y position in the plot, specified as a tuple of
473
- x and y position between 0 and 1. E.g. position=(0.8, 0.2) for a position
474
- in the bottom right corner, (0.2, 0.8) for the upper left corner.
475
- fontsize: Text size of the legend labels. Defaults to the size of
476
- the ThematicMap class.
477
- title_fontsize: Text size of the legend title. Defaults to the
478
- size * 1.2 of the ThematicMap class.
479
- markersize: Size of the color circles in the legend. Defaults to the size of
480
- the ThematicMap class.
481
- kwargs: Stores additional keyword arguments taken by the matplotlib legend
482
- method. Specify this as e.g. m.legend.kwargs["labelcolor"] = "red", where
483
- 'm' is the name of the ThematicMap instance.
350
+ >>> m.legend.title = "Meters"
351
+ >>> m.legend.position = (0.35, 0.28)
352
+
353
+ Change the attributes that only apply to numeric columns.
354
+
355
+ >>> m.label_sep = "to"
356
+ >>> m.label_suffix = "num"
357
+ >>> m.rounding = 2
358
+ >>> m.plot()
359
+
360
+ Setting labels manually. For better control, it might be wise to also set the bins
361
+ manually. The following bins will create a plot with the color groups
362
+ 0-2, 3-5, 6-7 and 8-9. The legend labels can then be set accordingly.
363
+
364
+ >>> m = sg.ThematicMap(points, column="number")
365
+ >>> m.bins = [0, 2, 5, 7, 9]
366
+ >>> m.legend.labels = ["0 to 2 num", "3 to 5 num", "6 to 7 num", "8 to 9 num"]
367
+ >>> m.plot()
368
+
369
+ We will get the same groups if we exclude the first and last bin values. The
370
+ minimum and maximum values will be filled anyway.
371
+
372
+ >>> m = sg.ThematicMap(points, column="number")
373
+ >>> m.bins = [2, 5, 7]
374
+ >>> m.legend.labels = ["0 to 2 num", "3 to 5 num", "6 to 7 num", "8 to 9 num"]
375
+ >>> m.plot()
484
376
 
485
- Examples
486
- --------
487
377
  """
488
378
 
489
- pass
379
+ def __init__(
380
+ self,
381
+ labels: list[str] | None = None,
382
+ pretty_labels: bool = False,
383
+ label_suffix: str | None = None,
384
+ label_sep: str = "-",
385
+ rounding: int | None = None,
386
+ thousand_sep: str | None = None,
387
+ decimal_mark: str | None = None,
388
+ **kwargs,
389
+ ):
390
+ super().__init__(**kwargs)
391
+
392
+ self.pretty_labels = pretty_labels
393
+ self.thousand_sep = thousand_sep
394
+ self.decimal_mark = decimal_mark
395
+
396
+ self.label_sep = label_sep
397
+ self.label_suffix = "" if not label_suffix else label_suffix
398
+ self._rounding = rounding
399
+ self._rounding_has_been_set = True if rounding else False
400
+
401
+ def _get_rounding(self, array: Series | np.ndarray) -> int:
402
+ def isinteger(x):
403
+ return np.equal(np.mod(x, 1), 0)
404
+
405
+ if np.all(isinteger(array)):
406
+ return 0
407
+
408
+ closest_to_zero_idx = np.argmin(np.abs(array))
409
+ closest_to_zero = np.abs(array[closest_to_zero_idx])
410
+
411
+ between_1_and_0 = 1 > closest_to_zero > 0
412
+ if between_1_and_0:
413
+ return int(abs(np.log10(abs(closest_to_zero)))) + 1
414
+
415
+ std_ = np.std(array)
416
+ max_ = np.max(array)
417
+ if max_ > 30 and std_ > 5:
418
+ return 0
419
+ if max_ > 5 and std_ > 1:
420
+ return 1
421
+ if max_ > 1 and std_ > 0.1:
422
+ return 2
423
+ return int(abs(np.log10(std_))) + 1
424
+
425
+ @staticmethod
426
+ def _set_rounding(bins, rounding: int | float):
427
+ if rounding == 0:
428
+ return [int(round(bin, 0)) for bin in bins]
429
+ else:
430
+ return [round(bin, rounding) for bin in bins]
431
+
432
+ def _remove_max_legend_value(self):
433
+ if not self._legend:
434
+ raise ValueError("Cannot modify legend before it is created.")
435
+
436
+ def _prepare_continous_legend(
437
+ self,
438
+ bins: list[float],
439
+ colors: list[str],
440
+ nan_label: str,
441
+ bin_values: dict,
442
+ ):
443
+ # TODO: clean up this messy method
444
+
445
+ for attr in self.__dict__.keys():
446
+ if attr in self.kwargs:
447
+ self[attr] = self.kwargs.pop(attr)
448
+
449
+ self._patches, self._categories = [], []
450
+
451
+ for color in colors:
452
+ self._patches.append(
453
+ Line2D(
454
+ [0],
455
+ [0],
456
+ linestyle="none",
457
+ marker="o",
458
+ alpha=self.kwargs.get("alpha", 1),
459
+ markersize=self._markersize,
460
+ markerfacecolor=color,
461
+ markeredgewidth=0,
462
+ )
463
+ )
464
+
465
+ if self.labels:
466
+ if len(self.labels) != len(colors):
467
+ raise ValueError(
468
+ "Label list must be same length as the number of groups. "
469
+ f"Got k={len(colors)} and labels={len(colors)}."
470
+ f"labels: {', '.join(self.labels)}"
471
+ f"colors: {', '.join(colors)}"
472
+ )
473
+ self._categories = self.labels
474
+
475
+ elif len(bins) == len(colors):
476
+ for i, _ in enumerate(bins):
477
+ min_ = np.min(bin_values[i])
478
+ max_ = np.max(bin_values[i])
479
+ min_rounded = self._set_rounding([min_], self._rounding)[0]
480
+ max_rounded = self._set_rounding([max_], self._rounding)[0]
481
+ if min_ == max_:
482
+ self._categories.append(f"{min_rounded} {self.label_suffix}")
483
+ else:
484
+ self._categories.append(
485
+ f"{min_rounded} {self.label_suffix} {self.label_sep} "
486
+ f"{max_rounded} {self.label_suffix}"
487
+ )
488
+
489
+ else:
490
+ for i, (cat1, cat2) in enumerate(zip(bins[:-1], bins[1:], strict=True)):
491
+ if nan_label in str(cat1) or nan_label in str(cat2):
492
+ self._categories.append(nan_label)
493
+ continue
494
+
495
+ min_ = np.min(bin_values[i])
496
+ max_ = np.max(bin_values[i])
497
+ min_rounded = self._set_rounding([min_], self._rounding)[0]
498
+ max_rounded = self._set_rounding([max_], self._rounding)[0]
499
+ if self.pretty_labels:
500
+ if i != 0 and self._rounding == 0:
501
+ cat1 = int(cat1 + 1)
502
+ elif i != 0:
503
+ cat1 = cat1 + float(f"1e-{self._rounding}")
504
+
505
+ cat1 = self._format_number(cat1)
506
+ cat2 = self._format_number(cat2)
507
+
508
+ if min_ == max_:
509
+ label = self._two_value_label(cat1, cat2)
510
+ self._categories.append(label)
511
+ continue
512
+
513
+ label = self._two_value_label(cat1, cat2)
514
+ self._categories.append(label)
515
+
516
+ elif min_ == max_:
517
+ min_rounded = self._format_number(min_rounded)
518
+ label = self._one_value_label(min_rounded)
519
+ self._categories.append(label)
520
+ else:
521
+ min_rounded = self._format_number(min_rounded)
522
+ max_rounded = self._format_number(max_rounded)
523
+ label = self._two_value_label(min_rounded, max_rounded)
524
+ self._categories.append(label)
525
+
526
+ def _actually_add_legend(self, ax):
527
+ legend = ax.legend(
528
+ self._patches,
529
+ self._categories,
530
+ fontsize=self._fontsize,
531
+ title=self.title,
532
+ title_fontsize=self._title_fontsize,
533
+ bbox_to_anchor=self._position + (self.width, self.height),
534
+ fancybox=False,
535
+ framealpha=self.framealpha,
536
+ edgecolor=self.edgecolor,
537
+ labelspacing=self.labelspacing,
538
+ **self.kwargs,
539
+ )
540
+
541
+ if self.title_color:
542
+ plt.setp(legend.get_title(), color=self.title_color)
543
+
544
+ return ax
545
+
546
+ def _two_value_label(self, value1, value2):
547
+ return (
548
+ f"{value1} {self.label_suffix} {self.label_sep} "
549
+ f"{value2} {self.label_suffix}"
550
+ )
551
+
552
+ def _one_value_label(self, value1):
553
+ return f"{value1} {self.label_suffix}"
554
+
555
+ def _format_number(self, number):
556
+ if not self.thousand_sep and not self.decimal_mark:
557
+ return number
558
+
559
+ if self.thousand_sep:
560
+ number = f"{number:,}".replace(",", "*temp_thousand*")
561
+
562
+ if self.decimal_mark:
563
+ number = str(number).replace(".", "*temp_decimal*")
564
+
565
+ if self.thousand_sep == "." and not self.decimal_mark:
566
+ number = number.replace(".", ",").replace(
567
+ "*temp_thousand*", self.thousand_sep
568
+ )
569
+ elif not self.thousand_sep:
570
+ number = number.replace("*temp_decimal*", self.decimal_mark)
571
+ elif not self.decimal_mark:
572
+ number = number.replace("*temp_thousand*", self.thousand_sep)
573
+ else:
574
+ number = number.replace("*temp_thousand*", self.thousand_sep).replace(
575
+ "*temp_decimal*", self.decimal_mark
576
+ )
577
+ return number
578
+
579
+ @property
580
+ def rounding(self):
581
+ return self._rounding
582
+
583
+ @rounding.setter
584
+ def rounding(self, new_value: bool):
585
+ self._rounding = new_value
586
+ self._rounding_has_been_set = True