ssb-sgis 1.2.4__tar.gz → 1.2.7__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-1.2.4 → ssb_sgis-1.2.7}/PKG-INFO +1 -1
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/pyproject.toml +1 -1
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/conversion.py +0 -27
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/geocoding.py +4 -4
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/neighbors.py +2 -1
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/sfilter.py +8 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/io/dapla_functions.py +8 -3
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/maps/explore.py +12 -14
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/maps/httpserver.py +2 -1
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/maps/map.py +18 -16
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/maps/maps.py +10 -12
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/maps/wms.py +2 -1
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/_get_route.py +83 -37
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/_points.py +3 -5
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/networkanalysis.py +6 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/parallel/parallel.py +3 -3
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/raster/base.py +3 -5
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/raster/image_collection.py +14 -172
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/raster/regex.py +2 -2
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/LICENSE +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/README.md +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/__init__.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/conf.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/debug_config.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/exceptions.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/__init__.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/bounds.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/buffer_dissolve_explode.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/centerlines.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/cleaning.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/duplicates.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/general.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/geometry_types.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/overlay.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/point_operations.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/polygon_operations.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/polygons_as_rings.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/runners.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/geopandas_tools/utils.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/helpers.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/io/__init__.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/io/_is_dapla.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/io/opener.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/io/read_parquet.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/maps/__init__.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/maps/examine.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/maps/legend.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/maps/norge_i_bilder.json +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/maps/thematicmap.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/maps/tilesources.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/__init__.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/_od_cost_matrix.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/_service_area.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/closing_network_holes.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/cutting_lines.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/directednetwork.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/finding_isolated_networks.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/network.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/networkanalysisrules.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/nodes.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/networkanalysis/traveling_salesman.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/parallel/__init__.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/py.typed +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/raster/__init__.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/raster/indices.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/raster/sentinel_config.py +0 -0
- {ssb_sgis-1.2.4 → ssb_sgis-1.2.7}/src/sgis/raster/zonal.py +0 -0
|
@@ -43,14 +43,6 @@ except ImportError:
|
|
|
43
43
|
"""Placeholder."""
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
try:
|
|
47
|
-
from torchgeo.datasets.geo import RasterDataset
|
|
48
|
-
except ImportError:
|
|
49
|
-
|
|
50
|
-
class RasterDataset: # type: ignore
|
|
51
|
-
"""Placeholder."""
|
|
52
|
-
|
|
53
|
-
|
|
54
46
|
def crs_to_string(crs: Any) -> str:
|
|
55
47
|
"""Extract the string of a CRS-like object."""
|
|
56
48
|
if crs is None:
|
|
@@ -415,21 +407,6 @@ def to_gdf(
|
|
|
415
407
|
except Exception:
|
|
416
408
|
pass
|
|
417
409
|
|
|
418
|
-
if isinstance(obj, RasterDataset):
|
|
419
|
-
# read the entire dataset
|
|
420
|
-
obj = obj[obj.bounds]
|
|
421
|
-
crs = obj["crs"]
|
|
422
|
-
array = np.array(obj["image"])
|
|
423
|
-
transform = get_transform_from_bounds(obj["bbox"], shape=array.shape)
|
|
424
|
-
return gpd.GeoDataFrame(
|
|
425
|
-
pd.DataFrame(
|
|
426
|
-
_array_to_geojson(array, transform),
|
|
427
|
-
columns=["value", "geometry"],
|
|
428
|
-
),
|
|
429
|
-
geometry="geometry",
|
|
430
|
-
crs=crs,
|
|
431
|
-
)
|
|
432
|
-
|
|
433
410
|
if is_array_like(geometry) and len(geometry) == len(obj): # type: ignore
|
|
434
411
|
geometry = GeoSeries(
|
|
435
412
|
_make_one_shapely_geom(g) for g in geometry if g is not None # type: ignore
|
|
@@ -442,10 +419,6 @@ def to_gdf(
|
|
|
442
419
|
# get done with iterators that would get consumed by 'all' later
|
|
443
420
|
if isinstance(obj, Iterator) and not isinstance(obj, Sized):
|
|
444
421
|
obj = list(obj)
|
|
445
|
-
# obj = GeoSeries(
|
|
446
|
-
# (_make_one_shapely_geom(g) for g in obj if g is not None), index=index
|
|
447
|
-
# )
|
|
448
|
-
# return GeoDataFrame({geom_col: obj}, geometry=geom_col, crs=crs, **kwargs)
|
|
449
422
|
|
|
450
423
|
if hasattr(obj, "__len__") and not len(obj):
|
|
451
424
|
return GeoDataFrame({"geometry": []}, crs=crs)
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
try:
|
|
2
|
-
import geocoder
|
|
3
|
-
except ImportError:
|
|
4
|
-
pass
|
|
5
1
|
from geopandas import GeoDataFrame
|
|
6
2
|
|
|
7
3
|
from .conversion import to_gdf
|
|
@@ -9,6 +5,8 @@ from .conversion import to_gdf
|
|
|
9
5
|
|
|
10
6
|
def address_to_gdf(address: str, crs=4326) -> GeoDataFrame:
|
|
11
7
|
"""Takes an address and returns a point GeoDataFrame."""
|
|
8
|
+
import geocoder
|
|
9
|
+
|
|
12
10
|
g = geocoder.osm(address).json
|
|
13
11
|
coords = g["lng"], g["lat"]
|
|
14
12
|
return to_gdf(coords, crs=4326).to_crs(crs)
|
|
@@ -16,6 +14,8 @@ def address_to_gdf(address: str, crs=4326) -> GeoDataFrame:
|
|
|
16
14
|
|
|
17
15
|
def address_to_coords(address: str, crs=4326) -> tuple[float, float]:
|
|
18
16
|
"""Takes an address and returns a tuple of xy coordinates."""
|
|
17
|
+
import geocoder
|
|
18
|
+
|
|
19
19
|
g = geocoder.osm(address).json
|
|
20
20
|
coords = g["lng"], g["lat"]
|
|
21
21
|
point = to_gdf(coords, crs=4326).to_crs(crs)
|
|
@@ -15,7 +15,6 @@ from geopandas import GeoSeries
|
|
|
15
15
|
from pandas import DataFrame
|
|
16
16
|
from pandas import MultiIndex
|
|
17
17
|
from pandas import Series
|
|
18
|
-
from sklearn.neighbors import NearestNeighbors
|
|
19
18
|
|
|
20
19
|
from ..conf import _get_instance
|
|
21
20
|
from ..conf import config
|
|
@@ -467,6 +466,8 @@ def k_nearest_neighbors(
|
|
|
467
466
|
of the neighbors.
|
|
468
467
|
|
|
469
468
|
"""
|
|
469
|
+
from sklearn.neighbors import NearestNeighbors
|
|
470
|
+
|
|
470
471
|
if not len(to_array) or not len(from_array):
|
|
471
472
|
return np.array([]), np.array([])
|
|
472
473
|
|
|
@@ -83,6 +83,8 @@ def sfilter(
|
|
|
83
83
|
"""
|
|
84
84
|
if not isinstance(gdf, (GeoDataFrame | GeoSeries)):
|
|
85
85
|
raise TypeError(gdf_type_error_message)
|
|
86
|
+
if not len(gdf):
|
|
87
|
+
return gdf
|
|
86
88
|
|
|
87
89
|
other = _sfilter_checks(other, crs=gdf.crs)
|
|
88
90
|
|
|
@@ -159,6 +161,9 @@ def sfilter_split(
|
|
|
159
161
|
>>> not_intersecting = df1.loc[~filt]
|
|
160
162
|
|
|
161
163
|
"""
|
|
164
|
+
if not len(gdf):
|
|
165
|
+
return gdf, gdf
|
|
166
|
+
|
|
162
167
|
if not isinstance(gdf, (GeoDataFrame | GeoSeries)):
|
|
163
168
|
raise TypeError(gdf_type_error_message)
|
|
164
169
|
|
|
@@ -235,6 +240,9 @@ def sfilter_inverse(
|
|
|
235
240
|
"""
|
|
236
241
|
if not isinstance(gdf, (GeoDataFrame | GeoSeries)):
|
|
237
242
|
raise TypeError(gdf_type_error_message)
|
|
243
|
+
if not len(gdf):
|
|
244
|
+
return gdf
|
|
245
|
+
|
|
238
246
|
other = _sfilter_checks(other, crs=gdf.crs)
|
|
239
247
|
indices = _get_sfilter_indices(
|
|
240
248
|
gdf, other, predicate, distance, n_jobs, rtree_runner
|
|
@@ -185,7 +185,9 @@ def _read_geopandas_from_iterable(
|
|
|
185
185
|
except ArrowInvalid as e:
|
|
186
186
|
if file_system.isfile(path):
|
|
187
187
|
raise ArrowInvalid(e, path) from e
|
|
188
|
-
|
|
188
|
+
first_path = next(iter(paths.index))
|
|
189
|
+
_, crs = _get_bounds_parquet(first_path, file_system)
|
|
190
|
+
return GeoDataFrame(cols | {"geometry": []}, crs=crs)
|
|
189
191
|
paths = list(bounds_series.index)
|
|
190
192
|
|
|
191
193
|
results: list[pyarrow.Table] = _read_pyarrow_with_treads(
|
|
@@ -203,7 +205,9 @@ def _read_geopandas_from_iterable(
|
|
|
203
205
|
print(e)
|
|
204
206
|
raise e
|
|
205
207
|
else:
|
|
206
|
-
|
|
208
|
+
first_path = next(iter(paths))
|
|
209
|
+
_, crs = _get_bounds_parquet(first_path, file_system)
|
|
210
|
+
df = GeoDataFrame(cols | {"geometry": []}, crs=crs)
|
|
207
211
|
|
|
208
212
|
return df
|
|
209
213
|
|
|
@@ -786,7 +790,8 @@ def _read_partitioned_parquet(
|
|
|
786
790
|
|
|
787
791
|
# add columns to empty DataFrame
|
|
788
792
|
first_path = next(iter(child_paths + [path]))
|
|
789
|
-
|
|
793
|
+
_, crs = _get_bounds_parquet(first_path, file_system)
|
|
794
|
+
df = GeoDataFrame(columns=_get_columns(first_path, file_system), crs=crs)
|
|
790
795
|
if kwargs.get("columns"):
|
|
791
796
|
return df[list(kwargs["columns"])]
|
|
792
797
|
return df
|
|
@@ -43,6 +43,7 @@ from ..geopandas_tools.general import clean_geoms
|
|
|
43
43
|
from ..geopandas_tools.general import make_all_singlepart
|
|
44
44
|
from ..geopandas_tools.geometry_types import get_geom_type
|
|
45
45
|
from ..geopandas_tools.geometry_types import to_single_geom_type
|
|
46
|
+
from ..geopandas_tools.sfilter import sfilter
|
|
46
47
|
from ..helpers import _get_file_system
|
|
47
48
|
from ..helpers import dict_zip
|
|
48
49
|
from .wms import WmsLoader
|
|
@@ -69,14 +70,6 @@ from .map import _determine_best_name
|
|
|
69
70
|
from .tilesources import kartverket
|
|
70
71
|
from .tilesources import xyz
|
|
71
72
|
|
|
72
|
-
try:
|
|
73
|
-
from torchgeo.datasets.geo import RasterDataset
|
|
74
|
-
except ImportError:
|
|
75
|
-
|
|
76
|
-
class RasterDataset:
|
|
77
|
-
"""Placeholder."""
|
|
78
|
-
|
|
79
|
-
|
|
80
73
|
# the geopandas._explore raises a deprication warning. Ignoring for now.
|
|
81
74
|
warnings.filterwarnings(
|
|
82
75
|
action="ignore", category=matplotlib.MatplotlibDeprecationWarning
|
|
@@ -207,9 +200,9 @@ def _single_band_to_arr_is_too_much_nodata(
|
|
|
207
200
|
if band.has_array and mask is None:
|
|
208
201
|
arr = band.values
|
|
209
202
|
elif band.has_array:
|
|
210
|
-
arr = band.clip(mask).values
|
|
203
|
+
arr = band.copy().clip(mask).values
|
|
211
204
|
else:
|
|
212
|
-
arr = band.load(indexes=1, bounds=mask).values
|
|
205
|
+
arr = band.copy().load(indexes=1, bounds=mask).values
|
|
213
206
|
|
|
214
207
|
if _is_too_much_nodata([arr], band.nodata, max_nodata_percentage):
|
|
215
208
|
return True
|
|
@@ -495,10 +488,13 @@ class Explore(Map):
|
|
|
495
488
|
if not isinstance(center, GeoDataFrame)
|
|
496
489
|
else center
|
|
497
490
|
)
|
|
491
|
+
centerbuffer = centerpoint.buffer(size)
|
|
498
492
|
|
|
499
493
|
for label, gdf in self._gdfs.items():
|
|
500
494
|
keep_geom_type = False if get_geom_type(gdf) == "mixed" else True
|
|
501
|
-
gdf = gdf.clip(
|
|
495
|
+
gdf = sfilter(gdf, centerbuffer).clip(
|
|
496
|
+
centerbuffer, keep_geom_type=keep_geom_type
|
|
497
|
+
)
|
|
502
498
|
self._gdfs[label] = gdf
|
|
503
499
|
self._gdf = pd.concat(self._gdfs.values(), ignore_index=True)
|
|
504
500
|
|
|
@@ -555,7 +551,7 @@ class Explore(Map):
|
|
|
555
551
|
kwargs.pop("column", None)
|
|
556
552
|
|
|
557
553
|
for label, gdf in self._gdfs.items():
|
|
558
|
-
gdf = gdf.clip(self.mask)
|
|
554
|
+
gdf = sfilter(gdf, self.mask).clip(self.mask)
|
|
559
555
|
collections = gdf.loc[gdf.geom_type == "GeometryCollection"]
|
|
560
556
|
if len(collections):
|
|
561
557
|
collections = make_all_singlepart(collections)
|
|
@@ -618,6 +614,8 @@ class Explore(Map):
|
|
|
618
614
|
arr,
|
|
619
615
|
bounds=[[miny, minx], [maxy, maxx]],
|
|
620
616
|
show=self._show_rasters,
|
|
617
|
+
vmin=arr.min(),
|
|
618
|
+
vmax=arr.max(),
|
|
621
619
|
**kwargs,
|
|
622
620
|
)
|
|
623
621
|
image_overlay.layer_name = Path(label).stem
|
|
@@ -1399,9 +1397,9 @@ def _add_one_image(
|
|
|
1399
1397
|
def load(band_id: str) -> Band:
|
|
1400
1398
|
band = image[band_id]
|
|
1401
1399
|
if band.has_array and mask is not None:
|
|
1402
|
-
band = band.clip(mask, copy=True)
|
|
1400
|
+
band = band.copy().clip(mask, copy=True)
|
|
1403
1401
|
elif not band.has_array:
|
|
1404
|
-
band = band.load(indexes=1, bounds=mask)
|
|
1402
|
+
band = band.copy().load(indexes=1, bounds=mask)
|
|
1405
1403
|
return band
|
|
1406
1404
|
|
|
1407
1405
|
for red, blue, green in rbg_bands:
|
|
@@ -24,10 +24,11 @@ def run_html_server(contents: str | None = None, port: int = 3000) -> None:
|
|
|
24
24
|
if "JUPYTERHUB_SERVICE_PREFIX" in os.environ:
|
|
25
25
|
# Create a link using the https://github.com/jupyterhub/jupyter-server-proxy
|
|
26
26
|
display_address = os.environ["JUPYTERHUB_SERVICE_PREFIX"] + f"proxy/{port}/"
|
|
27
|
+
stop_address = os.environ["JUPYTERHUB_SERVICE_PREFIX"] + f"proxy/{port}/stop"
|
|
27
28
|
display_content = HTML(
|
|
28
29
|
f"""
|
|
29
30
|
<p>Click <a href='{display_address}'>here</a> to open in browser.</p>
|
|
30
|
-
<p>Click <a href='{
|
|
31
|
+
<p>Click <a href='{stop_address}'>here</a> to stop.</p>
|
|
31
32
|
"""
|
|
32
33
|
)
|
|
33
34
|
else:
|
|
@@ -14,12 +14,12 @@ import numpy as np
|
|
|
14
14
|
import pandas as pd
|
|
15
15
|
from geopandas import GeoDataFrame
|
|
16
16
|
from geopandas import GeoSeries
|
|
17
|
+
from pandas.api.types import is_dict_like
|
|
17
18
|
|
|
18
19
|
try:
|
|
19
20
|
from jenkspy import jenks_breaks
|
|
20
21
|
except ImportError:
|
|
21
22
|
pass
|
|
22
|
-
from mapclassify import classify
|
|
23
23
|
from pandas.errors import PerformanceWarning
|
|
24
24
|
from shapely import Geometry
|
|
25
25
|
|
|
@@ -34,14 +34,6 @@ from ..raster.image_collection import Band
|
|
|
34
34
|
from ..raster.image_collection import Image
|
|
35
35
|
from ..raster.image_collection import ImageCollection
|
|
36
36
|
|
|
37
|
-
try:
|
|
38
|
-
from torchgeo.datasets.geo import RasterDataset
|
|
39
|
-
except ImportError:
|
|
40
|
-
|
|
41
|
-
class RasterDataset:
|
|
42
|
-
"""Placeholder."""
|
|
43
|
-
|
|
44
|
-
|
|
45
37
|
# the geopandas._explore raises a deprication warning. Ignoring for now.
|
|
46
38
|
warnings.filterwarnings(
|
|
47
39
|
action="ignore", category=matplotlib.MatplotlibDeprecationWarning
|
|
@@ -303,9 +295,10 @@ class Map:
|
|
|
303
295
|
else:
|
|
304
296
|
return series.astype("string")
|
|
305
297
|
|
|
306
|
-
for
|
|
298
|
+
for label, gdf in self._gdfs.items():
|
|
307
299
|
if self.column in gdf:
|
|
308
|
-
|
|
300
|
+
gdf[self.column] = to_string_via_int(gdf[self.column])
|
|
301
|
+
self._gdfs[label] = gdf
|
|
309
302
|
self._gdf[self.column] = to_string_via_int(self._gdf[self.column])
|
|
310
303
|
|
|
311
304
|
def __bool__(self) -> bool:
|
|
@@ -442,7 +435,6 @@ class Map:
|
|
|
442
435
|
GeoDataFrame,
|
|
443
436
|
GeoSeries,
|
|
444
437
|
Geometry,
|
|
445
|
-
RasterDataset,
|
|
446
438
|
ImageCollection,
|
|
447
439
|
Image,
|
|
448
440
|
Band,
|
|
@@ -605,22 +597,29 @@ class Map:
|
|
|
605
597
|
return False
|
|
606
598
|
|
|
607
599
|
def _make_categories_colors_dict(self) -> None:
|
|
608
|
-
|
|
609
|
-
|
|
600
|
+
if "color" in self.kwargs and is_dict_like(self.kwargs["color"]):
|
|
601
|
+
if self._column is None and not all(
|
|
602
|
+
key in self.kwargs["color"] for key in self._gdfs
|
|
603
|
+
):
|
|
604
|
+
raise ValueError(
|
|
605
|
+
"When specifying 'color' as dict-like, you must also pass a column "
|
|
606
|
+
"or all gdfs passed must have labels/names corresponding to keys in the color dict."
|
|
607
|
+
)
|
|
608
|
+
self._categories_colors_dict = self.kwargs.pop("color")
|
|
609
|
+
elif not self._cmap and len(self._unique_values) <= len(_CATEGORICAL_CMAP):
|
|
610
|
+
# custom categorical cmap
|
|
610
611
|
self._categories_colors_dict = {
|
|
611
612
|
category: _CATEGORICAL_CMAP[i]
|
|
612
613
|
for i, category in enumerate(self._unique_values)
|
|
613
614
|
} | self._categories_colors_dict
|
|
614
615
|
elif self._cmap:
|
|
615
616
|
cmap = matplotlib.colormaps.get_cmap(self._cmap)
|
|
616
|
-
|
|
617
617
|
self._categories_colors_dict = {
|
|
618
618
|
category: colors.to_hex(cmap(int(i)))
|
|
619
619
|
for i, category in enumerate(self._unique_values)
|
|
620
620
|
} | self._categories_colors_dict
|
|
621
621
|
else:
|
|
622
622
|
cmap = matplotlib.colormaps.get_cmap("tab20")
|
|
623
|
-
|
|
624
623
|
self._categories_colors_dict = {
|
|
625
624
|
category: colors.to_hex(cmap(int(i)))
|
|
626
625
|
for i, category in enumerate(self._unique_values)
|
|
@@ -664,6 +663,9 @@ class Map:
|
|
|
664
663
|
if self.scheme == "jenks":
|
|
665
664
|
bins = jenks_breaks(gdf[column].dropna(), n_classes=n_classes)
|
|
666
665
|
else:
|
|
666
|
+
# local import because slow
|
|
667
|
+
from mapclassify import classify
|
|
668
|
+
|
|
667
669
|
binning = classify(
|
|
668
670
|
np.asarray(gdf[column].dropna()),
|
|
669
671
|
scheme=self.scheme,
|
|
@@ -29,18 +29,12 @@ from ..geopandas_tools.general import get_common_crs
|
|
|
29
29
|
from ..geopandas_tools.general import is_wkt
|
|
30
30
|
from ..geopandas_tools.geocoding import address_to_gdf
|
|
31
31
|
from ..geopandas_tools.geometry_types import get_geom_type
|
|
32
|
+
from ..geopandas_tools.sfilter import sfilter
|
|
32
33
|
from .explore import Explore
|
|
33
34
|
from .map import Map
|
|
34
35
|
from .thematicmap import ThematicMap
|
|
35
36
|
from .wms import WmsLoader
|
|
36
37
|
|
|
37
|
-
try:
|
|
38
|
-
from torchgeo.datasets.geo import RasterDataset
|
|
39
|
-
except ImportError:
|
|
40
|
-
|
|
41
|
-
class RasterDataset:
|
|
42
|
-
"""Placeholder."""
|
|
43
|
-
|
|
44
38
|
|
|
45
39
|
def _get_location_mask(kwargs: dict, gdfs) -> tuple[GeoDataFrame | None, dict]:
|
|
46
40
|
try:
|
|
@@ -479,8 +473,10 @@ def clipmap(
|
|
|
479
473
|
if m.gdfs is None and not len(m.rasters):
|
|
480
474
|
return m
|
|
481
475
|
|
|
482
|
-
m._gdfs = {
|
|
483
|
-
|
|
476
|
+
m._gdfs = {
|
|
477
|
+
label: sfilter(gdf, mask).clip(mask) for label, gdf in m._gdfs.items()
|
|
478
|
+
}
|
|
479
|
+
m._gdf = sfilter(m._gdf, mask).clip(mask)
|
|
484
480
|
m._nan_idx = m._gdf[m._column].isna()
|
|
485
481
|
m._get_unique_values()
|
|
486
482
|
m.explore(center=center, size=size)
|
|
@@ -494,8 +490,10 @@ def clipmap(
|
|
|
494
490
|
if m.gdfs is None:
|
|
495
491
|
return m
|
|
496
492
|
|
|
497
|
-
m._gdfs = {
|
|
498
|
-
|
|
493
|
+
m._gdfs = {
|
|
494
|
+
label: sfilter(gdf, mask).clip(mask) for label, gdf in m._gdfs.items()
|
|
495
|
+
}
|
|
496
|
+
m._gdf = sfilter(m._gdf, mask).clip(mask)
|
|
499
497
|
m._nan_idx = m._gdf[m._column].isna()
|
|
500
498
|
m._get_unique_values()
|
|
501
499
|
|
|
@@ -530,7 +528,7 @@ def explore_locals(
|
|
|
530
528
|
|
|
531
529
|
frame = inspect.currentframe().f_back
|
|
532
530
|
|
|
533
|
-
allowed_types = (GeoDataFrame, GeoSeries, Geometry
|
|
531
|
+
allowed_types = (GeoDataFrame, GeoSeries, Geometry)
|
|
534
532
|
|
|
535
533
|
local_gdfs = {}
|
|
536
534
|
while True:
|
|
@@ -20,7 +20,7 @@ JSON_YEARS = [str(year) for year in range(1999, 2025)]
|
|
|
20
20
|
DEFAULT_YEARS: tuple[str] = tuple(
|
|
21
21
|
str(year)
|
|
22
22
|
for year in range(
|
|
23
|
-
int(datetime.datetime.now().year) -
|
|
23
|
+
int(datetime.datetime.now().year) - 10,
|
|
24
24
|
int(datetime.datetime.now().year) + 1,
|
|
25
25
|
)
|
|
26
26
|
)
|
|
@@ -111,6 +111,7 @@ class NorgeIBilderWms(WmsLoader):
|
|
|
111
111
|
this_tile["year"] = year
|
|
112
112
|
else:
|
|
113
113
|
this_tile["year"] = "9999"
|
|
114
|
+
|
|
114
115
|
all_tiles.append(this_tile)
|
|
115
116
|
|
|
116
117
|
self.tiles = sorted(all_tiles, key=lambda x: x["year"])
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
|
|
3
|
+
import joblib
|
|
3
4
|
import pandas as pd
|
|
4
5
|
from geopandas import GeoDataFrame
|
|
5
6
|
from igraph import Graph
|
|
@@ -10,6 +11,7 @@ def _get_route_frequencies(
|
|
|
10
11
|
graph: Graph,
|
|
11
12
|
roads: GeoDataFrame,
|
|
12
13
|
weight_df: DataFrame,
|
|
14
|
+
n_jobs: int,
|
|
13
15
|
) -> GeoDataFrame:
|
|
14
16
|
"""Function used in the get_route_frequencies method of NetworkAnalysis."""
|
|
15
17
|
warnings.filterwarnings("ignore", category=RuntimeWarning)
|
|
@@ -18,26 +20,25 @@ def _get_route_frequencies(
|
|
|
18
20
|
|
|
19
21
|
od_pairs = weight_df.index
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
line_ids["multiplier"] = weight_df.loc[ori_id, des_id].iloc[0]
|
|
23
|
+
ori_ids = od_pairs.get_level_values(0).unique()
|
|
24
|
+
if n_jobs == 1:
|
|
25
|
+
nested_results: list[list[DataFrame]] = [
|
|
26
|
+
_get_one_route_frequency(
|
|
27
|
+
ori_id, od_pairs=od_pairs, graph=graph, weight_df=weight_df
|
|
28
|
+
)
|
|
29
|
+
for ori_id in ori_ids
|
|
30
|
+
]
|
|
31
|
+
del nested_results
|
|
32
|
+
else:
|
|
33
|
+
with joblib.Parallel(n_jobs) as parallel:
|
|
34
|
+
nested_results: list[list[DataFrame]] = parallel(
|
|
35
|
+
joblib.delayed(_get_one_route_frequency)(
|
|
36
|
+
ori_id, od_pairs=od_pairs, graph=graph, weight_df=weight_df
|
|
37
|
+
)
|
|
38
|
+
for ori_id in ori_ids
|
|
39
|
+
)
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
resultlist = [x for y in nested_results for x in y]
|
|
41
42
|
|
|
42
43
|
if not resultlist:
|
|
43
44
|
return pd.DataFrame(columns=["frequency", "geometry"])
|
|
@@ -53,34 +54,56 @@ def _get_route_frequencies(
|
|
|
53
54
|
return roads_visited
|
|
54
55
|
|
|
55
56
|
|
|
57
|
+
def _get_one_route_frequency(
|
|
58
|
+
ori_id: int, od_pairs: pd.MultiIndex, graph: Graph, weight_df: pd.DataFrame
|
|
59
|
+
):
|
|
60
|
+
relevant_pairs = od_pairs[od_pairs.get_level_values(0) == ori_id]
|
|
61
|
+
destinations = relevant_pairs.get_level_values(1)
|
|
62
|
+
|
|
63
|
+
res = graph.get_shortest_paths(
|
|
64
|
+
weights="weight", v=ori_id, to=destinations, output="epath"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
results = []
|
|
68
|
+
for i, des_id in enumerate(destinations):
|
|
69
|
+
indices = graph.es[res[i]]
|
|
70
|
+
|
|
71
|
+
if not indices:
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
line_ids = DataFrame({"src_tgt_wt": indices["src_tgt_wt"]})
|
|
75
|
+
line_ids["origin"] = ori_id
|
|
76
|
+
line_ids["destination"] = des_id
|
|
77
|
+
line_ids["multiplier"] = weight_df.loc[ori_id, des_id].iloc[0]
|
|
78
|
+
|
|
79
|
+
results.append(line_ids)
|
|
80
|
+
return results
|
|
81
|
+
|
|
82
|
+
|
|
56
83
|
def _get_route(
|
|
57
84
|
graph: Graph,
|
|
58
85
|
weight: str,
|
|
59
86
|
roads: GeoDataFrame,
|
|
60
87
|
od_pairs: pd.MultiIndex,
|
|
88
|
+
n_jobs: int,
|
|
61
89
|
) -> GeoDataFrame:
|
|
62
90
|
"""Function used in the get_route method of NetworkAnalysis."""
|
|
63
91
|
warnings.filterwarnings("ignore", category=RuntimeWarning)
|
|
64
92
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if not indices:
|
|
79
|
-
continue
|
|
80
|
-
|
|
81
|
-
line_ids = _create_line_id_df(indices["src_tgt_wt"], ori_id, des_id)
|
|
93
|
+
ori_ids = od_pairs.get_level_values(0).unique()
|
|
94
|
+
if n_jobs == 1:
|
|
95
|
+
nested_results: list[list[DataFrame]] = [
|
|
96
|
+
_get_one_route(ori_id, od_pairs=od_pairs, graph=graph) for ori_id in ori_ids
|
|
97
|
+
]
|
|
98
|
+
del nested_results
|
|
99
|
+
else:
|
|
100
|
+
with joblib.Parallel(n_jobs) as parallel:
|
|
101
|
+
nested_results: list[list[DataFrame]] = parallel(
|
|
102
|
+
joblib.delayed(_get_one_route)(ori_id, od_pairs=od_pairs, graph=graph)
|
|
103
|
+
for ori_id in ori_ids
|
|
104
|
+
)
|
|
82
105
|
|
|
83
|
-
|
|
106
|
+
resultlist = [x for y in nested_results for x in y]
|
|
84
107
|
|
|
85
108
|
if not resultlist:
|
|
86
109
|
warnings.warn(
|
|
@@ -98,6 +121,29 @@ def _get_route(
|
|
|
98
121
|
return lines[["origin", "destination", weight, "geometry"]]
|
|
99
122
|
|
|
100
123
|
|
|
124
|
+
def _get_one_route(
|
|
125
|
+
ori_id: int,
|
|
126
|
+
od_pairs: pd.MultiIndex,
|
|
127
|
+
graph: Graph,
|
|
128
|
+
) -> list[DataFrame]:
|
|
129
|
+
relevant_pairs = od_pairs[od_pairs.get_level_values(0) == ori_id]
|
|
130
|
+
destinations = relevant_pairs.get_level_values(1)
|
|
131
|
+
|
|
132
|
+
results = graph.get_shortest_paths(
|
|
133
|
+
weights="weight", v=ori_id, to=destinations, output="epath"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
out_lines = []
|
|
137
|
+
for i, des_id in enumerate(destinations):
|
|
138
|
+
indices = graph.es[results[i]]
|
|
139
|
+
if not indices:
|
|
140
|
+
continue
|
|
141
|
+
line_ids: DataFrame = _create_line_id_df(indices["src_tgt_wt"], ori_id, des_id)
|
|
142
|
+
out_lines.append(line_ids)
|
|
143
|
+
|
|
144
|
+
return out_lines
|
|
145
|
+
|
|
146
|
+
|
|
101
147
|
def _get_k_routes(
|
|
102
148
|
graph: Graph,
|
|
103
149
|
weight: str,
|
|
@@ -45,14 +45,12 @@ class Points:
|
|
|
45
45
|
return [0 for _ in distances]
|
|
46
46
|
|
|
47
47
|
if rules.nodedist_multiplier and rules.nodedist_kmh:
|
|
48
|
-
raise ValueError(
|
|
49
|
-
"Can only specify one of 'nodedist_multiplier' and 'nodedist_kmh'"
|
|
50
|
-
)
|
|
48
|
+
raise ValueError("Cannot set both 'nodedist_multiplier' and 'nodedist_kmh'")
|
|
51
49
|
|
|
52
50
|
if rules.nodedist_multiplier:
|
|
53
|
-
if rules.weight
|
|
51
|
+
if rules.weight == "minutes":
|
|
54
52
|
raise ValueError(
|
|
55
|
-
"
|
|
53
|
+
"Cannot set 'nodedist_multiplier' when the 'weight' is minutes"
|
|
56
54
|
)
|
|
57
55
|
return [x * rules.nodedist_multiplier for x in distances]
|
|
58
56
|
|
|
@@ -436,6 +436,7 @@ class NetworkAnalysis:
|
|
|
436
436
|
rowwise: bool = False,
|
|
437
437
|
strict: bool = False,
|
|
438
438
|
frequency_col: str = "frequency",
|
|
439
|
+
n_jobs: int | None = None,
|
|
439
440
|
) -> GeoDataFrame:
|
|
440
441
|
"""Finds the number of times each line segment was visited in all trips.
|
|
441
442
|
|
|
@@ -465,6 +466,7 @@ class NetworkAnalysis:
|
|
|
465
466
|
to False.
|
|
466
467
|
frequency_col: Name of column with the number of times each road was
|
|
467
468
|
visited. Defaults to 'frequency'.
|
|
469
|
+
n_jobs: Number of parallell jobs.
|
|
468
470
|
|
|
469
471
|
Returns:
|
|
470
472
|
A GeoDataFrame with all line segments that were visited at least once,
|
|
@@ -635,6 +637,7 @@ class NetworkAnalysis:
|
|
|
635
637
|
graph=self.graph,
|
|
636
638
|
roads=self.network.gdf,
|
|
637
639
|
weight_df=weight_df,
|
|
640
|
+
n_jobs=n_jobs,
|
|
638
641
|
)
|
|
639
642
|
|
|
640
643
|
if isinstance(results, GeoDataFrame):
|
|
@@ -665,6 +668,7 @@ class NetworkAnalysis:
|
|
|
665
668
|
rowwise: bool = False,
|
|
666
669
|
destination_count: int | None = None,
|
|
667
670
|
cutoff: int | float | None = None,
|
|
671
|
+
n_jobs: int | None = None,
|
|
668
672
|
) -> GeoDataFrame:
|
|
669
673
|
"""Returns the geometry of the low-cost route between origins and destinations.
|
|
670
674
|
|
|
@@ -685,6 +689,7 @@ class NetworkAnalysis:
|
|
|
685
689
|
cutoff: the maximum cost (weight) for the trips. Defaults to None,
|
|
686
690
|
meaning all rows will be included. NaNs will also be removed if cutoff
|
|
687
691
|
is specified.
|
|
692
|
+
n_jobs: Number of parallell jobs.
|
|
688
693
|
|
|
689
694
|
Returns:
|
|
690
695
|
A DataFrame with the geometry of the routes between origin and destination.
|
|
@@ -738,6 +743,7 @@ class NetworkAnalysis:
|
|
|
738
743
|
weight=self.rules.weight,
|
|
739
744
|
roads=self.network.gdf,
|
|
740
745
|
od_pairs=od_pairs,
|
|
746
|
+
n_jobs=n_jobs,
|
|
741
747
|
)
|
|
742
748
|
|
|
743
749
|
if cutoff is not None:
|
|
@@ -13,7 +13,7 @@ from typing import Any
|
|
|
13
13
|
from pandas.api.types import is_array_like
|
|
14
14
|
|
|
15
15
|
try:
|
|
16
|
-
|
|
16
|
+
from gcsfs import GCSFileSystem
|
|
17
17
|
except ImportError:
|
|
18
18
|
pass
|
|
19
19
|
|
|
@@ -575,7 +575,7 @@ class Parallel:
|
|
|
575
575
|
A GeoDataFrame, or a list of GeoDataFrames if concat is False.
|
|
576
576
|
"""
|
|
577
577
|
if "file_system" not in kwargs:
|
|
578
|
-
kwargs["file_system"] =
|
|
578
|
+
kwargs["file_system"] = GCSFileSystem()
|
|
579
579
|
|
|
580
580
|
if strict:
|
|
581
581
|
res = self.map(read_geopandas, files, kwargs=kwargs)
|
|
@@ -653,7 +653,7 @@ class Parallel:
|
|
|
653
653
|
if funcdict is None:
|
|
654
654
|
funcdict = {}
|
|
655
655
|
|
|
656
|
-
fs =
|
|
656
|
+
fs = GCSFileSystem()
|
|
657
657
|
|
|
658
658
|
for _, data, folder, postfunc in dict_zip_union(in_data, out_data, funcdict):
|
|
659
659
|
if data is None or (
|
|
@@ -38,10 +38,8 @@ def _get_transform_from_bounds(
|
|
|
38
38
|
obj: GeoDataFrame | GeoSeries | Geometry | tuple, shape: tuple[int, ...]
|
|
39
39
|
) -> Affine:
|
|
40
40
|
minx, miny, maxx, maxy = to_bbox(obj)
|
|
41
|
-
if len(shape)
|
|
42
|
-
height, width = shape
|
|
43
|
-
elif len(shape) == 3:
|
|
44
|
-
_, height, width = shape
|
|
41
|
+
if len(shape) in [2, 3]:
|
|
42
|
+
height, width = shape[-2:]
|
|
45
43
|
else:
|
|
46
44
|
return None
|
|
47
45
|
# raise ValueError(shape)
|
|
@@ -104,7 +102,7 @@ def _array_to_geojson(
|
|
|
104
102
|
return _array_to_geojson_loop(array, transform, mask, processes)
|
|
105
103
|
|
|
106
104
|
except Exception as err:
|
|
107
|
-
raise err.__class__(array.shape
|
|
105
|
+
raise err.__class__(f"{array.shape}: {err}") from err
|
|
108
106
|
|
|
109
107
|
|
|
110
108
|
def _array_to_geojson_loop(array, transform, mask, processes):
|
|
@@ -26,14 +26,11 @@ import numpy as np
|
|
|
26
26
|
import pandas as pd
|
|
27
27
|
import pyproj
|
|
28
28
|
import rasterio
|
|
29
|
-
from affine import Affine
|
|
30
29
|
from geopandas import GeoDataFrame
|
|
31
30
|
from geopandas import GeoSeries
|
|
31
|
+
from pandas.api.types import is_array_like
|
|
32
32
|
from pandas.api.types import is_dict_like
|
|
33
33
|
from rasterio.enums import MergeAlg
|
|
34
|
-
from scipy import stats
|
|
35
|
-
from scipy.ndimage import binary_dilation
|
|
36
|
-
from scipy.ndimage import binary_erosion
|
|
37
34
|
from shapely import Geometry
|
|
38
35
|
from shapely import box
|
|
39
36
|
from shapely import unary_union
|
|
@@ -64,42 +61,11 @@ except ImportError:
|
|
|
64
61
|
"""Placeholder."""
|
|
65
62
|
|
|
66
63
|
|
|
67
|
-
try:
|
|
68
|
-
from rioxarray.exceptions import NoDataInBounds
|
|
69
|
-
from rioxarray.merge import merge_arrays
|
|
70
|
-
from rioxarray.rioxarray import _generate_spatial_coords
|
|
71
|
-
except ImportError:
|
|
72
|
-
pass
|
|
73
|
-
try:
|
|
74
|
-
from xarray import DataArray
|
|
75
|
-
from xarray import Dataset
|
|
76
|
-
from xarray import combine_by_coords
|
|
77
|
-
except ImportError:
|
|
78
|
-
|
|
79
|
-
class DataArray:
|
|
80
|
-
"""Placeholder."""
|
|
81
|
-
|
|
82
|
-
class Dataset:
|
|
83
|
-
"""Placeholder."""
|
|
84
|
-
|
|
85
|
-
def combine_by_coords(*args, **kwargs) -> None:
|
|
86
|
-
raise ImportError("xarray")
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
try:
|
|
90
|
-
from gcsfs.core import GCSFile
|
|
91
|
-
except ImportError:
|
|
92
|
-
|
|
93
|
-
class GCSFile:
|
|
94
|
-
"""Placeholder."""
|
|
95
|
-
|
|
96
|
-
|
|
97
64
|
from ..conf import _get_instance
|
|
98
65
|
from ..conf import config
|
|
99
66
|
from ..geopandas_tools.bounds import get_total_bounds
|
|
100
67
|
from ..geopandas_tools.conversion import to_bbox
|
|
101
68
|
from ..geopandas_tools.conversion import to_gdf
|
|
102
|
-
from ..geopandas_tools.conversion import to_geoseries
|
|
103
69
|
from ..geopandas_tools.conversion import to_shapely
|
|
104
70
|
from ..geopandas_tools.general import get_common_crs
|
|
105
71
|
from ..helpers import _fix_path
|
|
@@ -799,41 +765,6 @@ class _ImageBandBase(_ImageBase):
|
|
|
799
765
|
|
|
800
766
|
return missing_metadata_attributes | nonmissing_metadata_attributes
|
|
801
767
|
|
|
802
|
-
def _to_xarray(self, array: np.ndarray, transform: Affine) -> DataArray:
|
|
803
|
-
"""Convert the raster to an xarray.DataArray."""
|
|
804
|
-
attrs = {"crs": self.crs}
|
|
805
|
-
for attr in set(self.metadata_attributes).union({"date"}):
|
|
806
|
-
try:
|
|
807
|
-
attrs[attr] = getattr(self, attr)
|
|
808
|
-
except Exception:
|
|
809
|
-
pass
|
|
810
|
-
|
|
811
|
-
if len(array.shape) == 2:
|
|
812
|
-
height, width = array.shape
|
|
813
|
-
dims = ["y", "x"]
|
|
814
|
-
elif len(array.shape) == 3:
|
|
815
|
-
height, width = array.shape[1:]
|
|
816
|
-
dims = ["band", "y", "x"]
|
|
817
|
-
elif not any(dim for dim in array.shape):
|
|
818
|
-
DataArray(
|
|
819
|
-
name=self.name or self.__class__.__name__,
|
|
820
|
-
attrs=attrs,
|
|
821
|
-
)
|
|
822
|
-
else:
|
|
823
|
-
raise ValueError(
|
|
824
|
-
f"Array should be 2 or 3 dimensional. Got shape {array.shape}"
|
|
825
|
-
)
|
|
826
|
-
|
|
827
|
-
coords = _generate_spatial_coords(transform, width, height)
|
|
828
|
-
|
|
829
|
-
return DataArray(
|
|
830
|
-
array,
|
|
831
|
-
coords=coords,
|
|
832
|
-
dims=dims,
|
|
833
|
-
name=self.name or self.__class__.__name__,
|
|
834
|
-
attrs=attrs,
|
|
835
|
-
)
|
|
836
|
-
|
|
837
768
|
|
|
838
769
|
class Band(_ImageBandBase):
|
|
839
770
|
"""Band holding a single 2 dimensional array representing an image band."""
|
|
@@ -929,7 +860,6 @@ class Band(_ImageBandBase):
|
|
|
929
860
|
self.transform = _get_transform_from_bounds(self._bounds, shape=data.shape)
|
|
930
861
|
self._from_array = True
|
|
931
862
|
self.values = data
|
|
932
|
-
|
|
933
863
|
self._res = _get_res_from_bounds(self._bounds, self.values.shape)
|
|
934
864
|
|
|
935
865
|
elif not isinstance(data, (str | Path | os.PathLike)):
|
|
@@ -1146,7 +1076,6 @@ class Band(_ImageBandBase):
|
|
|
1146
1076
|
self.transform = None
|
|
1147
1077
|
# activate setter
|
|
1148
1078
|
self.values = self._values
|
|
1149
|
-
|
|
1150
1079
|
return self
|
|
1151
1080
|
|
|
1152
1081
|
if self.has_array and bounds_was_none:
|
|
@@ -1231,8 +1160,7 @@ class Band(_ImageBandBase):
|
|
|
1231
1160
|
values.shape,
|
|
1232
1161
|
)
|
|
1233
1162
|
|
|
1234
|
-
|
|
1235
|
-
|
|
1163
|
+
height, width = values.shape[-2:]
|
|
1236
1164
|
if width and height:
|
|
1237
1165
|
self.transform = rasterio.transform.from_bounds(
|
|
1238
1166
|
*bounds, width, height
|
|
@@ -1264,7 +1192,7 @@ class Band(_ImageBandBase):
|
|
|
1264
1192
|
def has_array(self) -> bool:
|
|
1265
1193
|
"""Whether the array is loaded."""
|
|
1266
1194
|
try:
|
|
1267
|
-
if not
|
|
1195
|
+
if not is_array_like(self.values):
|
|
1268
1196
|
raise ValueError()
|
|
1269
1197
|
return True
|
|
1270
1198
|
except ValueError: # also catches _ArrayNotLoadedError
|
|
@@ -1501,20 +1429,12 @@ class Band(_ImageBandBase):
|
|
|
1501
1429
|
return df[(df[column] != self.nodata) & (df[column].notna())]
|
|
1502
1430
|
return df
|
|
1503
1431
|
|
|
1504
|
-
def to_xarray(self) -> DataArray:
|
|
1505
|
-
"""Convert the raster to an xarray.DataArray."""
|
|
1506
|
-
return self._to_xarray(
|
|
1507
|
-
self.values,
|
|
1508
|
-
transform=self.transform,
|
|
1509
|
-
# name=self.name or self.__class__.__name__.lower(),
|
|
1510
|
-
)
|
|
1511
|
-
|
|
1512
1432
|
def to_numpy(self) -> np.ndarray | np.ma.core.MaskedArray:
|
|
1513
1433
|
"""Convert the raster to a numpy.ndarray."""
|
|
1514
1434
|
return self._to_numpy(self.values).copy()
|
|
1515
1435
|
|
|
1516
1436
|
def _to_numpy(
|
|
1517
|
-
self, arr: np.ndarray
|
|
1437
|
+
self, arr: np.ndarray, masked: bool = True
|
|
1518
1438
|
) -> np.ndarray | np.ma.core.MaskedArray:
|
|
1519
1439
|
if not isinstance(arr, np.ndarray):
|
|
1520
1440
|
mask_arr = None
|
|
@@ -1891,13 +1811,6 @@ class Image(_ImageBandBase):
|
|
|
1891
1811
|
**self._common_init_kwargs_after_load,
|
|
1892
1812
|
)
|
|
1893
1813
|
|
|
1894
|
-
def to_xarray(self) -> DataArray:
|
|
1895
|
-
"""Convert the raster to an xarray.DataArray."""
|
|
1896
|
-
return self._to_xarray(
|
|
1897
|
-
np.array([band.values for band in self]),
|
|
1898
|
-
transform=self[0].transform,
|
|
1899
|
-
)
|
|
1900
|
-
|
|
1901
1814
|
@property
|
|
1902
1815
|
def band_ids(self) -> list[str]:
|
|
1903
1816
|
"""The Band ids."""
|
|
@@ -2539,6 +2452,10 @@ class ImageCollection(_ImageBase):
|
|
|
2539
2452
|
indexes: int | tuple[int] | None = None,
|
|
2540
2453
|
**kwargs,
|
|
2541
2454
|
) -> np.ndarray:
|
|
2455
|
+
from rioxarray.merge import merge_arrays
|
|
2456
|
+
from rioxarray.rioxarray import _generate_spatial_coords
|
|
2457
|
+
from xarray import DataArray
|
|
2458
|
+
|
|
2542
2459
|
arrs = []
|
|
2543
2460
|
kwargs["indexes"] = indexes
|
|
2544
2461
|
bounds = to_shapely(bounds) if bounds is not None else None
|
|
@@ -2777,69 +2694,6 @@ class ImageCollection(_ImageBase):
|
|
|
2777
2694
|
]
|
|
2778
2695
|
return self
|
|
2779
2696
|
|
|
2780
|
-
def to_xarray(
|
|
2781
|
-
self,
|
|
2782
|
-
**kwargs,
|
|
2783
|
-
) -> Dataset:
|
|
2784
|
-
"""Convert the raster to an xarray.Dataset.
|
|
2785
|
-
|
|
2786
|
-
Images are converted to 2d arrays for each unique bounds.
|
|
2787
|
-
The spatial dimensions will be labeled "x" and "y". The third
|
|
2788
|
-
dimension defaults to "date" if all images have date attributes.
|
|
2789
|
-
Otherwise defaults to the image name.
|
|
2790
|
-
"""
|
|
2791
|
-
if any(not band.has_array for img in self for band in img):
|
|
2792
|
-
raise ValueError("Arrays must be loaded.")
|
|
2793
|
-
|
|
2794
|
-
# if by is None:
|
|
2795
|
-
if all(img.date for img in self):
|
|
2796
|
-
by = ["date"]
|
|
2797
|
-
elif not pd.Index([img.name for img in self]).is_unique:
|
|
2798
|
-
raise ValueError("Images must have unique names.")
|
|
2799
|
-
else:
|
|
2800
|
-
by = ["name"]
|
|
2801
|
-
# elif isinstance(by, str):
|
|
2802
|
-
# by = [by]
|
|
2803
|
-
|
|
2804
|
-
xarrs: dict[str, DataArray] = {}
|
|
2805
|
-
for (bounds, band_id), collection in self.groupby(["bounds", "band_id"]):
|
|
2806
|
-
name = f"{band_id}_{'-'.join(str(int(x)) for x in bounds)}"
|
|
2807
|
-
first_band = collection[0][0]
|
|
2808
|
-
coords = _generate_spatial_coords(
|
|
2809
|
-
first_band.transform, first_band.width, first_band.height
|
|
2810
|
-
)
|
|
2811
|
-
values = np.array([band.to_numpy() for img in collection for band in img])
|
|
2812
|
-
assert len(values) == len(collection)
|
|
2813
|
-
|
|
2814
|
-
# coords["band_id"] = [
|
|
2815
|
-
# band.band_id or i for i, band in enumerate(collection[0])
|
|
2816
|
-
# ]
|
|
2817
|
-
for attr in by:
|
|
2818
|
-
coords[attr] = [getattr(img, attr) for img in collection]
|
|
2819
|
-
# coords["band"] = band_id #
|
|
2820
|
-
|
|
2821
|
-
dims = [*by, "y", "x"]
|
|
2822
|
-
# dims = ["band", "y", "x"]
|
|
2823
|
-
# dims = {}
|
|
2824
|
-
# for attr in by:
|
|
2825
|
-
# dims[attr] = [getattr(img, attr) for img in collection]
|
|
2826
|
-
|
|
2827
|
-
xarrs[name] = DataArray(
|
|
2828
|
-
values,
|
|
2829
|
-
coords=coords,
|
|
2830
|
-
dims=dims,
|
|
2831
|
-
# name=name,
|
|
2832
|
-
name=band_id,
|
|
2833
|
-
attrs={
|
|
2834
|
-
"crs": collection.crs,
|
|
2835
|
-
"band_id": band_id,
|
|
2836
|
-
}, # , "bounds": bounds},
|
|
2837
|
-
**kwargs,
|
|
2838
|
-
)
|
|
2839
|
-
|
|
2840
|
-
return combine_by_coords(list(xarrs.values()))
|
|
2841
|
-
# return Dataset(xarrs)
|
|
2842
|
-
|
|
2843
2697
|
def sample(self, n: int = 1, size: int = 500) -> "ImageCollection":
|
|
2844
2698
|
"""Sample one or more areas of a given size and set this as mask for the images."""
|
|
2845
2699
|
unioned = self.union_all()
|
|
@@ -3407,24 +3261,6 @@ def _slope_2d(array: np.ndarray, res: int | tuple[int], degrees: int) -> np.ndar
|
|
|
3407
3261
|
return degrees
|
|
3408
3262
|
|
|
3409
3263
|
|
|
3410
|
-
def _clip_xarray(
|
|
3411
|
-
xarr: DataArray,
|
|
3412
|
-
mask: tuple[int, int, int, int],
|
|
3413
|
-
crs: Any,
|
|
3414
|
-
**kwargs,
|
|
3415
|
-
) -> DataArray:
|
|
3416
|
-
# xarray needs a numpy array of polygons
|
|
3417
|
-
mask_arr: np.ndarray = to_geoseries(mask).values
|
|
3418
|
-
try:
|
|
3419
|
-
return xarr.rio.clip(
|
|
3420
|
-
mask_arr,
|
|
3421
|
-
crs=crs,
|
|
3422
|
-
**kwargs,
|
|
3423
|
-
)
|
|
3424
|
-
except NoDataInBounds:
|
|
3425
|
-
return np.array([])
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
3264
|
def _get_all_file_paths(path: str) -> set[str]:
|
|
3429
3265
|
return {_fix_path(x) for x in sorted(set(_glob_func(path + "/**")))}
|
|
3430
3266
|
|
|
@@ -3645,6 +3481,9 @@ def array_buffer(arr: np.ndarray, distance: int) -> np.ndarray:
|
|
|
3645
3481
|
Returns:
|
|
3646
3482
|
Array with buffered values.
|
|
3647
3483
|
"""
|
|
3484
|
+
from scipy.ndimage import binary_dilation
|
|
3485
|
+
from scipy.ndimage import binary_erosion
|
|
3486
|
+
|
|
3648
3487
|
if not np.all(np.isin(arr, (1, 0, True, False))):
|
|
3649
3488
|
raise ValueError("Array must be all 0s and 1s or boolean.")
|
|
3650
3489
|
|
|
@@ -3655,6 +3494,7 @@ def array_buffer(arr: np.ndarray, distance: int) -> np.ndarray:
|
|
|
3655
3494
|
arr = np.where(arr, 1, 0)
|
|
3656
3495
|
|
|
3657
3496
|
if distance > 0:
|
|
3497
|
+
|
|
3658
3498
|
return binary_dilation(arr, structure=structure).astype(dtype)
|
|
3659
3499
|
elif distance < 0:
|
|
3660
3500
|
|
|
@@ -3671,6 +3511,8 @@ def _plot_pixels_1d(
|
|
|
3671
3511
|
figsize: tuple,
|
|
3672
3512
|
first_date: pd.Timestamp,
|
|
3673
3513
|
) -> None:
|
|
3514
|
+
from scipy import stats
|
|
3515
|
+
|
|
3674
3516
|
coef, intercept = np.linalg.lstsq(
|
|
3675
3517
|
np.vstack([x, np.ones(x.shape[0])]).T,
|
|
3676
3518
|
y,
|
|
@@ -7,7 +7,7 @@ import pandas as pd
|
|
|
7
7
|
from ..io._is_dapla import is_dapla
|
|
8
8
|
|
|
9
9
|
try:
|
|
10
|
-
|
|
10
|
+
from gcsfs import GCSFileSystem
|
|
11
11
|
except ImportError:
|
|
12
12
|
pass
|
|
13
13
|
|
|
@@ -22,7 +22,7 @@ except ImportError:
|
|
|
22
22
|
if is_dapla():
|
|
23
23
|
|
|
24
24
|
def _open_func(*args, **kwargs) -> GCSFile:
|
|
25
|
-
return
|
|
25
|
+
return GCSFileSystem().open(*args, **kwargs)
|
|
26
26
|
|
|
27
27
|
else:
|
|
28
28
|
_open_func = open
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|