pysits 1.5.3.dev2__tar.gz → 2.0.0.dev0__tar.gz

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 (105) hide show
  1. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/PKG-INFO +12 -9
  2. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/README.md +8 -8
  3. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pyproject.toml +6 -3
  4. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/__init__.py +35 -10
  5. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/backend/functions.py +10 -1
  6. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/backend/loaders.py +39 -0
  7. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/conversions/common.py +21 -0
  8. pysits-2.0.0.dev0/pysits/conversions/stac.py +233 -0
  9. pysits-2.0.0.dev0/pysits/conversions/tibble.py +698 -0
  10. pysits-2.0.0.dev0/pysits/conversions/xarray.py +699 -0
  11. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/data/cube.py +17 -9
  12. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/data/ts.py +7 -8
  13. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/data/tuning.py +2 -7
  14. pysits-2.0.0.dev0/pysits/models/frame.py +37 -0
  15. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/ml.py +4 -0
  16. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/settings.py +2 -2
  17. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/config.py +51 -0
  18. pysits-2.0.0.dev0/pysits/sits/exporters/xarray.py +131 -0
  19. pysits-2.0.0.dev0/pysits/sits/impute.py +28 -0
  20. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/ml.py +18 -1
  21. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/segment.py +1 -0
  22. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/tiles.py +0 -6
  23. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/ts.py +6 -15
  24. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/visualization.py +8 -1
  25. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/visualization/base.py +7 -1
  26. pysits-2.0.0.dev0/tests/test_config.py +81 -0
  27. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/tests/test_conversions.py +167 -0
  28. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/tests/test_cube.py +72 -2
  29. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/tests/test_data.py +34 -1
  30. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/tests/test_indexing.py +100 -26
  31. pysits-2.0.0.dev0/tests/test_resolver.py +82 -0
  32. pysits-2.0.0.dev0/tests/test_rl_models.py +81 -0
  33. pysits-2.0.0.dev0/tests/test_tiles.py +121 -0
  34. pysits-2.0.0.dev0/tests/test_tuning.py +106 -0
  35. pysits-2.0.0.dev0/tests/test_validation.py +137 -0
  36. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/tests/test_visualization.py +7 -1
  37. pysits-2.0.0.dev0/tests/test_xarray.py +497 -0
  38. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/uv.lock +1005 -5
  39. pysits-1.5.3.dev2/pysits/conversions/tibble.py +0 -444
  40. pysits-1.5.3.dev2/pysits/conversions/tibble_arrow.py +0 -473
  41. pysits-1.5.3.dev2/pysits/conversions/xarray.py +0 -207
  42. pysits-1.5.3.dev2/pysits/models/frame.py +0 -191
  43. pysits-1.5.3.dev2/pysits/sits/exporters/xarray.py +0 -65
  44. pysits-1.5.3.dev2/pysits/sits/impute.py +0 -38
  45. pysits-1.5.3.dev2/tests/test_tiles.py +0 -56
  46. pysits-1.5.3.dev2/tests/test_validation.py +0 -50
  47. pysits-1.5.3.dev2/tests/test_xarray.py +0 -52
  48. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/.gitattributes +0 -0
  49. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/.github/workflows/ruff.yaml +0 -0
  50. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/.gitignore +0 -0
  51. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/.pre-commit-config.yaml +0 -0
  52. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/LICENSE +0 -0
  53. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/conda/meta.yaml +0 -0
  54. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/backend/__init__.py +0 -0
  55. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/backend/pkgs.py +0 -0
  56. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/conversions/__init__.py +0 -0
  57. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/conversions/clojure.py +0 -0
  58. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/conversions/decorators.py +0 -0
  59. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/conversions/dsl/__init__.py +0 -0
  60. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/conversions/dsl/base.py +0 -0
  61. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/conversions/dsl/mask.py +0 -0
  62. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/conversions/dsl/tuning.py +0 -0
  63. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/conversions/vector.py +0 -0
  64. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/docs/__init__.py +0 -0
  65. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/docs/content/sits_get_data.md +0 -0
  66. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/docs/decorators.py +0 -0
  67. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/extras/__init__.py +0 -0
  68. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/extras/earthdatalogin.py +0 -0
  69. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/extras/torch.py +0 -0
  70. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/jinja.py +0 -0
  71. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/__init__.py +0 -0
  72. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/base.py +0 -0
  73. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/data/__init__.py +0 -0
  74. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/data/accuracy.py +0 -0
  75. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/data/base.py +0 -0
  76. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/data/frame.py +0 -0
  77. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/data/frame_accessor.py +0 -0
  78. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/data/matrix.py +0 -0
  79. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/data/table.py +0 -0
  80. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/data/vector.py +0 -0
  81. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/models/resolver.py +0 -0
  82. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/__init__.py +0 -0
  83. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/classification.py +0 -0
  84. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/colors.py +0 -0
  85. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/context.py +0 -0
  86. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/cube.py +0 -0
  87. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/data.py +0 -0
  88. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/exporters/__init__.py +0 -0
  89. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/exporters/files.py +0 -0
  90. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/exporters/sf.py +0 -0
  91. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/tuning.py +0 -0
  92. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/sits/utils.py +0 -0
  93. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/templates/cube.html +0 -0
  94. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/templates/tuning.html +0 -0
  95. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/visualization/__init__.py +0 -0
  96. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/visualization/image.py +0 -0
  97. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/visualization/leaflet.py +0 -0
  98. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/pysits/visualization/tmap.py +0 -0
  99. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/tests/conftest.py +0 -0
  100. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/tests/test_classification.py +0 -0
  101. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/tests/test_dsl.py +0 -0
  102. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/tests/test_extras.py +0 -0
  103. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/tests/test_geopandas.py +0 -0
  104. /pysits-1.5.3.dev2/tests/test_models.py → /pysits-2.0.0.dev0/tests/test_ml_models.py +0 -0
  105. {pysits-1.5.3.dev2 → pysits-2.0.0.dev0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pysits
3
- Version: 1.5.3.dev2
3
+ Version: 2.0.0.dev0
4
4
  Summary: Python wrapper for the sits R package
5
5
  Project-URL: Homepage, https://github.com/e-sensing/pysits
6
6
  Project-URL: Issue Tracker, https://github.com/e-sensing/pysits/issues
@@ -34,6 +34,9 @@ Requires-Dist: ruff>=0.11.12; extra == 'dev'
34
34
  Provides-Extra: xarray
35
35
  Requires-Dist: affine>=2.4.0; extra == 'xarray'
36
36
  Requires-Dist: dask>=2025.3.0; extra == 'xarray'
37
+ Requires-Dist: odc-stac>=0.5.3; extra == 'xarray'
38
+ Requires-Dist: planetary-computer>=1.0.0; extra == 'xarray'
39
+ Requires-Dist: pystac>=1.15.0; extra == 'xarray'
37
40
  Requires-Dist: rioxarray>=0.18.2; extra == 'xarray'
38
41
  Requires-Dist: xarray>=2025.3.0; extra == 'xarray'
39
42
  Description-Content-Type: text/markdown
@@ -42,7 +45,7 @@ Description-Content-Type: text/markdown
42
45
 
43
46
  Python wrapper for the [sits](https://github.com/e-sensing/sits) R package.
44
47
 
45
- ## 📦 Installation
48
+ ## Installation
46
49
 
47
50
  To install `pysits` with pip:
48
51
 
@@ -58,7 +61,7 @@ pip install git+https://github.com/e-sensing/pysits.git
58
61
 
59
62
  > To use `pysits`, you must have [sits](https://github.com/e-sensing/sits) installed. For more information, refer to the [official sits documentation](https://e-sensing.github.io/sitsbook/setup.html).
60
63
 
61
- ## 🛠 Development setup (for contributors)
64
+ ## Development setup (for contributors)
62
65
 
63
66
  To set up a local development environment:
64
67
 
@@ -82,7 +85,7 @@ source .venv/bin/activate # or .venv\Scripts\activate on Windows
82
85
  uv pip install -e ".[dev]"
83
86
  ```
84
87
 
85
- ### 🔍 Run tests
88
+ ### Run tests
86
89
 
87
90
  We use `pytest` for testing:
88
91
 
@@ -90,7 +93,7 @@ We use `pytest` for testing:
90
93
  pytest
91
94
  ```
92
95
 
93
- ### 🧹 Code formatting
96
+ ### Code formatting
94
97
 
95
98
  To keep the codebase clean and consistent we use [ruff](https://github.com/astral-sh/ruff):
96
99
 
@@ -98,7 +101,7 @@ To keep the codebase clean and consistent we use [ruff](https://github.com/astra
98
101
  ruff format .
99
102
  ```
100
103
 
101
- ### 🧪 Linting
104
+ ### Linting
102
105
 
103
106
  We use [ruff](https://github.com/astral-sh/ruff) for static analysis:
104
107
 
@@ -108,11 +111,11 @@ ruff check .
108
111
 
109
112
  > The `examples/` directory is excluded from linting.
110
113
 
111
- ## 📚 Learn more
114
+ ## Learn more
112
115
 
113
116
  To learn all about `sits`, including its concepts, API, and real-world examples, we recommend accessing the [official sits book](https://e-sensing.github.io/sitsbook/). The book provides examples in both R and Python.
114
117
 
115
- ## 🤝 Contributing
118
+ ## Contributing
116
119
 
117
120
  We welcome contributions! Please:
118
121
 
@@ -120,6 +123,6 @@ We welcome contributions! Please:
120
123
  - Create a feature branch
121
124
  - Submit a pull request with a clear description
122
125
 
123
- ## 📄 License
126
+ ## License
124
127
 
125
128
  `pysits` is distributed under the GPL-2.0 license. See [LICENSE](./LICENSE) for more details.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Python wrapper for the [sits](https://github.com/e-sensing/sits) R package.
4
4
 
5
- ## 📦 Installation
5
+ ## Installation
6
6
 
7
7
  To install `pysits` with pip:
8
8
 
@@ -18,7 +18,7 @@ pip install git+https://github.com/e-sensing/pysits.git
18
18
 
19
19
  > To use `pysits`, you must have [sits](https://github.com/e-sensing/sits) installed. For more information, refer to the [official sits documentation](https://e-sensing.github.io/sitsbook/setup.html).
20
20
 
21
- ## 🛠 Development setup (for contributors)
21
+ ## Development setup (for contributors)
22
22
 
23
23
  To set up a local development environment:
24
24
 
@@ -42,7 +42,7 @@ source .venv/bin/activate # or .venv\Scripts\activate on Windows
42
42
  uv pip install -e ".[dev]"
43
43
  ```
44
44
 
45
- ### 🔍 Run tests
45
+ ### Run tests
46
46
 
47
47
  We use `pytest` for testing:
48
48
 
@@ -50,7 +50,7 @@ We use `pytest` for testing:
50
50
  pytest
51
51
  ```
52
52
 
53
- ### 🧹 Code formatting
53
+ ### Code formatting
54
54
 
55
55
  To keep the codebase clean and consistent we use [ruff](https://github.com/astral-sh/ruff):
56
56
 
@@ -58,7 +58,7 @@ To keep the codebase clean and consistent we use [ruff](https://github.com/astra
58
58
  ruff format .
59
59
  ```
60
60
 
61
- ### 🧪 Linting
61
+ ### Linting
62
62
 
63
63
  We use [ruff](https://github.com/astral-sh/ruff) for static analysis:
64
64
 
@@ -68,11 +68,11 @@ ruff check .
68
68
 
69
69
  > The `examples/` directory is excluded from linting.
70
70
 
71
- ## 📚 Learn more
71
+ ## Learn more
72
72
 
73
73
  To learn all about `sits`, including its concepts, API, and real-world examples, we recommend accessing the [official sits book](https://e-sensing.github.io/sitsbook/). The book provides examples in both R and Python.
74
74
 
75
- ## 🤝 Contributing
75
+ ## Contributing
76
76
 
77
77
  We welcome contributions! Please:
78
78
 
@@ -80,6 +80,6 @@ We welcome contributions! Please:
80
80
  - Create a feature branch
81
81
  - Submit a pull request with a clear description
82
82
 
83
- ## 📄 License
83
+ ## License
84
84
 
85
85
  `pysits` is distributed under the GPL-2.0 license. See [LICENSE](./LICENSE) for more details.
@@ -17,7 +17,7 @@
17
17
 
18
18
  [project]
19
19
  name = "pysits"
20
- version = "1.5.3.dev2"
20
+ version = "2.0.0.dev0"
21
21
  description = "Python wrapper for the sits R package"
22
22
  readme = "README.md"
23
23
  requires-python = ">=3.10,<4"
@@ -77,7 +77,10 @@ xarray = [
77
77
  "xarray>=2025.3.0",
78
78
  "dask>=2025.3.0",
79
79
  "rioxarray>=0.18.2",
80
- "affine>=2.4.0"
80
+ "affine>=2.4.0",
81
+ "odc-stac>=0.5.3",
82
+ "pystac>=1.15.0",
83
+ "planetary-computer>=1.0.0"
81
84
  ]
82
85
 
83
86
  dev = [
@@ -93,7 +96,7 @@ dev = [
93
96
  exclude = ["examples", "_resources"]
94
97
 
95
98
  [tool.ruff.lint]
96
- ignore = ["PLC0415"]
99
+ ignore = ["PLC0415", "PLW0108"]
97
100
  select = [
98
101
  "E", # pycodestyle
99
102
  "F", # pyflakes
@@ -27,7 +27,13 @@ from .sits.colors import (
27
27
  sits_colors_set,
28
28
  sits_colors_show,
29
29
  )
30
- from .sits.config import sits_config, sits_config_show, sits_config_user_file
30
+ from .sits.config import (
31
+ sits_config,
32
+ sits_config_show,
33
+ sits_config_user_file,
34
+ sits_config_value,
35
+ sits_parallel,
36
+ )
31
37
  from .sits.context import (
32
38
  cerrado_2classes,
33
39
  point_mt_6bands,
@@ -67,8 +73,15 @@ from .sits.data import (
67
73
  )
68
74
  from .sits.data import sits_summary as summary
69
75
  from .sits.exporters import sits_as_geopandas, sits_as_xarray, sits_to_csv, sits_to_xlsx
70
- from .sits.impute import impute_linear, sits_impute
76
+ from .sits.impute import (
77
+ impute_linear,
78
+ impute_mean,
79
+ impute_mean_window,
80
+ impute_median,
81
+ )
71
82
  from .sits.ml import (
83
+ sits_barlow_twins,
84
+ sits_contrastive_learning,
72
85
  sits_formula_linear,
73
86
  sits_formula_logref,
74
87
  sits_kfold_validate,
@@ -76,20 +89,25 @@ from .sits.ml import (
76
89
  sits_lighttae,
77
90
  sits_mlp,
78
91
  sits_model_export,
92
+ sits_pre_train,
79
93
  sits_resnet,
80
94
  sits_rfor,
95
+ sits_ssl_lejepa,
96
+ sits_ssl_mae,
97
+ sits_ssl_vicreg,
81
98
  sits_svm,
82
99
  sits_tae,
83
100
  sits_tempcnn,
84
101
  sits_train,
85
102
  sits_xgboost,
86
103
  )
87
- from .sits.segment import sits_segment, sits_slic
88
- from .sits.tiles import sits_mgrs_to_roi, sits_roi_to_tiles, sits_tiles_to_roi
104
+ from .sits.segment import sits_segment, sits_slic, sits_snic
105
+ from .sits.tiles import sits_roi_to_tiles, sits_tiles_to_roi
89
106
  from .sits.ts import (
90
107
  sits_cluster_clean,
91
108
  sits_cluster_dendro,
92
109
  sits_cluster_frequency,
110
+ sits_encode,
93
111
  sits_geo_dist,
94
112
  sits_get_class,
95
113
  sits_get_data,
@@ -103,7 +121,6 @@ from .sits.ts import (
103
121
  sits_reduce_imbalance,
104
122
  sits_sample,
105
123
  sits_sampling_design,
106
- sits_sgolay,
107
124
  sits_show_prediction,
108
125
  sits_som_clean_samples,
109
126
  sits_som_evaluate_cluster,
@@ -111,7 +128,6 @@ from .sits.ts import (
111
128
  sits_stats,
112
129
  sits_stratified_sampling,
113
130
  sits_validate,
114
- sits_whittaker,
115
131
  )
116
132
  from .sits.tuning import sits_tuning, sits_tuning_hparams
117
133
  from .sits.utils import (
@@ -152,6 +168,8 @@ __all__ = (
152
168
  "sits_config",
153
169
  "sits_config_show",
154
170
  "sits_config_user_file",
171
+ "sits_config_value",
172
+ "sits_parallel",
155
173
  # Data management
156
174
  "sits_bands",
157
175
  "sits_timeline",
@@ -184,13 +202,19 @@ __all__ = (
184
202
  "sits_model_export",
185
203
  "sits_formula_linear",
186
204
  "sits_formula_logref",
205
+ "sits_ssl_mae",
206
+ "sits_ssl_lejepa",
207
+ "sits_ssl_vicreg",
208
+ "sits_barlow_twins",
209
+ "sits_contrastive_learning",
210
+ "sits_pre_train",
187
211
  # Impute
188
- "sits_impute",
189
212
  "impute_linear",
213
+ "impute_mean",
214
+ "impute_median",
215
+ "impute_mean_window",
190
216
  # Time-series
191
217
  "sits_show_prediction",
192
- "sits_sgolay",
193
- "sits_whittaker",
194
218
  "sits_get_data",
195
219
  "sits_get_class",
196
220
  "sits_get_probs",
@@ -210,13 +234,14 @@ __all__ = (
210
234
  "sits_reduce_imbalance",
211
235
  "sits_sampling_design",
212
236
  "sits_stratified_sampling",
237
+ "sits_encode",
213
238
  # Tiles
214
- "sits_mgrs_to_roi",
215
239
  "sits_tiles_to_roi",
216
240
  "sits_roi_to_tiles",
217
241
  # Segments
218
242
  "sits_segment",
219
243
  "sits_slic",
244
+ "sits_snic",
220
245
  # Apply
221
246
  "sits_apply",
222
247
  # Exporters
@@ -17,7 +17,10 @@
17
17
 
18
18
  """backend functions."""
19
19
 
20
- from pysits.backend.loaders import load_function_from_package
20
+ from pysits.backend.loaders import (
21
+ load_function_from_package,
22
+ load_internal_function_from_package,
23
+ )
21
24
 
22
25
  # Base - plot
23
26
  r_fnc_plot = load_function_from_package("base::plot")
@@ -40,6 +43,9 @@ r_fnc_set_seed = load_function_from_package("base::set_seed")
40
43
  # Base - class (base)
41
44
  r_fnc_class = load_function_from_package("base::class")
42
45
 
46
+ # Base - eval (base)
47
+ r_fnc_eval = load_function_from_package("base::eval")
48
+
43
49
  # Base - as.data.frame (base)
44
50
  r_fnc_as_data_frame = load_function_from_package("base::as_data_frame")
45
51
 
@@ -48,3 +54,6 @@ r_fnc_colnames = load_function_from_package("base::colnames")
48
54
 
49
55
  # Base - rownames (base)
50
56
  r_fnc_rownames = load_function_from_package("base::rownames")
57
+
58
+ # sits - configuration (internal)
59
+ r_fnc_sits_conf = load_internal_function_from_package("sits:::.conf")
@@ -105,3 +105,42 @@ def load_function_from_package(name: str) -> Callable[..., Any]:
105
105
 
106
106
  # Return function
107
107
  return getattr(pkg, func_name)
108
+
109
+
110
+ def load_internal_function_from_package(name: str) -> Callable[..., Any]:
111
+ """Load an internal R function from a specified package.
112
+
113
+ Internal functions are those not exported by a package. They are addressed
114
+ in R with the ``package:::function`` notation.
115
+
116
+ Args:
117
+ name (str): The fully qualified name of the R function in the format
118
+ 'package:::function'. For example, 'sits:::.conf'.
119
+
120
+ Returns:
121
+ Callable[..., Any]: A Python callable that wraps the R function.
122
+ The exact signature depends on the underlying R function.
123
+
124
+ Raises:
125
+ ValueError: If the ``name`` doesn't follow the 'package:::function' format.
126
+
127
+ PackageNotFoundError: If the specified R package is not installed.
128
+
129
+ Examples:
130
+ >>> conf = load_internal_function_from_package('sits:::.conf')
131
+ >>> scale = conf('sources', 'BDC', 'collections', 'MOD13Q1-6.1')
132
+ """
133
+ # Parse package and function
134
+ package_name, _, func_name = name.partition(":::")
135
+
136
+ if not package_name or not func_name:
137
+ raise ValueError(
138
+ f"Invalid function name format: {name}. "
139
+ "Expected format: 'package:::function'"
140
+ )
141
+
142
+ # Import the package
143
+ importr(package_name, on_conflict="warn")
144
+
145
+ # Return function
146
+ return rpy2_r_interface(name)
@@ -25,8 +25,10 @@ from geopandas import GeoDataFrame as GeoPandasDataFrame
25
25
  from pandas import DataFrame as PandasDataFrame
26
26
  from rpy2.rinterface_lib.sexp import NULLType
27
27
  from rpy2.robjects import pandas2ri
28
+ from rpy2.robjects.language import LangVector
28
29
  from rpy2.robjects.robject import RObjectMixin
29
30
 
31
+ from pysits.backend.functions import r_fnc_eval
30
32
  from pysits.backend.pkgs import r_pkg_tibble
31
33
  from pysits.conversions.dsl.base import DSLObject
32
34
  from pysits.conversions.tibble import geopandas_to_tibble, pandas_to_tibble
@@ -179,6 +181,22 @@ def convert_to_r(obj):
179
181
  raise TypeError(f"Cannot convert object of type {obj_type} to R format")
180
182
 
181
183
 
184
+ def eval_r_language(obj):
185
+ """Evaluate an unevaluated R expression.
186
+
187
+ Args:
188
+ obj: The R object to evaluate.
189
+
190
+ Returns:
191
+ The evaluated R object. Objects that are not R expressions are returned
192
+ unchanged.
193
+ """
194
+ if isinstance(obj, LangVector):
195
+ return r_fnc_eval(obj)
196
+
197
+ return obj
198
+
199
+
182
200
  def convert_to_python(obj, as_type="str"):
183
201
  """Convert an R object to a Python representation.
184
202
 
@@ -206,6 +224,9 @@ def convert_to_python(obj, as_type="str"):
206
224
  def _convert(value, type_):
207
225
  result = []
208
226
 
227
+ # R expressions must be evaluated to be converted
228
+ value = eval_r_language(value)
229
+
209
230
  if isinstance(value, ro.ListVector):
210
231
  for k, v in value.items():
211
232
  result.append(
@@ -0,0 +1,233 @@
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
+ """STAC conversions."""
19
+
20
+ from __future__ import annotations
21
+
22
+ from urllib.parse import urlparse, urlunparse
23
+
24
+ import pystac
25
+ from affine import Affine
26
+ from numpy import datetime64
27
+ from odc.geo.geobox import GeoBox
28
+ from odc.geo.geom import CRS
29
+ from pandas import DataFrame as PandasDataFrame
30
+ from rasterio.warp import transform_bounds
31
+
32
+ #
33
+ # Prefix used by GDAL to read files through HTTP
34
+ #
35
+ VSICURL_PREFIX = "/vsicurl/"
36
+
37
+ #
38
+ # Host used by Microsoft Planetary Computer to store data
39
+ #
40
+ MPC_HOST_SUFFIX = "blob.core.windows.net"
41
+
42
+
43
+ #
44
+ # Grid operations
45
+ #
46
+ def files_geobox(files: PandasDataFrame) -> GeoBox:
47
+ """Create the geobox covering all files of a grid group.
48
+
49
+ The geobox uses the native resolution and CRS of the files, so data is read
50
+ without resampling.
51
+
52
+ Args:
53
+ files (PandasDataFrame): Files of a single grid group.
54
+
55
+ Returns:
56
+ GeoBox: Geobox covering all files.
57
+ """
58
+ xres = float(files["xres"].iloc[0])
59
+ yres = float(files["yres"].iloc[0])
60
+
61
+ # Extent covering all files of the group
62
+ xmin = float(files["xmin"].min())
63
+ xmax = float(files["xmax"].max())
64
+ ymin = float(files["ymin"].min())
65
+ ymax = float(files["ymax"].max())
66
+
67
+ # Shape (rounded, as files of a group share the same grid)
68
+ width = int(round((xmax - xmin) / xres))
69
+ height = int(round((ymax - ymin) / yres))
70
+
71
+ return GeoBox(
72
+ shape=(height, width),
73
+ affine=Affine(xres, 0.0, xmin, 0.0, -yres, ymax),
74
+ crs=CRS(str(files["crs"].iloc[0])),
75
+ )
76
+
77
+
78
+ def grid_group_name(files: PandasDataFrame, index: int) -> str:
79
+ """Create a name for a grid group.
80
+
81
+ Args:
82
+ files (PandasDataFrame): Files of a single grid group.
83
+
84
+ index (int): Position of the group, used when the CRS has no EPSG code.
85
+
86
+ Returns:
87
+ str: Group name (e.g., ``epsg32720-10m``).
88
+ """
89
+ crs = CRS(str(files["crs"].iloc[0]))
90
+ resolution = float(files["xres"].iloc[0])
91
+
92
+ try:
93
+ crs_name = f"epsg{crs.epsg}" if crs.epsg else f"grid{index}"
94
+
95
+ except Exception:
96
+ crs_name = f"grid{index}"
97
+
98
+ return f"{crs_name}-{resolution:g}m"
99
+
100
+
101
+ #
102
+ # STAC items
103
+ #
104
+ def stac_items_from_files(files: PandasDataFrame) -> list[pystac.Item]:
105
+ """Create STAC Items from cube files.
106
+
107
+ One item is created for each ``(tile, date)``, with one asset per band. All
108
+ item properties come from the cube metadata: no file is read.
109
+
110
+ Cubes can have more than one file for the same tile, date and band, as
111
+ collections may have multiple versions of an acquisition. Each version is
112
+ described as its own item, so all of them are used when data is read.
113
+
114
+ Args:
115
+ files (PandasDataFrame): Files of a single grid group.
116
+
117
+ Returns:
118
+ list[pystac.Item]: Items describing the files.
119
+ """
120
+ items = []
121
+
122
+ # Files of the same tile, date and band are versions of an acquisition
123
+ files = files.copy()
124
+ files["version"] = files.groupby(["tile", "date", "band"]).cumcount()
125
+
126
+ for (tile, date, version), group in files.groupby(
127
+ ["tile", "date", "version"], sort=True
128
+ ):
129
+ crs = str(group["crs"].iloc[0])
130
+ assets = {}
131
+
132
+ for _, file in group.iterrows():
133
+ transform = Affine(
134
+ a=float(file["xres"]),
135
+ b=0.0,
136
+ c=float(file["xmin"]),
137
+ d=0.0,
138
+ e=-float(file["yres"]),
139
+ f=float(file["ymax"]),
140
+ )
141
+
142
+ # Create asset
143
+ asset = pystac.Asset(
144
+ href=str(file["path"]),
145
+ roles=["data"],
146
+ )
147
+
148
+ # Define extra fields
149
+ asset.extra_fields.update(
150
+ {
151
+ "proj:wkt2": crs,
152
+ "proj:shape": [int(file["nrows"]), int(file["ncols"])],
153
+ "proj:transform": [*list(transform)[:6], 0.0, 0.0, 1.0],
154
+ }
155
+ )
156
+
157
+ # Save band
158
+ assets[str(file["band"])] = asset
159
+
160
+ # Footprint, in geographic coordinates (required by STAC)
161
+ bbox = list(
162
+ transform_bounds(
163
+ src_crs=crs,
164
+ dst_crs="EPSG:4326",
165
+ left=float(group["xmin"].min()),
166
+ bottom=float(group["ymin"].min()),
167
+ right=float(group["xmax"].max()),
168
+ top=float(group["ymax"].max()),
169
+ )
170
+ )
171
+
172
+ # Create item
173
+ item = pystac.Item(
174
+ id=f"{tile}_{date}_{version}",
175
+ geometry={
176
+ "type": "Polygon",
177
+ "coordinates": [
178
+ [
179
+ [bbox[0], bbox[1]],
180
+ [bbox[2], bbox[1]],
181
+ [bbox[2], bbox[3]],
182
+ [bbox[0], bbox[3]],
183
+ [bbox[0], bbox[1]],
184
+ ]
185
+ ],
186
+ },
187
+ bbox=bbox,
188
+ datetime=datetime64(str(date)).astype("datetime64[s]").item(),
189
+ properties={"tile": tile},
190
+ )
191
+
192
+ # Add assets
193
+ for band, asset in assets.items():
194
+ item.add_asset(band, asset)
195
+
196
+ # Save item
197
+ items.append(item)
198
+
199
+ # Return!
200
+ return items
201
+
202
+
203
+ #
204
+ # Data access
205
+ #
206
+ def signature_file_url(url: str) -> str:
207
+ """Refresh the credentials of a file URL.
208
+
209
+ Cubes store the credentials available when they were created. As these
210
+ credentials expire, they are refreshed before data is read.
211
+
212
+ Args:
213
+ url (str): File URL.
214
+
215
+ Returns:
216
+ str: URL with refreshed credentials, if required.
217
+ """
218
+ prefix = VSICURL_PREFIX if url.startswith(VSICURL_PREFIX) else ""
219
+ address = url[len(prefix) :]
220
+
221
+ parsed = urlparse(address)
222
+
223
+ # Only data with expiring credentials is refreshed
224
+ if not parsed.netloc.endswith(MPC_HOST_SUFFIX) or not parsed.query:
225
+ return url
226
+
227
+ try:
228
+ from planetary_computer import sign
229
+
230
+ except ImportError:
231
+ return url
232
+
233
+ return prefix + sign(urlunparse(parsed._replace(query="")))