ssb-sgis 0.1.4__tar.gz → 0.1.6__tar.gz
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.
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/PKG-INFO +17 -9
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/README.md +14 -6
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/pyproject.toml +3 -3
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/geopandas_tools/general.py +18 -7
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/geopandas_tools/geometry_types.py +1 -1
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/geopandas_tools/line_operations.py +1 -1
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/geopandas_tools/overlay.py +11 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/maps/explore.py +62 -99
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/maps/legend.py +300 -203
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/maps/map.py +69 -74
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/maps/maps.py +9 -5
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/maps/thematicmap.py +145 -61
- ssb_sgis-0.1.6/src/sgis/networkanalysis/_get_route.py +244 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/networkanalysis/_od_cost_matrix.py +7 -9
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/networkanalysis/_points.py +0 -18
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/networkanalysis/directednetwork.py +2 -2
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/networkanalysis/network.py +16 -25
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/networkanalysis/networkanalysis.py +301 -123
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/networkanalysis/networkanalysisrules.py +2 -2
- ssb_sgis-0.1.4/src/sgis/networkanalysis/_get_route.py +0 -190
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/LICENSE +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/__init__.py +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/dapla.py +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/exceptions.py +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/geopandas_tools/__init__.py +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/geopandas_tools/buffer_dissolve_explode.py +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/geopandas_tools/neighbors.py +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/geopandas_tools/point_operations.py +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/geopandas_tools/polygon_operations.py +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/helpers.py +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/maps/__init__.py +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/networkanalysis/__init__.py +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/networkanalysis/_service_area.py +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/networkanalysis/network_norway.py +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/py.typed +0 -0
- {ssb_sgis-0.1.4 → ssb_sgis-0.1.6}/src/sgis/read_parquet.py +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ssb-sgis
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: GIS
|
|
3
|
+
Version: 0.1.6
|
|
4
|
+
Summary: GIS functions used at Statistics Norway.
|
|
5
5
|
Home-page: https://github.com/statisticsnorway/ssb-sgis
|
|
6
6
|
License: MIT
|
|
7
7
|
Author: Statistics Norway
|
|
8
8
|
Author-email: ort@ssb.no
|
|
9
|
-
Requires-Python: >=3.10,<
|
|
9
|
+
Requires-Python: >=3.10,<3.12
|
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
|
11
11
|
Classifier: License :: OSI Approved :: MIT License
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -68,7 +68,7 @@ Preparing for network analysis:
|
|
|
68
68
|
|
|
69
69
|
```python
|
|
70
70
|
import sgis as sg
|
|
71
|
-
|
|
71
|
+
import pandas as pd
|
|
72
72
|
|
|
73
73
|
roads = sg.read_parquet_url(
|
|
74
74
|
"https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet"
|
|
@@ -97,14 +97,23 @@ nwa
|
|
|
97
97
|
log=True, detailed_log=True,
|
|
98
98
|
)
|
|
99
99
|
|
|
100
|
-
Get number of times each line segment was visited.
|
|
100
|
+
Get number of times each line segment was visited, with optional weighting.
|
|
101
101
|
|
|
102
102
|
```python
|
|
103
|
-
|
|
103
|
+
origins = points.iloc[:75]
|
|
104
|
+
destinations = points.iloc[75:150]
|
|
105
|
+
|
|
106
|
+
# creating uniform weights of 10
|
|
107
|
+
od_pairs = pd.MultiIndex.from_product([origins.index, destinations.index])
|
|
108
|
+
weights = pd.DataFrame(index=od_pairs)
|
|
109
|
+
weights["weight"] = 10
|
|
104
110
|
|
|
111
|
+
frequencies = nwa.get_route_frequencies(origins, destinations, weight_df=weights)
|
|
112
|
+
|
|
113
|
+
# plot the results
|
|
105
114
|
m = sg.ThematicMap(sg.buff(frequencies, 15), column="frequency", black=True)
|
|
106
115
|
m.cmap = "plasma"
|
|
107
|
-
m.title = "Number of times each road was used
|
|
116
|
+
m.title = "Number of times each road was used,\nweighted * 10"
|
|
108
117
|
m.plot()
|
|
109
118
|
```
|
|
110
119
|
|
|
@@ -141,6 +150,7 @@ service_areas = nwa.service_area(
|
|
|
141
150
|
breaks=np.arange(1, 11),
|
|
142
151
|
)
|
|
143
152
|
|
|
153
|
+
# plot the results
|
|
144
154
|
m = sg.ThematicMap(service_areas, column="minutes", black=True, size=10)
|
|
145
155
|
m.k = 10
|
|
146
156
|
m.title = "Roads that can be reached within 1 to 10 minutes"
|
|
@@ -164,8 +174,6 @@ m.plot()
|
|
|
164
174
|
|
|
165
175
|

|
|
166
176
|
|
|
167
|
-
More network analysis examples can be found here: https://github.com/statisticsnorway/ssb-sgis/blob/main/docs/network_analysis_demo_template.md
|
|
168
|
-
|
|
169
177
|
Road data for Norway can be downloaded here: https://kartkatalog.geonorge.no/metadata/nvdb-ruteplan-nettverksdatasett/8d0f9066-34f9-4423-be12-8e8523089313
|
|
170
178
|
|
|
171
179
|
## Developer information
|
|
@@ -34,7 +34,7 @@ Preparing for network analysis:
|
|
|
34
34
|
|
|
35
35
|
```python
|
|
36
36
|
import sgis as sg
|
|
37
|
-
|
|
37
|
+
import pandas as pd
|
|
38
38
|
|
|
39
39
|
roads = sg.read_parquet_url(
|
|
40
40
|
"https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet"
|
|
@@ -63,14 +63,23 @@ nwa
|
|
|
63
63
|
log=True, detailed_log=True,
|
|
64
64
|
)
|
|
65
65
|
|
|
66
|
-
Get number of times each line segment was visited.
|
|
66
|
+
Get number of times each line segment was visited, with optional weighting.
|
|
67
67
|
|
|
68
68
|
```python
|
|
69
|
-
|
|
69
|
+
origins = points.iloc[:75]
|
|
70
|
+
destinations = points.iloc[75:150]
|
|
71
|
+
|
|
72
|
+
# creating uniform weights of 10
|
|
73
|
+
od_pairs = pd.MultiIndex.from_product([origins.index, destinations.index])
|
|
74
|
+
weights = pd.DataFrame(index=od_pairs)
|
|
75
|
+
weights["weight"] = 10
|
|
70
76
|
|
|
77
|
+
frequencies = nwa.get_route_frequencies(origins, destinations, weight_df=weights)
|
|
78
|
+
|
|
79
|
+
# plot the results
|
|
71
80
|
m = sg.ThematicMap(sg.buff(frequencies, 15), column="frequency", black=True)
|
|
72
81
|
m.cmap = "plasma"
|
|
73
|
-
m.title = "Number of times each road was used
|
|
82
|
+
m.title = "Number of times each road was used,\nweighted * 10"
|
|
74
83
|
m.plot()
|
|
75
84
|
```
|
|
76
85
|
|
|
@@ -107,6 +116,7 @@ service_areas = nwa.service_area(
|
|
|
107
116
|
breaks=np.arange(1, 11),
|
|
108
117
|
)
|
|
109
118
|
|
|
119
|
+
# plot the results
|
|
110
120
|
m = sg.ThematicMap(service_areas, column="minutes", black=True, size=10)
|
|
111
121
|
m.k = 10
|
|
112
122
|
m.title = "Roads that can be reached within 1 to 10 minutes"
|
|
@@ -130,8 +140,6 @@ m.plot()
|
|
|
130
140
|
|
|
131
141
|

|
|
132
142
|
|
|
133
|
-
More network analysis examples can be found here: https://github.com/statisticsnorway/ssb-sgis/blob/main/docs/network_analysis_demo_template.md
|
|
134
|
-
|
|
135
143
|
Road data for Norway can be downloaded here: https://kartkatalog.geonorge.no/metadata/nvdb-ruteplan-nettverksdatasett/8d0f9066-34f9-4423-be12-8e8523089313
|
|
136
144
|
|
|
137
145
|
## Developer information
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "ssb-sgis"
|
|
3
|
-
version = "0.1.
|
|
4
|
-
description = "GIS
|
|
3
|
+
version = "0.1.6"
|
|
4
|
+
description = "GIS functions used at Statistics Norway."
|
|
5
5
|
authors = ["Statistics Norway <ort@ssb.no>"]
|
|
6
6
|
license = "MIT"
|
|
7
7
|
readme = "README.md"
|
|
@@ -17,7 +17,7 @@ classifiers = [
|
|
|
17
17
|
Changelog = "https://github.com/statisticsnorway/ssb-sgis/releases"
|
|
18
18
|
|
|
19
19
|
[tool.poetry.dependencies]
|
|
20
|
-
python = ">=3.10,<
|
|
20
|
+
python = ">=3.10,<3.12"
|
|
21
21
|
branca = "^0.6.0"
|
|
22
22
|
folium = "^0.14.0"
|
|
23
23
|
geopandas = "^0.12.2"
|
|
@@ -234,7 +234,7 @@ def random_points_in_polygons(
|
|
|
234
234
|
|
|
235
235
|
Examples
|
|
236
236
|
--------
|
|
237
|
-
|
|
237
|
+
First create and buffer 100 random points.
|
|
238
238
|
|
|
239
239
|
>>> import sgis as sg
|
|
240
240
|
>>> gdf = sg.random_points(100)
|
|
@@ -311,9 +311,22 @@ def random_points_in_polygons(
|
|
|
311
311
|
bounds = gdf__.bounds
|
|
312
312
|
temp_idx____ = gdf__["temp_idx____"].values
|
|
313
313
|
|
|
314
|
+
all_points = all_points.sort_index().drop(["temp_idx____", "index_right"], axis=1)
|
|
315
|
+
|
|
316
|
+
if gdf.index.is_unique:
|
|
317
|
+
gdf = gdf.drop("temp_idx____", axis=1)
|
|
318
|
+
return all_points
|
|
319
|
+
|
|
320
|
+
original_index = {
|
|
321
|
+
temp_idx: idx for temp_idx, idx in zip(gdf.temp_idx____, gdf.index)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
all_points.index = all_points.index.map(original_index)
|
|
325
|
+
all_points.index.name = None
|
|
326
|
+
|
|
314
327
|
gdf = gdf.drop("temp_idx____", axis=1)
|
|
315
328
|
|
|
316
|
-
return all_points
|
|
329
|
+
return all_points
|
|
317
330
|
|
|
318
331
|
|
|
319
332
|
def points_in_bounds(gdf: GeoDataFrame, n2: int):
|
|
@@ -355,8 +368,8 @@ def to_lines(*gdfs: GeoDataFrame, copy: bool = True) -> GeoDataFrame:
|
|
|
355
368
|
>>> poly1["poly1"] = 1
|
|
356
369
|
>>> line = sg.to_lines(poly1)
|
|
357
370
|
>>> line
|
|
358
|
-
|
|
359
|
-
0
|
|
371
|
+
geometry poly1
|
|
372
|
+
0 LINESTRING (0.00000 0.00000, 0.00000 1.00000, ... 1
|
|
360
373
|
|
|
361
374
|
Convert two overlapping polygons to linestrings.
|
|
362
375
|
|
|
@@ -372,13 +385,11 @@ def to_lines(*gdfs: GeoDataFrame, copy: bool = True) -> GeoDataFrame:
|
|
|
372
385
|
4 NaN 1.0 LINESTRING (0.50000 1.00000, 0.50000 1.50000, ...
|
|
373
386
|
5 NaN 1.0 LINESTRING (1.00000 0.50000, 0.50000 0.50000)
|
|
374
387
|
|
|
375
|
-
Plot before and after
|
|
388
|
+
Plot before and after.
|
|
376
389
|
|
|
377
390
|
>>> sg.qtm(poly1, poly2)
|
|
378
|
-
<Axes: >
|
|
379
391
|
>>> lines["l"] = lines.length
|
|
380
392
|
>>> sg.qtm(lines, "l")
|
|
381
|
-
<Axes: >
|
|
382
393
|
"""
|
|
383
394
|
|
|
384
395
|
if any(any(gdf.geom_type.isin(["Point", "MultiPoint"])) for gdf in gdfs):
|
|
@@ -698,7 +698,7 @@ def close_network_holes_to_deadends(
|
|
|
698
698
|
|
|
699
699
|
Fill gaps shorter than 1.1 meters.
|
|
700
700
|
|
|
701
|
-
>>> filled = sg.close_network_holes_to_deadends(roads, max_distance=1.1
|
|
701
|
+
>>> filled = sg.close_network_holes_to_deadends(roads, max_distance=1.1)
|
|
702
702
|
>>> roads = sg.get_largest_component(roads)
|
|
703
703
|
>>> roads.connected.value_counts()
|
|
704
704
|
1.0 100315
|
|
@@ -77,6 +77,9 @@ def overlay(
|
|
|
77
77
|
f"'how' was {how!r} but is expected to be in {', '.join(allowed_hows)}"
|
|
78
78
|
)
|
|
79
79
|
|
|
80
|
+
if df1.crs != df2.crs:
|
|
81
|
+
raise ValueError(f"crs mismatch. Got {df1.crs} and {df2.crs}")
|
|
82
|
+
|
|
80
83
|
df1 = clean_geoms(df1)
|
|
81
84
|
df2 = clean_geoms(df2)
|
|
82
85
|
|
|
@@ -295,6 +298,8 @@ def _union(pairs, df1, df2, left, right):
|
|
|
295
298
|
merged.append(intersections)
|
|
296
299
|
symmdiff = _symmetric_difference(pairs, df1, df2, left, right)
|
|
297
300
|
merged.append(symmdiff)
|
|
301
|
+
crs = merged[0].crs
|
|
302
|
+
merged = [gdf.to_crs(crs) for gdf in merged]
|
|
298
303
|
return pd.concat(merged, ignore_index=True).pipe(_push_geom_col)
|
|
299
304
|
|
|
300
305
|
|
|
@@ -305,6 +310,8 @@ def _identity(pairs, df1, left):
|
|
|
305
310
|
merged.append(intersections)
|
|
306
311
|
diff = _difference(pairs, df1, left)
|
|
307
312
|
merged.append(diff)
|
|
313
|
+
crs = merged[0].crs
|
|
314
|
+
merged = [gdf.to_crs(crs) for gdf in merged]
|
|
308
315
|
return pd.concat(merged, ignore_index=True).pipe(_push_geom_col)
|
|
309
316
|
|
|
310
317
|
|
|
@@ -317,6 +324,8 @@ def _symmetric_difference(pairs, df1, df2, left, right):
|
|
|
317
324
|
merged.append(clip_right)
|
|
318
325
|
diff_right = _add_from_right(df1, df2, right)
|
|
319
326
|
merged.append(diff_right)
|
|
327
|
+
crs = merged[0].crs
|
|
328
|
+
merged = [gdf.to_crs(crs) for gdf in merged]
|
|
320
329
|
return pd.concat(merged, ignore_index=True).pipe(_push_geom_col)
|
|
321
330
|
|
|
322
331
|
|
|
@@ -327,6 +336,8 @@ def _difference(pairs, df1, left):
|
|
|
327
336
|
merged.append(clip_left)
|
|
328
337
|
diff_left = _add_from_left(df1, left)
|
|
329
338
|
merged.append(diff_left)
|
|
339
|
+
crs = merged[0].crs
|
|
340
|
+
merged = [gdf.to_crs(crs) for gdf in merged]
|
|
330
341
|
return pd.concat(merged, ignore_index=True).pipe(_push_geom_col)
|
|
331
342
|
|
|
332
343
|
|
|
@@ -9,6 +9,7 @@ from statistics import mean
|
|
|
9
9
|
import branca as bc
|
|
10
10
|
import folium
|
|
11
11
|
import matplotlib
|
|
12
|
+
import numpy as np
|
|
12
13
|
import pandas as pd
|
|
13
14
|
from geopandas import GeoDataFrame
|
|
14
15
|
from shapely.geometry import LineString
|
|
@@ -30,7 +31,7 @@ NAN_COLOR = "#969696"
|
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
# cols to not show when hovering over geometries (tooltip)
|
|
33
|
-
COLS_TO_DROP = ["color", "geometry"]
|
|
34
|
+
COLS_TO_DROP = ["color", "col_as_int", "geometry"]
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
# from geopandas
|
|
@@ -71,49 +72,19 @@ class Explore(Map):
|
|
|
71
72
|
self.max_zoom = max_zoom
|
|
72
73
|
self.show_in_browser = show_in_browser
|
|
73
74
|
|
|
74
|
-
if
|
|
75
|
+
if any(get_geom_type(gdf) == "mixed" for gdf in self.gdfs):
|
|
76
|
+
self._make_all_polygon()
|
|
77
|
+
|
|
78
|
+
if self._is_categorical:
|
|
79
|
+
if len(self.gdfs) == 1:
|
|
80
|
+
self._split_categories()
|
|
81
|
+
else:
|
|
75
82
|
if not self._cmap:
|
|
76
83
|
self._cmap = "viridis"
|
|
77
84
|
self.cmap_start = kwargs.pop("cmap_start", 0)
|
|
78
85
|
self.cmap_stop = kwargs.pop("cmap_stop", 256)
|
|
79
86
|
|
|
80
87
|
def explore(self, column: str | None = None, **kwargs) -> None:
|
|
81
|
-
"""Interactive map of the GeoDataFrames with layers that can be toggles on/off.
|
|
82
|
-
|
|
83
|
-
It displays all the GeoDataFrames and displays them together in an interactive
|
|
84
|
-
map with a common legend. The layers can be toggled on and off.
|
|
85
|
-
|
|
86
|
-
Args:
|
|
87
|
-
column: The column to color the geometries by. Defaults to the column
|
|
88
|
-
that was specified last.
|
|
89
|
-
**kwargs: Keyword arguments to pass to geopandas.GeoDataFrame.explore, for
|
|
90
|
-
instance 'cmap' to change the colors, 'scheme' to change how the data
|
|
91
|
-
is grouped. This defaults to 'fisherjenks' for numeric data.
|
|
92
|
-
|
|
93
|
-
See also:
|
|
94
|
-
samplemap: same functionality, but shows only a random area of a given size.
|
|
95
|
-
clipmap: same functionality, but shows only the areas clipped by a given
|
|
96
|
-
mask.
|
|
97
|
-
|
|
98
|
-
Examples
|
|
99
|
-
--------
|
|
100
|
-
>>> from sgis import read_parquet_url
|
|
101
|
-
>>> roads = read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/roads_oslo_2022.parquet")
|
|
102
|
-
>>> points = read_parquet_url("https://media.githubusercontent.com/media/statisticsnorway/ssb-sgis/main/tests/testdata/points_oslo.parquet")
|
|
103
|
-
|
|
104
|
-
Simple explore of two GeoDataFrames.
|
|
105
|
-
|
|
106
|
-
>>> from sgis import Explore
|
|
107
|
-
>>> ex = Explore(roads, points)
|
|
108
|
-
>>> ex.explore()
|
|
109
|
-
|
|
110
|
-
With column.
|
|
111
|
-
|
|
112
|
-
>>> roads["meters"] = roads.length
|
|
113
|
-
>>> points["meters"] = points.length
|
|
114
|
-
>>> ex = Explore(roads, points, column="meters")
|
|
115
|
-
>>> ex.samplemap()
|
|
116
|
-
"""
|
|
117
88
|
if column:
|
|
118
89
|
self._column = column
|
|
119
90
|
self._update_column()
|
|
@@ -128,34 +99,6 @@ class Explore(Map):
|
|
|
128
99
|
sample_from_first: bool = True,
|
|
129
100
|
**kwargs,
|
|
130
101
|
) -> None:
|
|
131
|
-
"""Shows an interactive map of a random area of the GeoDataFrames.
|
|
132
|
-
|
|
133
|
-
It takes a random sample point of the GeoDataFrames, and shows all geometries
|
|
134
|
-
within a given radius of this point. Displays an interactive map with a common
|
|
135
|
-
legend. The layers can be toggled on and off.
|
|
136
|
-
|
|
137
|
-
The radius to plot can be changed with the 'size' parameter.
|
|
138
|
-
|
|
139
|
-
For more info about the labeling and coloring of the map, see the explore
|
|
140
|
-
method.
|
|
141
|
-
|
|
142
|
-
Args:
|
|
143
|
-
size: the radius to buffer the sample point by before clipping with the
|
|
144
|
-
data.
|
|
145
|
-
column: The column to color the geometries by. Defaults to the column
|
|
146
|
-
that was specified last.
|
|
147
|
-
sample_from_first: If True (default), the sample point is taken from
|
|
148
|
-
the first specified GeoDataFrame. If False, all GeoDataFrames are
|
|
149
|
-
considered.
|
|
150
|
-
**kwargs: Keyword arguments to pass to geopandas.GeoDataFrame.explore, for
|
|
151
|
-
instance 'cmap' to change the colors, 'scheme' to change how the data
|
|
152
|
-
is grouped. This defaults to 'fisherjenks' for numeric data.
|
|
153
|
-
|
|
154
|
-
See also:
|
|
155
|
-
explore: same functionality, but shows the entire area of the geometries.
|
|
156
|
-
clipmap: same functionality, but shows only the areas clipped by a given
|
|
157
|
-
mask.
|
|
158
|
-
"""
|
|
159
102
|
if column:
|
|
160
103
|
self._column = column
|
|
161
104
|
self._update_column()
|
|
@@ -193,27 +136,6 @@ class Explore(Map):
|
|
|
193
136
|
column: str | None = None,
|
|
194
137
|
**kwargs,
|
|
195
138
|
) -> None:
|
|
196
|
-
"""Shows an interactive map of a of the GeoDataFrames clipped by the mask.
|
|
197
|
-
|
|
198
|
-
It clips all the GeoDataFrames in the Explore instance to the mask extent,
|
|
199
|
-
and displays the resulting geometries in an interactive map with a common
|
|
200
|
-
legends. The layers can be toggled on and off.
|
|
201
|
-
|
|
202
|
-
For more info about the labeling and coloring of the map, see the explore
|
|
203
|
-
method.
|
|
204
|
-
|
|
205
|
-
Args:
|
|
206
|
-
mask: the geometry to clip the data by.
|
|
207
|
-
column: The column to color the geometries by. Defaults to the column
|
|
208
|
-
that was specified last.
|
|
209
|
-
**kwargs: Keyword arguments to pass to geopandas.GeoDataFrame.explore, for
|
|
210
|
-
instance 'cmap' to change the colors, 'scheme' to change how the data
|
|
211
|
-
is grouped. This defaults to 'fisherjenks' for numeric data.
|
|
212
|
-
|
|
213
|
-
See also:
|
|
214
|
-
explore: same functionality, but shows the entire area of the geometries.
|
|
215
|
-
samplemap: same functionality, but shows only a random area of a given size.
|
|
216
|
-
"""
|
|
217
139
|
if column:
|
|
218
140
|
self._column = column
|
|
219
141
|
self._update_column()
|
|
@@ -222,16 +144,15 @@ class Explore(Map):
|
|
|
222
144
|
gdfs: tuple[GeoDataFrame] = ()
|
|
223
145
|
for gdf in self._gdfs:
|
|
224
146
|
gdf = gdf.clip(mask)
|
|
147
|
+
collections = gdf.loc[gdf.geom_type == "GeometryCollection"]
|
|
148
|
+
if len(collections):
|
|
149
|
+
collections = collections.explode(index_parts=False)
|
|
150
|
+
gdf = pd.concat([gdf, collections], ignore_index=False)
|
|
225
151
|
gdfs = gdfs + (gdf,)
|
|
226
152
|
self._gdfs = gdfs
|
|
227
153
|
self._gdf = pd.concat(gdfs, ignore_index=True)
|
|
228
154
|
self._explore(**kwargs)
|
|
229
155
|
|
|
230
|
-
def _update_column(self):
|
|
231
|
-
self._is_categorical = self._check_if_categorical()
|
|
232
|
-
self._fill_missings()
|
|
233
|
-
self._gdf = pd.concat(self._gdfs, ignore_index=True)
|
|
234
|
-
|
|
235
156
|
def _explore(self, **kwargs):
|
|
236
157
|
self.kwargs = self.kwargs | kwargs
|
|
237
158
|
|
|
@@ -245,10 +166,46 @@ class Explore(Map):
|
|
|
245
166
|
else:
|
|
246
167
|
display(self.map)
|
|
247
168
|
|
|
169
|
+
def _split_categories(self):
|
|
170
|
+
new_gdfs, new_labels = [], []
|
|
171
|
+
for cat in self._unique_values:
|
|
172
|
+
gdf = self.gdf.loc[self.gdf[self.column] == cat]
|
|
173
|
+
new_gdfs.append(gdf)
|
|
174
|
+
new_labels.append(cat)
|
|
175
|
+
self._gdfs = new_gdfs
|
|
176
|
+
self._gdf = pd.concat(new_gdfs, ignore_index=True)
|
|
177
|
+
self.labels = new_labels
|
|
178
|
+
|
|
179
|
+
def _make_all_polygon(self):
|
|
180
|
+
"""Buffer gdf if mixed geometry types
|
|
181
|
+
|
|
182
|
+
Because Folium does not handle GeometryCollection well
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
new_gdfs = []
|
|
186
|
+
for gdf in self.gdfs:
|
|
187
|
+
if get_geom_type(gdf) == "mixed":
|
|
188
|
+
gdf[gdf._geometry_column_name] = gdf.buffer(0.1)
|
|
189
|
+
new_gdfs.append(gdf)
|
|
190
|
+
self._gdfs = new_gdfs
|
|
191
|
+
self._gdf = pd.concat(new_gdfs, ignore_index=True)
|
|
192
|
+
self._nan_idx = self._gdf[self._column].isna()
|
|
193
|
+
|
|
194
|
+
def _update_column(self):
|
|
195
|
+
self._is_categorical = self._check_if_categorical()
|
|
196
|
+
self._fill_missings()
|
|
197
|
+
self._gdf = pd.concat(self._gdfs, ignore_index=True)
|
|
198
|
+
|
|
248
199
|
def _create_categorical_map(self):
|
|
249
200
|
self._get_categorical_colors()
|
|
250
201
|
|
|
251
|
-
self.map = self._explore_return(
|
|
202
|
+
self.map = self._explore_return(
|
|
203
|
+
self._gdf,
|
|
204
|
+
return_="empty_map",
|
|
205
|
+
max_zoom=self.max_zoom,
|
|
206
|
+
popup=self.popup,
|
|
207
|
+
**self.kwargs,
|
|
208
|
+
)
|
|
252
209
|
|
|
253
210
|
for gdf, label in zip(self._gdfs, self.labels, strict=True):
|
|
254
211
|
if not len(gdf):
|
|
@@ -280,17 +237,22 @@ class Explore(Map):
|
|
|
280
237
|
|
|
281
238
|
def _create_continous_map(self):
|
|
282
239
|
self._prepare_continous_map()
|
|
283
|
-
|
|
284
|
-
|
|
240
|
+
if self.scheme:
|
|
241
|
+
classified = self._classify_from_bins(self._gdf, bins=self.bins)
|
|
242
|
+
classified_sequential = self._push_classification(classified)
|
|
243
|
+
n_colors = len(np.unique(classified_sequential)) - any(self._nan_idx)
|
|
244
|
+
unique_colors = self._get_continous_colors(n=n_colors)
|
|
285
245
|
|
|
286
246
|
self.map = self._explore_return(
|
|
287
247
|
self._gdf,
|
|
288
248
|
return_="empty_map",
|
|
249
|
+
max_zoom=self.max_zoom,
|
|
250
|
+
popup=self.popup,
|
|
289
251
|
**self.kwargs,
|
|
290
252
|
)
|
|
291
253
|
|
|
292
254
|
colorbar = bc.colormap.StepColormap(
|
|
293
|
-
|
|
255
|
+
unique_colors,
|
|
294
256
|
vmin=self._gdf[self._column].min(),
|
|
295
257
|
vmax=self._gdf[self._column].max(),
|
|
296
258
|
caption=self._column,
|
|
@@ -303,12 +265,13 @@ class Explore(Map):
|
|
|
303
265
|
continue
|
|
304
266
|
f = folium.FeatureGroup(name=label)
|
|
305
267
|
|
|
306
|
-
|
|
268
|
+
classified = self._classify_from_bins(gdf, bins=self.bins)
|
|
269
|
+
colorarray = unique_colors[classified]
|
|
307
270
|
|
|
308
271
|
gjs = self._explore_return(
|
|
309
272
|
gdf,
|
|
310
273
|
tooltip=self._tooltip_cols(gdf),
|
|
311
|
-
color=
|
|
274
|
+
color=colorarray,
|
|
312
275
|
return_="geojson",
|
|
313
276
|
**{key: value for key, value in self.kwargs.items() if key != "title"},
|
|
314
277
|
)
|
|
@@ -415,7 +378,7 @@ class Explore(Map):
|
|
|
415
378
|
if isinstance(tiles, xyzservices.TileProvider):
|
|
416
379
|
attr = attr if attr else tiles.html_attribution
|
|
417
380
|
map_kwds["min_zoom"] = tiles.get("min_zoom", 0)
|
|
418
|
-
map_kwds["max_zoom"] = tiles.get("max_zoom",
|
|
381
|
+
map_kwds["max_zoom"] = tiles.get("max_zoom", 30)
|
|
419
382
|
tiles = tiles.build_url(scale_factor="{r}")
|
|
420
383
|
|
|
421
384
|
m = folium.Map(
|