sgspy 1.0.2__cp314-cp314-manylinux_2_28_x86_64.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.cpython-314-x86_64-linux-gnu.so +0 -0
- sgspy/calculate/__init__.py +18 -0
- sgspy/calculate/pca/__init__.py +2 -0
- sgspy/calculate/pca/pca.py +158 -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 +202 -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 +228 -0
- sgspy/sample/strat/__init__.py +2 -0
- sgspy/sample/strat/strat.py +394 -0
- sgspy/sample/systematic/__init__.py +2 -0
- sgspy/sample/systematic/systematic.py +233 -0
- sgspy/stratify/__init__.py +27 -0
- sgspy/stratify/breaks/__init__.py +2 -0
- sgspy/stratify/breaks/breaks.py +222 -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 +244 -0
- sgspy/stratify/poly/__init__.py +2 -0
- sgspy/stratify/poly/poly.py +170 -0
- sgspy/stratify/quantiles/__init__.py +2 -0
- sgspy/stratify/quantiles/quantiles.py +276 -0
- sgspy/utils/__init__.py +18 -0
- sgspy/utils/plot.py +143 -0
- sgspy/utils/raster.py +605 -0
- sgspy/utils/vector.py +268 -0
- sgspy-1.0.2.data/data/sgspy/libonedal.so.3 +0 -0
- sgspy-1.0.2.data/data/sgspy/proj.db +0 -0
- sgspy-1.0.2.dist-info/METADATA +13 -0
- sgspy-1.0.2.dist-info/RECORD +40 -0
- sgspy-1.0.2.dist-info/WHEEL +5 -0
sgspy/utils/vector.py
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# ******************************************************************************
|
|
2
|
+
#
|
|
3
|
+
# Project: sgs
|
|
4
|
+
# Purpose: GDALDataset wrapper for vector operations
|
|
5
|
+
# Author: Joseph Meyer
|
|
6
|
+
# Date: June, 2025
|
|
7
|
+
#
|
|
8
|
+
# ******************************************************************************
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
import os
|
|
12
|
+
import site
|
|
13
|
+
import tempfile
|
|
14
|
+
from typing import Optional
|
|
15
|
+
import warnings
|
|
16
|
+
|
|
17
|
+
import matplotlib.pyplot as plt
|
|
18
|
+
import matplotlib #fpr type checking matplotlib.axes.Axes
|
|
19
|
+
|
|
20
|
+
from.import plot
|
|
21
|
+
from .plot import plot_vector
|
|
22
|
+
|
|
23
|
+
#ensure _sgs binary can be found
|
|
24
|
+
site_packages = list(filter(lambda x : 'site-packages' in x, site.getsitepackages()))[0]
|
|
25
|
+
sys.path.append(os.path.join(site_packages, "sgspy"))
|
|
26
|
+
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
|
|
27
|
+
from _sgs import GDALRasterWrapper
|
|
28
|
+
|
|
29
|
+
from _sgs import GDALVectorWrapper
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
import geopandas as gpd
|
|
33
|
+
GEOPANDAS = True
|
|
34
|
+
except ImportError as e:
|
|
35
|
+
GEOPANDAS = False
|
|
36
|
+
|
|
37
|
+
PROJDB_PATH = os.path.join(sys.prefix, "sgspy")
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# @ingroup user_utils
|
|
41
|
+
# This class represents a spatial vector, and is used as an input to many sgs functions.
|
|
42
|
+
#
|
|
43
|
+
# It has a number of additional uses, including displaying info about the vector, converting
|
|
44
|
+
# to a GDAL or GeoPandas object.
|
|
45
|
+
#
|
|
46
|
+
# Accessing vector info:
|
|
47
|
+
#
|
|
48
|
+
# vector metadata can be displayed using the info() function. All layers
|
|
49
|
+
# are displayed unless a specific layer is specified. The per-layer info
|
|
50
|
+
# includes: name, number of features, number of fields, geomtype, and bounds.
|
|
51
|
+
#
|
|
52
|
+
# Public Attributes:
|
|
53
|
+
# --------------------
|
|
54
|
+
# layer_names : list[str] @n
|
|
55
|
+
# a list of layer names
|
|
56
|
+
#
|
|
57
|
+
# Public Methods:
|
|
58
|
+
# --------------------
|
|
59
|
+
# info() @n
|
|
60
|
+
# takes an optional argument specify the band, and prints vector metadata to console
|
|
61
|
+
class SpatialVector:
|
|
62
|
+
|
|
63
|
+
def __init__(self,
|
|
64
|
+
image: str | GDALVectorWrapper):
|
|
65
|
+
"""
|
|
66
|
+
Constructing method for the SpatialVector class.
|
|
67
|
+
|
|
68
|
+
Has one required parameter to specify a gdal dataset. The following
|
|
69
|
+
attributes are populated:
|
|
70
|
+
self.cpp_vector
|
|
71
|
+
self.layer_names
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
--------------------
|
|
75
|
+
image: str | GDALVectorWrapper
|
|
76
|
+
specifies a path to a vector file or the C++ class object itself
|
|
77
|
+
"""
|
|
78
|
+
if type(image) is str:
|
|
79
|
+
self.cpp_vector = GDALVectorWrapper(image, PROJDB_PATH)
|
|
80
|
+
elif type(image) is GDALVectorWrapper:
|
|
81
|
+
self.cpp_vector = image
|
|
82
|
+
else:
|
|
83
|
+
raise TypeError("'image' parameter to SpatialVector constructor must be of type str or GDALVectorWrapper.")
|
|
84
|
+
|
|
85
|
+
self.layers = self.cpp_vector.get_layer_names()
|
|
86
|
+
|
|
87
|
+
def print_info(self,
|
|
88
|
+
layer_name: str,
|
|
89
|
+
layer_info: dict):
|
|
90
|
+
"""
|
|
91
|
+
prints layer information using the layer_info from self.cpp_vector.
|
|
92
|
+
|
|
93
|
+
This is an internal function not meant to be used by the end user.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
--------------------
|
|
97
|
+
name : str
|
|
98
|
+
str containing the layer name
|
|
99
|
+
layer_info : dict
|
|
100
|
+
dict containing 'feature_count', 'field_count', 'geometry_type', 'xmax', 'xmin', 'ymax', and 'ymin' items
|
|
101
|
+
"""
|
|
102
|
+
print("{} layer info:".format(layer_name))
|
|
103
|
+
print("feature count: {}".format(layer_info['feature_count']))
|
|
104
|
+
print("field count: {}".format(layer_info['field_count']))
|
|
105
|
+
print("geometry type: {}".format(layer_info['geometry_type']))
|
|
106
|
+
print("bounds (xmin, xmax, ymin, ymax): ({}, {}, {}, {})".format(
|
|
107
|
+
layer_info['xmin'],
|
|
108
|
+
layer_info['xmax'],
|
|
109
|
+
layer_info['ymin'],
|
|
110
|
+
layer_info['ymax']
|
|
111
|
+
))
|
|
112
|
+
if layer_info['crs']: print("crs: {}".format(layer_info['crs']))
|
|
113
|
+
print()
|
|
114
|
+
|
|
115
|
+
def info(self,
|
|
116
|
+
layer: Optional[int | str] = None):
|
|
117
|
+
"""
|
|
118
|
+
calls self.print_info depending on layer parameter. If no layer is given,
|
|
119
|
+
print all layers. A layer may be specified by either a str or an int.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
--------------------
|
|
123
|
+
layer : str or int
|
|
124
|
+
specifies the layer to print information on
|
|
125
|
+
"""
|
|
126
|
+
if layer is not None and type(layer) not in [int, str]:
|
|
127
|
+
raise TypeError("'layer' parameter, if given, must be of type int or str.")
|
|
128
|
+
|
|
129
|
+
if type(layer) == str:
|
|
130
|
+
self.print_info(layer, self.cpp_vector.get_layer_info(layer))
|
|
131
|
+
elif type(layer) == int:
|
|
132
|
+
self.print_info(self.layers[layer], self.cpp_vector.get_layer_info(self.layers[layer]))
|
|
133
|
+
else:
|
|
134
|
+
for layer in self.layers:
|
|
135
|
+
self.print_info(layer, self.cpp_vector.get_layer_info(layer))
|
|
136
|
+
|
|
137
|
+
def samples_as_wkt(self):
|
|
138
|
+
"""
|
|
139
|
+
Calls get_wkt_points on the underlying cpp class, to return
|
|
140
|
+
the samples as wkt strings.
|
|
141
|
+
|
|
142
|
+
This function requires that there be a layer named 'samples' which
|
|
143
|
+
is comprised entirely of Points or MultiPoints. These conditions
|
|
144
|
+
will be satisfied if this SpatialVector is the output of one of the
|
|
145
|
+
sampling functions in the sgs package.
|
|
146
|
+
"""
|
|
147
|
+
if "samples" not in self.layers:
|
|
148
|
+
print("this vector does not have a layer 'samples'")
|
|
149
|
+
else:
|
|
150
|
+
return self.cpp_vector.get_wkt_points('samples')
|
|
151
|
+
|
|
152
|
+
def plot(self,
|
|
153
|
+
geomtype: str,
|
|
154
|
+
ax: Optional[matplotlib.axes.Axes] = None,
|
|
155
|
+
layer: Optional[int | str] = None,
|
|
156
|
+
**kwargs):
|
|
157
|
+
"""
|
|
158
|
+
Calls plot_vector on self.
|
|
159
|
+
|
|
160
|
+
Paramters
|
|
161
|
+
--------------------
|
|
162
|
+
ax : matplotlib.axes.Axes
|
|
163
|
+
axes to plot the raster on
|
|
164
|
+
geomtype : str
|
|
165
|
+
the geometry type to try to print
|
|
166
|
+
layer : None | int | str
|
|
167
|
+
specification of which layer to print
|
|
168
|
+
**kwargs
|
|
169
|
+
any parameter which may be passed ot matplotlib.pyplot.plot
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
if ax is not None:
|
|
173
|
+
plot_vector(self, ax, geomtype, layer, **kwargs)
|
|
174
|
+
else:
|
|
175
|
+
fig, ax = plt.subplots()
|
|
176
|
+
plot_vector(self, ax, geomtype, layer, **kwargs)
|
|
177
|
+
plt.show()
|
|
178
|
+
|
|
179
|
+
@classmethod
|
|
180
|
+
def from_geopandas(cls, obj, layer_name: str=None):
|
|
181
|
+
"""
|
|
182
|
+
This function is used to convert a geopandas object into an sgspy.SpatialVector. The geopandas object
|
|
183
|
+
may either by of type GeoDataFrame or GeoSeries.
|
|
184
|
+
|
|
185
|
+
If a particular layer name is desired, it can be passed as a parameter.
|
|
186
|
+
|
|
187
|
+
Examples:
|
|
188
|
+
|
|
189
|
+
gdf = gpd.read_file("access.shp")
|
|
190
|
+
access = sgspy.SpatialVector.from_geopandas(gdf)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
gs = gpd['geometry'] #geometry column is a geoseries
|
|
194
|
+
access = sgspy.SpatialVector.from_geopandas(gs)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
gdf = gpd.read_file("access.shp")
|
|
198
|
+
gdf = gdf[gdf == "LineString"]
|
|
199
|
+
access = sgspy.SpatialVector.from_geopandas(gdf)
|
|
200
|
+
"""
|
|
201
|
+
if layer_name is not None and type(layer_name) is not str:
|
|
202
|
+
raise TypeError("layer_name, if given, must be of type 'str'.")
|
|
203
|
+
|
|
204
|
+
if not GEOPANDAS:
|
|
205
|
+
raise RuntimeError("from_geopandas() can only be called if geopandas was successfully imported, but it wasn't.")
|
|
206
|
+
|
|
207
|
+
if type(obj) is not gpd.geodataframe.GeoDataFrame and type(obj) is not gpd.geoseries.GeoSeries:
|
|
208
|
+
raise TypeError("the object passed must be of type geopandas GeoDataFrame or GeoSeries")
|
|
209
|
+
|
|
210
|
+
#get a the geopandas object as a geoseries
|
|
211
|
+
if type(obj) is gpd.geodataframe.GeoDataFrame:
|
|
212
|
+
if 'geometry' not in obj.columns:
|
|
213
|
+
raise RuntimeError("'geometry' must be a column in the geodataframe passed")
|
|
214
|
+
gdf = obj
|
|
215
|
+
gs = gdf['geometry']
|
|
216
|
+
else:
|
|
217
|
+
gs = obj
|
|
218
|
+
gdf = gpd.GeoDataFrame(gs)
|
|
219
|
+
|
|
220
|
+
if layer_name is None: layer_name = 'geopandas_geodataframe'
|
|
221
|
+
projection = gs.crs.to_wkt()
|
|
222
|
+
|
|
223
|
+
# the conversion to geojson may raise a warning about how the projection is unable to be converted to the new format correctly
|
|
224
|
+
#
|
|
225
|
+
# however, we have the projection from the geodataframes geoseries crs, so it won't be an issue
|
|
226
|
+
with warnings.catch_warnings():
|
|
227
|
+
warnings.simplefilter("ignore")
|
|
228
|
+
geojson = gdf.to_json().encode('utf-8')
|
|
229
|
+
|
|
230
|
+
cpp_vector = GDALVectorWrapper(geojson, projection, layer_name, PROJDB_PATH)
|
|
231
|
+
return cls(cpp_vector)
|
|
232
|
+
|
|
233
|
+
def to_geopandas(self):
|
|
234
|
+
"""
|
|
235
|
+
This function is used to convert an sgspy.SpatialVector into a geopandas geodataframe.
|
|
236
|
+
|
|
237
|
+
Examples:
|
|
238
|
+
|
|
239
|
+
access = sgspy.SpatialVector("access.shp")
|
|
240
|
+
gdf = access.to_geopandas()
|
|
241
|
+
"""
|
|
242
|
+
if not GEOPANDAS:
|
|
243
|
+
raise RuntimeError("to_geopandas() can only be called if geopandas was successfully imported, but it wasn't.")
|
|
244
|
+
|
|
245
|
+
tempdir = tempfile.gettempdir()
|
|
246
|
+
file = os.path.join(tempdir, "temp.geojson")
|
|
247
|
+
|
|
248
|
+
#get the projection info
|
|
249
|
+
projection = self.cpp_vector.get_projection()
|
|
250
|
+
|
|
251
|
+
#write the dataset to a tempfile
|
|
252
|
+
self.cpp_vector.write(file)
|
|
253
|
+
|
|
254
|
+
# This method of writing to a file then reading from that file is definitely clunky,
|
|
255
|
+
# however it's easy. Theres the possiblity of iterating through every field within
|
|
256
|
+
# every feature, and needing to then call a different function depending on the data
|
|
257
|
+
# type of the field (because C++ types are rigid). That may still be done in the future,
|
|
258
|
+
# but for now this works.
|
|
259
|
+
|
|
260
|
+
#have geopandas read the tempfile
|
|
261
|
+
gdf = gpd.read_file(file)
|
|
262
|
+
if projection != "": gdf.set_crs(projection, inplace=True, allow_override=True)
|
|
263
|
+
|
|
264
|
+
#remove the geojson file
|
|
265
|
+
os.remove(file)
|
|
266
|
+
|
|
267
|
+
return gdf
|
|
268
|
+
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: sgspy
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Requires-Dist: numpy
|
|
5
|
+
Requires-Dist: matplotlib
|
|
6
|
+
Requires-Dist: daal==2025.9.0
|
|
7
|
+
Requires-Dist: tbb==2022.*
|
|
8
|
+
Requires-Dist: mkl==2025.3.0
|
|
9
|
+
Requires-Dist: onemkl-license==2025.3.0
|
|
10
|
+
Provides-Extra: test
|
|
11
|
+
Requires-Dist: pytest; extra == "test"
|
|
12
|
+
Requires-Dist: geopandas; extra == "test"
|
|
13
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
sgspy/__init__.py,sha256=ONzqVZBLUs0Zm4s-x09HDoVWICfjDeygpzBscn9JHA0,2582
|
|
2
|
+
sgspy/_sgs.cpython-314-x86_64-linux-gnu.so,sha256=4hEMRHMnAS5xcGpk9QCSOkWPEzEYB8IPtBoHL52dG94,86466384
|
|
3
|
+
sgspy/calculate/__init__.py,sha256=CA6GtS3Lym_TtYYlMp6j02uf57DDlq3vGzN8qXxYDuc,331
|
|
4
|
+
sgspy/calculate/pca/__init__.py,sha256=L6jpsKWDSch-Rq4CqJSBO0elALd_yzuvTY2JASAipB8,39
|
|
5
|
+
sgspy/calculate/pca/pca.py,sha256=yaaZitdTQ0p25REkpUQwnIxNi0kiCeOE8mkxWl8aoZg,5422
|
|
6
|
+
sgspy/calculate/representation/__init__.py,sha256=yiDee7Cw6LnAbuATKMWXneVPBwhogV_5rIunK-USh7I,72
|
|
7
|
+
sgspy/calculate/representation/representation.py,sha256=nKNNlWmmF1uFyM9KuD9Y8Qj-YfYGfpSRuPZ_zUpNalE,72
|
|
8
|
+
sgspy/sample/__init__.py,sha256=Z20McdhG5A4J5qtwZ9O7CyH_tbXNKnYA54WINVwU-F0,422
|
|
9
|
+
sgspy/sample/ahels/__init__.py,sha256=2oAo9Fkck1BRqyVNfzr9rbWvQ4Jt2lg72EG7SIVbKzU,45
|
|
10
|
+
sgspy/sample/ahels/ahels.py,sha256=skPsJ3OhQAHBP9wHY3LGVlmnqkRlsidAFkv7CqpgWHM,63
|
|
11
|
+
sgspy/sample/clhs/__init__.py,sha256=vddVyTqOvsBoODikmdValw-pwEfYohlHo4Wlz-MnyQE,42
|
|
12
|
+
sgspy/sample/clhs/clhs.py,sha256=0F3Z4pIUZ0FeOPtK5WRO2ToNt7JF0M0Uh7Yw_yLU6uo,7528
|
|
13
|
+
sgspy/sample/nc/__init__.py,sha256=c5P2cH42Q23-hBguVF81b8KbcaFypg1PBw0TGY7nf6o,36
|
|
14
|
+
sgspy/sample/nc/nc.py,sha256=77sqsBpNYsyqVRoAoEnhAGM_WjQrNBnyHTdSUc67334,60
|
|
15
|
+
sgspy/sample/srs/__init__.py,sha256=0_Qm4f_6iE1Ds4AHQp_8t9UZZ7I0m46xWRvL4o6pKow,39
|
|
16
|
+
sgspy/sample/srs/srs.py,sha256=QqV1xdHo9a-jQv4C4nrtOpZdxVys_xmrDcYhR0LdwKA,7974
|
|
17
|
+
sgspy/sample/strat/__init__.py,sha256=WYiabUGZckk38Qhr1fFXhS8sh9BqyXtGEBPJ9lstpJo,45
|
|
18
|
+
sgspy/sample/strat/strat.py,sha256=KPzwzEj2HACUsOuMDrbUZOTM8GM3q6ldT6EAVDYuw2M,16171
|
|
19
|
+
sgspy/sample/systematic/__init__.py,sha256=frl-x5lN3zY7Q5HzbvwT0zz50Byh7VDoSgYDcG_vKbA,60
|
|
20
|
+
sgspy/sample/systematic/systematic.py,sha256=HPM2VTzFm2Q5Ge2Qhq-3yqL-u-KuiBCr2QzBiRykRVo,8336
|
|
21
|
+
sgspy/stratify/__init__.py,sha256=RULBeQemsCmVjHu57Me2tptDIlhe64M_PtvCsRpXe4Q,399
|
|
22
|
+
sgspy/stratify/breaks/__init__.py,sha256=45VAzOTJVGOn4kfU8w-mA2lq4PP5Z0PZjWbIt6RLj_k,48
|
|
23
|
+
sgspy/stratify/breaks/breaks.py,sha256=ekkanqFzjE3cUhVO9vwnukodJdnaSf3HXzOKBV6zhwo,9099
|
|
24
|
+
sgspy/stratify/kmeans/__init__.py,sha256=PjR6Uh8xhpId2HIl7vbysdduQpXO7l00FDmR9QAWa4M,48
|
|
25
|
+
sgspy/stratify/kmeans/kmeans.py,sha256=unem1jiJAwzdfdGnCRYn-47eeZXJccCaT7nL9d5ViXs,64
|
|
26
|
+
sgspy/stratify/map/__init__.py,sha256=uw8HivY0wg9Tke4XI8vtHCMNMYJOmncHb2Enl3jTg7s,71
|
|
27
|
+
sgspy/stratify/map/map_stratifications.py,sha256=RYdpBsZV61Evo73OzGx2KUXC0IufcgsZ6iChXh2PvWQ,10652
|
|
28
|
+
sgspy/stratify/poly/__init__.py,sha256=CfCIvJBQIqqS8aQG-52Ja9bRxrBN1jbcUBTkbkhD9KY,42
|
|
29
|
+
sgspy/stratify/poly/poly.py,sha256=Dt_wTxshxw02lbKVjLgS1yFWh5qJbVklTnVqPdzrSW0,6233
|
|
30
|
+
sgspy/stratify/quantiles/__init__.py,sha256=_g9MTbV7PIZxdshhEvJtZRLBCS5ehoUqZNCMBHrnOsQ,57
|
|
31
|
+
sgspy/stratify/quantiles/quantiles.py,sha256=ZjMoJNt2WUkpE7Jsbs_nPQcNP9rvf4EMYUyVOmGr9Y4,12195
|
|
32
|
+
sgspy/utils/__init__.py,sha256=XXEYEVdjUa345mCs0d5Bj7lVCyHEwMoDvselH-Deebs,287
|
|
33
|
+
sgspy/utils/plot.py,sha256=9gnW0C8vK5AUDKBi00830V9i5ciXVXROTH7A1TLqN0c,5528
|
|
34
|
+
sgspy/utils/raster.py,sha256=EOSZJOoWZ7IKo_iar4iHiWV74q_IT9HNQ4NRIfVoIQg,22945
|
|
35
|
+
sgspy/utils/vector.py,sha256=vHkIg7v85ljgO6MsRt-cRxLVw_bAZQPbiRto8k4YQko,9576
|
|
36
|
+
sgspy-1.0.2.data/data/sgspy/libonedal.so.3,sha256=sAmzMql47Yy9eO70uraCrAoAkJiYfYLMXs5CrwTr82I,10841849
|
|
37
|
+
sgspy-1.0.2.data/data/sgspy/proj.db,sha256=sGOmBu2wzZwqDJ9V5yu9mez2dvZCrqH4izR1mp9Q0Lk,9359360
|
|
38
|
+
sgspy-1.0.2.dist-info/METADATA,sha256=hiYB5ZO_eU0RMBYvkhNY45vq2MA07RhGMZb3EDhJJwQ,325
|
|
39
|
+
sgspy-1.0.2.dist-info/WHEEL,sha256=VOxCMwfIwo4MHdnSNLn06EoVps2ZTNo2Qo4M7S6O3iw,99
|
|
40
|
+
sgspy-1.0.2.dist-info/RECORD,,
|