ssb-sgis 1.0.2__py3-none-any.whl → 1.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. sgis/__init__.py +20 -9
  2. sgis/debug_config.py +24 -0
  3. sgis/exceptions.py +2 -2
  4. sgis/geopandas_tools/bounds.py +33 -36
  5. sgis/geopandas_tools/buffer_dissolve_explode.py +136 -35
  6. sgis/geopandas_tools/centerlines.py +4 -91
  7. sgis/geopandas_tools/cleaning.py +1576 -583
  8. sgis/geopandas_tools/conversion.py +38 -19
  9. sgis/geopandas_tools/duplicates.py +29 -8
  10. sgis/geopandas_tools/general.py +263 -100
  11. sgis/geopandas_tools/geometry_types.py +4 -4
  12. sgis/geopandas_tools/neighbors.py +19 -15
  13. sgis/geopandas_tools/overlay.py +2 -2
  14. sgis/geopandas_tools/point_operations.py +5 -5
  15. sgis/geopandas_tools/polygon_operations.py +510 -105
  16. sgis/geopandas_tools/polygons_as_rings.py +40 -8
  17. sgis/geopandas_tools/sfilter.py +29 -12
  18. sgis/helpers.py +3 -3
  19. sgis/io/dapla_functions.py +238 -19
  20. sgis/io/read_parquet.py +1 -1
  21. sgis/maps/examine.py +27 -12
  22. sgis/maps/explore.py +450 -65
  23. sgis/maps/legend.py +177 -76
  24. sgis/maps/map.py +206 -103
  25. sgis/maps/maps.py +178 -105
  26. sgis/maps/thematicmap.py +243 -83
  27. sgis/networkanalysis/_service_area.py +6 -1
  28. sgis/networkanalysis/closing_network_holes.py +2 -2
  29. sgis/networkanalysis/cutting_lines.py +15 -8
  30. sgis/networkanalysis/directednetwork.py +1 -1
  31. sgis/networkanalysis/finding_isolated_networks.py +15 -8
  32. sgis/networkanalysis/networkanalysis.py +17 -19
  33. sgis/networkanalysis/networkanalysisrules.py +1 -1
  34. sgis/networkanalysis/traveling_salesman.py +1 -1
  35. sgis/parallel/parallel.py +64 -27
  36. sgis/raster/__init__.py +0 -6
  37. sgis/raster/base.py +208 -0
  38. sgis/raster/cube.py +54 -8
  39. sgis/raster/image_collection.py +3257 -0
  40. sgis/raster/indices.py +17 -5
  41. sgis/raster/raster.py +138 -243
  42. sgis/raster/sentinel_config.py +120 -0
  43. sgis/raster/zonal.py +0 -1
  44. {ssb_sgis-1.0.2.dist-info → ssb_sgis-1.0.4.dist-info}/METADATA +6 -7
  45. ssb_sgis-1.0.4.dist-info/RECORD +62 -0
  46. sgis/raster/methods_as_functions.py +0 -0
  47. sgis/raster/torchgeo.py +0 -171
  48. ssb_sgis-1.0.2.dist-info/RECORD +0 -61
  49. {ssb_sgis-1.0.2.dist-info → ssb_sgis-1.0.4.dist-info}/LICENSE +0 -0
  50. {ssb_sgis-1.0.2.dist-info → ssb_sgis-1.0.4.dist-info}/WHEEL +0 -0
@@ -57,7 +57,7 @@ class NetworkAnalysis:
57
57
  log: A DataFrame with information about each analysis run.
58
58
 
59
59
  Examples:
60
- --------
60
+ ---------
61
61
  Read example data.
62
62
 
63
63
  >>> import sgis as sg
@@ -191,7 +191,7 @@ class NetworkAnalysis:
191
191
  straight lines between origin and destination.
192
192
 
193
193
  Examples:
194
- --------
194
+ ---------
195
195
  Create the NetworkAnalysis instance.
196
196
 
197
197
  >>> import sgis as sg
@@ -479,7 +479,7 @@ class NetworkAnalysis:
479
479
  that contain weights and all indices of 'origins' and 'destinations'.
480
480
 
481
481
  Examples:
482
- --------
482
+ ---------
483
483
  Create the NetworkAnalysis instance.
484
484
 
485
485
  >>> import sgis as sg
@@ -692,7 +692,7 @@ class NetworkAnalysis:
692
692
  containing the indices of the origins and destinations GeoDataFrames.
693
693
 
694
694
  Examples:
695
- --------
695
+ ---------
696
696
  Create the NetworkAnalysis instance.
697
697
 
698
698
  >>> import sgis as sg
@@ -820,7 +820,7 @@ class NetworkAnalysis:
820
820
  ValueError: if drop_middle_percent is not between 0 and 100.
821
821
 
822
822
  Examples:
823
- --------
823
+ ---------
824
824
  Create the NetworkAnalysis instance.
825
825
 
826
826
  >>> import sgis as sg
@@ -966,7 +966,7 @@ class NetworkAnalysis:
966
966
  precice results.
967
967
 
968
968
  Examples:
969
- --------
969
+ ---------
970
970
  Create the NetworkAnalysis instance.
971
971
 
972
972
  >>> import sgis as sg
@@ -1027,7 +1027,7 @@ class NetworkAnalysis:
1027
1027
 
1028
1028
  if dissolve:
1029
1029
  results = results.dissolve(by=["origin", self.rules.weight]).loc[
1030
- :, ["geometry"]
1030
+ :, [results.geometry.name]
1031
1031
  ]
1032
1032
 
1033
1033
  results = results.reset_index()
@@ -1038,7 +1038,7 @@ class NetworkAnalysis:
1038
1038
  ].rename(columns={"temp_idx": "origin"})[["origin"]]
1039
1039
 
1040
1040
  if len(missing):
1041
- missing["geometry"] = pd.NA
1041
+ missing[results.geometry.name] = pd.NA
1042
1042
  results = pd.concat([results, missing], ignore_index=True)
1043
1043
 
1044
1044
  results["origin"] = results["origin"].map(self.origins.idx_dict)
@@ -1093,7 +1093,7 @@ class NetworkAnalysis:
1093
1093
  service_area: Faster method where lines are not cut to get precice results.
1094
1094
 
1095
1095
  Examples:
1096
- --------
1096
+ ---------
1097
1097
  Create the NetworkAnalysis instance.
1098
1098
 
1099
1099
  >>> import sgis as sg
@@ -1151,7 +1151,7 @@ class NetworkAnalysis:
1151
1151
  if not all(results.geometry.isna()):
1152
1152
  if dissolve:
1153
1153
  results = results.dissolve(by=["origin", self.rules.weight]).loc[
1154
- :, ["geometry"]
1154
+ :, [results.geometry.name]
1155
1155
  ]
1156
1156
  else:
1157
1157
  results = results.dissolve(
@@ -1166,7 +1166,7 @@ class NetworkAnalysis:
1166
1166
  ].rename(columns={"temp_idx": "origin"})[["origin"]]
1167
1167
 
1168
1168
  if len(missing):
1169
- missing["geometry"] = pd.NA
1169
+ missing[results.geometry.name] = pd.NA
1170
1170
  results = pd.concat([results, missing], ignore_index=True)
1171
1171
 
1172
1172
  results["origin"] = results["origin"].map(self.origins.idx_dict)
@@ -1329,7 +1329,7 @@ class NetworkAnalysis:
1329
1329
  df["cost_std"] = results[self.rules.weight].std()
1330
1330
 
1331
1331
  if fun == "service_area":
1332
- df["percent_missing"] = results["geometry"].isna().mean() * 100
1332
+ df["percent_missing"] = results[results.geometry.name].isna().mean() * 100
1333
1333
  else:
1334
1334
  df["destinations_count"] = len(self.destinations.gdf)
1335
1335
 
@@ -1456,7 +1456,7 @@ class NetworkAnalysis:
1456
1456
  else:
1457
1457
  points = self.origins.gdf
1458
1458
 
1459
- points = points.drop_duplicates("geometry")
1459
+ points = points.drop_duplicates(points.geometry.name)
1460
1460
 
1461
1461
  self.network.gdf["meters_"] = self.network.gdf.length
1462
1462
 
@@ -1573,7 +1573,7 @@ class NetworkAnalysis:
1573
1573
  This method is best stored in the NetworkAnalysis class,
1574
1574
  since the point classes are instantiated each time an analysis is run.
1575
1575
  """
1576
- if self.wkts[what] != [geom.wkt for geom in points.geometry]:
1576
+ if not np.array_equal(self.wkts[what], points.geometry.to_wkt().values):
1577
1577
  return True
1578
1578
 
1579
1579
  if not all(x in self.graph.vs["name"] for x in list(points.temp_idx.values)):
@@ -1590,17 +1590,15 @@ class NetworkAnalysis:
1590
1590
  """
1591
1591
  self.wkts = {}
1592
1592
 
1593
- self.wkts["network"] = [geom.wkt for geom in self.network.gdf.geometry]
1593
+ self.wkts["network"] = self.network.gdf.geometry.to_wkt().values
1594
1594
 
1595
1595
  if not hasattr(self, "origins"):
1596
1596
  return
1597
1597
 
1598
- self.wkts["origins"] = [geom.wkt for geom in self.origins.gdf.geometry]
1598
+ self.wkts["origins"] = self.origins.gdf.geometry.to_wkt().values
1599
1599
 
1600
1600
  if self.destinations is not None:
1601
- self.wkts["destinations"] = [
1602
- geom.wkt for geom in self.destinations.gdf.geometry
1603
- ]
1601
+ self.wkts["destinations"] = self.destinations.gdf.geometry.to_wkt().values
1604
1602
 
1605
1603
  @staticmethod
1606
1604
  def _sort_breaks(breaks: str | list | tuple | int | float) -> list[float | int]:
@@ -50,7 +50,7 @@ class NetworkAnalysisRules:
50
50
  to 1, the weight will be equal to the straigt line distance.
51
51
 
52
52
  Examples:
53
- --------
53
+ ---------
54
54
  Read testdata.
55
55
 
56
56
  >>> import sgis as sg
@@ -32,7 +32,7 @@ def traveling_salesman_problem(
32
32
  List of Points making up the traveling salesman's path.
33
33
 
34
34
  Examples:
35
- --------
35
+ ---------
36
36
  >>> import sgis as sg
37
37
  >>> from shapely.geometry import LineString
38
38
  >>> points = sg.to_gdf(
sgis/parallel/parallel.py CHANGED
@@ -2,6 +2,7 @@ import functools
2
2
  import inspect
3
3
  import itertools
4
4
  import multiprocessing
5
+ import pickle
5
6
  import warnings
6
7
  from collections.abc import Callable
7
8
  from collections.abc import Collection
@@ -31,7 +32,6 @@ try:
31
32
  from ..io.dapla_functions import read_geopandas
32
33
  from ..io.dapla_functions import write_geopandas
33
34
 
34
- # from ..io.write_municipality_data import write_municipality_data
35
35
  except ImportError:
36
36
  pass
37
37
 
@@ -39,11 +39,8 @@ except ImportError:
39
39
  try:
40
40
  from dapla import read_pandas
41
41
  from dapla import write_pandas
42
- from dapla.gcs import GCSFileSystem
43
42
  except ImportError:
44
-
45
- class GCSFileSystem:
46
- """Placeholder."""
43
+ pass
47
44
 
48
45
 
49
46
  class Parallel:
@@ -132,7 +129,7 @@ class Parallel:
132
129
  'iterable'.
133
130
 
134
131
  Examples:
135
- --------
132
+ ---------
136
133
  Multiply each list element by 2.
137
134
 
138
135
  >>> iterable = [1, 2, 3]
@@ -183,7 +180,7 @@ class Parallel:
183
180
  func_with_kwargs = functools.partial(func, **kwargs)
184
181
 
185
182
  if self.processes == 1:
186
- return list(map(func_with_kwargs, iterable))
183
+ return [func_with_kwargs(item) for item in iterable]
187
184
 
188
185
  iterable = list(iterable)
189
186
 
@@ -192,23 +189,42 @@ class Parallel:
192
189
 
193
190
  if not processes:
194
191
  return []
192
+ elif processes == 1:
193
+ return [func_with_kwargs(item) for item in iterable]
195
194
 
196
- if self.backend == "multiprocessing":
197
- with multiprocessing.get_context(self.context).Pool(
198
- processes, maxtasksperchild=self.maxtasksperchild, **self.kwargs
199
- ) as pool:
200
- try:
201
- return pool.map(
202
- func_with_kwargs, iterable, chunksize=self.chunksize
203
- )
204
- except Exception as e:
205
- pool.terminate()
206
- raise e
195
+ try:
196
+ if self.backend == "multiprocessing":
197
+ with multiprocessing.get_context(self.context).Pool(
198
+ processes, maxtasksperchild=self.maxtasksperchild, **self.kwargs
199
+ ) as pool:
200
+ try:
201
+ return pool.map(
202
+ func_with_kwargs, iterable, chunksize=self.chunksize
203
+ )
204
+ except Exception as e:
205
+ pool.terminate()
206
+ raise e
207
207
 
208
- with joblib.Parallel(
209
- n_jobs=processes, backend=self.backend, **self.kwargs
210
- ) as parallel:
211
- return parallel(joblib.delayed(func)(item, **kwargs) for item in iterable)
208
+ with joblib.Parallel(
209
+ n_jobs=processes, backend=self.backend, **self.kwargs
210
+ ) as parallel:
211
+ return parallel(
212
+ joblib.delayed(func)(item, **kwargs) for item in iterable
213
+ )
214
+ except pickle.PickleError as e:
215
+ unpicklable = []
216
+ for k, v in locals().items():
217
+ try:
218
+ pickle.dumps(v)
219
+ except pickle.PickleError:
220
+ unpicklable.append(k)
221
+ except TypeError:
222
+ pass
223
+ if unpicklable:
224
+ raise pickle.PickleError(
225
+ f"Cannot unpickle objects: {unpicklable}"
226
+ ) from e
227
+ raise e
212
228
 
213
229
  def starmap(
214
230
  self,
@@ -236,7 +252,7 @@ class Parallel:
236
252
  'iterable'.
237
253
 
238
254
  Examples:
239
- --------
255
+ ---------
240
256
  Multiply each list element by 2.
241
257
 
242
258
  >>> iterable = [(1, 2), (2, 3), (3, 4)]
@@ -786,7 +802,11 @@ def _write_one_muni(
786
802
 
787
803
  if not len(gdf_muni):
788
804
  if write_empty:
789
- gdf_muni = gdf_muni.drop(columns="geometry", errors="ignore")
805
+ try:
806
+ geom_col = gdf.geometry.name
807
+ except AttributeError:
808
+ geom_col = "geometry"
809
+ gdf_muni = gdf_muni.drop(columns=geom_col, errors="ignore")
790
810
  gdf_muni["geometry"] = None
791
811
  write_pandas(gdf_muni, out)
792
812
  return
@@ -814,7 +834,11 @@ def _write_one_muni_with_neighbors(
814
834
 
815
835
  if not len(gdf_neighbor):
816
836
  if write_empty:
817
- gdf_neighbor = gdf_neighbor.drop(columns="geometry", errors="ignore")
837
+ try:
838
+ geom_col = gdf.geometry.name
839
+ except AttributeError:
840
+ geom_col = "geometry"
841
+ gdf_neighbor = gdf_neighbor.drop(columns=geom_col, errors="ignore")
818
842
  gdf_neighbor["geometry"] = None
819
843
  write_pandas(gdf_neighbor, out)
820
844
  return
@@ -860,7 +884,9 @@ def _fix_missing_muni_numbers(
860
884
  )
861
885
 
862
886
  try:
863
- municipalities = municipalities[[muni_number_col, "geometry"]].to_crs(gdf.crs)
887
+ municipalities = municipalities[
888
+ [muni_number_col, municipalities.geometry.name]
889
+ ].to_crs(gdf.crs)
864
890
  except Exception as e:
865
891
  raise e.__class__(e, to_print) from e
866
892
 
@@ -950,7 +976,18 @@ def _clean_intersection(
950
976
  df1: GeoDataFrame, df2: GeoDataFrame, to_print: str | None = None
951
977
  ) -> GeoDataFrame:
952
978
  print(to_print, "- intersection chunk len:", len(df1))
953
- return clean_overlay(df1, df2, how="intersection")
979
+ cols_to_keep = df1.columns.union(df2.columns.difference({df2.geometry.name}))
980
+ df1["_range_idx"] = range(len(df1))
981
+ joined = df1.sjoin(df2, predicate="within", how="left")
982
+ within = joined.loc[joined["_range_idx"].notna(), cols_to_keep]
983
+ not_within = joined.loc[joined["_range_idx"].isna(), df1.columns]
984
+ return pd.concat(
985
+ [
986
+ within,
987
+ clean_overlay(not_within, df2, how="intersection"),
988
+ ],
989
+ ignore_index=True,
990
+ )
954
991
 
955
992
 
956
993
  def chunkwise(
sgis/raster/__init__.py CHANGED
@@ -1,6 +0,0 @@
1
- try:
2
- from .torchgeo import SENTINEL2_FILENAME_REGEX
3
- from .torchgeo import SENTINEL_2_BANDS
4
- from .torchgeo import SENTINEL_2_RBG_BANDS
5
- except ImportError:
6
- pass
sgis/raster/base.py CHANGED
@@ -1,8 +1,216 @@
1
+ import json
2
+ import numbers
3
+ import warnings
4
+ from collections.abc import Callable
1
5
  from contextlib import contextmanager
6
+ from typing import Any
2
7
 
8
+ import joblib
3
9
  import numpy as np
4
10
  import pandas as pd
5
11
  import rasterio
12
+ from affine import Affine
13
+ from geopandas import GeoDataFrame
14
+ from geopandas import GeoSeries
15
+ from rasterio import features
16
+ from rasterio.enums import MergeAlg
17
+ from shapely import Geometry
18
+ from shapely.geometry import shape
19
+
20
+ from ..geopandas_tools.conversion import to_bbox
21
+
22
+
23
+ def _get_transform_from_bounds(
24
+ obj: GeoDataFrame | GeoSeries | Geometry | tuple, shape: tuple[float, ...]
25
+ ) -> Affine:
26
+ minx, miny, maxx, maxy = to_bbox(obj)
27
+ if len(shape) == 2:
28
+ height, width = shape
29
+ elif len(shape) == 3:
30
+ _, height, width = shape
31
+ else:
32
+ return None
33
+ # raise ValueError(shape)
34
+ return rasterio.transform.from_bounds(minx, miny, maxx, maxy, width, height)
35
+
36
+
37
+ def _get_shape_from_bounds(
38
+ obj: GeoDataFrame | GeoSeries | Geometry | tuple,
39
+ res: int,
40
+ indexes: int | tuple[int],
41
+ ) -> tuple[int, int]:
42
+ resx, resy = (res, res) if isinstance(res, numbers.Number) else res
43
+
44
+ minx, miny, maxx, maxy = to_bbox(obj)
45
+
46
+ # minx = math.floor(minx / res) * res
47
+ # maxx = math.ceil(maxx / res) * res
48
+ # miny = math.floor(miny / res) * res
49
+ # maxy = math.ceil(maxy / res) * res
50
+
51
+ # # Compute output array shape. We guarantee it will cover the output
52
+ # # bounds completely
53
+ # width = round((maxx - minx) // res)
54
+ # height = round((maxy - miny) // res)
55
+
56
+ # if not isinstance(indexes, int):
57
+ # return len(indexes), height, width
58
+ # return height, width
59
+
60
+ diffx = maxx - minx
61
+ diffy = maxy - miny
62
+ width = int(diffx / resx)
63
+ height = int(diffy / resy)
64
+ if not isinstance(indexes, int):
65
+ return len(indexes), width, height
66
+ return height, width
67
+
68
+
69
+ def _array_to_geojson(
70
+ array: np.ndarray, transform: Affine, processes: int
71
+ ) -> list[tuple]:
72
+ if hasattr(array, "mask"):
73
+ if isinstance(array.mask, np.ndarray):
74
+ mask = array.mask == False
75
+ else:
76
+ mask = None
77
+ array = array.data
78
+ else:
79
+ mask = None
80
+
81
+ try:
82
+ return _array_to_geojson_loop(array, transform, mask, processes)
83
+ except ValueError:
84
+ try:
85
+ array = array.astype(np.float32)
86
+ return _array_to_geojson_loop(array, transform, mask, processes)
87
+
88
+ except Exception as err:
89
+ raise err.__class__(array.shape, err) from err
90
+
91
+
92
+ def _array_to_geojson_loop(array, transform, mask, processes):
93
+ if processes == 1:
94
+ return [
95
+ (value, shape(geom))
96
+ for geom, value in features.shapes(array, transform=transform, mask=mask)
97
+ ]
98
+ else:
99
+ with joblib.Parallel(n_jobs=processes, backend="threading") as parallel:
100
+ return parallel(
101
+ joblib.delayed(_value_geom_pair)(value, geom)
102
+ for geom, value in features.shapes(
103
+ array, transform=transform, mask=mask
104
+ )
105
+ )
106
+
107
+
108
+ def _value_geom_pair(value, geom):
109
+ return (value, shape(geom))
110
+
111
+
112
+ def _gdf_to_arr(
113
+ gdf: GeoDataFrame,
114
+ res: int | float,
115
+ fill: int = 0,
116
+ all_touched: bool = False,
117
+ merge_alg: Callable = MergeAlg.replace,
118
+ default_value: int = 1,
119
+ dtype: Any | None = None,
120
+ ) -> np.ndarray:
121
+ """Construct Raster from a GeoDataFrame or GeoSeries.
122
+
123
+ The GeoDataFrame should have
124
+
125
+ Args:
126
+ gdf: The GeoDataFrame to rasterize.
127
+ res: Resolution of the raster in units of the GeoDataFrame's coordinate reference system.
128
+ fill: Fill value for areas outside of input geometries (default is 0).
129
+ all_touched: Whether to consider all pixels touched by geometries,
130
+ not just those whose center is within the polygon (default is False).
131
+ merge_alg: Merge algorithm to use when combining geometries
132
+ (default is 'MergeAlg.replace').
133
+ default_value: Default value to use for the rasterized pixels
134
+ (default is 1).
135
+ dtype: Data type of the output array. If None, it will be
136
+ determined automatically.
137
+
138
+ Returns:
139
+ A Raster instance based on the specified GeoDataFrame and parameters.
140
+
141
+ Raises:
142
+ TypeError: If 'transform' is provided in kwargs, as this is
143
+ computed based on the GeoDataFrame bounds and resolution.
144
+ """
145
+ if isinstance(gdf, GeoSeries):
146
+ values = gdf.index
147
+ gdf = gdf.to_frame("geometry")
148
+ elif isinstance(gdf, GeoDataFrame):
149
+ if len(gdf.columns) > 2:
150
+ raise ValueError(
151
+ "gdf should have only a geometry column and one numeric column to "
152
+ "use as array values. "
153
+ "Alternatively only a geometry column and a numeric index."
154
+ )
155
+ elif len(gdf.columns) == 1:
156
+ values = gdf.index
157
+ else:
158
+ col: str = next(
159
+ iter([col for col in gdf if col != gdf._geometry_column_name])
160
+ )
161
+ values = gdf[col]
162
+
163
+ if isinstance(values, pd.MultiIndex):
164
+ raise ValueError("Index cannot be MultiIndex.")
165
+
166
+ shape = _get_shape_from_bounds(gdf.total_bounds, res=res, indexes=1)
167
+ transform = _get_transform_from_bounds(gdf.total_bounds, shape)
168
+
169
+ return features.rasterize(
170
+ _gdf_to_geojson_with_col(gdf, values),
171
+ out_shape=shape,
172
+ transform=transform,
173
+ fill=fill,
174
+ all_touched=all_touched,
175
+ merge_alg=merge_alg,
176
+ default_value=default_value,
177
+ dtype=dtype,
178
+ )
179
+
180
+
181
+ def _gdf_to_geojson_with_col(gdf: GeoDataFrame, values: np.ndarray) -> list[dict]:
182
+ with warnings.catch_warnings():
183
+ warnings.filterwarnings("ignore", category=UserWarning)
184
+ return [
185
+ (feature["geometry"], val)
186
+ for val, feature in zip(
187
+ values, json.loads(gdf.to_json())["features"], strict=False
188
+ )
189
+ ]
190
+
191
+
192
+ def _shapely_to_raster(
193
+ geometry: Geometry,
194
+ res: int | float,
195
+ fill: int = 0,
196
+ all_touched: bool = False,
197
+ merge_alg: Callable = MergeAlg.replace,
198
+ default_value: int = 1,
199
+ dtype: Any | None = None,
200
+ ) -> np.array:
201
+ shape = _get_shape_from_bounds(geometry.bounds, res=res, indexes=1)
202
+ transform = _get_transform_from_bounds(geometry.bounds, shape)
203
+
204
+ return features.rasterize(
205
+ [(geometry, default_value)],
206
+ out_shape=shape,
207
+ transform=transform,
208
+ fill=fill,
209
+ all_touched=all_touched,
210
+ merge_alg=merge_alg,
211
+ default_value=default_value,
212
+ dtype=dtype,
213
+ )
6
214
 
7
215
 
8
216
  @contextmanager