ssb-sgis 1.2.4__py3-none-any.whl → 1.2.7__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.
@@ -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
- return GeoDataFrame(cols | {"geometry": []})
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
- df = GeoDataFrame(cols | {"geometry": []})
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
- df = pd.DataFrame(columns=_get_columns(first_path, file_system))
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
sgis/maps/explore.py CHANGED
@@ -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(centerpoint.buffer(size), keep_geom_type=keep_geom_type)
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:
sgis/maps/httpserver.py CHANGED
@@ -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='{display_address}/stop'>here</a> to stop.</p>
31
+ <p>Click <a href='{stop_address}'>here</a> to stop.</p>
31
32
  """
32
33
  )
33
34
  else:
sgis/maps/map.py CHANGED
@@ -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 i, gdf in enumerate(self._gdfs):
298
+ for label, gdf in self._gdfs.items():
307
299
  if self.column in gdf:
308
- self._gdfs[i][self.column] = to_string_via_int(gdf[self.column])
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
- # custom categorical cmap
609
- if not self._cmap and len(self._unique_values) <= len(_CATEGORICAL_CMAP):
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,
sgis/maps/maps.py CHANGED
@@ -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 = {label: gdf.clip(mask) for label, gdf in m._gdfs.items()}
483
- m._gdf = m._gdf.clip(mask)
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 = {label: gdf.clip(mask) for label, gdf in m._gdfs.items()}
498
- m._gdf = m._gdf.clip(mask)
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, RasterDataset)
531
+ allowed_types = (GeoDataFrame, GeoSeries, Geometry)
534
532
 
535
533
  local_gdfs = {}
536
534
  while True:
sgis/maps/wms.py CHANGED
@@ -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) - 8,
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
- for ori_id in od_pairs.get_level_values(0).unique():
22
- relevant_pairs = od_pairs[od_pairs.get_level_values(0) == ori_id]
23
- destinations = relevant_pairs.get_level_values(1)
24
-
25
- res = graph.get_shortest_paths(
26
- weights="weight", v=ori_id, to=destinations, output="epath"
27
- )
28
-
29
- for i, des_id in enumerate(destinations):
30
- indices = graph.es[res[i]]
31
-
32
- if not indices:
33
- continue
34
-
35
- line_ids = DataFrame({"src_tgt_wt": indices["src_tgt_wt"]})
36
- line_ids["origin"] = ori_id
37
- line_ids["destination"] = des_id
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
- resultlist.append(line_ids)
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
- resultlist: list[DataFrame] = []
66
-
67
- for ori_id in od_pairs.get_level_values(0).unique():
68
- relevant_pairs = od_pairs[od_pairs.get_level_values(0) == ori_id]
69
- destinations = relevant_pairs.get_level_values(1)
70
-
71
- res = graph.get_shortest_paths(
72
- weights="weight", v=ori_id, to=destinations, output="epath"
73
- )
74
-
75
- for i, des_id in enumerate(destinations):
76
- indices = graph.es[res[i]]
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
- resultlist.append(line_ids)
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 != "meters":
51
+ if rules.weight == "minutes":
54
52
  raise ValueError(
55
- "Can only specify 'nodedist_multiplier' when the 'weight' is meters"
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:
sgis/parallel/parallel.py CHANGED
@@ -13,7 +13,7 @@ from typing import Any
13
13
  from pandas.api.types import is_array_like
14
14
 
15
15
  try:
16
- import dapla as dp
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"] = dp.FileClient.get_gcs_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 = dp.FileClient.get_gcs_file_system()
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 (
sgis/raster/base.py CHANGED
@@ -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) == 2:
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, err) from err
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
- width, height = values.shape[-2:]
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 isinstance(self.values, (np.ndarray | DataArray)):
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 | DataArray, masked: bool = True
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,
sgis/raster/regex.py CHANGED
@@ -7,7 +7,7 @@ import pandas as pd
7
7
  from ..io._is_dapla import is_dapla
8
8
 
9
9
  try:
10
- import dapla as dp
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 dp.FileClient.get_gcs_file_system().open(*args, **kwargs)
25
+ return GCSFileSystem().open(*args, **kwargs)
26
26
 
27
27
  else:
28
28
  _open_func = open
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ssb-sgis
3
- Version: 1.2.4
3
+ Version: 1.2.7
4
4
  Summary: GIS functions used at Statistics Norway.
5
5
  Home-page: https://github.com/statisticsnorway/ssb-sgis
6
6
  License: MIT
@@ -7,61 +7,61 @@ sgis/geopandas_tools/bounds.py,sha256=YJyF0gp78hFAjLLZmDquRKCBAtbt7QouG3snTcJeNQ
7
7
  sgis/geopandas_tools/buffer_dissolve_explode.py,sha256=ReIgoeh6CUVcLmsUZ_pyoWYg6iBZzYiGmFq6CMOKRvE,19535
8
8
  sgis/geopandas_tools/centerlines.py,sha256=Q65Sx01SeAlulBEd9oaZkB2maBBNdLcJwAbTILg4SPU,11848
9
9
  sgis/geopandas_tools/cleaning.py,sha256=fST0xFztmyn-QUOAfvjZmu7aO_zPiolWK7gd7TR6ffI,24393
10
- sgis/geopandas_tools/conversion.py,sha256=w3W0Utaw7SESRR659percNLwOY9_yfg6DL5hcuM1CUA,25017
10
+ sgis/geopandas_tools/conversion.py,sha256=CrasgWHAnUmLC5tP73ZTDjQ6ahKFHQGqWj86PUif24M,24176
11
11
  sgis/geopandas_tools/duplicates.py,sha256=TDDM4u1n7SIkyJrOfl1Lno92AmUPqtXBHsj1IUKC0hI,14992
12
12
  sgis/geopandas_tools/general.py,sha256=YRpNEdwTHyFdQOdAfbCmYXS7PxoDjXxoagwpteXkYdI,43937
13
- sgis/geopandas_tools/geocoding.py,sha256=n47aFQMm4yX1MsPnTM4dFjwegCA1ZmGUDj1uyu7OJV4,691
13
+ sgis/geopandas_tools/geocoding.py,sha256=sZjUW52ULhQWDLmU51C9_itBePkDuWkp8swvYaiYmJk,679
14
14
  sgis/geopandas_tools/geometry_types.py,sha256=ijQDbQaZPqPGjBl707H4yooNXpk21RXyatI7itnvqLk,7603
15
- sgis/geopandas_tools/neighbors.py,sha256=tMs8jUU0np5QvIysUdF0lLEdXwiXTBotTSdgUXclEfY,17480
15
+ sgis/geopandas_tools/neighbors.py,sha256=qTvnOyQyrcWQd0BDrmOvFQd8csMh005e_idJGmB4g7k,17485
16
16
  sgis/geopandas_tools/overlay.py,sha256=5i9u8GgFuU0fCqzELsbIaoUPhw-E7eZHl_yKB0wEcGM,23464
17
17
  sgis/geopandas_tools/point_operations.py,sha256=JM4hvfIVxZaZdGNlGzcCurrKzkgC_b9hzbFYN42f9WY,6972
18
18
  sgis/geopandas_tools/polygon_operations.py,sha256=v-B9IgbFfm4dVHKPyzvmnNiqVCdtl9ddpCsQpZZ-9sU,49284
19
19
  sgis/geopandas_tools/polygons_as_rings.py,sha256=BX_GZS6F9I4NbEpiOlNBd7zywJjdfdJVi_MkeONBuiM,14941
20
20
  sgis/geopandas_tools/runners.py,sha256=J4lH0RXYDYTLVeQFgNv8gEY0E97QGIQ4zPW5vfoxgDU,12979
21
- sgis/geopandas_tools/sfilter.py,sha256=BPz6-_9B7QdyYmVatZXavdHj7FIW_ztIyJHQOkKJt7A,10284
21
+ sgis/geopandas_tools/sfilter.py,sha256=CZ_-c4t1CQCwJ7RHCKo1Na9u-aAg18xXnJAMiUqoaj8,10411
22
22
  sgis/geopandas_tools/utils.py,sha256=X0pRvB1tWgV_0BCrRS1HU9LtLGnZCpvVPxyqM9JGb0Y,1415
23
23
  sgis/helpers.py,sha256=4N6vFWQ3TYVzRHNcWY_fNa_GkFuaZB3vtCkkFND-qs0,9628
24
24
  sgis/io/__init__.py,sha256=uyBr20YDqB2bQttrd5q1JuGOvX32A-MSvS7Wmw5f5qg,177
25
25
  sgis/io/_is_dapla.py,sha256=wmfkSe98IrLhUg3dtXZusV6OVC8VlY1kbc5EQDf3P-Q,358
26
- sgis/io/dapla_functions.py,sha256=AbP400wE8wd3KhfoX_BoEC6ba9fR0gCg8qWSqdkJ5dU,30815
26
+ sgis/io/dapla_functions.py,sha256=ISF1vaBZI0D9pMngWVvHDvQr9wFHagQaAGY3z5P3Iqo,31116
27
27
  sgis/io/opener.py,sha256=HWO3G1NB6bpXKM94JadCD513vjat1o1TFjWGWzyVasg,898
28
28
  sgis/io/read_parquet.py,sha256=FvZYv1rLkUlrSaUY6QW6E1yntmntTeQuZ9ZRgCDO4IM,3776
29
29
  sgis/maps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  sgis/maps/examine.py,sha256=Pb0dH8JazU5E2svfQrzHO1Bi-sjy5SeyY6zoeMO34jE,9369
31
- sgis/maps/explore.py,sha256=4ly_zeMiHUZThBzoVt62Z4smEZH97qsnzodaaRiBJco,47697
32
- sgis/maps/httpserver.py,sha256=eCDoB9x74kSLiGEj2X3O91t3oscY_ia17UNuaaJ6tCc,2472
31
+ sgis/maps/explore.py,sha256=1kdxHpEPlzgkvsYRoqkJxh0sKyjQLualWy-PhHnEwl4,47812
32
+ sgis/maps/httpserver.py,sha256=TETSGOgLjKl3TquPGoIP0tCJCz7BIwmXrrzSCT7jhXE,2550
33
33
  sgis/maps/legend.py,sha256=qq2RkebuaNAdFztlXrDOWbN0voeK5w5VycmRKyx0NdM,26512
34
- sgis/maps/map.py,sha256=q0gqOg_DD1ea7B_8__nuFN1dYA7o3uIxS6KuJwmVNhQ,30269
35
- sgis/maps/maps.py,sha256=Ti4nfm6bz_8ZWVHoEHF61moI3LQH9Ee6Jd8NHzInWkw,23135
34
+ sgis/maps/map.py,sha256=XWf3QJ6a4gZno2NziK1dKLRktJGGr-vn6eHudBlW9Uc,30758
35
+ sgis/maps/maps.py,sha256=66xCarrpvlgsts3ut-SnbIiexEwyy_AIed4O4tysUig,23139
36
36
  sgis/maps/norge_i_bilder.json,sha256=VKmb7rg4jvgc8_Ve1fFnHyZ_Dkv4T5GTA0UCpqpFAi4,492751
37
37
  sgis/maps/thematicmap.py,sha256=Z3o_Bca0oty5Cn35pZfX5Qy52sXDVIMVSFD6IlZrovo,25111
38
38
  sgis/maps/tilesources.py,sha256=F4mFHxPwkiPJdVKzNkScTX6xbJAMIUtlTq4mQ83oguw,1746
39
- sgis/maps/wms.py,sha256=UjsKAvrZkcYRgjVGGeg6XA4Lkx6WU9w6WTyeSkwN78w,6931
39
+ sgis/maps/wms.py,sha256=LSBtKkG5d-sggK_qO8BvOHBvPZZV_7AAo25kHF6kPio,6933
40
40
  sgis/networkanalysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
- sgis/networkanalysis/_get_route.py,sha256=9I3t9pnccUPr4mozy3TJCOpGCCf3UOIojmsbifubZbA,6368
41
+ sgis/networkanalysis/_get_route.py,sha256=dMX4Vm6O90ISIZPjQWuZMVMuEubkeSdC2osMCbFvrRU,7750
42
42
  sgis/networkanalysis/_od_cost_matrix.py,sha256=zkyPX7ObT996ahaFJ2oI0D0SqQWbWyfy_qLtXwValPg,3434
43
- sgis/networkanalysis/_points.py,sha256=ajCy17dAmRq3bWRkNu_0LHreCVJ5Uh8DzAKWxyw7ipw,4481
43
+ sgis/networkanalysis/_points.py,sha256=128QEepBGAyI5XakMUVvNyOnJE0Ts7hvGiVUt1YTXiU,4439
44
44
  sgis/networkanalysis/_service_area.py,sha256=jE0X54yS4eMfZYJXeKe_NgMKPDpau-05xWZaxDi_c6Y,5546
45
45
  sgis/networkanalysis/closing_network_holes.py,sha256=FYZ677nRwLmDkP6bQ1ssQ_h29RzAG463U4xmbu5ksfg,14572
46
46
  sgis/networkanalysis/cutting_lines.py,sha256=ZQAt0cufaPeNAEqUzp-imu26AIL9S5-lw6Xifa8RoWk,9818
47
47
  sgis/networkanalysis/directednetwork.py,sha256=Mrc2zHip4P5RNxnyffKm-xU832AVQeSHz-YZueAc0pM,11413
48
48
  sgis/networkanalysis/finding_isolated_networks.py,sha256=Wg4ILhm7uS9RLOGcL0WN8uZBMJYjdljJc8L5DU5nIPY,3754
49
49
  sgis/networkanalysis/network.py,sha256=zV9bAbVdTgTohg2o2RFGy2uhOJrd3Ma57hwIAStxMAQ,7847
50
- sgis/networkanalysis/networkanalysis.py,sha256=-g7slZLFNxUZSUMvVmf7zax-9IOXz1NGCtR6LcsBzBQ,68476
50
+ sgis/networkanalysis/networkanalysis.py,sha256=KkmMt_AH4iRBGe2acNbhABVpyDV8FP_O5TKnDoZFLpM,68692
51
51
  sgis/networkanalysis/networkanalysisrules.py,sha256=9sXigaCzvKhXFwpeVNMtOiIK3_Hzp9yDpFklmEEAPak,12956
52
52
  sgis/networkanalysis/nodes.py,sha256=atFSpqz-_uJHMrf6MC0zhrrcWIydRMFZrsaHC2xr1GU,3374
53
53
  sgis/networkanalysis/traveling_salesman.py,sha256=Jjo6bHY4KJ-eK0LycyTy0sWxZjgITs5MBllZ_G9FhTE,5655
54
54
  sgis/parallel/__init__.py,sha256=fw_Fl3IJk1bKzrRBhZIoOpznJqwd09NVHJJFj2ZLeIU,32
55
- sgis/parallel/parallel.py,sha256=qPhCiL81SZNKnJqFrW-rHrJeZvC7jO9_FjxO0RMdEUo,40079
55
+ sgis/parallel/parallel.py,sha256=V7O5mEZdfJpcOPrmn2H4bEGtbzA0FggjQ8dGhznjr80,40052
56
56
  sgis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  sgis/raster/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
- sgis/raster/base.py,sha256=tiZEuMcVK6hOm_aIjWhQ1WGshcjsxT1fFkuBSLFiMC0,7785
59
- sgis/raster/image_collection.py,sha256=W5x4PsaOnXALAXTDj3q-X6TMVgVD9kyFXmFdJewe9lY,126533
58
+ sgis/raster/base.py,sha256=8JdXXDj8CgJQt5WIkkNbpM5U0pYyQrWQY9dszfUaUQ4,7743
59
+ sgis/raster/image_collection.py,sha256=H7d9c6f-LsGXBK8oCpWBlgd2zbLxYvfNMYllnbrO_rk,121697
60
60
  sgis/raster/indices.py,sha256=efJmgfPg_VuSzXFosXV661IendF8CwPFWtMhyP4TMUg,222
61
- sgis/raster/regex.py,sha256=kYhVpRYzoXutx1dSYmqMoselWXww7MMEsTPmLZwHjbM,3759
61
+ sgis/raster/regex.py,sha256=4idTJ9vFtsGtbxcjJrx2VrpJJuDMP3bLdqF93Vc_cmY,3752
62
62
  sgis/raster/sentinel_config.py,sha256=nySDqn2R8M6W8jguoBeSAK_zzbAsqmaI59i32446FwY,1268
63
63
  sgis/raster/zonal.py,sha256=D4Gyptw-yOLTCO41peIuYbY-DANsJCG19xXDlf1QAz4,2299
64
- ssb_sgis-1.2.4.dist-info/LICENSE,sha256=np3IfD5m0ZUofn_kVzDZqliozuiO6wrktw3LRPjyEiI,1073
65
- ssb_sgis-1.2.4.dist-info/METADATA,sha256=0vA7hJ78jGcP1uHtgeeNYN-Ne_9xZQ_fX3SRamcESxw,11740
66
- ssb_sgis-1.2.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
67
- ssb_sgis-1.2.4.dist-info/RECORD,,
64
+ ssb_sgis-1.2.7.dist-info/LICENSE,sha256=np3IfD5m0ZUofn_kVzDZqliozuiO6wrktw3LRPjyEiI,1073
65
+ ssb_sgis-1.2.7.dist-info/METADATA,sha256=oGAi8NDi4GwCfUxWBbJVgRDROiMrb_xrqf13vwhc-n0,11740
66
+ ssb_sgis-1.2.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
67
+ ssb_sgis-1.2.7.dist-info/RECORD,,