pysits 1.5.3.dev0__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.
- LICENSE +339 -0
- README.md +83 -0
- pysits/__init__.py +243 -0
- pysits/backend/__init__.py +18 -0
- pysits/backend/functions.py +50 -0
- pysits/backend/loaders.py +92 -0
- pysits/backend/pkgs.py +35 -0
- pysits/conversions/__init__.py +18 -0
- pysits/conversions/clojure.py +68 -0
- pysits/conversions/common.py +280 -0
- pysits/conversions/decorators.py +148 -0
- pysits/conversions/dsl/__init__.py +18 -0
- pysits/conversions/dsl/base.py +43 -0
- pysits/conversions/dsl/mask.py +337 -0
- pysits/conversions/dsl/tuning.py +129 -0
- pysits/conversions/tibble.py +444 -0
- pysits/conversions/tibble_arrow.py +469 -0
- pysits/conversions/vector.py +137 -0
- pysits/conversions/xarray.py +207 -0
- pysits/docs/__init__.py +22 -0
- pysits/docs/content/sits_get_data.md +85 -0
- pysits/docs/decorators.py +58 -0
- pysits/extras/__init__.py +82 -0
- pysits/extras/earthdatalogin.py +44 -0
- pysits/extras/torch.py +44 -0
- pysits/jinja.py +56 -0
- pysits/models/__init__.py +18 -0
- pysits/models/base.py +34 -0
- pysits/models/data/__init__.py +22 -0
- pysits/models/data/accuracy.py +94 -0
- pysits/models/data/base.py +51 -0
- pysits/models/data/cube.py +210 -0
- pysits/models/data/frame.py +138 -0
- pysits/models/data/frame_accessor.py +133 -0
- pysits/models/data/matrix.py +119 -0
- pysits/models/data/table.py +49 -0
- pysits/models/data/ts.py +161 -0
- pysits/models/data/tuning.py +307 -0
- pysits/models/data/vector.py +47 -0
- pysits/models/frame.py +191 -0
- pysits/models/ml.py +27 -0
- pysits/models/resolver.py +147 -0
- pysits/settings.py +44 -0
- pysits/sits/__init__.py +18 -0
- pysits/sits/classification.py +42 -0
- pysits/sits/colors.py +50 -0
- pysits/sits/config.py +45 -0
- pysits/sits/context.py +36 -0
- pysits/sits/cube.py +214 -0
- pysits/sits/data.py +220 -0
- pysits/sits/exporters/__init__.py +29 -0
- pysits/sits/exporters/files.py +37 -0
- pysits/sits/exporters/sf.py +30 -0
- pysits/sits/exporters/xarray.py +65 -0
- pysits/sits/impute.py +38 -0
- pysits/sits/ml.py +110 -0
- pysits/sits/segment.py +38 -0
- pysits/sits/tiles.py +35 -0
- pysits/sits/ts.py +216 -0
- pysits/sits/tuning.py +82 -0
- pysits/sits/utils.py +126 -0
- pysits/sits/visualization.py +122 -0
- pysits/templates/cube.html +196 -0
- pysits/templates/tuning.html +183 -0
- pysits/visualization/__init__.py +24 -0
- pysits/visualization/base.py +163 -0
- pysits/visualization/image.py +48 -0
- pysits/visualization/leaflet.py +94 -0
- pysits/visualization/tmap.py +85 -0
- pysits-1.5.3.dev0.dist-info/METADATA +123 -0
- pysits-1.5.3.dev0.dist-info/RECORD +73 -0
- pysits-1.5.3.dev0.dist-info/WHEEL +4 -0
- pysits-1.5.3.dev0.dist-info/licenses/LICENSE +339 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (C) 2025 sits developers.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software; you can redistribute it and/or modify it
|
|
5
|
+
# under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
#
|
|
17
|
+
|
|
18
|
+
"""tibble conversions."""
|
|
19
|
+
|
|
20
|
+
import warnings
|
|
21
|
+
from collections.abc import Callable
|
|
22
|
+
|
|
23
|
+
from geopandas import GeoDataFrame as GeoPandasDataFrame
|
|
24
|
+
from pandas import DataFrame as PandasDataFrame
|
|
25
|
+
from pandas import to_datetime as pandas_to_datetime
|
|
26
|
+
from rpy2 import robjects
|
|
27
|
+
from rpy2.robjects import StrVector, pandas2ri
|
|
28
|
+
from rpy2.robjects.conversion import localconverter
|
|
29
|
+
from rpy2.robjects.vectors import DataFrame as RDataFrame
|
|
30
|
+
from shapely import wkt
|
|
31
|
+
|
|
32
|
+
from pysits.backend.functions import r_fnc_class
|
|
33
|
+
from pysits.backend.pkgs import r_pkg_base, r_pkg_sf
|
|
34
|
+
from pysits.models.frame import SITSFrameArray
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
#
|
|
38
|
+
# Auxiliary functions
|
|
39
|
+
#
|
|
40
|
+
def _column_to_datetime(data: PandasDataFrame, colname: str) -> PandasDataFrame:
|
|
41
|
+
"""Transform a columns from R to a valid datetime column in Python.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
data (pandas.DataFrame): Pandas Data Frame from an R tibble/data.frame.
|
|
45
|
+
|
|
46
|
+
colname (str): Column to be converted datetime (from R format to Python format).
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
pandas.DataFrame: Pandas data frame with ``colname`` as datetime.
|
|
50
|
+
"""
|
|
51
|
+
# Convert if column is available
|
|
52
|
+
if colname in data.columns:
|
|
53
|
+
data[colname] = pandas_to_datetime(data[colname], origin="1970-01-01", unit="D")
|
|
54
|
+
|
|
55
|
+
# Return!
|
|
56
|
+
return data
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _sf_to_shapely(sf_object: RDataFrame) -> list:
|
|
60
|
+
"""Transform a columns from R to a valid geometry column in Python.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
sf_object (rpy2.robjects.vectors.ListVector): R (tibble/data.frame) Data frame.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
list: List of Shapely geometries.
|
|
67
|
+
"""
|
|
68
|
+
# Extract geometry and convert to WKT in R directly
|
|
69
|
+
geom_wkt = r_pkg_sf.st_as_text(r_pkg_sf.st_geometry(sf_object))
|
|
70
|
+
|
|
71
|
+
# Convert R character vector to Python list of strings
|
|
72
|
+
geom_wkt_py = list(geom_wkt)
|
|
73
|
+
|
|
74
|
+
# Convert each WKT string to a Shapely geometry
|
|
75
|
+
return [wkt.loads(g) for g in geom_wkt_py]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
#
|
|
79
|
+
# Base conversion function
|
|
80
|
+
#
|
|
81
|
+
def _tibble_to_pandas(
|
|
82
|
+
data: RDataFrame,
|
|
83
|
+
nested_columns: list | None = None,
|
|
84
|
+
table_processor: Callable[[PandasDataFrame], PandasDataFrame] | None = None,
|
|
85
|
+
nested_processor: Callable[[PandasDataFrame], PandasDataFrame] | None = None,
|
|
86
|
+
) -> PandasDataFrame:
|
|
87
|
+
"""Convert an R tibble containing nested data frames to a Pandas DataFrame.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
data (RDataFrame): An R tibble/data.frame object that contains
|
|
91
|
+
nested data frames.
|
|
92
|
+
|
|
93
|
+
nested_columns (list): List of column names that contain nested
|
|
94
|
+
data frames.
|
|
95
|
+
|
|
96
|
+
table_processor (Callable | None, optional):
|
|
97
|
+
A function to process the main table after conversion. The function should
|
|
98
|
+
take a pandas DataFrame as input and return a processed pandas DataFrame.
|
|
99
|
+
Defaults to None.
|
|
100
|
+
|
|
101
|
+
nested_processor (Callable | None, optional):
|
|
102
|
+
A function to process each nested data frame after conversion. The function
|
|
103
|
+
should take a pandas DataFrame as input and return a processed pandas
|
|
104
|
+
DataFrame. Defaults to None.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
PandasDataFrame: A pandas DataFrame where the nested columns are converted to
|
|
108
|
+
SITSFrameArray objects containing the processed nested
|
|
109
|
+
data frames.
|
|
110
|
+
"""
|
|
111
|
+
# Check if the data is an SF object
|
|
112
|
+
has_geometries = "sf" in r_fnc_class(data)
|
|
113
|
+
|
|
114
|
+
# Define shapely geometries and CRS
|
|
115
|
+
shapely_crs = None
|
|
116
|
+
shapely_geometries = None
|
|
117
|
+
|
|
118
|
+
# Handle SF objects
|
|
119
|
+
if has_geometries:
|
|
120
|
+
# Convert geometry to Shapely geometries
|
|
121
|
+
shapely_geometries = _sf_to_shapely(data)
|
|
122
|
+
|
|
123
|
+
# Get CRS
|
|
124
|
+
shapely_crs = r_pkg_sf.st_crs(data)
|
|
125
|
+
|
|
126
|
+
# Check if CRS is available
|
|
127
|
+
if "NULL" not in r_fnc_class(shapely_crs):
|
|
128
|
+
shapely_crs = shapely_crs.rx2("wkt")[0]
|
|
129
|
+
|
|
130
|
+
# Drop geometry column
|
|
131
|
+
data = r_pkg_sf.st_drop_geometry(data)
|
|
132
|
+
|
|
133
|
+
# Convert columns definitions
|
|
134
|
+
nested_columns = nested_columns if nested_columns else []
|
|
135
|
+
|
|
136
|
+
# Extract columns from the data
|
|
137
|
+
data_columns = r_pkg_base.colnames(data)
|
|
138
|
+
|
|
139
|
+
# Remove invalid columns
|
|
140
|
+
data_columns_valid = []
|
|
141
|
+
|
|
142
|
+
for data_column in data_columns:
|
|
143
|
+
col = data.rx2(data_column)
|
|
144
|
+
|
|
145
|
+
# Remove invalid columns
|
|
146
|
+
if r_fnc_class(col[0])[0] not in ["function", "NULL"]:
|
|
147
|
+
data_columns_valid.append(data_column)
|
|
148
|
+
|
|
149
|
+
# Replace old data columns with the filtered one
|
|
150
|
+
data_columns = data_columns_valid
|
|
151
|
+
|
|
152
|
+
# If user define nested columns, verify if they are available.
|
|
153
|
+
if nested_columns:
|
|
154
|
+
# Filter available nested columns
|
|
155
|
+
nested_columns = [v for v in nested_columns if v in data_columns]
|
|
156
|
+
|
|
157
|
+
# If selected columns are available, remove them from the data columns
|
|
158
|
+
# This allows us to handle them individually.
|
|
159
|
+
if nested_columns:
|
|
160
|
+
data_columns = list(set(data_columns).difference(nested_columns))
|
|
161
|
+
|
|
162
|
+
# Select regular columns (using ``[]``) and convert to Pandas
|
|
163
|
+
rdf_data = data.rx(StrVector(data_columns))
|
|
164
|
+
rdf_data = pandas2ri.rpy2py(rdf_data)
|
|
165
|
+
|
|
166
|
+
# Handle nested columns if available
|
|
167
|
+
for nested_column in nested_columns:
|
|
168
|
+
# Select nested column (using ``[[]]``)
|
|
169
|
+
nested_column_data = data.rx2(nested_column)
|
|
170
|
+
|
|
171
|
+
# As it is a nested column, handle it as a list of ``tibble/data.frame``
|
|
172
|
+
nested_column_processed = []
|
|
173
|
+
|
|
174
|
+
for nested_row in nested_column_data:
|
|
175
|
+
# Convert to pandas
|
|
176
|
+
nested_row_df = pandas2ri.rpy2py(nested_row)
|
|
177
|
+
|
|
178
|
+
# If a processor function is available, apply data to use
|
|
179
|
+
if nested_processor and isinstance(nested_row_df, PandasDataFrame):
|
|
180
|
+
nested_row_df = nested_processor(nested_row_df)
|
|
181
|
+
|
|
182
|
+
# Save
|
|
183
|
+
nested_column_processed.append(nested_row_df)
|
|
184
|
+
|
|
185
|
+
# Convert column to SITS Array and save to the main data frame
|
|
186
|
+
rdf_data[nested_column] = SITSFrameArray(nested_column_processed)
|
|
187
|
+
|
|
188
|
+
# If a processor function is available to the main table, use it
|
|
189
|
+
if table_processor:
|
|
190
|
+
rdf_data = table_processor(rdf_data)
|
|
191
|
+
|
|
192
|
+
# Transform dataframe to geodataframe
|
|
193
|
+
if shapely_geometries:
|
|
194
|
+
rdf_data = GeoPandasDataFrame(
|
|
195
|
+
rdf_data, geometry=shapely_geometries, crs=shapely_crs
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
# Return!
|
|
199
|
+
return rdf_data
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
#
|
|
203
|
+
# General function
|
|
204
|
+
#
|
|
205
|
+
def tibble_to_pandas(data: RDataFrame) -> PandasDataFrame:
|
|
206
|
+
"""Convert any tibble to Pandas DataFrame.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
data (rpy2.robjects.vectors.DataFrame): R (tibble/data.frame) Data frame.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
pandas.DataFrame: R Data Frame as Pandas.
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
# Define table processor
|
|
216
|
+
def _table_processor(x):
|
|
217
|
+
"""Table processor."""
|
|
218
|
+
# Update date columns
|
|
219
|
+
x = _column_to_datetime(x, "start_date")
|
|
220
|
+
x = _column_to_datetime(x, "end_date")
|
|
221
|
+
|
|
222
|
+
return x
|
|
223
|
+
|
|
224
|
+
# Convert and return
|
|
225
|
+
return _tibble_to_pandas(
|
|
226
|
+
data=data,
|
|
227
|
+
table_processor=_table_processor,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def tibble_nested_to_pandas(
|
|
232
|
+
data: RDataFrame,
|
|
233
|
+
nested_columns: list,
|
|
234
|
+
table_processor: Callable[[PandasDataFrame], PandasDataFrame] | None = None,
|
|
235
|
+
nested_processor: Callable[[PandasDataFrame], PandasDataFrame] | None = None,
|
|
236
|
+
) -> PandasDataFrame:
|
|
237
|
+
"""(Public) Convert an R tibble containing nested data frames to a Pandas DataFrame.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
data (RDataFrame): An R tibble/data.frame object that contains
|
|
241
|
+
nested data frames.
|
|
242
|
+
|
|
243
|
+
nested_columns (list): List of column names that contain nested
|
|
244
|
+
data frames.
|
|
245
|
+
|
|
246
|
+
table_processor (Callable | None, optional):
|
|
247
|
+
A function to process the main table after conversion. The function should
|
|
248
|
+
take a pandas DataFrame as input and return a processed pandas DataFrame.
|
|
249
|
+
Defaults to None.
|
|
250
|
+
|
|
251
|
+
nested_processor (Callable | None, optional):
|
|
252
|
+
A function to process each nested data frame after conversion. The function
|
|
253
|
+
should take a pandas DataFrame as input and return a processed pandas
|
|
254
|
+
DataFrame. Defaults to None.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
PandasDataFrame: A pandas DataFrame where the nested columns are converted to
|
|
258
|
+
SITSFrameArray objects containing the processed nested
|
|
259
|
+
data frames.
|
|
260
|
+
"""
|
|
261
|
+
return _tibble_to_pandas(
|
|
262
|
+
data=data,
|
|
263
|
+
nested_columns=nested_columns,
|
|
264
|
+
table_processor=table_processor,
|
|
265
|
+
nested_processor=nested_processor,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
#
|
|
270
|
+
# SITS conversions function
|
|
271
|
+
#
|
|
272
|
+
def tibble_sits_to_pandas(data: RDataFrame) -> PandasDataFrame:
|
|
273
|
+
"""Convert sits tibble to Pandas Data Frame.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
data (rpy2.robjects.vectors.DataFrame): R (tibble/data.frame) Data frame.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
pandas.DataFrame: R Data Frame as Pandas.
|
|
280
|
+
"""
|
|
281
|
+
# Define column order (from R)
|
|
282
|
+
column_order = [
|
|
283
|
+
"longitude",
|
|
284
|
+
"latitude",
|
|
285
|
+
"start_date",
|
|
286
|
+
"end_date",
|
|
287
|
+
"label",
|
|
288
|
+
"cube",
|
|
289
|
+
"time_series",
|
|
290
|
+
]
|
|
291
|
+
|
|
292
|
+
# Define nested columns
|
|
293
|
+
nested_columns = ["time_series", "predicted"]
|
|
294
|
+
|
|
295
|
+
# Define table processor
|
|
296
|
+
def _table_processor(x):
|
|
297
|
+
"""Table processor."""
|
|
298
|
+
# Update date columns
|
|
299
|
+
x = _column_to_datetime(x, "start_date")
|
|
300
|
+
x = _column_to_datetime(x, "end_date")
|
|
301
|
+
|
|
302
|
+
x_columns = list(filter(lambda y: y in x.columns, column_order))
|
|
303
|
+
x_columns = x_columns + list(set(x.columns).difference(x_columns))
|
|
304
|
+
|
|
305
|
+
return x[x_columns]
|
|
306
|
+
|
|
307
|
+
# Define nested processor
|
|
308
|
+
def _nested_processor(x):
|
|
309
|
+
"""Nested processor."""
|
|
310
|
+
return _column_to_datetime(x, "Index")
|
|
311
|
+
|
|
312
|
+
# Convert and return
|
|
313
|
+
return _tibble_to_pandas(
|
|
314
|
+
data=data,
|
|
315
|
+
nested_columns=nested_columns,
|
|
316
|
+
table_processor=_table_processor,
|
|
317
|
+
nested_processor=_nested_processor,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
#
|
|
322
|
+
# Cube conversions function
|
|
323
|
+
#
|
|
324
|
+
def tibble_cube_to_pandas(data: RDataFrame) -> PandasDataFrame:
|
|
325
|
+
"""Convert sits tibble to Pandas Data Frame.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
data (rpy2.robjects.vectors.DataFrame): R (tibble/data.frame) Data frame.
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
pandas.DataFrame: R Data Frame as Pandas.
|
|
332
|
+
"""
|
|
333
|
+
# Define column order (from R)
|
|
334
|
+
column_order = [
|
|
335
|
+
"source",
|
|
336
|
+
"collection",
|
|
337
|
+
"satellite",
|
|
338
|
+
"sensor",
|
|
339
|
+
"tile",
|
|
340
|
+
"xmin",
|
|
341
|
+
"xmax",
|
|
342
|
+
"ymin",
|
|
343
|
+
"ymax",
|
|
344
|
+
"crs",
|
|
345
|
+
"labels",
|
|
346
|
+
"file_info",
|
|
347
|
+
"vector_info",
|
|
348
|
+
]
|
|
349
|
+
|
|
350
|
+
# Define nested columns
|
|
351
|
+
nested_columns = ["labels", "file_info", "vector_info"]
|
|
352
|
+
|
|
353
|
+
# Define table processor
|
|
354
|
+
def _table_processor(x):
|
|
355
|
+
"""Table processor."""
|
|
356
|
+
columns_available = [v for v in column_order if v in x.columns]
|
|
357
|
+
|
|
358
|
+
return x[columns_available]
|
|
359
|
+
|
|
360
|
+
# Define nested processor
|
|
361
|
+
def _nested_processor(x):
|
|
362
|
+
"""Nested processor."""
|
|
363
|
+
x = _column_to_datetime(x, "date")
|
|
364
|
+
x = _column_to_datetime(x, "start_date")
|
|
365
|
+
x = _column_to_datetime(x, "end_date")
|
|
366
|
+
|
|
367
|
+
return x
|
|
368
|
+
|
|
369
|
+
# Convert and return
|
|
370
|
+
return _tibble_to_pandas(
|
|
371
|
+
data=data,
|
|
372
|
+
nested_columns=nested_columns,
|
|
373
|
+
table_processor=_table_processor,
|
|
374
|
+
nested_processor=_nested_processor,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
#
|
|
379
|
+
# Pandas to R conversions
|
|
380
|
+
#
|
|
381
|
+
def pandas_to_tibble(data: PandasDataFrame) -> RDataFrame:
|
|
382
|
+
"""Convert a pandas DataFrame to an R DataFrame object.
|
|
383
|
+
|
|
384
|
+
This function converts a pandas DataFrame to an R DataFrame using
|
|
385
|
+
rpy2's conversion infrastructure. It handles the conversion context
|
|
386
|
+
to ensure proper type mapping between Python and R objects.
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
data (pandas.DataFrame): The pandas DataFrame to convert to R.
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
rpy2.robjects.vectors.DataFrame: The converted R DataFrame object.
|
|
393
|
+
|
|
394
|
+
Notes:
|
|
395
|
+
- The function uses rpy2's localconverter to ensure proper conversion context
|
|
396
|
+
- Handles both DataFrame and non-DataFrame inputs
|
|
397
|
+
- Preserves column names and data types where possible
|
|
398
|
+
- For non-DataFrame inputs, falls back to rpy2's default converter
|
|
399
|
+
"""
|
|
400
|
+
with localconverter(robjects.default_converter + pandas2ri.converter):
|
|
401
|
+
return robjects.conversion.py2rpy(data)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def geopandas_to_tibble(data: GeoPandasDataFrame) -> RDataFrame:
|
|
405
|
+
"""Convert pandas DataFrame or GeoDataFrame to R DataFrame or sf object.
|
|
406
|
+
|
|
407
|
+
Removes columns that contain embedded pandas DataFrames.
|
|
408
|
+
"""
|
|
409
|
+
data = GeoPandasDataFrame(data)
|
|
410
|
+
|
|
411
|
+
if data.crs is None:
|
|
412
|
+
raise ValueError("GeoDataFrame must have a CRS")
|
|
413
|
+
|
|
414
|
+
# Identify columns where no cell is a DataFrame
|
|
415
|
+
safe_columns = [col for col in data.columns if not data[col].dtype.name == "sits"]
|
|
416
|
+
|
|
417
|
+
# Warn if columns are dropped
|
|
418
|
+
dropped_columns = set(data.columns) - set(safe_columns)
|
|
419
|
+
if dropped_columns:
|
|
420
|
+
warnings.warn(
|
|
421
|
+
f"Warning: Dropping columns with embedded DataFrames: {dropped_columns}"
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# Keep only safe columns
|
|
425
|
+
data_safe = data[safe_columns].copy()
|
|
426
|
+
|
|
427
|
+
# If GeoDataFrame, convert geometry to WKT and include geometry column
|
|
428
|
+
if isinstance(data, GeoPandasDataFrame):
|
|
429
|
+
geom_col = data.geometry.name
|
|
430
|
+
data_safe[geom_col] = data.geometry.to_wkt()
|
|
431
|
+
|
|
432
|
+
# Convert to R DataFrame
|
|
433
|
+
with localconverter(robjects.default_converter + pandas2ri.converter):
|
|
434
|
+
r_df = robjects.conversion.py2rpy(data_safe)
|
|
435
|
+
|
|
436
|
+
# If GeoDataFrame, convert to sf
|
|
437
|
+
if isinstance(data, GeoPandasDataFrame):
|
|
438
|
+
r_df = r_pkg_sf.st_as_sf(
|
|
439
|
+
r_df,
|
|
440
|
+
wkt=robjects.StrVector([geom_col]),
|
|
441
|
+
crs=robjects.StrVector([data.crs.to_wkt()]),
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
return r_df
|