sgspy 1.0.1__cp310-cp310-win_amd64.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.
- sgspy/__init__.py +82 -0
- sgspy/_sgs.cp310-win_amd64.lib +0 -0
- sgspy/_sgs.cp310-win_amd64.pyd +0 -0
- sgspy/calculate/__init__.py +18 -0
- sgspy/calculate/pca/__init__.py +2 -0
- sgspy/calculate/pca/pca.py +152 -0
- sgspy/calculate/representation/__init__.py +2 -0
- sgspy/calculate/representation/representation.py +3 -0
- sgspy/sample/__init__.py +30 -0
- sgspy/sample/ahels/__init__.py +2 -0
- sgspy/sample/ahels/ahels.py +3 -0
- sgspy/sample/clhs/__init__.py +2 -0
- sgspy/sample/clhs/clhs.py +198 -0
- sgspy/sample/nc/__init__.py +2 -0
- sgspy/sample/nc/nc.py +3 -0
- sgspy/sample/srs/__init__.py +2 -0
- sgspy/sample/srs/srs.py +224 -0
- sgspy/sample/strat/__init__.py +2 -0
- sgspy/sample/strat/strat.py +390 -0
- sgspy/sample/systematic/__init__.py +2 -0
- sgspy/sample/systematic/systematic.py +229 -0
- sgspy/stratify/__init__.py +27 -0
- sgspy/stratify/breaks/__init__.py +2 -0
- sgspy/stratify/breaks/breaks.py +218 -0
- sgspy/stratify/kmeans/__init__.py +2 -0
- sgspy/stratify/kmeans/kmeans.py +3 -0
- sgspy/stratify/map/__init__.py +2 -0
- sgspy/stratify/map/map_stratifications.py +240 -0
- sgspy/stratify/poly/__init__.py +2 -0
- sgspy/stratify/poly/poly.py +166 -0
- sgspy/stratify/quantiles/__init__.py +2 -0
- sgspy/stratify/quantiles/quantiles.py +272 -0
- sgspy/utils/__init__.py +18 -0
- sgspy/utils/plot.py +143 -0
- sgspy/utils/raster.py +602 -0
- sgspy/utils/vector.py +262 -0
- sgspy-1.0.1.data/data/sgspy/Lerc.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/aec.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/charset-1.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/freexl-1.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/gdal.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/geos.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/geos_c.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/geotiff.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/gif.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/hdf5.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/hdf5_cpp.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/hdf5_hl.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/hdf5_hl_cpp.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/iconv-2.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/jpeg62.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/json-c.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/legacy.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libcrypto-3-x64.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libcurl.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libecpg.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libecpg_compat.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libexpat.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/liblzma.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libpgtypes.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libpng16.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libpq.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libsharpyuv.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libssl-3-x64.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libwebp.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libwebpdecoder.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libwebpdemux.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libwebpmux.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/libxml2.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/lz4.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/minizip.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/netcdf.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/openjp2.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/pcre2-16.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/pcre2-32.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/pcre2-8.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/pcre2-posix.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/proj.db +0 -0
- sgspy-1.0.1.data/data/sgspy/proj_9.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/qhull_r.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/spatialite.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/sqlite3.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/szip.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/tiff.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/turbojpeg.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/uriparser.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/zlib1.dll +0 -0
- sgspy-1.0.1.data/data/sgspy/zstd.dll +0 -0
- sgspy-1.0.1.dist-info/METADATA +13 -0
- sgspy-1.0.1.dist-info/RECORD +91 -0
- sgspy-1.0.1.dist-info/WHEEL +4 -0
sgspy/sample/srs/srs.py
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# ******************************************************************************
|
|
2
|
+
#
|
|
3
|
+
# Project: sgs
|
|
4
|
+
# Purpose: simple random sampling (srs)
|
|
5
|
+
# Author: Joseph Meyer
|
|
6
|
+
# Date: June, 2025
|
|
7
|
+
#
|
|
8
|
+
# ******************************************************************************
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# @defgroup user_srs srs
|
|
12
|
+
# @ingroup user_sample
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
import matplotlib.pyplot as plt
|
|
21
|
+
|
|
22
|
+
from sgspy.utils import (
|
|
23
|
+
SpatialRaster,
|
|
24
|
+
SpatialVector,
|
|
25
|
+
plot,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
sys.path.append(os.path.join(os.path.dirname(__file__), "..", ".."))
|
|
29
|
+
from _sgs import srs_cpp
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# @ingroup user_srs
|
|
33
|
+
# This function conducts simple random sampling on the raster given.
|
|
34
|
+
# Sample points are randomly selected from data pixels (can't be nodata).
|
|
35
|
+
# All sample points are at least mindist distance away from eachother.
|
|
36
|
+
# If unable to get the full number of sample points, a message is printed.
|
|
37
|
+
#
|
|
38
|
+
# An access vector of LineString or MultiLineString type can be provided.
|
|
39
|
+
# buff_outer specifies the buffer distance around the geometry which
|
|
40
|
+
# is allowed to be included in the sampling, buff_inner specifies the
|
|
41
|
+
# buffer distance around the geometry which is not allowed to be included
|
|
42
|
+
# in the sampling. buff_outer must be larger than buff_inner. For a multi
|
|
43
|
+
# layer vector, layer_name must be specified.
|
|
44
|
+
#
|
|
45
|
+
# A vector containing existing sample points can be provided. If this is
|
|
46
|
+
# the case then all of the points in the existing sample are automatically
|
|
47
|
+
# added and random samples are chosen as required until num_samples number
|
|
48
|
+
# of samples are chosen.
|
|
49
|
+
#
|
|
50
|
+
# Examples
|
|
51
|
+
# --------------------
|
|
52
|
+
# rast = sgspy.SpatialRaster("raster.tif") @n
|
|
53
|
+
# samples = sgspy.sample.srs(rast, num_samples=250)
|
|
54
|
+
#
|
|
55
|
+
# rast = sgspy.SpatialRaster("raster.tif") @n
|
|
56
|
+
# samples = sgspy.sample.srs(rast, num_samples=250, mindist=100, plot=True, filename="srs_samples.shp") @n
|
|
57
|
+
#
|
|
58
|
+
# rast = sgspy.SpatialRaster("raster.tif") @n
|
|
59
|
+
# access = sgspy.SpatialVector("access_network.shp") @n
|
|
60
|
+
# samples = sgspy.sample.srs(rast, num_samples=200, mindist=100, access=access, buff_outer=300)
|
|
61
|
+
#
|
|
62
|
+
# rast = sgspy.SpatialRaster("raster.tif") @n
|
|
63
|
+
# access = sgspy.SpatialVector("access_network.shp") @n
|
|
64
|
+
# samples = sgspy.sample.srs(rast, num_samples=200, access=access, buff_inner=50, buff_outer=300)
|
|
65
|
+
#
|
|
66
|
+
# rast = sgspy.SpatialRaster("raster.tif") @n
|
|
67
|
+
# existing = sgspy.SpatialVector("existing_samples.shp") @n
|
|
68
|
+
# samples = sgspy.sample.srs(rast, num_samples=200, existing=existing)
|
|
69
|
+
#
|
|
70
|
+
# Parameters
|
|
71
|
+
# --------------------
|
|
72
|
+
# rast : SpatialRaster @n
|
|
73
|
+
# raster data structure containing the raster to sample @n @n
|
|
74
|
+
# num_samples : int @n
|
|
75
|
+
# the target number of samples @n @n
|
|
76
|
+
# mindist : float @n
|
|
77
|
+
# the minimum distance each sample point must be from each other @n @n
|
|
78
|
+
# existing : SpatialVector @n
|
|
79
|
+
# a vector specifying existing sample points @n @n
|
|
80
|
+
# access : SpatialVector @n
|
|
81
|
+
# a vector specifying access network @n @n
|
|
82
|
+
# layer_name : str @n
|
|
83
|
+
# the layer within access that is to be used for sampling @n @n
|
|
84
|
+
# buff_inner : int | float @n
|
|
85
|
+
# buffer boundary specifying distance from access which CANNOT be sampled @n @n
|
|
86
|
+
# buff_outer : int | float @n
|
|
87
|
+
# buffer boundary specifying distance from access which CAN be sampled @n @n
|
|
88
|
+
# plot : bool @n
|
|
89
|
+
# whether to plot the samples or not @n @n
|
|
90
|
+
# filename : str @n
|
|
91
|
+
# the filename to write to, or '' if file should not be written @n @n
|
|
92
|
+
#
|
|
93
|
+
#
|
|
94
|
+
# Returns
|
|
95
|
+
# --------------------
|
|
96
|
+
# a SpatialVector object containing point geometries of sample locations
|
|
97
|
+
def srs(
|
|
98
|
+
rast: SpatialRaster,
|
|
99
|
+
num_samples: int,
|
|
100
|
+
mindist: [int | float] = 0,
|
|
101
|
+
existing: Optional[SpatialVector] = None,
|
|
102
|
+
access: Optional[SpatialVector] = None,
|
|
103
|
+
layer_name: Optional[str] = None,
|
|
104
|
+
buff_inner: Optional[int | float] = None,
|
|
105
|
+
buff_outer: Optional[int | float] = None,
|
|
106
|
+
plot: bool = False,
|
|
107
|
+
filename: str = ''):
|
|
108
|
+
|
|
109
|
+
if type(rast) is not SpatialRaster:
|
|
110
|
+
raise TypeError("'rast' parameter must be of type sgspy.SpatialRaster.")
|
|
111
|
+
|
|
112
|
+
if type(num_samples) is not int:
|
|
113
|
+
raise TypeError("'num_samples' parameter must be of type int.")
|
|
114
|
+
|
|
115
|
+
if type(mindist) not in [int, float]:
|
|
116
|
+
raise TypeError("'mindist' parameter must be of type int or float.")
|
|
117
|
+
|
|
118
|
+
if existing is not None and type(existing) is not SpatialVector:
|
|
119
|
+
raise TypeError("'existing' parameter, if given, must be of type sgspy.SpatialVector.")
|
|
120
|
+
|
|
121
|
+
if access is not None and type(access) is not SpatialVector:
|
|
122
|
+
raise TypeError("'access' parameter, if given, must be of type sgspy.SpatialVector.")
|
|
123
|
+
|
|
124
|
+
if layer_name is not None and type(layer_name) is not str:
|
|
125
|
+
raise TypeError("'layer_name' parameter, if given, must be of type str.")
|
|
126
|
+
|
|
127
|
+
if buff_inner is not None and type(buff_inner) not in [int, float]:
|
|
128
|
+
raise TypeError("'buff_inner' parameter, if given, must be of type int or float.")
|
|
129
|
+
|
|
130
|
+
if buff_outer is not None and type(buff_outer) not in [int, float]:
|
|
131
|
+
raise TypeError("'buff_outer' parameter, if given, must be of type int or float.")
|
|
132
|
+
|
|
133
|
+
if type(plot) is not bool:
|
|
134
|
+
raise TypeError("'plot' parameter must be of type bool.")
|
|
135
|
+
|
|
136
|
+
if type(filename) is not str:
|
|
137
|
+
raise TypeError("'filename' paramter must be of type str.")
|
|
138
|
+
|
|
139
|
+
if rast.closed:
|
|
140
|
+
raise RuntimeError("the C++ object which the raster object wraps has been cleaned up and closed.")
|
|
141
|
+
|
|
142
|
+
if num_samples < 1:
|
|
143
|
+
raise ValueError("num_samples must be greater than 0")
|
|
144
|
+
|
|
145
|
+
if mindist is None:
|
|
146
|
+
mindist = 0
|
|
147
|
+
|
|
148
|
+
if mindist < 0:
|
|
149
|
+
raise ValueError("mindist must be greater than or equal to 0")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
if (access):
|
|
154
|
+
if layer_name is None:
|
|
155
|
+
if len(access.layers) > 1:
|
|
156
|
+
raise ValueError("if there are multiple layers in the access vector, layer_name parameter must be passed.")
|
|
157
|
+
layer_name = access.layers[0]
|
|
158
|
+
|
|
159
|
+
if layer_name not in access.layers:
|
|
160
|
+
raise ValueError("layer specified by 'layer_name' does not exist in the access vector")
|
|
161
|
+
|
|
162
|
+
if buff_inner is None or buff_inner < 0:
|
|
163
|
+
buff_inner = 0
|
|
164
|
+
|
|
165
|
+
if buff_outer is None or buff_outer < 0:
|
|
166
|
+
raise ValueError("if an access vector is given, buff_outer must be a float greater than 0.")
|
|
167
|
+
|
|
168
|
+
if buff_inner >= buff_outer:
|
|
169
|
+
raise ValueError("buff_outer must be greater than buff_inner")
|
|
170
|
+
|
|
171
|
+
access_vector = access.cpp_vector
|
|
172
|
+
else:
|
|
173
|
+
access_vector = None
|
|
174
|
+
layer_name = ""
|
|
175
|
+
buff_inner = -1
|
|
176
|
+
buff_outer = -1
|
|
177
|
+
|
|
178
|
+
if (existing):
|
|
179
|
+
existing_vector = existing.cpp_vector
|
|
180
|
+
else:
|
|
181
|
+
existing_vector = None
|
|
182
|
+
|
|
183
|
+
temp_dir = rast.cpp_raster.get_temp_dir()
|
|
184
|
+
if temp_dir == "":
|
|
185
|
+
temp_dir = tempfile.mkdtemp()
|
|
186
|
+
rast.cpp_raster.set_temp_dir(temp_dir)
|
|
187
|
+
|
|
188
|
+
#call random sampling function
|
|
189
|
+
[sample_coordinates, cpp_vector, num_points] = srs_cpp(
|
|
190
|
+
rast.cpp_raster,
|
|
191
|
+
num_samples,
|
|
192
|
+
mindist,
|
|
193
|
+
existing_vector,
|
|
194
|
+
access_vector,
|
|
195
|
+
layer_name,
|
|
196
|
+
buff_inner,
|
|
197
|
+
buff_outer,
|
|
198
|
+
plot,
|
|
199
|
+
temp_dir,
|
|
200
|
+
filename
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
if num_points < num_samples:
|
|
204
|
+
print("unable to find the full {} samples within the given constraints. Sampled {} points.".format(num_samples, num_points))
|
|
205
|
+
|
|
206
|
+
#plot new vector if requested
|
|
207
|
+
if plot:
|
|
208
|
+
try:
|
|
209
|
+
fig, ax = plt.subplots()
|
|
210
|
+
rast.plot(ax, band=rast.bands[0])
|
|
211
|
+
title = "samples on " + rast.bands[0]
|
|
212
|
+
|
|
213
|
+
if access:
|
|
214
|
+
access.plot('LineString', ax)
|
|
215
|
+
title += " with access"
|
|
216
|
+
|
|
217
|
+
ax.plot(sample_coordinates[0], sample_coordinates[1], '.r')
|
|
218
|
+
ax.set_title(label=title)
|
|
219
|
+
plt.show()
|
|
220
|
+
|
|
221
|
+
except Exception as e:
|
|
222
|
+
print("unable to plot output: " + str(e))
|
|
223
|
+
|
|
224
|
+
return SpatialVector(cpp_vector)
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
# ******************************************************************************
|
|
2
|
+
#
|
|
3
|
+
# Project: sgs
|
|
4
|
+
# Purpose: stratified random sampling (srs)
|
|
5
|
+
# Author: Joseph Meyer
|
|
6
|
+
# Date: September, 2025
|
|
7
|
+
#
|
|
8
|
+
# ******************************************************************************
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
# @defgroup user_strat strat
|
|
12
|
+
# @ingroup user_sample
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
import matplotlib.pyplot as plt
|
|
21
|
+
|
|
22
|
+
from sgspy.utils import(
|
|
23
|
+
SpatialRaster,
|
|
24
|
+
SpatialVector,
|
|
25
|
+
plot,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
sys.path.append(os.path.join(os.path.dirname(__file__), "..", ".."))
|
|
29
|
+
from _sgs import strat_cpp
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# @ingroup user_strat
|
|
33
|
+
# This function conducts stratified sampling using the stratified
|
|
34
|
+
# raster given. There are two methods employed to determine which
|
|
35
|
+
# pixels to sample:
|
|
36
|
+
# - The 'random' method randomly selects pixels within a given strata.
|
|
37
|
+
# - The 'Queinnec' method prioritizes pixels which are surrounded by other pixels of the same strata.
|
|
38
|
+
# The 'wrow' and 'wcol' parameters determine the size of the surrounding area required for a pixel to be
|
|
39
|
+
# prioritized.
|
|
40
|
+
#
|
|
41
|
+
# The desired number of samples is given by num_samples.
|
|
42
|
+
#
|
|
43
|
+
# **IMPORTANT**
|
|
44
|
+
# the num_strata is required, and represents the number of strata within the input raster band.
|
|
45
|
+
# sgspy EXPECTS num_strata TO BE 0-INDEXED, meaning if the num_strat value of 4 is given, sgspy
|
|
46
|
+
# expects the strata values to be 0, 1, 2, 3. Giving a num_strata of 3 in this case will
|
|
47
|
+
# result in an ERROR because the function will be only able to store data for 3 strata. All
|
|
48
|
+
# sgspy stratification functions output their rasters in this format, however if you are
|
|
49
|
+
# using your own strat raster it will be important. The recommendation in this case
|
|
50
|
+
# is to either preprocess the data so it follows this pattern, or give a value for num_strata
|
|
51
|
+
# equal to the largest integer in the strat raster + 1 (because it expects 0 to be a strata as well).
|
|
52
|
+
#
|
|
53
|
+
# The allocation parameter specifies the proportion of total samples will be distributed between
|
|
54
|
+
# each strata. The 'prop' method is the default, and attempts to allocate the samples proportionally according to
|
|
55
|
+
# their prevalence in the overall raster. The 'equal' method attempts to distribute the samples equally among strata.
|
|
56
|
+
# the 'manual' method requires that the weights parameter be given, and attempts to allocate according to the
|
|
57
|
+
# proportions given in the weights parameter. In the case where 'optim' allocation is used, an additional raster must be passed
|
|
58
|
+
# to the mrast parameter, and if that raster contains more than 1 band the mrast_band
|
|
59
|
+
# parameter must be given specifying which band. The optim method is specified by Gregoire and Valentine,
|
|
60
|
+
# and optimizes the desired proportions based on the proportion of each strata AND the within-strata
|
|
61
|
+
# variance in the specified raster band. https://doi.org/10.1201/9780203498880 Section 5.4.4.
|
|
62
|
+
#
|
|
63
|
+
# The 'existing' parameter, if passed, must be a SpatialVector of type Point or MultiPoint.
|
|
64
|
+
# These points specify samples within an already-existing network. The SpatialVector may
|
|
65
|
+
# only have one layer. If the force parameter is set to True, every pixel in the existing
|
|
66
|
+
# sample will be added no matter what. if the force parameter is false, then the existing
|
|
67
|
+
# samples will be prioritized over other pixels in the same strata.
|
|
68
|
+
#
|
|
69
|
+
# The 'access' parameter, if passed, must be a SpatialVector of type LineString or MultiLineString.
|
|
70
|
+
# buff_outer specifies the buffer distance around the geometry which
|
|
71
|
+
# is allowed to be included in the sampling, buff_inner specifies the
|
|
72
|
+
# geometry which is not allowed to be included in the sampling. buff_outer
|
|
73
|
+
# must be larger than buff_inner. For a multi-layer vector, layer_name
|
|
74
|
+
# must be specified.
|
|
75
|
+
#
|
|
76
|
+
# Examples
|
|
77
|
+
# --------------------
|
|
78
|
+
# rast = sgspy.SpatialRaster("raster.tif") @n
|
|
79
|
+
# srast = sgspy.stratify.quantiles(rast, num_strata=5) @n
|
|
80
|
+
# samples = sgspy.sample.strat(srast, band=0, num_samples=200, num_strata=5) #uses Queinnec method with proportional allocation by default
|
|
81
|
+
#
|
|
82
|
+
# rast = sgspy.SpatialRaster("raster.tif") @n
|
|
83
|
+
# srast = sgspy.stratify.quantiles(rast, num_strata=5) @n
|
|
84
|
+
# samples = sgspy.sample.strat(srast, band=0, num_samples=200, num_strata=5, method="random", mindist=200, plot=True, filename="samples.shp")
|
|
85
|
+
#
|
|
86
|
+
# rast = sgspy.SpatialRaster("raster.tif") @n
|
|
87
|
+
# srast = sgspy.stratify.quantiles(rast, num_strata=5) @n
|
|
88
|
+
# samples = sgspy.sample.strat(srast, band=0, num_samples=200, num_strata=5, method="Queinnec", allocation="optim", mrast=rast)
|
|
89
|
+
#
|
|
90
|
+
# rast = sgspy.SpatialRaster("raster.tif") @n
|
|
91
|
+
# srast = sgspy.stratify.quantiles(rast, num_strata=5) @n
|
|
92
|
+
# samples = sgspy.sample.strat(rast, band=0, num_samples=200, num_strata=5, allocation="manual", weights=[0.1, 0.1, 0.2, 0.2, 0.4])
|
|
93
|
+
#
|
|
94
|
+
# rast = sgspy.SpatialRaster("raster.tif") @n
|
|
95
|
+
# access = sgspy.SpatialVector("access_network.shp") @n
|
|
96
|
+
# srast = sgspy.stratify.quantiles(rast, num_strata=5) @n
|
|
97
|
+
# samples = sgspy.sample.strat(rast, band=0, num_samples=200, num_strata=5, allocation="equal", access=access, buff_inner=100, buff_outer=300)
|
|
98
|
+
#
|
|
99
|
+
# rast = sgspy.SpatialRaster("raster.tif") @n
|
|
100
|
+
# existng = sgspy.SpatialVector("existing_samples.shp") @n
|
|
101
|
+
# srast =sgspy.stratify.quantiles(rast, num_strata=5) @n
|
|
102
|
+
# samples = sgspy.sample.strat(rast, band=0, num_samples=200, num_strata=5, allocation="prop", existing=existing, force=True)
|
|
103
|
+
#
|
|
104
|
+
# Parameters
|
|
105
|
+
# --------------------
|
|
106
|
+
# strat_rast : SpatialRaster
|
|
107
|
+
# the raster to sample
|
|
108
|
+
# band : int | str @n
|
|
109
|
+
# the band within the strat_rast to use, either a 0-indexed int value or the name of the band @n @n
|
|
110
|
+
# num_samples : int @n
|
|
111
|
+
# the desired number of samples @n @n
|
|
112
|
+
# num_strata : int @n
|
|
113
|
+
# the number of strata in the strat_rast. If this number is incorrect it may cause
|
|
114
|
+
# undefined behavior in the C++ code which determines sample locations. @n @n
|
|
115
|
+
# wrow : int @n
|
|
116
|
+
# the number of rows to be considered in the focal window for the 'Queinnec' method @n @n
|
|
117
|
+
# wcol : int @n
|
|
118
|
+
# the number of columns to be considered in the focal window for the 'Queinnec' method @n @n
|
|
119
|
+
# allocation : str @n
|
|
120
|
+
# the allocation method to determine the number of samples per strata. One of 'prop', 'equal', 'optim', or 'manual' @n @n
|
|
121
|
+
# weights : list[float] @n
|
|
122
|
+
# the allocation percentages of each strata if the allocation method is 'manual' @n @n
|
|
123
|
+
# mrast : SpatialRaster @n
|
|
124
|
+
# the raster used to calculate 'optim' allocation if 'optim' allocation is used @n @n
|
|
125
|
+
# mrast_band : str | int @n
|
|
126
|
+
# specifies the band within mrast to use @n @n
|
|
127
|
+
# method : str @n
|
|
128
|
+
# the sampling method, either 'random', or 'Queinnec' @n @n
|
|
129
|
+
# mindist : float @n
|
|
130
|
+
# the minimum distance allowed between sample points @n @n
|
|
131
|
+
# existing : SpatialVector @n
|
|
132
|
+
# a vector of Point or Multipoint which are part of a pre-existing sample network @n @n
|
|
133
|
+
# force : bool @n
|
|
134
|
+
# whether to automatically include all points in the existing network or not @n @n
|
|
135
|
+
# access : SpatialVector @n
|
|
136
|
+
# a vector of LineString or MultiLineString geometries to sample near to @n @n
|
|
137
|
+
# layer_name : str @n @n
|
|
138
|
+
# the layer within 'access' to use for access buffering @n @n
|
|
139
|
+
# buff_inner : float @n
|
|
140
|
+
# the inner buffer around the access LineStrings, where samples should not occur @n @n
|
|
141
|
+
# buff_outer : float @n
|
|
142
|
+
# the outer buffer around the access LineStrings, The area in which samples must occur @n @n
|
|
143
|
+
# plot : bool @n
|
|
144
|
+
# whether or not to plot the output samples @n @n
|
|
145
|
+
# filename : str @n
|
|
146
|
+
# the output filename to write to if desired @n @n
|
|
147
|
+
#
|
|
148
|
+
#
|
|
149
|
+
# Returns
|
|
150
|
+
# --------------------
|
|
151
|
+
# a SpatialVector object containing point geometries of sample locations
|
|
152
|
+
def strat(
|
|
153
|
+
strat_rast: SpatialRaster,
|
|
154
|
+
band: int | str,
|
|
155
|
+
num_samples: int,
|
|
156
|
+
num_strata: int,
|
|
157
|
+
wrow: int = 3,
|
|
158
|
+
wcol: int = 3,
|
|
159
|
+
allocation: str = "prop",
|
|
160
|
+
weights: Optional[list[float]] = None,
|
|
161
|
+
mrast: Optional[SpatialRaster] = None,
|
|
162
|
+
mrast_band: Optional[int | str] = None,
|
|
163
|
+
method: str = "Queinnec",
|
|
164
|
+
mindist: Optional[int | float] = None,
|
|
165
|
+
existing: Optional[SpatialVector] = None,
|
|
166
|
+
force: bool = False,
|
|
167
|
+
access: Optional[SpatialVector] = None,
|
|
168
|
+
layer_name: Optional[str] = None,
|
|
169
|
+
buff_inner: Optional[int | float] = None,
|
|
170
|
+
buff_outer: Optional[int | float] = None,
|
|
171
|
+
plot: bool = False,
|
|
172
|
+
filename: str = "",
|
|
173
|
+
):
|
|
174
|
+
|
|
175
|
+
if type(strat_rast) is not SpatialRaster:
|
|
176
|
+
raise TypeError("'strat_rast' parameter must be of type sgspy.SpatialRaster.")
|
|
177
|
+
|
|
178
|
+
if type(band) not in [int, str]:
|
|
179
|
+
raise TypeError("'band' parameter must be of type int or str.")
|
|
180
|
+
|
|
181
|
+
if type(num_samples) is not int:
|
|
182
|
+
raise TypeError("'num_samples' parameter must be of type int.")
|
|
183
|
+
|
|
184
|
+
if type(num_strata) is not int:
|
|
185
|
+
raise TypeError("'num_strata' parameter must be of type int.")
|
|
186
|
+
|
|
187
|
+
if type(wrow) is not int:
|
|
188
|
+
raise TypeError("'wrow' parameter must be of type int.")
|
|
189
|
+
|
|
190
|
+
if type(wcol) is not int:
|
|
191
|
+
raise TypeError("'wcol' parameter must be of type int.")
|
|
192
|
+
|
|
193
|
+
if type(allocation) is not str:
|
|
194
|
+
raise TypeError("'allocation' parameter must be of type str.")
|
|
195
|
+
|
|
196
|
+
if weights is not None and type(weights) is not list:
|
|
197
|
+
raise TypeError("'weights' parameter, if given, must be a list of float values.")
|
|
198
|
+
|
|
199
|
+
if mrast is not None and type(mrast) is not SpatialRaster:
|
|
200
|
+
raise TypeError("'mrast' parameter, if given, must be of type sgspy.SpatialRaster.")
|
|
201
|
+
|
|
202
|
+
if mrast_band is not None and type(mrast_band) not in [int, str]:
|
|
203
|
+
raise TypeError("'mrast_band' parameter, if given, must be of type int or str.")
|
|
204
|
+
|
|
205
|
+
if type(method) is not str:
|
|
206
|
+
raise TypeError("'method' parameter must be of type str.")
|
|
207
|
+
|
|
208
|
+
if mindist is not None and type(mindist) not in [int, float]:
|
|
209
|
+
raise TypeError("'mindist' parameter must be of type int or float.")
|
|
210
|
+
|
|
211
|
+
if existing is not None and type(existing) is not SpatialVector:
|
|
212
|
+
raise TypeError("'existing' parameter must be of type sgspy.SpatialVector.")
|
|
213
|
+
|
|
214
|
+
if type(force) is not bool:
|
|
215
|
+
raise TypeError("'force' parameter must be of type bool.")
|
|
216
|
+
|
|
217
|
+
if access is not None and type(access) is not SpatialVector:
|
|
218
|
+
raise TypeError("'access' parameter must be of type sgspy.SpatialVector.")
|
|
219
|
+
|
|
220
|
+
if layer_name is not None and type(layer_name) is not str:
|
|
221
|
+
raise TypeError("'layer_name' parameter must be of type str.")
|
|
222
|
+
|
|
223
|
+
if buff_inner is not None and type(buff_inner) not in [int, float]:
|
|
224
|
+
raise TypeError("'buff_inner' parameter must be of type int or float.")
|
|
225
|
+
|
|
226
|
+
if buff_outer is not None and type(buff_outer) not in [int, float]:
|
|
227
|
+
raise TypeError("'buff_outer' parameter must be of type int or float.")
|
|
228
|
+
|
|
229
|
+
if type(plot) is not bool:
|
|
230
|
+
raise TypeError("'plot' paramter must be of type bool.")
|
|
231
|
+
|
|
232
|
+
if type(filename) is not str:
|
|
233
|
+
raise TypeError("'filename' parameter must be of type str.")
|
|
234
|
+
|
|
235
|
+
if strat_rast.closed:
|
|
236
|
+
raise RuntimeError("the C++ object which the strat_rast object wraps has been cleaned up and closed.")
|
|
237
|
+
|
|
238
|
+
if mrast is not None and mrast.closed:
|
|
239
|
+
raise RuntimeError("the C++ object which the raster object wraps has been cleaned up and closed.")
|
|
240
|
+
|
|
241
|
+
if type(band) is str:
|
|
242
|
+
if band not in strat_rast.bands:
|
|
243
|
+
msg = "band " + str(band) + " not in given raster."
|
|
244
|
+
raise ValueError(msg);
|
|
245
|
+
|
|
246
|
+
band = strat_rast.get_band_index(band) #get band as 0-indexed integer
|
|
247
|
+
else: #type(band) is int
|
|
248
|
+
if band >= len(strat_rast.bands):
|
|
249
|
+
msg = "0-indexed band of " + str(band) + " given, but raster only has " + str(len(raster.bands)) + " bands."
|
|
250
|
+
raise ValueError(msg)
|
|
251
|
+
|
|
252
|
+
if num_samples < 1:
|
|
253
|
+
raise ValueError("num_samples must be greater than 0")
|
|
254
|
+
|
|
255
|
+
if method not in ["random", "Queinnec"]:
|
|
256
|
+
raise ValueError("method must be either 'random' or 'Queinnec'.")
|
|
257
|
+
|
|
258
|
+
if allocation not in ["prop", "optim", "equal", "manual"]:
|
|
259
|
+
raise ValueError("method must be one of 'prop', 'optim', 'equal', or 'manual'.")
|
|
260
|
+
|
|
261
|
+
if allocation == "manual":
|
|
262
|
+
if weights is None:
|
|
263
|
+
raise ValueError("for manual allocation, weights must be given.")
|
|
264
|
+
|
|
265
|
+
if np.sum(weights) != 1:
|
|
266
|
+
raise ValueError("weights must sum to 1.")
|
|
267
|
+
|
|
268
|
+
if len(weights) != num_strata:
|
|
269
|
+
raise ValueError("length of 'weights' must be the same as the number of strata.")
|
|
270
|
+
|
|
271
|
+
if allocation == "optim":
|
|
272
|
+
if not mrast:
|
|
273
|
+
raise ValueError("the 'mrast' parameter must be provided if a SpatialRaster if allocation is 'optim'.")
|
|
274
|
+
|
|
275
|
+
if mrast_band is None:
|
|
276
|
+
if len(mrast.bands) != 1:
|
|
277
|
+
raise ValueError("the 'mrast_band' parameter must be given if the 'mrast' SpatialRaster contains more than 1 band.")
|
|
278
|
+
|
|
279
|
+
mrast_band = 1
|
|
280
|
+
elif type(mrast_band) is str:
|
|
281
|
+
if band not in mrast.bands:
|
|
282
|
+
msg = "band " + str(band) + " not in mraster."
|
|
283
|
+
raise ValueError(msg)
|
|
284
|
+
|
|
285
|
+
mrast_band = mrast.get_band_index(mrast_band)
|
|
286
|
+
else: #type(band) is int
|
|
287
|
+
if (mrast_band >= len(mrast.bands)):
|
|
288
|
+
msg = "0-indexed band of " + str(band) + "given, but raster only has " + str(len(mrast.bands)) + " bands."
|
|
289
|
+
raise ValueError(msg)
|
|
290
|
+
|
|
291
|
+
mrast_cpp_raster = mrast.cpp_raster
|
|
292
|
+
else:
|
|
293
|
+
mrast_cpp_raster = None
|
|
294
|
+
mrast_band = -1
|
|
295
|
+
|
|
296
|
+
if wrow not in [3, 5, 7]:
|
|
297
|
+
raise ValueError("wrow must be one of 3, 5, 7.")
|
|
298
|
+
|
|
299
|
+
if wcol not in [3, 5, 7]:
|
|
300
|
+
raise ValueError("wcol must be one of 3, 5, 7.")
|
|
301
|
+
|
|
302
|
+
if (access):
|
|
303
|
+
if layer_name is None:
|
|
304
|
+
if len(access.layers) > 1:
|
|
305
|
+
raise ValueError("if there are multiple layers in the access vector, layer_name must be defined.")
|
|
306
|
+
layer_name = access.layers[0]
|
|
307
|
+
|
|
308
|
+
if layer_name not in access.layers:
|
|
309
|
+
raise ValueError("layer specified by 'layer_name' does not exist in the access vector")
|
|
310
|
+
|
|
311
|
+
if buff_inner is None or buff_inner < 0:
|
|
312
|
+
buff_inner = 0
|
|
313
|
+
|
|
314
|
+
if buff_outer is None or buff_outer <= 0:
|
|
315
|
+
raise ValueError("if an access vector is given, buff_outer must be a float greater than 0.")
|
|
316
|
+
|
|
317
|
+
if buff_inner >= buff_outer:
|
|
318
|
+
raise ValueError("buff_outer must be greater than buff_inner")
|
|
319
|
+
|
|
320
|
+
access_vector = access.cpp_vector
|
|
321
|
+
else:
|
|
322
|
+
access_vector = None
|
|
323
|
+
layer_name = ""
|
|
324
|
+
buff_inner = -1
|
|
325
|
+
buff_outer = -1
|
|
326
|
+
|
|
327
|
+
if (existing):
|
|
328
|
+
existing_vector = existing.cpp_vector
|
|
329
|
+
else:
|
|
330
|
+
existing_vector = None
|
|
331
|
+
|
|
332
|
+
if allocation != "manual":
|
|
333
|
+
weights = []
|
|
334
|
+
|
|
335
|
+
if mindist is None:
|
|
336
|
+
mindist = 0
|
|
337
|
+
|
|
338
|
+
if mindist < 0:
|
|
339
|
+
raise ValueError("mindist must be greater than or equal to 0")
|
|
340
|
+
|
|
341
|
+
temp_dir = strat_rast.cpp_raster.get_temp_dir()
|
|
342
|
+
if temp_dir == "":
|
|
343
|
+
temp_dir = tempfile.mkdtemp()
|
|
344
|
+
strat_rast.cpp_raster.set_temp_dir(temp_dir)
|
|
345
|
+
|
|
346
|
+
[sample_coordinates, samples, num_points] = strat_cpp(
|
|
347
|
+
strat_rast.cpp_raster,
|
|
348
|
+
band,
|
|
349
|
+
num_samples,
|
|
350
|
+
num_strata,
|
|
351
|
+
allocation,
|
|
352
|
+
weights,
|
|
353
|
+
mrast_cpp_raster,
|
|
354
|
+
mrast_band,
|
|
355
|
+
method,
|
|
356
|
+
wrow,
|
|
357
|
+
wcol,
|
|
358
|
+
mindist,
|
|
359
|
+
existing_vector,
|
|
360
|
+
force,
|
|
361
|
+
access_vector,
|
|
362
|
+
layer_name,
|
|
363
|
+
buff_inner,
|
|
364
|
+
buff_outer,
|
|
365
|
+
plot,
|
|
366
|
+
filename,
|
|
367
|
+
temp_dir
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
if num_points < num_samples:
|
|
371
|
+
print("unable to find the full {} samples within the given constraints. Sampled {} points.".format(num_samples, num_points))
|
|
372
|
+
|
|
373
|
+
#plot new vector if requested
|
|
374
|
+
if plot:
|
|
375
|
+
try:
|
|
376
|
+
fig, ax = plt.subplots()
|
|
377
|
+
strat_rast.plot(ax, band=strat_rast.bands[band])
|
|
378
|
+
title = "samples on " + strat_rast.bands[band]
|
|
379
|
+
|
|
380
|
+
if access:
|
|
381
|
+
access.plot('LineString', ax)
|
|
382
|
+
title += " with access"
|
|
383
|
+
|
|
384
|
+
ax.plot(sample_coordinates[0], sample_coordinates[1], '.r')
|
|
385
|
+
ax.set_title(label=title)
|
|
386
|
+
plt.show()
|
|
387
|
+
except Exception as e:
|
|
388
|
+
print("unable to plot output: " + str(e))
|
|
389
|
+
|
|
390
|
+
return SpatialVector(samples)
|