├── tests ├── __init__.py ├── data │ ├── __init__.py │ ├── L5_2020-01-01 │ │ ├── __init__.py │ │ ├── L5_B1.tif │ │ ├── L5_B2.tif │ │ ├── L5_B3.tif │ │ ├── L5_B4.tif │ │ ├── L5_B5.tif │ │ └── L5_B7.tif │ ├── L8_2020-01-01 │ │ ├── __init__.py │ │ ├── L8_B1.tif │ │ ├── L8_B10.tif │ │ ├── L8_B2.tif │ │ ├── L8_B3.tif │ │ ├── L8_B4.tif │ │ ├── L8_B5.tif │ │ ├── L8_B6.tif │ │ └── L8_B7.tif │ ├── S2_2020-01-01 │ │ ├── __init__.py │ │ ├── S2_B01.tif │ │ ├── S2_B02.tif │ │ ├── S2_B03.tif │ │ ├── S2_B04.tif │ │ ├── S2_B05.tif │ │ ├── S2_B06.tif │ │ ├── S2_B07.tif │ │ ├── S2_B08.tif │ │ ├── S2_B09.tif │ │ ├── S2_B11.tif │ │ ├── S2_B12.tif │ │ └── S2_B8A.tif │ ├── S2_2020-01-02 │ │ ├── __init__.py │ │ ├── S2_B02.tif │ │ ├── S2_B03.tif │ │ └── S2_B04.tif │ ├── S2_2020-01-03 │ │ ├── __init__.py │ │ ├── S2_B02.tif │ │ ├── S2_B03.tif │ │ └── S2_B04.tif │ ├── S2_2020-01-04 │ │ ├── __init__.py │ │ ├── S2_B02.tif │ │ ├── S2_B03.tif │ │ └── S2_B04.tif │ ├── files │ │ ├── reclass.csv │ │ ├── spectral_signature_2.csv │ │ ├── roi.gpkg │ │ ├── file1.dbf │ │ ├── spectral_signature_1.csv │ │ ├── file1.csv │ │ ├── file2.csv │ │ ├── landsat_5_metadata_mtl.xml │ │ ├── sentinel_2_metadata_test_l2a.xml │ │ ├── sentinel_2_metadata_test_l1c.xml │ │ └── landsat_8_metadata_mtl.xml │ └── S2_2020-01-05 │ │ └── S2_2020-01-05.tif ├── test_log.py ├── test_temporary.py ├── test_plot_tools.py ├── test_files_directories.py ├── test_raster_label.py ├── test_band_erosion.py ├── test_output_manager.py ├── test_raster_to_vector.py ├── test_vector_to_raster.py ├── run_tests.py ├── test_band_sieve.py ├── test_download_tools.py ├── test_band_neighbor_pixels.py ├── test_band_stack_split.py ├── test_band_resample.py ├── test_raster_report.py ├── test_band_dilation.py ├── test_band_clustering.py ├── test_raster_edit.py ├── test_band_mask.py ├── test_raster_zonal_stats.py ├── test_band_pca.py ├── test_raster_reclassification.py ├── test_raster_vector.py ├── test_progress.py ├── test_band_spectral_distance.py ├── test_cross_classification.py ├── test_band_clip.py ├── test_band_mosaic.py ├── test_preprocess_products.py ├── test_processor.py ├── test_band_combination.py └── test_table_manager.py ├── src └── remotior_sensus │ ├── core │ ├── __init__.py │ ├── messages.py │ ├── temporary.py │ ├── output_manager.py │ └── log.py │ ├── tools │ ├── __init__.py │ ├── band_stack.py │ ├── raster_to_vector.py │ ├── raster_split.py │ ├── band_sieve.py │ ├── band_dilation.py │ └── band_erosion.py │ ├── util │ ├── __init__.py │ ├── system_tools.py │ ├── dates_times.py │ ├── read_write_files.py │ └── plot_tools.py │ └── __init__.py ├── requirements_readthedocs.txt ├── docs ├── source │ ├── _static │ │ ├── html_css.css │ │ ├── logo.png │ │ ├── bandset.jpg │ │ ├── favicon.ico │ │ ├── fromGIStoRS.png │ │ └── processing.jpg │ ├── modules.rst │ ├── api.rst │ ├── api_core.rst │ ├── api_tools.rst │ ├── remotior_sensus.core.log.rst │ ├── remotior_sensus.core.session.rst │ ├── remotior_sensus.tools.mosaic.rst │ ├── remotior_sensus.core.messages.rst │ ├── remotior_sensus.core.progress.rst │ ├── remotior_sensus.core.processor.rst │ ├── remotior_sensus.core.temporary.rst │ ├── remotior_sensus.tools.band_pca.rst │ ├── tutorials.rst │ ├── remotior_sensus.tools.band_calc.rst │ ├── remotior_sensus.tools.band_clip.rst │ ├── remotior_sensus.tools.band_mask.rst │ ├── remotior_sensus.util.plot_tools.rst │ ├── remotior_sensus.tools.band_sieve.rst │ ├── remotior_sensus.tools.band_stack.rst │ ├── remotior_sensus.util.dates_times.rst │ ├── remotior_sensus.util.shared_tools.rst │ ├── remotior_sensus.util.system_tools.rst │ ├── remotior_sensus.core.table_manager.rst │ ├── remotior_sensus.tools.band_erosion.rst │ ├── remotior_sensus.tools.raster_label.rst │ ├── remotior_sensus.tools.raster_split.rst │ ├── remotior_sensus.util.pytorch_tools.rst │ ├── remotior_sensus.util.raster_vector.rst │ ├── remotior_sensus.core.configurations.rst │ ├── remotior_sensus.core.output_manager.rst │ ├── remotior_sensus.tools.band_dilation.rst │ ├── remotior_sensus.tools.band_resample.rst │ ├── remotior_sensus.tools.raster_report.rst │ ├── remotior_sensus.util.download_tools.rst │ ├── remotior_sensus.core.bandset_catalog.rst │ ├── remotior_sensus.tools.raster_edit.rst │ ├── remotior_sensus.tools.band_clustering.rst │ ├── remotior_sensus.tools.band_combination.rst │ ├── remotior_sensus.util.files_directories.rst │ ├── remotior_sensus.util.read_write_files.rst │ ├── remotior_sensus.tools.download_products.rst │ ├── remotior_sensus.tools.raster_to_vector.rst │ ├── remotior_sensus.tools.vector_to_raster.rst │ ├── remotior_sensus.core.processor_functions.rst │ ├── remotior_sensus.core.spectral_signatures.rst │ ├── remotior_sensus.core.multiprocess_manager.rst │ ├── remotior_sensus.tools.band_classification.rst │ ├── remotior_sensus.tools.preprocess_products.rst │ ├── remotior_sensus.tools.raster_zonal_stats.rst │ ├── basic_tutorials.rst │ ├── remotior_sensus.tools.band_neighbor_pixels.rst │ ├── remotior_sensus.tools.cross_classification.rst │ ├── remotior_sensus.tools.band_spectral_distance.rst │ ├── remotior_sensus.tools.raster_reclassification.rst │ ├── remotior_sensus.rst │ ├── tutorial_create_sentinel2_jpg.rst │ ├── tutorial_ndvi.rst │ ├── quickstart.rst │ ├── tutorial_random_forest.rst │ ├── remotior_sensus.util.rst │ ├── remotior_sensus.core.rst │ ├── remotior_sensus.tools.rst │ ├── installation.rst │ ├── index.rst │ ├── conf.py │ ├── changelog.rst │ └── introduction.rst ├── Makefile └── make.bat ├── environment.yml ├── environment_readthedocs.yml ├── .readthedocs.yaml ├── LICENSE ├── pyproject.toml ├── .github └── workflows │ └── publish-to-pypi.yml ├── .gitignore └── README.rst /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/remotior_sensus/core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/remotior_sensus/tools/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/remotior_sensus/util/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data/L5_2020-01-01/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data/L8_2020-01-01/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data/S2_2020-01-01/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data/S2_2020-01-02/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data/S2_2020-01-03/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data/S2_2020-01-04/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements_readthedocs.txt: -------------------------------------------------------------------------------- 1 | sphinx_rtd_theme==1.3.0 2 | -------------------------------------------------------------------------------- /docs/source/_static/html_css.css: -------------------------------------------------------------------------------- 1 | .wy-nav-content {max-width: 100%} -------------------------------------------------------------------------------- /tests/data/files/reclass.csv: -------------------------------------------------------------------------------- 1 | 1, 1 2 | 425, 425 3 | 500, Null 4 | raster > 500, 2000 -------------------------------------------------------------------------------- /tests/data/files/spectral_signature_2.csv: -------------------------------------------------------------------------------- 1 | 0.1, 0.49 2 | 0.21, 0.6 3 | 0.38, 0.66 4 | 0.16, 0.7 -------------------------------------------------------------------------------- /tests/data/files/roi.gpkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/files/roi.gpkg -------------------------------------------------------------------------------- /tests/data/files/file1.dbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/files/file1.dbf -------------------------------------------------------------------------------- /docs/source/_static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/docs/source/_static/logo.png -------------------------------------------------------------------------------- /tests/data/files/spectral_signature_1.csv: -------------------------------------------------------------------------------- 1 | 0.2, 0.49, 0.02 2 | 0.31, 0.56, 0.02 3 | 0.18, 0.665, 0.02 4 | 0.16, 0.705, 0.02 -------------------------------------------------------------------------------- /docs/source/_static/bandset.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/docs/source/_static/bandset.jpg -------------------------------------------------------------------------------- /docs/source/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/docs/source/_static/favicon.ico -------------------------------------------------------------------------------- /docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | remotior_sensus 2 | =============== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | remotior_sensus 8 | -------------------------------------------------------------------------------- /docs/source/_static/fromGIStoRS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/docs/source/_static/fromGIStoRS.png -------------------------------------------------------------------------------- /docs/source/_static/processing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/docs/source/_static/processing.jpg -------------------------------------------------------------------------------- /tests/data/L5_2020-01-01/L5_B1.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L5_2020-01-01/L5_B1.tif -------------------------------------------------------------------------------- /tests/data/L5_2020-01-01/L5_B2.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L5_2020-01-01/L5_B2.tif -------------------------------------------------------------------------------- /tests/data/L5_2020-01-01/L5_B3.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L5_2020-01-01/L5_B3.tif -------------------------------------------------------------------------------- /tests/data/L5_2020-01-01/L5_B4.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L5_2020-01-01/L5_B4.tif -------------------------------------------------------------------------------- /tests/data/L5_2020-01-01/L5_B5.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L5_2020-01-01/L5_B5.tif -------------------------------------------------------------------------------- /tests/data/L5_2020-01-01/L5_B7.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L5_2020-01-01/L5_B7.tif -------------------------------------------------------------------------------- /tests/data/L8_2020-01-01/L8_B1.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L8_2020-01-01/L8_B1.tif -------------------------------------------------------------------------------- /tests/data/L8_2020-01-01/L8_B10.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L8_2020-01-01/L8_B10.tif -------------------------------------------------------------------------------- /tests/data/L8_2020-01-01/L8_B2.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L8_2020-01-01/L8_B2.tif -------------------------------------------------------------------------------- /tests/data/L8_2020-01-01/L8_B3.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L8_2020-01-01/L8_B3.tif -------------------------------------------------------------------------------- /tests/data/L8_2020-01-01/L8_B4.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L8_2020-01-01/L8_B4.tif -------------------------------------------------------------------------------- /tests/data/L8_2020-01-01/L8_B5.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L8_2020-01-01/L8_B5.tif -------------------------------------------------------------------------------- /tests/data/L8_2020-01-01/L8_B6.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L8_2020-01-01/L8_B6.tif -------------------------------------------------------------------------------- /tests/data/L8_2020-01-01/L8_B7.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/L8_2020-01-01/L8_B7.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-01/S2_B01.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-01/S2_B01.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-01/S2_B02.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-01/S2_B02.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-01/S2_B03.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-01/S2_B03.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-01/S2_B04.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-01/S2_B04.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-01/S2_B05.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-01/S2_B05.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-01/S2_B06.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-01/S2_B06.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-01/S2_B07.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-01/S2_B07.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-01/S2_B08.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-01/S2_B08.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-01/S2_B09.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-01/S2_B09.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-01/S2_B11.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-01/S2_B11.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-01/S2_B12.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-01/S2_B12.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-01/S2_B8A.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-01/S2_B8A.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-02/S2_B02.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-02/S2_B02.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-02/S2_B03.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-02/S2_B03.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-02/S2_B04.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-02/S2_B04.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-03/S2_B02.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-03/S2_B02.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-03/S2_B03.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-03/S2_B03.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-03/S2_B04.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-03/S2_B04.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-04/S2_B02.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-04/S2_B02.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-04/S2_B03.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-04/S2_B03.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-04/S2_B04.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-04/S2_B04.tif -------------------------------------------------------------------------------- /tests/data/S2_2020-01-05/S2_2020-01-05.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/semiautomaticgit/remotior_sensus/HEAD/tests/data/S2_2020-01-05/S2_2020-01-05.tif -------------------------------------------------------------------------------- /docs/source/api.rst: -------------------------------------------------------------------------------- 1 | API Reference 2 | =========================================== 3 | 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | remotior_sensus.rst 9 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: remotior_sensus 2 | channels: 3 | - conda-forge 4 | - defaults 5 | dependencies: 6 | - python=3.10 7 | - numpy 8 | - scipy 9 | - scikit-learn 10 | - gdal 11 | - pytorch -------------------------------------------------------------------------------- /docs/source/api_core.rst: -------------------------------------------------------------------------------- 1 | Core Modules 2 | =========================================== 3 | 4 | The following are the core modules. 5 | 6 | .. toctree:: 7 | :maxdepth: 3 8 | 9 | remotior_sensus.core.rst 10 | -------------------------------------------------------------------------------- /docs/source/api_tools.rst: -------------------------------------------------------------------------------- 1 | Tools 2 | =========================================== 3 | 4 | The following are the available tools. 5 | 6 | .. toctree:: 7 | :maxdepth: 3 8 | 9 | remotior_sensus.tools.rst 10 | -------------------------------------------------------------------------------- /environment_readthedocs.yml: -------------------------------------------------------------------------------- 1 | name: remotior_sensus 2 | channels: 3 | - conda-forge 4 | - defaults 5 | dependencies: 6 | - python=3.10 7 | - numpy 8 | - scipy 9 | - gdal 10 | - sphinx_rtd_theme 11 | -------------------------------------------------------------------------------- /tests/data/files/file1.csv: -------------------------------------------------------------------------------- 1 | id,main,field1,field2,field3,name 2 | 1,101,0,0,1281,a 3 | 2,101,0,1,811,a 4 | 3,101,0,2,181,a 5 | 4,102,1,0,26,c 6 | 5,102,1,1,110,c 7 | 6,102,1,2,170,c 8 | 7,103,2,0,843,b 9 | 9,105,2,0,843,t -------------------------------------------------------------------------------- /tests/data/files/file2.csv: -------------------------------------------------------------------------------- 1 | id,value,field1,field2,field3,name 2 | 1,101,0,0,100,d 3 | 2,101,0,1,400,d 4 | 3,101,0,2,600,e 5 | 4,102,1,0,200,d 6 | 5,102,1,1,100,d 7 | 6,102,1,2,200,e 8 | 7,103,2,0,300,e 9 | 8,105,2,0,300,f -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.log.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core.log module 2 | ================================ 3 | 4 | .. automodule:: remotior_sensus.core.log 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.session.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core.session module 2 | ==================================== 3 | 4 | .. automodule:: remotior_sensus.core.session 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.mosaic.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.mosaic module 2 | ==================================== 3 | 4 | .. automodule:: remotior_sensus.tools.mosaic 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.messages.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core.messages module 2 | ===================================== 3 | 4 | .. automodule:: remotior_sensus.core.messages 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.progress.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core.progress module 2 | ===================================== 3 | 4 | .. automodule:: remotior_sensus.core.progress 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.processor.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core.processor module 2 | ====================================== 3 | 4 | .. automodule:: remotior_sensus.core.processor 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.temporary.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core.temporary module 2 | ====================================== 3 | 4 | .. automodule:: remotior_sensus.core.temporary 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_pca.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_pca module 2 | ======================================= 3 | 4 | .. automodule:: remotior_sensus.tools.band_pca 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/tutorials.rst: -------------------------------------------------------------------------------- 1 | Tutorials 2 | =========================================== 3 | 4 | The following tutorials are available. 5 | 6 | 7 | .. toctree:: 8 | :maxdepth: 1 9 | :titlesonly: 10 | 11 | tutorial_create_sentinel2_jpg.rst -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_calc.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_calc module 2 | ======================================== 3 | 4 | .. automodule:: remotior_sensus.tools.band_calc 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_clip.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_clip module 2 | ======================================== 3 | 4 | .. automodule:: remotior_sensus.tools.band_clip 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_mask.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_mask module 2 | ======================================== 3 | 4 | .. automodule:: remotior_sensus.tools.band_mask 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.util.plot_tools.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.util.plot\_tools module 2 | ======================================== 3 | 4 | .. automodule:: remotior_sensus.util.plot_tools 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_sieve.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_sieve module 2 | ========================================= 3 | 4 | .. automodule:: remotior_sensus.tools.band_sieve 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_stack.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_stack module 2 | ========================================= 3 | 4 | .. automodule:: remotior_sensus.tools.band_stack 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.util.dates_times.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.util.dates\_times module 2 | ========================================= 3 | 4 | .. automodule:: remotior_sensus.util.dates_times 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.util.shared_tools.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.util.shared\_tools module 2 | ========================================== 3 | 4 | .. automodule:: remotior_sensus.util.shared_tools 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.util.system_tools.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.util.system\_tools module 2 | ========================================== 3 | 4 | .. automodule:: remotior_sensus.util.system_tools 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.table_manager.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core.table\_manager module 2 | =========================================== 3 | 4 | .. automodule:: remotior_sensus.core.table_manager 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_erosion.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_erosion module 2 | =========================================== 3 | 4 | .. automodule:: remotior_sensus.tools.band_erosion 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.raster_label.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.raster\_label module 2 | =========================================== 3 | 4 | .. automodule:: remotior_sensus.tools.raster_label 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.raster_split.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.raster\_split module 2 | =========================================== 3 | 4 | .. automodule:: remotior_sensus.tools.raster_split 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.util.pytorch_tools.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.util.pytorch\_tools module 2 | =========================================== 3 | 4 | .. automodule:: remotior_sensus.util.pytorch_tools 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.util.raster_vector.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.util.raster\_vector module 2 | =========================================== 3 | 4 | .. automodule:: remotior_sensus.util.raster_vector 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.configurations.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core.configurations module 2 | =========================================== 3 | 4 | .. automodule:: remotior_sensus.core.configurations 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.output_manager.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core.output\_manager module 2 | ============================================ 3 | 4 | .. automodule:: remotior_sensus.core.output_manager 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_dilation.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_dilation module 2 | ============================================ 3 | 4 | .. automodule:: remotior_sensus.tools.band_dilation 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_resample.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_resample module 2 | ============================================ 3 | 4 | .. automodule:: remotior_sensus.tools.band_resample 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.raster_report.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.raster\_report module 2 | ============================================ 3 | 4 | .. automodule:: remotior_sensus.tools.raster_report 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.util.download_tools.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.util.download\_tools module 2 | ============================================ 3 | 4 | .. automodule:: remotior_sensus.util.download_tools 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.bandset_catalog.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core.bandset\_catalog module 2 | ============================================= 3 | 4 | .. automodule:: remotior_sensus.core.bandset_catalog 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.raster_edit.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.raster\_edit module 2 | ====================================================== 3 | 4 | .. automodule:: remotior_sensus.tools.raster_edit 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_clustering.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_clustering module 2 | ============================================== 3 | 4 | .. automodule:: remotior_sensus.tools.band_clustering 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_combination.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_combination module 2 | =============================================== 3 | 4 | .. automodule:: remotior_sensus.tools.band_combination 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.util.files_directories.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.util.files\_directories module 2 | =============================================== 3 | 4 | .. automodule:: remotior_sensus.util.files_directories 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.util.read_write_files.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.util.read\_write\_files module 2 | =============================================== 3 | 4 | .. automodule:: remotior_sensus.util.read_write_files 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.download_products.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.download\_products module 2 | ================================================ 3 | 4 | .. automodule:: remotior_sensus.tools.download_products 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.raster_to_vector.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.raster\_to\_vector module 2 | ================================================ 3 | 4 | .. automodule:: remotior_sensus.tools.raster_to_vector 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.vector_to_raster.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.vector\_to\_raster module 2 | ================================================ 3 | 4 | .. automodule:: remotior_sensus.tools.vector_to_raster 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.processor_functions.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core.processor\_functions module 2 | ================================================= 3 | 4 | .. automodule:: remotior_sensus.core.processor_functions 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.spectral_signatures.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core.spectral\_signatures module 2 | ================================================= 3 | 4 | .. automodule:: remotior_sensus.core.spectral_signatures 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.multiprocess_manager.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core.multiprocess\_manager module 2 | ================================================== 3 | 4 | .. automodule:: remotior_sensus.core.multiprocess_manager 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_classification.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_classification module 2 | ================================================== 3 | 4 | .. automodule:: remotior_sensus.tools.band_classification 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.preprocess_products.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.preprocess\_products module 2 | ================================================== 3 | 4 | .. automodule:: remotior_sensus.tools.preprocess_products 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.raster_zonal_stats.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.raster\_zonal\_stats module 2 | =================================================== 3 | 4 | .. automodule:: remotior_sensus.tools.raster_zonal_stats 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/basic_tutorials.rst: -------------------------------------------------------------------------------- 1 | Basic Tutorials 2 | =========================================== 3 | 4 | The following tutorials are available. 5 | 6 | 7 | .. toctree:: 8 | :maxdepth: 1 9 | :titlesonly: 10 | 11 | quickstart.rst 12 | tutorial_ndvi.rst 13 | tutorial_random_forest.rst -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_neighbor_pixels.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_neighbor\_pixels module 2 | ==================================================== 3 | 4 | .. automodule:: remotior_sensus.tools.band_neighbor_pixels 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.cross_classification.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.cross\_classification module 2 | =================================================== 3 | 4 | .. automodule:: remotior_sensus.tools.cross_classification 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.band_spectral_distance.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.band\_spectral\_distance module 2 | ======================================================= 3 | 4 | .. automodule:: remotior_sensus.tools.band_spectral_distance 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.raster_reclassification.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools.raster\_reclassification module 2 | ====================================================== 3 | 4 | .. automodule:: remotior_sensus.tools.raster_reclassification 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "mambaforge-22.9" 7 | 8 | sphinx: 9 | configuration: docs/source/conf.py 10 | 11 | conda: 12 | environment: environment_readthedocs.yml 13 | 14 | formats: 15 | - pdf 16 | 17 | python: 18 | install: 19 | - requirements: requirements_readthedocs.txt 20 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus package 2 | ======================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | remotior_sensus.core 11 | remotior_sensus.tools 12 | 13 | 14 | Module contents 15 | --------------- 16 | 17 | .. automodule:: remotior_sensus 18 | :members: 19 | :undoc-members: 20 | :show-inheritance: 21 | -------------------------------------------------------------------------------- /docs/source/tutorial_create_sentinel2_jpg.rst: -------------------------------------------------------------------------------- 1 | Create Sentinel-2 jpg file 2 | =========================================== 3 | 4 | .. raw:: html 5 | 6 | 7 | 8 | Link to the guide: 9 | https://colab.research.google.com/gist/semiautomaticgit/0eb0a3e0e9794b66f162a46455b8a00d/create_sentinel2_jpg.ipynb -------------------------------------------------------------------------------- /docs/source/tutorial_ndvi.rst: -------------------------------------------------------------------------------- 1 | Download Sentinel-2 Data and Calculate NDVI 2 | =========================================== 3 | 4 | .. raw:: html 5 | 6 | 7 | 8 | Link to the guide: 9 | https://colab.research.google.com/gist/semiautomaticgit/e20479e1cbfbf76f078bfb4f211f23fa/download_sentinel-2_data_and_calculate_ndvi.ipynb 10 | -------------------------------------------------------------------------------- /tests/test_log.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import remotior_sensus 4 | 5 | 6 | class TestLog(TestCase): 7 | 8 | def test_create(self): 9 | rs = remotior_sensus.Session( 10 | n_processes=2, available_ram=1000, log_level=10 11 | ) 12 | cfg = rs.configurations 13 | cfg.logger.log.debug('>>> test logger file path') 14 | self.assertTrue(rs.files_directories.is_file(cfg.logger.file_path)) 15 | 16 | # clear temporary directory 17 | rs.close() 18 | -------------------------------------------------------------------------------- /tests/test_temporary.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import remotior_sensus 4 | 5 | 6 | class TestTemporary(TestCase): 7 | 8 | def test_create_root_temporary_directory(self): 9 | rs = remotior_sensus.Session( 10 | n_processes=2, available_ram=1000, log_level=10 11 | ) 12 | cfg = rs.configurations 13 | cfg.logger.log.debug('>>> test temp directory') 14 | self.assertTrue(rs.files_directories.is_directory(cfg.temp.dir)) 15 | 16 | # clear temporary directory 17 | rs.close() 18 | -------------------------------------------------------------------------------- /docs/source/quickstart.rst: -------------------------------------------------------------------------------- 1 | Quickstart 2 | =========================================== 3 | 4 | Following the video of the tutorial. 5 | 6 | https://www.youtube.com/watch?v=uv264j_oclU 7 | 8 | .. raw:: html 9 | 10 | 11 | 12 | 13 | .. raw:: html 14 | 15 | 16 | 17 | Link to the guide: 18 | https://colab.research.google.com/gist/semiautomaticgit/0fdb7c2c5c0b3b990cf342d226b8ee43/quickstart.ipynb 19 | -------------------------------------------------------------------------------- /docs/source/tutorial_random_forest.rst: -------------------------------------------------------------------------------- 1 | Random Forest Classification 2 | =========================================== 3 | 4 | Following the video of the tutorial. 5 | 6 | https://www.youtube.com/watch?v=Rc61lSWOgt4 7 | 8 | .. raw:: html 9 | 10 | 11 | 12 | 13 | .. raw:: html 14 | 15 | 16 | 17 | Link to the guide: 18 | https://colab.research.google.com/gist/semiautomaticgit/5b3c6dca89b3764a69cf083cf3d0674f/random_forest_classification.ipynb 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2022-2025 Luca Congedo. 2 | 3 | Remotior Sensus is free software: you can redistribute it and/or modify it 4 | under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, 6 | or (at your option) any later version. 7 | Remotior Sensus is distributed in the hope that it will be useful, 8 | but WITHOUT ANY WARRANTY; without even the implied warranty 9 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | See the GNU General Public License for more details. 11 | You should have received a copy of the GNU General Public License 12 | along with Remotior Sensus. If not, see . -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.util.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.util package 2 | ============================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | remotior_sensus.util.dates_times 11 | remotior_sensus.util.download_tools 12 | remotior_sensus.util.files_directories 13 | remotior_sensus.util.plot_tools 14 | remotior_sensus.util.pytorch_tools 15 | remotior_sensus.util.raster_vector 16 | remotior_sensus.util.read_write_files 17 | remotior_sensus.util.shared_tools 18 | remotior_sensus.util.system_tools 19 | 20 | Module contents 21 | --------------- 22 | 23 | .. automodule:: remotior_sensus.util 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | -------------------------------------------------------------------------------- /tests/test_plot_tools.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | from remotior_sensus.util import plot_tools 4 | 5 | 6 | class TestPlotTools(TestCase): 7 | 8 | def test_plot(self): 9 | pass 10 | """ 11 | ax = plot_tools.prepare_plot() 12 | plots, plot_names, x_ticks, y_ticks, v_lines = ( 13 | plot_tools.add_lines_to_plot( 14 | name_list=['test1', 'test2'], 15 | wavelength_list=[[0.4, 0.5, 0.6], [0.3, 0.5, 0.7]], 16 | value_list=[[0.7, 0.4, 0.9], [1.1, 1.3, 1.2]], 17 | color_list=['red', 'blue']) 18 | ) 19 | plot_tools.create_plot( 20 | ax=ax, plots=plots, plot_names=plot_names, x_ticks=x_ticks, 21 | y_ticks=y_ticks, v_lines=v_lines 22 | ) 23 | """ 24 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.core.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.core package 2 | ============================= 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | remotior_sensus.core.bandset_catalog 11 | remotior_sensus.core.configurations 12 | remotior_sensus.core.log 13 | remotior_sensus.core.messages 14 | remotior_sensus.core.multiprocess_manager 15 | remotior_sensus.core.output_manager 16 | remotior_sensus.core.processor 17 | remotior_sensus.core.processor_functions 18 | remotior_sensus.core.progress 19 | remotior_sensus.core.session 20 | remotior_sensus.core.spectral_signatures 21 | remotior_sensus.core.table_manager 22 | remotior_sensus.core.temporary 23 | 24 | Module contents 25 | --------------- 26 | 27 | .. automodule:: remotior_sensus.core 28 | :members: 29 | :undoc-members: 30 | :show-inheritance: 31 | -------------------------------------------------------------------------------- /tests/test_files_directories.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import remotior_sensus 4 | 5 | 6 | class TestFilesDirectories(TestCase): 7 | 8 | def test_files_directories(self): 9 | rs = remotior_sensus.Session( 10 | n_processes=2, available_ram=1000, log_level=10 11 | ) 12 | cfg = rs.configurations 13 | cfg.logger.log.debug('>>> test files directories') 14 | path = '/home/user/file.tif' 15 | root = '/home/user/' 16 | relative_path = rs.files_directories.absolute_to_relative_path( 17 | path=path, root=root 18 | ) 19 | absolute_path = rs.files_directories.relative_to_absolute_path( 20 | path=relative_path, root=root 21 | ) 22 | self.assertEqual(absolute_path, path) 23 | 24 | # clear temporary directory 25 | rs.close() 26 | -------------------------------------------------------------------------------- /tests/test_raster_label.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestRasterLabel(TestCase): 8 | 9 | def test_raster_label(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | data_path = Path(__file__).parent / 'data' 15 | p = str(data_path / 'S2_2020-01-02' / 'S2_B02.tif') 16 | cfg.logger.log.debug('>>> test raster label') 17 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.vrt_suffix) 18 | label = rs.raster_label(raster_path=p, output_path=temp, 19 | virtual_output=True) 20 | self.assertTrue(rs.files_directories.is_file(label.path)) 21 | 22 | # clear temporary directory 23 | rs.close() 24 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /tests/test_band_erosion.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestBandErosion(TestCase): 8 | 9 | def test_band_erosion(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | data_path = Path(__file__).parent / 'data' 15 | file_list = [ 16 | str(data_path / 'S2_2020-01-01' / 'S2_B02.tif'), 17 | str(data_path / 'S2_2020-01-01' / 'S2_B03.tif') 18 | ] 19 | cfg.logger.log.debug('>>> test band_erosion') 20 | erosion = rs.band_erosion( 21 | input_bands=file_list, output_path=cfg.temp.dir, 22 | value_list=[1, 425], size=1, circular_structure=True, 23 | prefix='erosion_' 24 | ) 25 | self.assertTrue(rs.files_directories.is_file(erosion.paths[0])) 26 | 27 | # clear temporary directory 28 | rs.close() 29 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "remotior_sensus" 7 | dynamic = ["version"] 8 | authors = [ 9 | { name="Luca Congedo", email="ing.congedoluca@gmail.com" }, 10 | ] 11 | description = "Remotior Sensus is software to process remote sensing and GIS data" 12 | readme = "README.rst" 13 | requires-python = ">=3.8" 14 | classifiers = [ 15 | "Programming Language :: Python :: 3", 16 | "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", 17 | "Operating System :: OS Independent", 18 | "Topic :: Scientific/Engineering :: GIS", 19 | "Topic :: Scientific/Engineering :: Image Processing", 20 | ] 21 | 22 | [project.urls] 23 | "Homepage" = "https://github.com/semiautomaticgit/remotior_sensus" 24 | "Bug Tracker" = "https://github.com/semiautomaticgit/remotior_sensus/issues" 25 | 26 | [tool.hatch.version] 27 | path = "src/remotior_sensus/__init__.py" 28 | 29 | [tool.hatch.build] 30 | exclude = [ 31 | "*.idea", 32 | "*.git", 33 | "*build", 34 | ] -------------------------------------------------------------------------------- /tests/test_output_manager.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestOutputManager(TestCase): 8 | 9 | def test_output_manager(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | data_path = Path(__file__).parent / 'data' 15 | path = str(data_path / 'S2_2020-01-01' / 'S2_B02.tif') 16 | cfg.logger.log.debug('>>> test output manager') 17 | output = rs.output_manager(path=path) 18 | self.assertEqual(output.path, path) 19 | # create BandSet Catalog 20 | catalog = rs.bandset_catalog() 21 | cfg.logger.log.debug('>>> test add to bandset') 22 | # add to BandSet 1 23 | output.add_to_bandset( 24 | bandset_catalog=catalog, bandset_number=1, band_number=1 25 | ) 26 | self.assertEqual(catalog.get_bandset(1).get_band(1).path, path) 27 | 28 | # clear temporary directory 29 | rs.close() 30 | -------------------------------------------------------------------------------- /tests/test_raster_to_vector.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestRasterToVector(TestCase): 8 | 9 | def test_raster_to_vector(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | data_path = Path(__file__).parent / 'data' 15 | p = str(data_path / 'S2_2020-01-01' / 'S2_B02.tif') 16 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.gpkg_suffix) 17 | cfg.logger.log.debug('>>> test raster_to_vector') 18 | vector = rs.raster_to_vector(p, temp) 19 | self.assertTrue(rs.files_directories.is_file(vector.path)) 20 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.gpkg_suffix) 21 | cfg.logger.log.debug('>>> test raster_to_vector dissolve') 22 | vector = rs.raster_to_vector(p, temp, dissolve=True) 23 | self.assertTrue(rs.files_directories.is_file(vector.path)) 24 | 25 | # clear temporary directory 26 | rs.close() 27 | -------------------------------------------------------------------------------- /.github/workflows/publish-to-pypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python distribution to PyPI 2 | 3 | on: push 4 | 5 | jobs: 6 | build-n-publish: 7 | name: Build and publish Python distribution to PyPI 8 | runs-on: ubuntu-latest 9 | # Specifying a GitHub environment is optional, but strongly encouraged 10 | environment: release 11 | permissions: 12 | # IMPORTANT: this permission is mandatory for trusted publishing 13 | id-token: write 14 | steps: 15 | - uses: actions/checkout@master 16 | - name: Set up Python 3.10 17 | uses: actions/setup-python@v3 18 | with: 19 | python-version: "3.10" 20 | - name: Install pypa/build 21 | run: >- 22 | python -m 23 | pip install 24 | build 25 | --user 26 | - name: Build a binary wheel and a source tarball 27 | run: >- 28 | python -m 29 | build 30 | --sdist 31 | --wheel 32 | --outdir dist/ 33 | . 34 | - name: Publish distribution to PyPI 35 | if: startsWith(github.ref, 'refs/tags') 36 | uses: pypa/gh-action-pypi-publish@release/v1 37 | -------------------------------------------------------------------------------- /src/remotior_sensus/__init__.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | 18 | from remotior_sensus.core.session import Session 19 | from remotior_sensus.core import configurations 20 | 21 | __version__ = '0.5.2' 22 | 23 | configurations.version = __version__ 24 | -------------------------------------------------------------------------------- /tests/test_vector_to_raster.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestVectorToRaster(TestCase): 8 | 9 | def test_vector_to_raster(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | cfg.logger.log.debug('>>> test vector to raster') 15 | data_path = Path(__file__).parent / 'data' 16 | v = str(data_path / 'files' / 'roi.gpkg') 17 | r = str(data_path / 'S2_2020-01-01' / 'S2_B02.tif') 18 | temp = cfg.temp.temporary_file_path( 19 | name='raster', name_suffix=cfg.tif_suffix 20 | ) 21 | raster = rs.vector_to_raster(vector_path=v, align_raster=r, 22 | constant=1, output_path=temp) 23 | self.assertTrue(rs.files_directories.is_file(raster.path)) 24 | raster = rs.vector_to_raster(vector_path=v, align_raster=r, 25 | method='area_based', vector_field='class') 26 | self.assertTrue(rs.files_directories.is_file(raster.path)) 27 | 28 | # clear temporary directory 29 | rs.close() 30 | -------------------------------------------------------------------------------- /tests/run_tests.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | 18 | import unittest 19 | 20 | # protect the entry point for multiprocess 21 | if __name__ == '__main__': 22 | testLoader = unittest.TestLoader() 23 | test_dir = '.' 24 | # units to test 25 | pattern = 'test*.py' 26 | d = testLoader.discover(test_dir, pattern=pattern) 27 | textTestRunner = unittest.TextTestRunner(verbosity=2) 28 | textTestRunner.run(d) 29 | -------------------------------------------------------------------------------- /tests/test_band_sieve.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestBandSieve(TestCase): 8 | 9 | def test_band_sieve(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | data_path = Path(__file__).parent / 'data' 15 | file_list = [ 16 | str(data_path / 'S2_2020-01-01' / 'S2_B02.tif'), 17 | str(data_path / 'S2_2020-01-01' / 'S2_B03.tif') 18 | ] 19 | cfg.logger.log.debug('>>> test band_sieve') 20 | sieve = rs.band_sieve( 21 | input_bands=file_list, output_path=cfg.temp.dir, size=2, 22 | connected=False, prefix='sieve_' 23 | ) 24 | self.assertTrue(rs.files_directories.is_file(sieve.paths[0])) 25 | 26 | coordinate_list = [230250, 4674550, 230320, 4674440] 27 | sieve = rs.band_sieve( 28 | input_bands=file_list, output_path=cfg.temp.dir, size=2, 29 | connected=False, prefix='sieve_', extent_list=coordinate_list 30 | ) 31 | self.assertTrue(rs.files_directories.is_file(sieve.paths[0])) 32 | 33 | # clear temporary directory 34 | rs.close() 35 | -------------------------------------------------------------------------------- /tests/test_download_tools.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import remotior_sensus 4 | from remotior_sensus.util import download_tools, files_directories 5 | 6 | 7 | class TestDownloadTools(TestCase): 8 | 9 | def test_download_tools(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | cfg.logger.log.debug('>>> test download') 15 | url = 'https://www.python.org' 16 | temp = cfg.temp.temporary_file_path(name_suffix='.html') 17 | download_tools.download_file(url=url, output_path=temp) 18 | self.assertTrue(files_directories.is_file(temp)) 19 | url_2 = ''.join( 20 | ['https://storage.googleapis.com/gcp-public-data-sentinel-2/', 21 | 'L2/tiles/33/S/VB/S2A_MSIL2A_20210104T094411_N0214_R036_T33S', 22 | 'VB_20210104T122314.SAFE/GRANULE/L2A_T33SVB_A028919_20210104', 23 | 'T094407/IMG_DATA/R60m/T33SVB_20210104T094411_B01_60m.jp2'] 24 | ) 25 | temp_2 = cfg.temp.temporary_file_path(name_suffix='.jp2') 26 | download_tools.download_file(url=url_2, output_path=temp_2) 27 | self.assertTrue(files_directories.is_file(temp_2)) 28 | 29 | # clear temporary directory 30 | rs.close() 31 | -------------------------------------------------------------------------------- /tests/data/files/landsat_5_metadata_mtl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | L1TP 5 | 02 6 | T1 7 | 8 | 9 | LANDSAT_5 10 | MSS 11 | 2011-01-01 12 | 20.7 13 | 0.98 14 | 15 | 16 | 1.52E-03 17 | 1.31E-03 18 | 1.38E-03 19 | NULL 20 | 0.0026 21 | 0.0040 22 | 0.0107 23 | NULL 24 | 25 | 26 | 607.76 27 | 1260.56 28 | 29 | 30 | -------------------------------------------------------------------------------- /docs/source/remotior_sensus.tools.rst: -------------------------------------------------------------------------------- 1 | remotior\_sensus.tools package 2 | ============================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | remotior_sensus.tools.band_calc 11 | remotior_sensus.tools.band_classification 12 | remotior_sensus.tools.band_clip 13 | remotior_sensus.tools.band_clustering 14 | remotior_sensus.tools.band_combination 15 | remotior_sensus.tools.band_dilation 16 | remotior_sensus.tools.band_erosion 17 | remotior_sensus.tools.band_mask 18 | remotior_sensus.tools.band_neighbor_pixels 19 | remotior_sensus.tools.band_pca 20 | remotior_sensus.tools.band_resample 21 | remotior_sensus.tools.band_sieve 22 | remotior_sensus.tools.band_spectral_distance 23 | remotior_sensus.tools.band_stack 24 | remotior_sensus.tools.cross_classification 25 | remotior_sensus.tools.download_products 26 | remotior_sensus.tools.mosaic 27 | remotior_sensus.tools.preprocess_products 28 | remotior_sensus.tools.raster_edit 29 | remotior_sensus.tools.raster_label 30 | remotior_sensus.tools.raster_reclassification 31 | remotior_sensus.tools.raster_report 32 | remotior_sensus.tools.raster_split 33 | remotior_sensus.tools.raster_zonal_stats 34 | remotior_sensus.tools.raster_to_vector 35 | remotior_sensus.tools.vector_to_raster 36 | 37 | Module contents 38 | --------------- 39 | 40 | .. automodule:: remotior_sensus.tools 41 | :members: 42 | :undoc-members: 43 | :show-inheritance: 44 | -------------------------------------------------------------------------------- /tests/test_band_neighbor_pixels.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestNeighborPixels(TestCase): 8 | 9 | def test_neighbor_pixels(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | data_path = Path(__file__).parent / 'data' 15 | file_list = [ 16 | str(data_path / 'S2_2020-01-01' / 'S2_B02.tif'), 17 | str(data_path / 'S2_2020-01-01' / 'S2_B03.tif') 18 | ] 19 | cfg.logger.log.debug('>>> test neighbor_pixels') 20 | neighbor = rs.band_neighbor_pixels( 21 | input_bands=file_list, output_path=cfg.temp.dir, size=1, 22 | circular_structure=True, stat_name='Mean', prefix='neighbor_' 23 | ) 24 | self.assertTrue(rs.files_directories.is_file(neighbor.paths[0])) 25 | cfg.logger.log.debug('>>> test neighbor_pixels with coordinates') 26 | coordinate_list = [230250, 4674550, 230320, 4674440] 27 | neighbor = rs.band_neighbor_pixels( 28 | input_bands=file_list, output_path=cfg.temp.dir, size=1, 29 | circular_structure=True, stat_name='Mean', prefix='neighbor_', 30 | extent_list=coordinate_list 31 | ) 32 | self.assertTrue(rs.files_directories.is_file(neighbor.paths[0])) 33 | 34 | # clear temporary directory 35 | rs.close() 36 | -------------------------------------------------------------------------------- /tests/test_band_stack_split.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestBandStackSplit(TestCase): 8 | 9 | def test_band_stack_split(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | cfg.logger.log.debug('>>> test band stack') 15 | catalog = rs.bandset_catalog() 16 | file_list = ['S2_2020-01-01/S2_B02.tif', 'S2_2020-01-01/S2_B03.tif', 17 | 'S2_2020-01-01/S2_B04.tif'] 18 | date = '2021-01-01' 19 | data_path = Path(__file__).parent / 'data' 20 | catalog.create_bandset( 21 | file_list, wavelengths=['Sentinel-2'], date=date, bandset_number=1, 22 | root_directory=str(data_path) 23 | ) 24 | cfg.logger.log.debug('>>> test band stack') 25 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 26 | stack = rs.band_stack(input_bands=1, output_path=temp, 27 | bandset_catalog=catalog) 28 | cfg.logger.log.debug('>>> test raster split') 29 | self.assertTrue(rs.files_directories.is_file(stack.path)) 30 | split = rs.raster_split(raster_path=stack.path, 31 | output_path=cfg.temp.dir) 32 | self.assertTrue(rs.files_directories.is_file(split.paths[0])) 33 | 34 | # clear temporary directory 35 | rs.close() 36 | -------------------------------------------------------------------------------- /tests/test_band_resample.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestBandResample(TestCase): 8 | 9 | def test_band_resample(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | data_path = Path(__file__).parent / 'data' 15 | file_list = [ 16 | str(data_path / 'S2_2020-01-01' / 'S2_B02.tif'), 17 | str(data_path / 'S2_2020-01-01' / 'S2_B03.tif') 18 | ] 19 | cfg.logger.log.debug('>>> test band resample') 20 | resample = rs.band_resample( 21 | input_bands=file_list, output_path=cfg.temp.dir, resampling='mode', 22 | resample_pixel_factor=2, prefix='resample_' 23 | ) 24 | self.assertTrue(rs.files_directories.is_file(resample.paths[0])) 25 | 26 | # reproject band set (input files from bandset) 27 | reproject = rs.band_resample( 28 | input_bands=file_list, output_path=cfg.temp.dir, 29 | prefix='reproj_', epsg_code='32632', align_raster=None, 30 | resampling='nearest_neighbour', nodata_value=None, 31 | x_y_resolution=[20.0, 20.0], resample_pixel_factor=None, 32 | output_data_type=None, same_extent=False, virtual_output=False, 33 | compress=True, compress_format='LZW' 34 | ) 35 | self.assertTrue(rs.files_directories.is_file(reproject.paths[0])) 36 | 37 | # clear temporary directory 38 | rs.close() 39 | -------------------------------------------------------------------------------- /src/remotior_sensus/util/system_tools.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | 18 | """ 19 | Tools to manage operating systems 20 | """ 21 | 22 | from platform import system 23 | import sys 24 | 25 | from remotior_sensus.core import configurations as cfg 26 | 27 | 28 | # get system information 29 | def get_system_info(): 30 | if sys.maxsize > 2 ** 32: 31 | cfg.sys_64bit = True 32 | else: 33 | cfg.sys_64bit = False 34 | # file system encoding 35 | cfg.file_sys_encoding = sys.getfilesystemencoding() 36 | # system information 37 | cfg.sys_name = system() 38 | cfg.logger.log.info( 39 | 'version:%s; system: %s; 64bit: %s; n_processes: %s; ram: %s; ' 40 | 'temp.dir: %s' 41 | % (cfg.version, cfg.sys_name, cfg.sys_64bit, cfg.n_processes, 42 | cfg.available_ram, cfg.temp.dir) 43 | ) 44 | -------------------------------------------------------------------------------- /tests/test_raster_report.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | from remotior_sensus.util import read_write_files 6 | 7 | 8 | class TestRasterReport(TestCase): 9 | 10 | def test_report(self): 11 | rs = remotior_sensus.Session( 12 | n_processes=2, available_ram=1000, log_level=10 13 | ) 14 | cfg = rs.configurations 15 | data_path = Path(__file__).parent / 'data' 16 | p = str(data_path / 'S2_2020-01-01' / 'S2_B02.tif') 17 | cfg.logger.log.debug('>>> test raster_report') 18 | report = rs.raster_report(p) 19 | table = read_write_files.open_text_file(report.path) 20 | table_f = read_write_files.format_csv_new_delimiter( 21 | table, cfg.tab_delimiter 22 | ) 23 | self.assertGreater(len(table_f), 0) 24 | table_html = read_write_files.format_csv_text_html(table) 25 | self.assertGreater(len(table_html), 0) 26 | table_split = table.split(cfg.new_line) 27 | self.assertGreater(int(table_split[1][0]), 0) 28 | coordinate_list = [230250, 4674550, 230320, 4674440] 29 | report = rs.raster_report(p, extent_list=coordinate_list) 30 | table = read_write_files.open_text_file(report.path) 31 | table_f = read_write_files.format_csv_new_delimiter( 32 | table, cfg.tab_delimiter 33 | ) 34 | self.assertGreater(len(table_f), 0) 35 | table_html = read_write_files.format_csv_text_html(table) 36 | self.assertGreater(len(table_html), 0) 37 | table_split = table.split(cfg.new_line) 38 | self.assertGreater(int(table_split[1][0]), 0) 39 | 40 | # clear temporary directory 41 | rs.close() 42 | -------------------------------------------------------------------------------- /tests/test_band_dilation.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestBandDilation(TestCase): 8 | 9 | def test_band_dilation(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | data_path = Path(__file__).parent / 'data' 15 | file_list = [ 16 | str(data_path / 'S2_2020-01-01' / 'S2_B02.tif'), 17 | str(data_path / 'S2_2020-01-01' / 'S2_B03.tif') 18 | ] 19 | cfg.logger.log.debug('>>> test band_dilation') 20 | dilation = rs.band_dilation( 21 | input_bands=file_list, output_path=cfg.temp.dir, 22 | value_list=[1, 425], size=3, circular_structure=True, 23 | prefix='dilation_' 24 | ) 25 | self.assertTrue(rs.files_directories.is_file(dilation.paths[0])) 26 | cfg.logger.log.debug('>>> test band_dilation without output') 27 | dilation = rs.band_dilation( 28 | input_bands=file_list, value_list=[1, 425], size=3, 29 | circular_structure=True, prefix='dilation_' 30 | ) 31 | self.assertTrue(rs.files_directories.is_file(dilation.paths[0])) 32 | cfg.logger.log.debug('>>> test band_dilation with coordinate list') 33 | coordinate_list = [230250, 4674550, 230320, 4674440] 34 | dilation = rs.band_dilation( 35 | input_bands=file_list, value_list=[1, 425], size=3, 36 | circular_structure=True, prefix='dilation_', 37 | extent_list=coordinate_list 38 | ) 39 | self.assertTrue(rs.files_directories.is_file(dilation.paths[0])) 40 | 41 | # clear temporary directory 42 | rs.close() 43 | -------------------------------------------------------------------------------- /tests/test_band_clustering.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestBandClassification(TestCase): 8 | 9 | def test_band_classification(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | cfg.logger.log.debug('>>> test semiautomatic classification') 15 | # create BandSet 16 | catalog = rs.bandset_catalog() 17 | file_list = ['L8_2020-01-01/L8_B2.tif', 'L8_2020-01-01/L8_B3.tif', 18 | 'L8_2020-01-01/L8_B4.tif', 'L8_2020-01-01/L8_B5.tif', 19 | 'L8_2020-01-01/L8_B6.tif', 'L8_2020-01-01/L8_B7.tif'] 20 | data_path = Path(__file__).parent / 'data' 21 | catalog.create_bandset(file_list, root_directory=str(data_path)) 22 | temp = cfg.temp.temporary_file_path( 23 | name='class', name_suffix=cfg.tif_suffix 24 | ) 25 | rs.band_clustering( 26 | input_bands=catalog.get(1), output_raster_path=temp, 27 | algorithm_name=cfg.minimum_distance, class_number=3, 28 | max_iter=2, seed_signatures=cfg.random_pixel 29 | ) 30 | temp = cfg.temp.temporary_file_path( 31 | name='class', name_suffix=cfg.tif_suffix 32 | ) 33 | clustering = rs.band_clustering( 34 | input_bands=catalog.get(1), output_raster_path=temp, 35 | algorithm_name=cfg.minimum_distance, class_number=3, 36 | max_iter=2, seed_signatures=cfg.band_mean 37 | ) 38 | self.assertTrue(rs.files_directories.is_file(clustering.path)) 39 | self.assertTrue( 40 | rs.files_directories.is_file(clustering.extra['signature_path']) 41 | ) 42 | 43 | # clear temporary directory 44 | rs.close() 45 | -------------------------------------------------------------------------------- /tests/test_raster_edit.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestRasterEdit(TestCase): 8 | 9 | def test_raster_edit(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | data_path = Path(__file__).parent / 'data' 15 | p = str(data_path / 'S2_2020-01-01' / 'S2_B02.tif') 16 | v = str(data_path / 'files' / 'roi.gpkg') 17 | cfg.logger.log.debug('>>> test raster edit') 18 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 19 | rs.files_directories.copy_file(in_path=p, out_path=temp) 20 | edit = rs.raster_edit( 21 | raster_path=temp, vector_path=v, constant_value=10 22 | ) 23 | self.assertTrue(edit.extra['column_start'] > 0) 24 | cfg.logger.log.debug('>>> test undo raster edit') 25 | edit_2 = rs.raster_edit( 26 | raster_path=temp, column_start=edit.extra['column_start'], 27 | row_start=edit.extra['row_start'], 28 | old_array=edit.extra['old_array'] 29 | ) 30 | self.assertTrue(edit_2.extra['column_start'] is None) 31 | cfg.logger.log.debug('>>> test raster edit with expression') 32 | temp_3 = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 33 | rs.files_directories.copy_file(in_path=p, out_path=temp_3) 34 | expression = 'where(%s%s%s > 500, 10, %s%s%s)' % ( 35 | cfg.variable_band_quotes, cfg.variable_raster_name, 36 | cfg.variable_band_quotes, cfg.variable_band_quotes, 37 | cfg.variable_raster_name, cfg.variable_band_quotes 38 | ) 39 | edit_3 = rs.raster_edit( 40 | raster_path=temp_3, vector_path=v, expression=expression 41 | ) 42 | self.assertTrue(edit_3.extra['column_start'] > 0) 43 | 44 | # clear temporary directory 45 | rs.close() 46 | -------------------------------------------------------------------------------- /tests/test_band_mask.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestBandMask(TestCase): 8 | 9 | def test_band_mask(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | cfg.logger.log.debug('>>> test band mask') 15 | catalog = rs.bandset_catalog() 16 | file_list = ['S2_2020-01-01/S2_B02.tif', 'S2_2020-01-01/S2_B03.tif', 17 | 'S2_2020-01-01/S2_B04.tif'] 18 | data_path = Path(__file__).parent / 'data' 19 | catalog.create_bandset( 20 | file_list, wavelengths=['Sentinel-2'], bandset_number=1, 21 | root_directory=str(data_path) 22 | ) 23 | cfg.logger.log.debug('>>> test band mask') 24 | v = str(data_path / 'files' / 'roi.gpkg') 25 | output = rs.band_mask(input_bands=1, input_mask=v, 26 | bandset_catalog=catalog, 27 | output_path=cfg.temp.dir) 28 | self.assertTrue(output.check) 29 | self.assertTrue(rs.files_directories.is_file(output.paths[0])) 30 | files = [ 31 | str(data_path / 'S2_2020-01-01' / 'S2_B02.tif'), 32 | str(data_path / 'S2_2020-01-01' / 'S2_B03.tif'), 33 | str(data_path / 'S2_2020-01-01' / 'S2_B04.tif') 34 | ] 35 | mask_path = str(data_path / 'S2_2020-01-01' / 'S2_B02.tif') 36 | output = rs.band_mask(input_bands=files, 37 | input_mask=mask_path, 38 | prefix='mask_', buffer=1, mask_values=[1, 425], 39 | nodata_value=-32768, virtual_output=True, 40 | output_path=cfg.temp.dir) 41 | self.assertTrue(output.check) 42 | self.assertTrue(rs.files_directories.is_file(output.paths[0])) 43 | 44 | # clear temporary directory 45 | rs.close() 46 | -------------------------------------------------------------------------------- /tests/data/files/sentinel_2_metadata_test_l2a.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2020-01-01T01:00:00.000Z 4 | Level-2A 5 | Sentinel-2A 6 | 10000 7 | -1000 8 | -1000 9 | -1000 10 | -1000 11 | -1000 12 | -1000 13 | -1000 14 | -1000 15 | -1000 16 | -1000 17 | -1000 18 | -1000 19 | -1000 20 | 1884.1 21 | 1959 22 | 1823 23 | 1512 24 | 1424 25 | 1287.6 26 | 1162.1 27 | 1041 28 | 955 29 | 812 30 | 367 31 | 245 32 | 85 33 | 34 | -------------------------------------------------------------------------------- /tests/data/files/sentinel_2_metadata_test_l1c.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2020-01-01T01:00:00.000Z 4 | Level-1C 5 | Sentinel-2A 6 | 10000 7 | -1000 8 | -1000 9 | -1000 10 | -1000 11 | -1000 12 | -1000 13 | -1000 14 | -1000 15 | -1000 16 | -1000 17 | -1000 18 | -1000 19 | -1000 20 | 1884.1 21 | 1959 22 | 1823 23 | 1512 24 | 1424 25 | 1287.6 26 | 1162.1 27 | 1041 28 | 955 29 | 812 30 | 367 31 | 245 32 | 85 33 | 34 | -------------------------------------------------------------------------------- /src/remotior_sensus/core/messages.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | 18 | """Manager of messages. 19 | 20 | Console messages during processes. 21 | 22 | Typical usage example: 23 | 24 | >>> # display a warning message 25 | >>> warning('warning message') 26 | """ 27 | 28 | 29 | def warning(message: str): 30 | """Warning message. 31 | 32 | Prints a warning message. 33 | 34 | Args: 35 | message: message. 36 | 37 | Examples: 38 | Display a message 39 | >>> warning('warning message') 40 | """ 41 | print('⚠ warning: %s' % message) 42 | 43 | 44 | def error(message: str): 45 | """Error message. 46 | 47 | Prints an error message. 48 | 49 | Args: 50 | message: message. 51 | 52 | Examples: 53 | Display a message 54 | >>> error('error message') 55 | """ 56 | print('▲ error: %s' % message) 57 | 58 | 59 | def info(message: str): 60 | """Info message. 61 | 62 | Prints an info message. 63 | 64 | Args: 65 | message: message. 66 | 67 | Examples: 68 | Display a message 69 | >>> info('error message') 70 | """ 71 | print('info: %s' % message) 72 | -------------------------------------------------------------------------------- /tests/test_raster_zonal_stats.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestRasterZonalStats(TestCase): 8 | 9 | def test_raster_zonal_stats(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | data_path = Path(__file__).parent / 'data' 15 | v = str(data_path / 'files' / 'roi.gpkg') 16 | r = str(data_path / 'S2_2020-01-01' / 'S2_B02.tif') 17 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.csv_suffix) 18 | cfg.logger.log.debug('>>> test raster_zonal_stats') 19 | stats = rs.raster_zonal_stats( 20 | raster_path=r, reference_path=v, vector_field='class', 21 | stat_names=['Sum', 'Mean'], output_path=temp, 22 | ) 23 | self.assertTrue(rs.files_directories.is_file(stats.path)) 24 | self.assertTrue(stats.extra['table'] is not None) 25 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.csv_suffix) 26 | cfg.logger.log.debug('>>> test raster_zonal_stats percentile') 27 | stats2 = rs.raster_zonal_stats( 28 | raster_path=r, reference_path=v, vector_field='class', 29 | stat_names=['Percentile', 'Max', 'Min'], stat_percentile=[1, 99], 30 | output_path=temp, 31 | ) 32 | self.assertTrue(rs.files_directories.is_file(stats2.path)) 33 | self.assertTrue(stats2.extra['table'] is not None) 34 | 35 | cfg.logger.log.debug('>>> test raster_zonal_stats raster') 36 | raster = rs.vector_to_raster(vector_path=v, align_raster=r, 37 | vector_field='class') 38 | temp_2 = cfg.temp.temporary_file_path(name_suffix=cfg.csv_suffix) 39 | stats = rs.raster_zonal_stats( 40 | raster_path=r, reference_path=raster.path, 41 | stat_names=['Sum', 'Mean'], output_path=temp_2, 42 | ) 43 | self.assertTrue(rs.files_directories.is_file(stats.path)) 44 | self.assertTrue(stats.extra['table'] is not None) 45 | 46 | # clear temporary directory 47 | rs.close() 48 | -------------------------------------------------------------------------------- /tests/test_band_pca.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestBandPCA(TestCase): 8 | 9 | def test_band_pca(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | cfg.logger.log.debug('>>> test band pca') 15 | catalog = rs.bandset_catalog() 16 | file_list = ['S2_2020-01-01/S2_B02.tif', 'S2_2020-01-01/S2_B03.tif', 17 | 'S2_2020-01-01/S2_B04.tif'] 18 | data_path = Path(__file__).parent / 'data' 19 | catalog.create_bandset( 20 | file_list, wavelengths=['Sentinel-2'], bandset_number=1, 21 | root_directory=str(data_path) 22 | ) 23 | cfg.logger.log.debug('>>> test band PCA input BandSet') 24 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 25 | output = rs.band_pca(input_bands=catalog.get_bandset(1), 26 | output_path=temp) 27 | self.assertTrue(output.check) 28 | self.assertTrue(rs.files_directories.is_file(output.paths[0])) 29 | cfg.logger.log.debug('>>> test band PCA input BandSet with coordinate') 30 | coordinate_list = [230250, 4674550, 230320, 4674440] 31 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 32 | output = rs.band_pca(input_bands=catalog.get_bandset(1), 33 | output_path=temp, 34 | extent_list=coordinate_list) 35 | self.assertTrue(output.check) 36 | self.assertTrue(rs.files_directories.is_file(output.paths[0])) 37 | cfg.logger.log.debug('>>> test band PCA input BandSet with coordinate') 38 | bs = catalog.get_bandset(1) 39 | bs.box_coordinate_list = [230250, 4674550, 230320, 4674440] 40 | output = rs.band_pca(input_bands=catalog.get_bandset(1), 41 | extent_list=coordinate_list) 42 | self.assertTrue(output.check) 43 | self.assertTrue(rs.files_directories.is_file(output.paths[0])) 44 | 45 | # clear temporary directory 46 | rs.close() 47 | -------------------------------------------------------------------------------- /docs/source/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | =============== 3 | 4 | This section describes Remotior Sensus installation along with 5 | the required dependencies. 6 | 7 | 8 | Dependencies 9 | ____________ 10 | 11 | Remotior Sensus requires `GDAL`, `NumPy` and `SciPy` for most functionalities. 12 | Optionally, `scikit-learn` and `PyTorch` are required for machine learning. 13 | Python >= 3.10 is recommended. 14 | 15 | .. _Installation with Conda: 16 | 17 | Installation with Conda 18 | _______________________ 19 | 20 | Before installing Remotior Sensus please install the dependencies using 21 | a `Conda` environment (if you don't know `Conda` please read 22 | https://conda-forge.org/docs). 23 | For instance, you can use 24 | `Miniforge `_ 25 | to create a `Conda` environment. 26 | 27 | .. code-block:: console 28 | 29 | $ conda create -c conda-forge --name environment python=3.10 30 | Proceed ([y]/n)? y 31 | $ conda activate environment 32 | 33 | Install Remotior Sensus using `Conda` (the fundamental dependencies are also installed): 34 | 35 | .. code-block:: console 36 | 37 | $ conda install -c conda-forge remotior-sensus 38 | 39 | For machine learning functionalities run: 40 | 41 | .. code-block:: console 42 | 43 | $ conda install -c conda-forge remotior-sensus scikit-learn pytorch 44 | 45 | 46 | Installation in Linux 47 | _______________________ 48 | 49 | The suggested way to install Remotior Sensus is using `Conda` (see 50 | `Installation with Conda`_). 51 | 52 | Depending on the system, one could install the required dependencies as: 53 | 54 | .. code-block:: console 55 | 56 | $ sudo apt-get install python3-numpy python3-scipy gdal-bin 57 | 58 | For Remotior Sensus package installation use `pip`: 59 | 60 | .. code-block:: console 61 | 62 | $ pip install -U remotior-sensus 63 | 64 | Installation in OS X 65 | ____________________ 66 | 67 | The suggested way to install Remotior Sensus is using `Conda` (see 68 | `Installation with Conda`_). 69 | 70 | 71 | Installation in Windows 72 | _______________________ 73 | 74 | The suggested way to install Remotior Sensus is using `Conda` (see 75 | `Installation with Conda`_). 76 | 77 | 78 | Package installation 79 | ____________________ 80 | 81 | Given that dependencies are installed, for Remotior Sensus package 82 | installation use `pip`: 83 | 84 | .. code-block:: console 85 | 86 | $ pip install -U remotior-sensus 87 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | Remotior Sensus 2 | =============== 3 | 4 | .. image:: https://img.shields.io/badge/Website-darkgreen 5 | :target: https://fromgistors.blogspot.com/p/remotior-sensus.html 6 | 7 | .. image:: https://img.shields.io/badge/Documentation-blue 8 | :target: https://remotior-sensus.readthedocs.io 9 | 10 | .. image:: https://img.shields.io/badge/Bug%20reports-red 11 | :target: https://github.com/semiautomaticgit/remotior_sensus/issues 12 | 13 | .. image:: https://img.shields.io/pypi/v/remotior-sensus?label=PyPI%20version 14 | :target: https://pypi.org/project/remotior-sensus 15 | 16 | .. image:: https://img.shields.io/pypi/dm/remotior-sensus?label=PyPI%20downloads 17 | :target: https://pypi.org/project/remotior-sensus 18 | 19 | .. image:: https://img.shields.io/conda/v/conda-forge/remotior-sensus?label=Conda%20version 20 | :target: https://anaconda.org/conda-forge/remotior-sensus 21 | 22 | .. image:: https://img.shields.io/conda/d/conda-forge/remotior-sensus?label=Conda%20downloads 23 | :target: https://anaconda.org/conda-forge/remotior-sensus 24 | 25 | .. image:: https://img.shields.io/conda/l/conda-forge/remotior-sensus 26 | :target: https://www.gnu.org/licenses/ 27 | 28 | .. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.10038132.svg 29 | :target: https://doi.org/10.5281/zenodo.10038132 30 | 31 | .. image:: https://colab.research.google.com/assets/colab-badge.svg 32 | :target: https://remotior-sensus.readthedocs.io/en/latest/quickstart.html 33 | 34 | .. image:: _static/logo.png 35 | :width: 60pt 36 | :align: center 37 | 38 | 39 | Remotior Sensus (which is Latin for "a more remote sense") 40 | is a Python package that allows for the processing 41 | of remote sensing images and GIS data. 42 | 43 | **Source code:** https://github.com/semiautomaticgit/remotior_sensus 44 | 45 | **Bug reports:** https://github.com/semiautomaticgit/remotior_sensus/issues 46 | 47 | 48 | 49 | Documentation 50 | _____________ 51 | 52 | 53 | .. toctree:: 54 | :maxdepth: 3 55 | :titlesonly: 56 | 57 | introduction.rst 58 | installation.rst 59 | quickstart.rst 60 | basic_tutorials.rst 61 | tutorials.rst 62 | api_tools.rst 63 | api_core.rst 64 | api.rst 65 | changelog.rst 66 | 67 | `Except where otherwise noted, content of this work is licensed under a` 68 | `Creative Commons Attribution-ShareAlike 4.0 International License 69 | `_. 70 | -------------------------------------------------------------------------------- /tests/test_raster_reclassification.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | # noinspection PyProtectedMember 6 | from remotior_sensus.tools.raster_reclassification import ( 7 | unique_values_table, _import_reclassification_table 8 | ) 9 | 10 | 11 | class TestRasterReclassification(TestCase): 12 | 13 | def test_reclassification(self): 14 | rs = remotior_sensus.Session( 15 | n_processes=2, available_ram=1000, log_level=10 16 | ) 17 | cfg = rs.configurations 18 | data_path = Path(__file__).parent / 'data' 19 | p = str(data_path / 'S2_2020-01-01' / 'S2_B02.tif') 20 | cfg.logger.log.debug('>>> test unique values list') 21 | unique_list = unique_values_table(raster_path=p) 22 | self.assertGreater(len(unique_list), 0) 23 | unique_list = unique_values_table(raster_path=p, incremental=True) 24 | self.assertGreater(len(unique_list), 0) 25 | reclass_file = str(data_path / 'files' / 'reclass.csv') 26 | unique_list = _import_reclassification_table(csv_path=reclass_file) 27 | self.assertGreater(len(unique_list.extra['table']), 0) 28 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 29 | reclassification_2 = rs.raster_reclassification( 30 | raster_path=p, output_path=temp, 31 | reclassification_table=unique_list.extra['table'] 32 | ) 33 | self.assertTrue(rs.files_directories.is_file(reclassification_2.path)) 34 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 35 | reclassification = rs.raster_reclassification( 36 | raster_path=reclassification_2.path, output_path=temp, 37 | reclassification_table=[[1, -10], ['nan', 6000]] 38 | ) 39 | self.assertTrue(rs.files_directories.is_file(reclassification.path)) 40 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 41 | coordinate_list = [230250, 4674550, 230320, 4674440] 42 | reclassification = rs.raster_reclassification( 43 | raster_path=p, output_path=temp, extent_list=coordinate_list, 44 | reclassification_table=[['raster <= 3000', 1], ['raster > 500', 2]] 45 | ) 46 | self.assertTrue(rs.files_directories.is_file(reclassification.path)) 47 | 48 | # clear temporary directory 49 | rs.close() 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ -------------------------------------------------------------------------------- /tests/test_raster_vector.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | from remotior_sensus.util import raster_vector, files_directories 6 | 7 | 8 | class TestRasterVector(TestCase): 9 | 10 | def test_raster_vector(self): 11 | rs = remotior_sensus.Session( 12 | n_processes=2, available_ram=1000, log_level=10 13 | ) 14 | cfg = rs.configurations 15 | data_path = Path(__file__).parent / 'data' 16 | p = str(data_path / 'S2_2020-01-01' / 'S2_B02.tif') 17 | crs = raster_vector.get_crs(p) 18 | self.assertGreater(len(crs), 0) 19 | raster_list = [ 20 | str(data_path / 'S2_2020-01-01' / 'S2_B02.tif'), 21 | str(data_path / 'S2_2020-01-03' / 'S2_B02.tif') 22 | ] 23 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.vrt_suffix) 24 | cfg.logger.log.debug('>>> test create_virtual_raster') 25 | raster_vector.create_virtual_raster( 26 | input_raster_list=raster_list, output=temp, relative_to_vrt=False 27 | ) 28 | self.assertTrue(files_directories.is_file(temp)) 29 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.vrt_suffix) 30 | cfg.logger.log.debug('>>> test create_virtual_raster_2_mosaic') 31 | raster_vector.create_virtual_raster_2_mosaic( 32 | input_raster_list=raster_list, output=temp 33 | ) 34 | self.assertTrue(files_directories.is_file(temp)) 35 | p = str(data_path / 'S2_2020-01-01' / 'S2_B02.tif') 36 | cfg.logger.log.debug('>>> test read raster') 37 | n = raster_vector.get_number_bands(p) 38 | self.assertEqual(n, 1) 39 | # virtual raster of BandSet 40 | cfg.logger.log.debug('>>> test create_virtual_raster BandSet') 41 | # box coordinate list 42 | catalog = rs.bandset_catalog() 43 | file_list = ['S2_2020-01-01/S2_B02.tif', 'S2_2020-01-01/S2_B03.tif', 44 | 'S2_2020-01-01/S2_B04.tif'] 45 | coordinate_list = [230250, 4674550, 230320, 4674440] 46 | catalog.create_bandset( 47 | file_list, wavelengths=['Sentinel-2'], 48 | root_directory=str(data_path), box_coordinate_list=coordinate_list 49 | ) 50 | temp_2 = cfg.temp.temporary_file_path(name_suffix=cfg.vrt_suffix) 51 | raster_vector.create_virtual_raster( 52 | bandset=catalog.get(1), output=temp_2 53 | ) 54 | self.assertTrue(files_directories.is_file(temp_2)) 55 | 56 | # clear temporary directory 57 | rs.close() 58 | -------------------------------------------------------------------------------- /tests/test_progress.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import remotior_sensus 4 | import time 5 | 6 | 7 | class TestProgress(TestCase): 8 | 9 | def test_create(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | cfg.logger.log.debug('>>> test progress') 15 | 16 | cfg.progress.update( 17 | process=__name__.split('.')[-1].replace('_', ' '), 18 | message='starting', start=True 19 | ) 20 | for e in range(0, 100): 21 | time.sleep(0.02) 22 | cfg.progress.update( 23 | message='test message', 24 | step=int(100 * (e + 1) / 100), 25 | percentage=int(100 * (e + 1) / 100) 26 | ) 27 | cfg.progress.update(end=True) 28 | 29 | cfg.logger.log.debug('>>> test progress callback') 30 | rs.set(progress_callback=cfg.progress.print_progress) 31 | cfg.progress.update( 32 | process=__name__.split('.')[-1].replace('_', ' '), 33 | message='starting', start=True 34 | ) 35 | for e in range(0, 100): 36 | time.sleep(0.02) 37 | cfg.progress.update( 38 | message='test message', 39 | step=int(100 * (e + 1) / 100), 40 | percentage=int(100 * (e + 1) / 100) 41 | ) 42 | cfg.progress.update(end=True) 43 | 44 | cfg.logger.log.debug('>>> test progress smtp') 45 | smtp_user = None 46 | smtp_password = None 47 | smtp_server = None 48 | smtp_recipients = None 49 | rs.set(smtp_user=smtp_user, smtp_password=smtp_password, 50 | smtp_server=smtp_server, smtp_recipients=smtp_recipients, 51 | smtp_notification=True) 52 | cfg.progress.update( 53 | process=__name__.split('.')[-1].replace('_', ' '), 54 | message='starting', start=True 55 | ) 56 | for e in range(0, 100): 57 | time.sleep(0.02) 58 | cfg.progress.update( 59 | message='test message', 60 | step=int(100 * (e + 1) / 100), 61 | percentage=int(100 * (e + 1) / 100) 62 | ) 63 | cfg.progress.update(end=True) 64 | self.assertTrue(smtp_user is None) 65 | self.assertTrue(smtp_password is None) 66 | self.assertTrue(smtp_server is None) 67 | self.assertTrue(smtp_recipients is None) 68 | 69 | # clear temporary directory 70 | rs.close() 71 | -------------------------------------------------------------------------------- /src/remotior_sensus/util/dates_times.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | 18 | """ 19 | Tools to manage dates and times 20 | """ 21 | 22 | from datetime import datetime 23 | from remotior_sensus.core import configurations as cfg 24 | 25 | 26 | # get date string from name 27 | def date_string_from_directory_name(directory_name): 28 | date = False 29 | # format YYYY-MM-DD 30 | try: 31 | datetime.strptime(directory_name[-10:], '%Y-%m-%d') 32 | date = directory_name[-10:] 33 | except Exception as err: 34 | str(err) 35 | # format YYYYMMDD 36 | try: 37 | dir_part = directory_name.split('_') 38 | for d_p in dir_part: 39 | d_p_part = d_p.lower().split('t')[0] 40 | try: 41 | date_string = datetime.strptime(d_p_part, 42 | '%Y%m%d') 43 | d_p_part_string = date_string.strftime('%Y-%m-%d') 44 | date = d_p_part_string 45 | break 46 | except Exception as err: 47 | str(err) 48 | except Exception as err: 49 | str(err) 50 | cfg.logger.log.debug('date: %s' % date) 51 | return date 52 | 53 | 54 | # get time 55 | def get_time_string(): 56 | time = datetime.now().strftime('%Y%m%d_%H%M%S%f') 57 | return time 58 | 59 | 60 | # get date 61 | def get_date_string(): 62 | time = datetime.now().strftime('%m%d') 63 | return time 64 | 65 | 66 | # create date 67 | def create_date(string: str): 68 | # format YYYY-MM-DD 69 | try: 70 | date = datetime.strptime(string, '%Y-%m-%d') 71 | except Exception as err: 72 | cfg.logger.log.error(str(err)) 73 | date = None 74 | return date 75 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | 3 | # -- Path setup -------------------------------------------------------------- 4 | 5 | import os 6 | import sys 7 | 8 | sys.path.insert(0, os.path.abspath('../../src/')) 9 | 10 | # -- Project information ----------------------------------------------------- 11 | 12 | project = 'Remotior Sensus' 13 | copyright = '2022-2025, Luca Congedo' 14 | author = 'Luca Congedo' 15 | release = '0.5.2' 16 | version = '0.5.2.1' 17 | 18 | # -- General configuration --------------------------------------------------- 19 | 20 | master_doc = 'index' 21 | locale_dirs = ['locale/'] 22 | gettext_compact = False 23 | extensions = ['sphinx.ext.napoleon', 'sphinx.ext.doctest'] 24 | templates_path = ['_templates'] 25 | exclude_patterns = ['modules.rst', 'remotior_sensus.util.*'] 26 | autodoc_default_options = { 27 | 'special-members': '__init__', 28 | } 29 | pygments_style = 'sphinx' 30 | 31 | # -- Options for HTML output ------------------------------------------------- 32 | 33 | html_theme = 'sphinx_rtd_theme' 34 | html_show_sourcelink = False 35 | html_static_path = ['_static'] 36 | html_favicon = "_static/favicon.ico" 37 | html_show_sphinx = False 38 | html_css_files = ['html_css.css'] 39 | 40 | # -- Options for LaTeX output --------------------------------------------- 41 | 42 | latex_documents = [ 43 | ('index', 'Remotior_Sensus.tex', 'Remotior Sensus Documentation', 44 | 'Luca Congedo', 'manual'), 45 | ] 46 | latex_logo = '_static/logo.png' 47 | latex_use_parts = True 48 | latex_show_pagerefs = True 49 | latex_elements = { 50 | 'papersize': 'a4paper', 51 | 'sphinxsetup': """ 52 | pre_box-shadow=2pt 2pt, 53 | pre_background-TeXcolor={named}{white}, 54 | pre_box-shadow-TeXcolor={named}{black} 55 | """ 56 | } 57 | # -- Options for manual page output --------------------------------------- 58 | 59 | man_pages = [ 60 | ('index', 'remotior_sensus', 'Remotior Sensus Documentation', 61 | ['Luca Congedo'], 1) 62 | ] 63 | 64 | # -- Options for Texinfo output ------------------------------------------- 65 | 66 | texinfo_documents = [ 67 | ('index', 'Remotior_Sensus', 'Remotior Sensus Documentation', 68 | 'Luca Congedo', 'Remotior_Sensus', 69 | 'Software to process remote sensing and GIS data.', 70 | 'GIS and Remote Sensing'), 71 | ] 72 | 73 | # Napoleon settings 74 | napoleon_google_docstring = True 75 | napoleon_numpy_docstring = False 76 | napoleon_include_init_with_doc = False 77 | napoleon_include_private_with_doc = False 78 | napoleon_include_special_with_doc = True 79 | napoleon_use_admonition_for_examples = True 80 | napoleon_use_admonition_for_notes = False 81 | napoleon_use_admonition_for_references = False 82 | napoleon_use_ivar = False 83 | napoleon_use_param = True 84 | napoleon_use_rtype = True 85 | napoleon_preprocess_types = False 86 | napoleon_type_aliases = None 87 | napoleon_attr_annotations = True 88 | -------------------------------------------------------------------------------- /tests/test_band_spectral_distance.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestBandSpectralDistance(TestCase): 8 | 9 | def test_band_spectral_distance(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | catalog = rs.bandset_catalog() 15 | data_path = Path(__file__).parent / 'data' 16 | file_list_1 = ['S2_2020-01-03/S2_B02.tif', 'S2_2020-01-03/S2_B03.tif', 17 | 'S2_2020-01-03/S2_B04.tif'] 18 | file_list_2 = ['S2_2020-01-04/S2_B02.tif', 'S2_2020-01-04/S2_B03.tif', 19 | 'S2_2020-01-04/S2_B04.tif'] 20 | catalog.create_bandset( 21 | file_list_1, wavelengths=['Sentinel-2'], bandset_number=1, 22 | root_directory=str(data_path) 23 | ) 24 | catalog.create_bandset( 25 | file_list_2, wavelengths=['Sentinel-2'], bandset_number=2, 26 | root_directory=str(data_path) 27 | ) 28 | bandset_list = [catalog.get_bandset(1), catalog.get_bandset(2)] 29 | cfg.logger.log.debug('>>> test spectral distance') 30 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 31 | distance = rs.band_spectral_distance( 32 | input_bandsets=bandset_list, output_path=temp 33 | ) 34 | self.assertTrue(rs.files_directories.is_file(distance.path)) 35 | self.assertTrue(distance.check) 36 | cfg.logger.log.debug('>>> test spectral distance with catalog') 37 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 38 | distance = rs.band_spectral_distance( 39 | input_bandsets=[1, 2], output_path=temp, bandset_catalog=catalog 40 | ) 41 | self.assertTrue(rs.files_directories.is_file(distance.path)) 42 | self.assertTrue(distance.check) 43 | cfg.logger.log.debug('>>> test spectral distance using spectral angle') 44 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 45 | distance = rs.band_spectral_distance( 46 | input_bandsets=bandset_list, output_path=temp, 47 | algorithm_name=cfg.spectral_angle_mapping_a 48 | ) 49 | self.assertTrue(rs.files_directories.is_file(distance.path)) 50 | self.assertTrue(distance.check) 51 | cfg.logger.log.debug('>>> test spectral distance with threshold') 52 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 53 | distance = rs.band_spectral_distance( 54 | input_bandsets=bandset_list, output_path=temp, threshold=1000 55 | ) 56 | self.assertTrue(rs.files_directories.is_file(distance.path)) 57 | self.assertTrue(distance.check) 58 | 59 | # clear temporary directory 60 | rs.close() 61 | -------------------------------------------------------------------------------- /tests/test_cross_classification.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | from remotior_sensus.util import read_write_files, files_directories 6 | 7 | 8 | class TestCrossClassification(TestCase): 9 | 10 | def test_cross_classification(self): 11 | rs = remotior_sensus.Session( 12 | n_processes=2, available_ram=1000, log_level=10 13 | ) 14 | cfg = rs.configurations 15 | data_path = Path(__file__).parent / 'data' 16 | p1 = str(data_path / 'S2_2020-01-01' / 'S2_B04.tif') 17 | p2 = str(data_path / 'S2_2020-01-01' / 'S2_B02.tif') 18 | v = str(data_path / 'files' / 'roi.gpkg') 19 | cfg.logger.log.debug('>>> test cross_classification') 20 | coordinate_list = [230250, 4674550, 230320, 4674440] 21 | cross = rs.cross_classification( 22 | classification_path=p1, reference_path=v, vector_field='class', 23 | extent_list=coordinate_list 24 | ) 25 | self.assertTrue(files_directories.is_file(cross.paths[0])) 26 | cross = rs.cross_classification( 27 | classification_path=p1, reference_path=p2, cross_matrix=True 28 | ) 29 | raster, text = cross.paths 30 | table = read_write_files.open_text_file(text) 31 | table_f = read_write_files.format_csv_new_delimiter( 32 | table, cfg.tab_delimiter 33 | ) 34 | self.assertGreater(len(table_f), 0) 35 | table_split = table.split(cfg.new_line) 36 | self.assertGreater(int(table_split[1][0]), 0) 37 | cfg.logger.log.debug('>>> test cross_classification accuracy matrix') 38 | cross = rs.cross_classification( 39 | classification_path=p1, reference_path=p2, error_matrix=True 40 | ) 41 | raster, text = cross.paths 42 | table = read_write_files.open_text_file(text) 43 | table_f = read_write_files.format_csv_new_delimiter( 44 | table, cfg.tab_delimiter 45 | ) 46 | self.assertGreater(len(table_f), 0) 47 | table_split = table.split(cfg.new_line) 48 | self.assertGreater(int(table_split[1][0]), 0) 49 | cfg.logger.log.debug('>>> test cross_classification regression') 50 | temp2 = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 51 | cross = rs.cross_classification( 52 | classification_path=p1, reference_path=p2, output_path=temp2, 53 | regression_raster=True 54 | ) 55 | raster, text = cross.paths 56 | table = read_write_files.open_text_file(text) 57 | table_f = read_write_files.format_csv_new_delimiter( 58 | table, cfg.tab_delimiter 59 | ) 60 | self.assertGreater(len(table_f), 0) 61 | table_split = table.split(cfg.new_line) 62 | self.assertGreater(int(table_split[1][0]), 0) 63 | 64 | # clear temporary directory 65 | rs.close() 66 | -------------------------------------------------------------------------------- /tests/data/files/landsat_8_metadata_mtl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | L2SP 5 | 02 6 | T1 7 | 8 | 9 | LANDSAT_8 10 | OLI_TIRS 11 | 2021-01-01 12 | 20.7 13 | 0.98 14 | 15 | 16 | 2.75e-05 17 | 2.75e-05 18 | 2.75e-05 19 | 2.75e-05 20 | 2.75e-05 21 | 2.75e-05 22 | 2.75e-05 23 | -0.2 24 | -0.2 25 | -0.2 26 | -0.2 27 | -0.2 28 | -0.2 29 | -0.2 30 | 31 | 32 | 0.003418 33 | 149.0 34 | 35 | 36 | 1.2160E-02 37 | 1.2490E-02 38 | 1.1476E-02 39 | 9.7271E-03 40 | 5.9677E-03 41 | 1.4908E-03 42 | 5.0339E-04 43 | 1.1011E-02 44 | 2.4756E-03 45 | 3.8000E-04 46 | 3.4900E-04 47 | -60.80157 48 | -62.44802 49 | -57.38151 50 | -48.63534 51 | -29.83850 52 | -7.45416 53 | -2.51696 54 | -55.05358 55 | -12.37815 56 | 0.10000 57 | 0.10000 58 | 59 | 60 | -------------------------------------------------------------------------------- /tests/test_band_clip.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestBandClip(TestCase): 8 | 9 | def test_band_clip(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | cfg.logger.log.debug('>>> test band clip') 15 | catalog = rs.bandset_catalog() 16 | file_list = ['S2_2020-01-01/S2_B02.tif', 'S2_2020-01-01/S2_B03.tif', 17 | 'S2_2020-01-01/S2_B04.tif'] 18 | data_path = Path(__file__).parent / 'data' 19 | catalog.create_bandset( 20 | file_list, wavelengths=['Sentinel-2'], bandset_number=1, 21 | root_directory=str(data_path) 22 | ) 23 | cfg.logger.log.debug('>>> test band clip input BandSet') 24 | # box coordinate list 25 | extent_list = [230250, 4674510, 230320, 4674440] 26 | output = rs.band_clip(input_bands=1, 27 | output_path=cfg.temp.dir, prefix='clip_', 28 | extent_list=extent_list, bandset_catalog=catalog) 29 | self.assertTrue(output.check) 30 | # box coordinate list 31 | extent_list = [230250, 4674510, 230320, 4674440] 32 | output = rs.band_clip(input_bands=catalog.get_bandset(1), 33 | output_path=cfg.temp.dir, prefix='clip3_', 34 | extent_list=extent_list) 35 | self.assertTrue(output.check) 36 | cfg.logger.log.debug('>>> test band clip input BandSet multiband') 37 | catalog.create_bandset( 38 | [str(data_path / 'S2_2020-01-05' / 'S2_2020-01-05.tif')], 39 | wavelengths=['Sentinel-2'], bandset_number=2 40 | ) 41 | output = rs.band_clip(input_bands=catalog.get_bandset(2), 42 | output_path=cfg.temp.dir, prefix='clip_b_', 43 | extent_list=extent_list) 44 | self.assertTrue(output.check) 45 | # box coordinate list 46 | extent_list = [230250, 4674510, 230320, 4674440] 47 | output = rs.band_clip(input_bands=catalog.get_bandset(1), 48 | output_path=cfg.temp.dir, prefix='clip2_', 49 | extent_list=extent_list, virtual_output=True) 50 | self.assertTrue(output.check) 51 | self.assertTrue(rs.files_directories.is_file(output.paths[0])) 52 | 53 | v = str(data_path / 'files' / 'roi.gpkg') 54 | output = rs.band_clip(input_bands=catalog.get_bandset(1), 55 | output_path=cfg.temp.dir, prefix='clip3_', 56 | vector_path=v) 57 | self.assertTrue(output.check) 58 | self.assertTrue(rs.files_directories.is_file(output.paths[0])) 59 | v = str(data_path / 'files' / 'roi.gpkg') 60 | output = rs.band_clip(input_bands=catalog.get_bandset(1), 61 | output_path=cfg.temp.dir, prefix='clip4_', 62 | vector_path=v, vector_field='class') 63 | self.assertTrue(output.check) 64 | self.assertTrue(rs.files_directories.is_file(output.paths[0])) 65 | 66 | # clear temporary directory 67 | rs.close() 68 | -------------------------------------------------------------------------------- /src/remotior_sensus/util/read_write_files.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | 18 | """ 19 | Tools to manage text files 20 | """ 21 | 22 | from remotior_sensus.core import configurations as cfg 23 | from remotior_sensus.util.files_directories import ( 24 | create_parent_directory as file_create_parent_directory 25 | ) 26 | 27 | 28 | # write file 29 | def write_file(data, output_path, create_parent_directory=True, mode='w'): 30 | cfg.logger.log.debug('output_path: %s' % output_path) 31 | if create_parent_directory: 32 | file_create_parent_directory(output_path) 33 | # save combination to table 34 | with open(output_path, mode) as output_file: 35 | output_file.write(data) 36 | return output_path 37 | 38 | 39 | # open text file 40 | def open_text_file(input_path): 41 | with open(input_path, 'r') as f: 42 | text = f.read() 43 | cfg.logger.log.debug('input_path: %s' % str(input_path)) 44 | return text 45 | 46 | 47 | # format csv file to new delimiter 48 | def format_csv_new_delimiter(table, delimiter): 49 | table_f = table.replace(cfg.comma_delimiter, delimiter) 50 | return table_f 51 | 52 | 53 | # format csv file to html table 54 | def format_csv_text_html(table): 55 | text = ['', cfg.new_line, '', cfg.new_line, 56 | '', ''' 57 | ''', cfg.new_line, '', cfg.new_line, '', cfg.new_line, 66 | cfg.tab_delimiter, '', cfg.new_line] 67 | count = 0 68 | for line in table.split(cfg.new_line): 69 | if count == 0: 70 | text.append(cfg.tab_delimiter) 71 | text.append('') 72 | for record in line.split(cfg.comma_delimiter): 73 | text.append('' % record) 74 | text.append('%s%s' % (cfg.new_line, cfg.tab_delimiter)) 75 | elif len(line) > 0: 76 | text.append('') 77 | for record in line.split(cfg.comma_delimiter): 78 | text.append('' % record) 79 | text.append('%s%s' % (cfg.new_line, cfg.tab_delimiter)) 80 | count += 1 81 | text.append('
%s
%s
') 82 | text.append(cfg.new_line) 83 | text.append('') 84 | text.append(cfg.new_line) 85 | text.append('') 86 | html = ''.join(text) 87 | return html 88 | -------------------------------------------------------------------------------- /tests/test_band_mosaic.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestMosaicBands(TestCase): 8 | 9 | def test_mosaic_bands(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | catalog = rs.bandset_catalog() 15 | data_path = Path(__file__).parent / 'data' 16 | file_list = [ 17 | str(data_path / 'S2_2020-01-01' / 'S2_B02.tif'), 18 | str(data_path / 'S2_2020-01-02' / 'S2_B02.tif') 19 | ] 20 | temp = cfg.temp.dir 21 | cfg.logger.log.debug('>>> test mosaic') 22 | mosaic = rs.mosaic(file_list, temp) 23 | self.assertTrue(rs.files_directories.is_file(mosaic.paths[0])) 24 | self.assertTrue(mosaic.check) 25 | file_list_1 = ['S2_2020-01-01/S2_B02.tif', 'S2_2020-01-01/S2_B03.tif', 26 | 'S2_2020-01-01/S2_B04.tif'] 27 | file_list_2 = ['S2_2020-01-02/S2_B02.tif', 'S2_2020-01-02/S2_B03.tif', 28 | 'S2_2020-01-02/S2_B04.tif'] 29 | file_list_3 = ['S2_2020-01-03/S2_B02.tif', 'S2_2020-01-03/S2_B03.tif', 30 | 'S2_2020-01-03/S2_B04.tif'] 31 | catalog.create_bandset( 32 | file_list_1, wavelengths=['Sentinel-2'], bandset_number=1, 33 | root_directory=str(data_path) 34 | ) 35 | catalog.create_bandset( 36 | file_list_2, wavelengths=['Sentinel-2'], bandset_number=2, 37 | root_directory=str(data_path) 38 | ) 39 | catalog.create_bandset( 40 | file_list_3, wavelengths=['Sentinel-2'], bandset_number=3, 41 | root_directory=str(data_path) 42 | ) 43 | bandset_list = [catalog.get_bandset(1), catalog.get_bandset(2), 44 | catalog.get_bandset(3)] 45 | cfg.logger.log.debug('>>> test mosaic BandSet') 46 | mosaic = rs.mosaic(bandset_list, temp) 47 | self.assertTrue(rs.files_directories.is_file(mosaic.paths[0])) 48 | self.assertTrue(mosaic.check) 49 | bandset_list = [1, 2] 50 | mosaic = rs.mosaic( 51 | bandset_list, output_path=temp, bandset_catalog=catalog 52 | ) 53 | self.assertTrue(rs.files_directories.is_file(mosaic.paths[0])) 54 | self.assertTrue(mosaic.check) 55 | 56 | band_list_1 = [ 57 | str(data_path / 'S2_2020-01-01' / 'S2_B02.tif'), 58 | str(data_path / 'S2_2020-01-02' / 'S2_B02.tif'), 59 | str(data_path / 'S2_2020-01-02' / 'S2_B02.tif') 60 | ] 61 | band_list_2 = [ 62 | str(data_path / 'S2_2020-01-01' / 'S2_B03.tif'), 63 | str(data_path / 'S2_2020-01-02' / 'S2_B03.tif'), 64 | str(data_path / 'S2_2020-01-02' / 'S2_B03.tif') 65 | ] 66 | band_list_3 = [ 67 | str(data_path / 'S2_2020-01-01' / 'S2_B04.tif'), 68 | str(data_path / 'S2_2020-01-02' / 'S2_B04.tif'), 69 | str(data_path / 'S2_2020-01-02' / 'S2_B04.tif') 70 | ] 71 | band_list = [band_list_1, band_list_2, band_list_3] 72 | mosaic = rs.mosaic( 73 | band_list, output_path=temp, bandset_catalog=catalog, 74 | prefix='prefix', output_name='output_name' 75 | ) 76 | self.assertTrue(rs.files_directories.is_file(mosaic.paths[0])) 77 | self.assertTrue(mosaic.check) 78 | 79 | # clear temporary directory 80 | rs.close() 81 | -------------------------------------------------------------------------------- /tests/test_preprocess_products.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestPreprocessProducts(TestCase): 8 | 9 | def test_preprocess_products(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | # test Sentinel-2 15 | cfg.logger.log.debug('>>> test sentinel-2') 16 | data_path = Path(__file__).parent / 'data' 17 | files_path = data_path / 'files' 18 | table = rs.preprocess_products.create_product_table( 19 | input_path=str(data_path / 'S2_2020-01-01'), 20 | metadata_file_path=str( 21 | files_path / 'sentinel_2_metadata_test_l1c.xml' 22 | ), 23 | nodata_value=102 24 | ) 25 | out_1 = rs.preprocess_products.perform_preprocess( 26 | product_table=table, output_path=cfg.temp.dir + '/test_1', 27 | dos1_correction=False 28 | ) 29 | self.assertTrue(out_1.check) 30 | # test DOS1 31 | cfg.logger.log.debug('>>> test DOS1') 32 | out_2 = rs.preprocess_products.perform_preprocess( 33 | product_table=table, output_path=cfg.temp.dir + '/test_2', 34 | dos1_correction=True 35 | ) 36 | self.assertTrue(out_2.check) 37 | # test Landsat 38 | cfg.logger.log.debug('>>> test Landsat') 39 | table2 = rs.preprocess_products.create_product_table( 40 | input_path=str(data_path / 'L8_2020-01-01'), 41 | metadata_file_path=str(files_path / 'landsat_8_metadata_mtl.xml') 42 | ) 43 | out_3 = rs.preprocess_products.perform_preprocess( 44 | product_table=table2, output_path=cfg.temp.dir + '/test_3' 45 | ) 46 | self.assertTrue(out_3.check) 47 | 48 | table3 = rs.preprocess_products.create_product_table( 49 | input_path=str(data_path / 'L8_2020-01-01'), 50 | metadata_file_path=str(files_path / 'landsat_5_metadata_mtl.xml') 51 | ) 52 | out_4 = rs.preprocess_products.perform_preprocess( 53 | product_table=table3, output_path=cfg.temp.dir + '/test_4', 54 | dos1_correction=True 55 | ) 56 | self.assertTrue(out_4.check) 57 | # create BandSet Catalog 58 | catalog = rs.bandset_catalog() 59 | out_5 = rs.preprocess_products.preprocess( 60 | input_path=str(data_path / 'L5_2020-01-01'), 61 | output_path=cfg.temp.dir + '/test_5', 62 | metadata_file_path=str(files_path / 'landsat_5_metadata_mtl.xml'), 63 | dos1_correction=True, add_bandset=True, bandset_catalog=catalog 64 | ) 65 | self.assertTrue(out_5.check) 66 | self.assertEqual(catalog.get_bandset_count(), 2) 67 | self.assertEqual(catalog.get_bandset(2).get_band_count(), 3) 68 | # create BandSet Catalog 69 | catalog2 = rs.bandset_catalog() 70 | out_6 = rs.preprocess_products.preprocess( 71 | input_path=str(data_path / 'L5_2020-01-01'), 72 | output_path=cfg.temp.dir + '/test_5', 73 | metadata_file_path=str(files_path / 'landsat_5_metadata_mtl.xml'), 74 | dos1_correction=True, add_bandset=False, bandset_catalog=catalog2 75 | ) 76 | self.assertTrue(out_6.check) 77 | self.assertEqual(catalog.get_bandset_count(), 2) 78 | self.assertEqual(catalog.get_bandset(2).get_band_count(), 3) 79 | 80 | # clear temporary directory 81 | rs.close() 82 | -------------------------------------------------------------------------------- /src/remotior_sensus/core/temporary.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | 18 | import random 19 | import tempfile 20 | 21 | from remotior_sensus.core import configurations as cfg 22 | from remotior_sensus.util import files_directories, dates_times, shared_tools 23 | 24 | 25 | class Temporary(object): 26 | 27 | def __init__(self, temp_dir=None): 28 | self.dir = temp_dir 29 | 30 | # create root temporary directory 31 | @classmethod 32 | def create_root_temporary_directory(cls, prefix=None, directory=None): 33 | times = dates_times.get_date_string() 34 | t_dir = tempfile.mkdtemp( 35 | prefix='{}_{}'.format(prefix, times), dir=directory 36 | ) 37 | return cls(t_dir) 38 | 39 | # clear root temporary directory 40 | def clear(self): 41 | if self.dir is not None: 42 | remove_root_temporary_directory(self.dir) 43 | self.dir = None 44 | return self.dir 45 | 46 | # create temporary directory 47 | def create_temporary_directory(self): 48 | times = dates_times.get_time_string() 49 | directory = shared_tools.join_path( 50 | self.dir, '{}{}'.format('t', times) 51 | ).replace('\\', '/') 52 | files_directories.create_directory(directory) 53 | return directory 54 | 55 | # create temporary file path 56 | def temporary_file_path(self, name_suffix=None, name_prefix=None, 57 | name=None, directory=None): 58 | times = dates_times.get_time_string() 59 | if name is None: 60 | r = str(random.randint(0, 10000)) 61 | name = 't{}_{}'.format(times, r) 62 | else: 63 | directory = shared_tools.join_path( 64 | self.dir, '{}{}'.format('t', times) 65 | ).replace('\\', '/') 66 | files_directories.create_directory(directory) 67 | if name_suffix is not None: 68 | name = '%s%s' % (name, name_suffix) 69 | if name_prefix is not None: 70 | name = '%s%s' % (name_prefix, name) 71 | if directory is None: 72 | directory = self.dir 73 | path = shared_tools.join_path(directory, name).replace('\\', '/') 74 | return path 75 | 76 | # create temporary raster file path 77 | def temporary_raster_path( 78 | self, name=None, name_suffix=None, name_prefix=None, 79 | extension='.tif' 80 | ): 81 | file_path = self.temporary_file_path( 82 | name_suffix=name_suffix, name_prefix=name_prefix, name=name 83 | ) 84 | path = '%s%s' % (file_path, extension) 85 | return path 86 | 87 | 88 | # remove root temporary directory 89 | def remove_root_temporary_directory(directory): 90 | # close log handlers 91 | try: 92 | for h in cfg.logger.log.handlers: 93 | h.close() 94 | except Exception as err: 95 | str(err) 96 | try: 97 | cfg.logger.log.handlers = [] 98 | except Exception as err: 99 | str(err) 100 | files_directories.remove_directory(directory) 101 | return directory 102 | -------------------------------------------------------------------------------- /docs/source/changelog.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | =============== 3 | 4 | v0.5.2 5 | ________ 6 | 7 | * Performance improvement for the tool "Vector to raster" with the method 8 | area_based. 9 | * Improvement of the multiprocess iterator. 10 | * Minor fixes 11 | 12 | v0.5.1 13 | ________ 14 | 15 | * Fixed issue with the tool "Vector to raster" where a few polygons were 16 | randomly skipped using the method area_based. 17 | * Minor fixes 18 | 19 | v0.5.0 20 | ________ 21 | 22 | * New tool "Raster label" for calculating the area of contiguous 23 | patches in a raster. The output is a raster where each pixel value represents 24 | the pixel count of the patch thereof. 25 | * Performance improvement for the tool "Vector to raster" with the method 26 | area_based. 27 | * Minor fixes 28 | 29 | v0.4.4 30 | ________ 31 | 32 | * Changed raster_zonal_stats to accept raster input as reference_path 33 | * Fixed handling nan value as nodata 34 | 35 | v0.4.3 36 | ________ 37 | 38 | * First experimental implementation of Pytorch for band_calc 39 | * Minor fixes 40 | 41 | v0.4.2 42 | ________ 43 | 44 | * Minor fixes 45 | 46 | v0.4.1 47 | ________ 48 | 49 | * Fixed preprocessing calculation 50 | * Minor fixes 51 | 52 | v0.4.0 53 | ________ 54 | 55 | * Added tool "Band clustering" for unsupervised K-means classification of 56 | bandset 57 | * Added tool "Raster edit" for direct editing of pixel values based on vector 58 | * Added tool "Raster zonal stats" for calculating statistics of a raster 59 | intersecting a vector. 60 | * Improved the NoData handling for multiprocess calculation 61 | * In "Band clip", "Band dilation", "Band erosion", "Band sieve", 62 | "Band neighbor", "Band resample" added the option multiple_resolution to 63 | keep original resolution of individual rasters, or use the resolution of the 64 | first raster for all the bands 65 | * In "Cross classification" fixed area based accuracy and added kappa hat 66 | metric 67 | * In "Band combination" added option no_raster_output to avoid the creation of 68 | output raster, producing only the table of combinations 69 | * In "Band calc" replaced nanpercentile with optimized calculation function 70 | * Improved extraction of ROIs in "Band classification" 71 | * Minor bug fixing and removed Requests dependency 72 | 73 | v0.3.5 74 | ________ 75 | 76 | * Fixed Copernicus access token error 77 | * Fixed automatic band wavelength definition in BandSet 78 | 79 | v0.3.04 80 | ________ 81 | 82 | * Fixed Jupyter interface 83 | 84 | v0.3.03 85 | ________ 86 | 87 | * Fixed Jupyter interface 88 | 89 | v0.3.02 90 | ________ 91 | 92 | * Fixed Jupyter interface 93 | 94 | v0.3.01 95 | ________ 96 | 97 | * Added functions for interactive interface in Jupyter environment 98 | * Fixed Sentinel-2 band 8A identification in preprocess products 99 | 100 | v0.2.01 101 | ________ 102 | 103 | * In Download Products added the functions to search and download Collections 104 | from Microsoft Planetary Computer: Sentinel-2, Landsat, ASTER, 105 | MODIS Surface Reflectance 8-Day, and Copernicus DEM 106 | 107 | 108 | v0.1.24 109 | ________ 110 | 111 | * Fixed band calc calculation with multiband raster as bandset 112 | * Fixed preview path for Copernicus products 113 | 114 | v0.1.23 115 | ________ 116 | 117 | * Minor fixes 118 | 119 | v0.1.22 120 | ________ 121 | 122 | * Fixed prepare input function 123 | * Fixed logger for multiprocess 124 | 125 | 126 | v0.1.21 127 | ________ 128 | 129 | * Fixed requirements 130 | 131 | 132 | v0.1.20 133 | ________ 134 | 135 | * Fixed Copernicus search and download service 136 | 137 | 138 | v0.1.19 139 | ________ 140 | 141 | * Fixed Copernicus search and download service 142 | 143 | v0.1.18 144 | ________ 145 | 146 | * Added Copernicus download service from 147 | https://catalogue.dataspace.copernicus.eu 148 | if copernicus_user and copernicus_password are provided. 149 | 150 | v0.1.17 151 | ________ 152 | 153 | * Fixed spectral signature calculation for multiband raster 154 | * Fixed closing multiprocess at exit 155 | 156 | v0.1.16 157 | ________ 158 | 159 | * Fixed issue in block size calculation for multiprocess in case of large 160 | input raster and low RAM; 161 | * Fixed management of bandsets using multiband rasters; 162 | * Minor fixes to multiprocess download; -------------------------------------------------------------------------------- /tests/test_processor.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | import numpy as np 3 | from pathlib import Path 4 | import multiprocessing 5 | from remotior_sensus.core import processor 6 | import remotior_sensus 7 | # noinspection PyProtectedMember 8 | from remotior_sensus.core.multiprocess_manager import ( 9 | _calculate_block_size, _compute_raster_pieces 10 | ) 11 | from remotior_sensus.core.processor_functions import ( 12 | reclassify_raster, band_calculation 13 | ) 14 | 15 | 16 | class TestProcessor(TestCase): 17 | 18 | def test_processor(self): 19 | rs = remotior_sensus.Session( 20 | n_processes=2, available_ram=1000, log_level=10 21 | ) 22 | cfg = rs.configurations 23 | cfg.logger.log.debug('>>> test reclassification') 24 | process_parameters = [ 25 | 1, cfg.temp, 500, cfg.gdal_path, multiprocessing.Manager().Queue(), 26 | cfg.refresh_time, cfg.memory_unit_array_12, cfg.log_level, 27 | None, None 28 | ] 29 | data_path = Path(__file__).parent / 'data' 30 | raster_path = str(data_path / 'S2_2020-01-01' / 'S2_B02.tif') 31 | calc_datatype = [np.float32] 32 | input_nodata_as_value = False 33 | use_value_as_nodata = [1] 34 | (raster_x_size, raster_y_size, block_size_x, block_size_y, 35 | list_range_x, list_range_y, tot_blocks, 36 | number_of_bands) = _calculate_block_size( 37 | raster_path, 1, cfg.memory_unit_array_12, 0, 38 | available_ram=100 39 | ) 40 | # compute raster pieces 41 | pieces = _compute_raster_pieces( 42 | raster_x_size, raster_y_size, block_size_x, block_size_y, 43 | list_range_y, 1, separate_bands=False, 44 | unique_section=True 45 | ) 46 | # use raster files directly (not vrt) 47 | input_parameters = [[[raster_path]], calc_datatype, None, 48 | pieces[0], None, None, 49 | use_value_as_nodata, None, 50 | input_nodata_as_value, None, 51 | 0, None, True] 52 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 53 | compress = cfg.raster_compression 54 | compress_format = cfg.raster_compression_format 55 | any_nodata_mask = False 56 | output_nodata_value = -10 57 | keep_output_array = True 58 | keep_output_argument = False 59 | output_parameters = [[temp], [cfg.raster_data_type], 60 | compress, compress_format, any_nodata_mask, 61 | [output_nodata_value], [1], 62 | keep_output_array, keep_output_argument] 63 | dtype_list = [(cfg.old_value, 'U1024'), (cfg.new_value, 'U1024')] 64 | function = reclassify_raster 65 | function_argument = np.array( 66 | ([('1', '2'), ('1110', '3')]), dtype=dtype_list 67 | ) 68 | function_variable = cfg.variable_raster_name 69 | (output_array_list, out_files, proc_error, 70 | logger) = processor.function_initiator( 71 | process_parameters, input_parameters, output_parameters, 72 | function, [function_argument], [function_variable], 73 | False, False, False, 74 | False, 75 | ) 76 | self.assertEqual(output_array_list[0][0][0, 0], output_nodata_value) 77 | self.assertTrue(rs.files_directories.is_file(out_files[0][0])) 78 | cfg.logger.log.debug('>>> test band calc') 79 | function = band_calculation 80 | function_argument = '_array_function_placeholder * 2' 81 | function_variable = cfg.variable_raster_name 82 | (output_array_list, out_files, proc_error, 83 | logger) = processor.function_initiator( 84 | process_parameters, input_parameters, output_parameters, 85 | function, [function_argument], [function_variable], 86 | False, False, False, 87 | False 88 | ) 89 | self.assertEqual(output_array_list[0][0][0, 0], output_nodata_value) 90 | self.assertTrue(rs.files_directories.is_file(out_files[0][0])) 91 | 92 | # clear temporary directory 93 | rs.close() 94 | -------------------------------------------------------------------------------- /tests/test_band_combination.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | from remotior_sensus.util import read_write_files 6 | 7 | 8 | class TestBandCombination(TestCase): 9 | 10 | def test_band_combination(self): 11 | rs = remotior_sensus.Session( 12 | n_processes=2, available_ram=1000, log_level=10 13 | ) 14 | cfg = rs.configurations 15 | cfg.logger.log.debug('>>> test band combination') 16 | catalog = rs.bandset_catalog() 17 | file_list = ['S2_2020-01-01/S2_B02.tif', 'S2_2020-01-01/S2_B03.tif', 18 | 'S2_2020-01-01/S2_B04.tif'] 19 | date = '2021-01-01' 20 | data_path = Path(__file__).parent / 'data' 21 | catalog.create_bandset( 22 | file_list, wavelengths=['Sentinel-2'], date=date, bandset_number=1, 23 | root_directory=str(data_path) 24 | ) 25 | cfg.logger.log.debug('>>> test band combination input BandSet') 26 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 27 | combination = rs.band_combination( 28 | input_bands=catalog.get_bandset(1), output_path=temp 29 | ) 30 | raster, text = combination.paths 31 | table = read_write_files.open_text_file(text) 32 | table_f = read_write_files.format_csv_new_delimiter( 33 | table, cfg.tab_delimiter 34 | ) 35 | self.assertGreater(len(table_f), 0) 36 | cfg.logger.log.debug('>>> test band combination no_raster_output') 37 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 38 | combination = rs.band_combination( 39 | input_bands=catalog.get_bandset(1), output_path=temp, 40 | no_raster_output=True 41 | ) 42 | raster, text = combination.paths 43 | table = read_write_files.open_text_file(text) 44 | table_f = read_write_files.format_csv_new_delimiter( 45 | table, cfg.tab_delimiter 46 | ) 47 | self.assertGreater(len(table_f), 0) 48 | cfg.logger.log.debug('>>> test band combination input multiband') 49 | catalog.create_bandset( 50 | file_list, wavelengths=['Sentinel-2'], date=date, bandset_number=1, 51 | root_directory=str(data_path) 52 | ) 53 | catalog.create_bandset( 54 | [str(data_path / 'S2_2020-01-05' / 'S2_2020-01-05.tif')], 55 | bandset_number=2 56 | ) 57 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 58 | combination = rs.band_combination( 59 | input_bands=catalog.get_bandset(2), output_path=temp 60 | ) 61 | raster, text = combination.paths 62 | table = read_write_files.open_text_file(text) 63 | table_f = read_write_files.format_csv_new_delimiter( 64 | table, cfg.tab_delimiter 65 | ) 66 | self.assertGreater(len(table_f), 0) 67 | cfg.logger.log.debug('>>> test band combination input BandSet number') 68 | bs = catalog.get_bandset(1) 69 | bs.box_coordinate_list = [230250, 4674550, 230320, 4674440] 70 | combination = rs.band_combination( 71 | input_bands=1, bandset_catalog=catalog 72 | ) 73 | raster, text = combination.paths 74 | table = read_write_files.open_text_file(text) 75 | table_f = read_write_files.format_csv_new_delimiter( 76 | table, cfg.tab_delimiter 77 | ) 78 | self.assertGreater(len(table_f), 0) 79 | temp = cfg.temp.temporary_file_path(name_suffix=cfg.tif_suffix) 80 | file_list = [ 81 | str(data_path / 'S2_2020-01-01' / 'S2_B02.tif'), 82 | str(data_path / 'S2_2020-01-01' / 'S2_B03.tif'), 83 | str(data_path / 'S2_2020-01-01' / 'S2_B04.tif') 84 | ] 85 | cfg.logger.log.debug('>>> test band combination input file list') 86 | combination = rs.band_combination(input_bands=file_list, 87 | output_path=temp) 88 | raster, text = combination.paths 89 | table = read_write_files.open_text_file(text) 90 | table_f = read_write_files.format_csv_new_delimiter( 91 | table, cfg.tab_delimiter 92 | ) 93 | self.assertGreater(len(table_f), 0) 94 | table_split = table.split(cfg.new_line) 95 | self.assertGreater(int(table_split[1][0]), 0) 96 | cfg.logger.log.debug('>>> test band combination without output table') 97 | combination = rs.band_combination( 98 | input_bands=file_list, output_table=False 99 | ) 100 | combinations_array = combination.extra['combinations'] 101 | self.assertGreater(combinations_array.shape[0], 1) 102 | 103 | # clear temporary directory 104 | rs.close() 105 | -------------------------------------------------------------------------------- /src/remotior_sensus/util/plot_tools.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | 18 | """ 19 | Tools to manage plots 20 | """ 21 | 22 | try: 23 | import matplotlib.pyplot as plt 24 | import matplotlib.pyplot as mpl_plot 25 | except Exception as error: 26 | str(error) 27 | 28 | 29 | # prepare plot 30 | def prepare_plot(x_label=None, y_label=None): 31 | if x_label is None: 32 | x_label = 'Wavelength' 33 | if y_label is None: 34 | y_label = 'Values' 35 | figure, ax = plt.subplots() 36 | # Set empty ticks 37 | ax.set_xticks([]) 38 | ax.set_yticks([]) 39 | ax.set_aspect('auto') 40 | ax.grid('on') 41 | ax.set_xlabel(x_label) 42 | ax.set_ylabel(y_label) 43 | return ax 44 | 45 | 46 | # prepare plot 47 | def prepare_scatter_plot(x_label=None, y_label=None): 48 | if x_label is None: 49 | x_label = 'Band X' 50 | if y_label is None: 51 | y_label = 'Band Y' 52 | figure, ax = plt.subplots() 53 | # Set empty ticks 54 | ax.set_xticks([]) 55 | ax.set_yticks([]) 56 | ax.set_aspect('auto') 57 | ax.grid('on') 58 | ax.set_xlabel(x_label) 59 | ax.set_ylabel(y_label) 60 | return ax 61 | 62 | 63 | # add list of values to plot 64 | def add_lines_to_plot( 65 | name_list, wavelength_list, value_list, color_list, 66 | legend_max_chars=15 67 | ): 68 | plots = [] 69 | plot_names = [] 70 | v_lines = [] 71 | wavelength_min = 1000000 72 | wavelength_max = 0 73 | value_min = 10000000 74 | value_max = 0 75 | for _id in range(len(name_list)): 76 | plot, = plt.plot( 77 | wavelength_list[_id], value_list[_id], color_list[_id] 78 | ) 79 | v_lines.extend(wavelength_list[_id]) 80 | wavelength_min = min(min(wavelength_list[_id]), wavelength_min) 81 | wavelength_max = max(max(wavelength_list[_id]), wavelength_max) 82 | value_min = min(min(value_list[_id]), value_min) 83 | value_max = max(max(value_list[_id]), value_max) 84 | plots.append(plot) 85 | plot_names.append(name_list[_id][:legend_max_chars]) 86 | x_min = wavelength_min 87 | x_ticks = [x_min] 88 | for x in range(10): 89 | x_min += (wavelength_max - wavelength_min) / 10 90 | x_ticks.append(x_min) 91 | y_min = value_min 92 | y_ticks = [y_min] 93 | for y in range(10): 94 | y_min += (value_max - value_min) / 10 95 | y_ticks.append(y_min) 96 | return plots, plot_names, x_ticks, y_ticks, set(v_lines) 97 | 98 | 99 | # create plot 100 | def create_plot( 101 | ax, plots, plot_names, x_ticks=None, y_ticks=None, v_lines=None, 102 | return_plot=None 103 | ): 104 | if x_ticks is None: 105 | x_ticks = [0, 1] 106 | if y_ticks is None: 107 | y_ticks = [0, 1] 108 | if v_lines is not None: 109 | for x in v_lines: 110 | ax.axvline(x, color='black', linestyle='dashed') 111 | ax.legend( 112 | plots, plot_names, bbox_to_anchor=(0.0, 0.0, 1.1, 1.0), loc=1, 113 | borderaxespad=0. 114 | ).set_draggable(True) 115 | ax.set_xticks(x_ticks) 116 | ax.set_yticks(y_ticks) 117 | if return_plot is True: 118 | return plt 119 | else: 120 | plt.show() 121 | return True 122 | 123 | 124 | # create plot 125 | def create_scatter_plot( 126 | ax, plots, plot_names, x_ticks=None, y_ticks=None 127 | ): 128 | if x_ticks is None: 129 | x_ticks = [0, 1] 130 | if y_ticks is None: 131 | y_ticks = [0, 1] 132 | ax.legend( 133 | plots, plot_names, bbox_to_anchor=(0.0, 0.0, 1.1, 1.0), loc=1, 134 | borderaxespad=0. 135 | ).set_draggable(True) 136 | ax.set_xticks(x_ticks) 137 | ax.set_yticks(y_ticks) 138 | plt.show() 139 | 140 | 141 | # add values to plot 142 | def add_values_to_scatter_plot(histogram, ax): 143 | pal = mpl_plot.get_cmap('rainbow') 144 | pal.set_under('w', 0.0) 145 | plot = ax.imshow( 146 | histogram[0].T, origin='lower', interpolation='none', 147 | extent=[histogram[1][0], histogram[1][-1], histogram[2][0], 148 | histogram[2][-1]], cmap=pal, vmin=0.001 149 | ) 150 | return plot 151 | -------------------------------------------------------------------------------- /src/remotior_sensus/tools/band_stack.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | """ 18 | Band stack. 19 | 20 | This tool allows for stacking single bands in a multiband raster. 21 | 22 | Typical usage example: 23 | 24 | >>> # import Remotior Sensus and start the session 25 | >>> import remotior_sensus 26 | >>> rs = remotior_sensus.Session() 27 | >>> # start the process 28 | >>> stack = rs.band_stack(input_bands=['path_1', 'path_2'], 29 | ... output_path='output_path') 30 | """ # noqa: E501 31 | 32 | from typing import Union, Optional 33 | 34 | from remotior_sensus.core import configurations as cfg 35 | from remotior_sensus.core.bandset_catalog import BandSet 36 | from remotior_sensus.core.bandset_catalog import BandSetCatalog 37 | from remotior_sensus.core.output_manager import OutputManager 38 | from remotior_sensus.util import (raster_vector, shared_tools) 39 | 40 | 41 | def band_stack( 42 | input_bands: Union[list, int, BandSet], 43 | output_path: Optional[str] = None, 44 | overwrite: Optional[bool] = False, 45 | extent_list: Optional[list] = None, 46 | bandset_catalog: Optional[BandSetCatalog] = None, 47 | n_processes: Optional[int] = None, 48 | virtual_output: Optional[bool] = None, 49 | progress_message: Optional[bool] = True 50 | ) -> OutputManager: 51 | """Stack single bands. 52 | 53 | This tool allows for stacking single bands in a multiband raster. 54 | 55 | Args: 56 | input_bands: list of paths of input rasters, or number of BandSet, or 57 | BandSet object. 58 | output_path: string of output path. 59 | overwrite: if True, output overwrites existing files. 60 | extent_list: list of boundary coordinates left top right bottom. 61 | bandset_catalog: BandSetCatalog object required if input_bands is a 62 | BandSet number. 63 | n_processes: number of parallel processes. 64 | virtual_output: if True (and output_path is directory), save output 65 | as virtual raster. 66 | progress_message: if True then start progress message, if False does 67 | not start the progress message (useful if launched from other tools). 68 | 69 | Returns: 70 | object :func:`~remotior_sensus.core.output_manager.OutputManager` with 71 | - path = output path 72 | 73 | Examples: 74 | Perform band stack 75 | >>> stack = band_stack(input_bands=['path_1', 'path_2'], 76 | ... output_path='output_path') 77 | """ # noqa: E501 78 | cfg.logger.log.info('start') 79 | cfg.progress.update( 80 | process=__name__.split('.')[-1].replace('_', ' '), message='starting', 81 | start=progress_message 82 | ) 83 | # prepare process files 84 | prepared = shared_tools.prepare_process_files( 85 | input_bands=input_bands, output_path=output_path, overwrite=overwrite, 86 | n_processes=n_processes, bandset_catalog=bandset_catalog, 87 | box_coordinate_list=extent_list, virtual_output=virtual_output 88 | ) 89 | input_raster_list = prepared['input_raster_list'] 90 | out_path = prepared['output_path'] 91 | if input_bands is BandSet: 92 | bandset_x = input_bands 93 | elif input_bands is int: 94 | bandset_x = bandset_catalog.get(input_bands) 95 | else: 96 | bandset_x = BandSet.create(paths=input_raster_list) 97 | if virtual_output: 98 | virtual_path = out_path 99 | else: 100 | virtual_path = cfg.temp.temporary_file_path(name_suffix=cfg.vrt_suffix) 101 | raster_vector.create_virtual_raster(output=virtual_path, bandset=bandset_x) 102 | cfg.progress.update(message='stack', step=2, steps=2, minimum=1, 103 | maximum=99, percentage=50) 104 | if virtual_output is not True: 105 | raster_vector.gdal_copy_raster(input_raster=virtual_path, 106 | output=out_path) 107 | cfg.progress.update(end=True) 108 | cfg.logger.log.info('end; band stack: %s' % str(out_path)) 109 | return OutputManager(path=out_path) 110 | -------------------------------------------------------------------------------- /src/remotior_sensus/tools/raster_to_vector.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | """ 18 | Raster to vector. 19 | 20 | This tool allows for the conversion from raster to vector. 21 | A new geopackage is created from the raster conversion. 22 | 23 | Typical usage example: 24 | 25 | >>> # import Remotior Sensus and start the session 26 | >>> import remotior_sensus 27 | >>> rs = remotior_sensus.Session() 28 | >>> # start the process 29 | >>> vector = rs.raster_to_vector(raster_path='file.tif',output_path='vector.gpkg') 30 | """ # noqa: E501 31 | 32 | from typing import Optional 33 | 34 | from remotior_sensus.core import configurations as cfg 35 | from remotior_sensus.core.output_manager import OutputManager 36 | from remotior_sensus.util import files_directories, shared_tools 37 | 38 | 39 | def raster_to_vector( 40 | raster_path, output_path: Optional[str] = None, 41 | dissolve: Optional[bool] = None, field_name: Optional[str] = None, 42 | extent_list: Optional[list] = None, 43 | n_processes: Optional[int] = None, available_ram: Optional[int] = None, 44 | progress_message: Optional[bool] = True 45 | ) -> OutputManager: 46 | """Performs the conversion from raster to vector. 47 | 48 | This tool performs the conversion from raster to vector. 49 | Parallel processes are used for the conversion, resulting in a vector output 50 | which is split as many in portions as the process numbers. 51 | The argument dissolve allows for merging these portions, 52 | but it requires additional processing time depending on vector size. 53 | 54 | Args: 55 | raster_path: path of raster used as input. 56 | output_path: string of output path. 57 | dissolve: if True, dissolve adjacent polygons having the same values; 58 | if False, polygons are not dissolved and the process is rapider. 59 | field_name: name of the output vector field to store raster values 60 | (default = DN). 61 | extent_list: list of boundary coordinates left top right bottom. 62 | n_processes: number of parallel processes. 63 | available_ram: number of megabytes of RAM available to processes. 64 | progress_message: if True then start progress message, if False does 65 | not start the progress message (useful if launched from other tools). 66 | 67 | Returns: 68 | object :func:`~remotior_sensus.core.output_manager.OutputManager` with 69 | - path = output path 70 | 71 | Examples: 72 | Perform the conversion to vector of a raster 73 | >>> raster_to_vector(raster_path='file.tif',output_path='vector.gpkg') 74 | """ # noqa: E501 75 | cfg.logger.log.info('start') 76 | cfg.progress.update( 77 | process=__name__.split('.')[-1].replace('_', ' '), message='starting', 78 | start=progress_message 79 | ) 80 | raster_path = files_directories.input_path(raster_path) 81 | if extent_list is not None: 82 | # prepare process files 83 | prepared = shared_tools.prepare_process_files( 84 | input_bands=[raster_path], output_path=output_path, 85 | n_processes=n_processes, box_coordinate_list=extent_list 86 | ) 87 | input_raster_list = prepared['input_raster_list'] 88 | raster_path = input_raster_list[0] 89 | if output_path is None: 90 | output_path = cfg.temp.temporary_file_path(name_suffix=cfg.gpkg_suffix) 91 | output_path = files_directories.output_path(output_path, cfg.gpkg_suffix) 92 | files_directories.create_parent_directory(output_path) 93 | if n_processes is None: 94 | n_processes = cfg.n_processes 95 | # perform conversion 96 | cfg.multiprocess.multiprocess_raster_to_vector( 97 | raster_path=raster_path, output_vector_path=output_path, 98 | field_name=field_name, n_processes=n_processes, 99 | dissolve_output=dissolve, min_progress=1, max_progress=100, 100 | available_ram=available_ram 101 | ) 102 | cfg.progress.update(end=True) 103 | cfg.logger.log.info('end; output_path: %s' % output_path) 104 | return OutputManager(path=output_path) 105 | -------------------------------------------------------------------------------- /docs/source/introduction.rst: -------------------------------------------------------------------------------- 1 | 2 | .. |fromGIStoRS| image:: _static/fromGIStoRS.png 3 | :width: 30pt 4 | 5 | .. |logo| image:: _static/logo.png 6 | :width: 32pt 7 | 8 | 9 | .. |br| raw:: html 10 | 11 |
12 | 13 | 14 | Introduction 15 | =========================================== 16 | 17 | |logo| Remotior Sensus, developed by Luca Congedo, is a Python package that allows 18 | for the processing of remote sensing images and GIS data. 19 | 20 | The main objective is to simplify the processing of remote sensing data 21 | through practical and integrated APIs that span from the download and 22 | preprocessing of satellite images to the postprocessing of classifications 23 | and GIS data. 24 | Basic dependencies are `NumPy `_, 25 | `SciPy `_ for calculations, and `GDAL `_ 26 | for managing spatial data. 27 | 28 | The main features are: 29 | 30 | - **Search and Download** of remote sensing data such as Landsat and Sentinel-2. 31 | - **Preprocessing** of several products such as Landsat and Sentinel-2 images. 32 | - **Processing and postprocessing** tools to perform image classification through machine learning, manage GIS data and perform spatial analyses. 33 | - **Parallel processing** available for most processing tools. 34 | 35 | 36 | ``WARNING: Remotior Sensus is still in early development; 37 | new tools are going to be added, tools and APIs may change, 38 | and one may encounter issues and bugs using Remotior Sensus.`` 39 | 40 | Management of Raster Bands 41 | __________________________ 42 | 43 | Most tools accept raster bands as input, defined through the file path. 44 | 45 | In addition, raster bands can be managed through a catalog of BandSets 46 | (see :meth:`~remotior\_sensus.core.bandset\_catalog`), 47 | where each BandSet is an object that includes information about single bands 48 | (from the file path to the spatial and spectral characteristics). 49 | Bands in a BandSet can be referenced by the properties thereof, 50 | such as order number or center wavelength. 51 | 52 | .. image:: _static/bandset.jpg 53 | :align: center 54 | 55 | Multiple BandSets can be defined and identified by their reference number. 56 | Therefore, BandSets can be used as input for operations on multiple bands 57 | such as Principal Components Analysis, classification, mosaic, 58 | or band calculation. 59 | 60 | In band calculations (see :meth:`~remotior_sensus.tools.band_calc`) 61 | name alias of bands based on center wavelength (e.g. blue, red) can be used 62 | to simplify the structure of calculation expression. 63 | 64 | 65 | Performance 66 | ___________ 67 | 68 | Most tools are designed to run in parallel processes, through a simple 69 | and effective parallelization approach based on dividing the raster input 70 | in sections that are distributed to available threads, maximizing the use 71 | of available RAM. 72 | This allows even complex algorithms to run in parallel. 73 | Optionally, the output file can be a virtual raster collecting the output 74 | rasters (corresponding to the sections) written independently by parallel 75 | processes; this avoids the time required to produce a unique raster output. 76 | Most tools allow for on the fly reprojection of input data. 77 | 78 | .. image:: _static/processing.jpg 79 | :align: center 80 | 81 | 82 | Machine Learning 83 | ________________ 84 | 85 | Remotior Sensus optional dependencies are `PyTorch `_ 86 | and `scikit-learn `_, which are 87 | integrated in the tool :meth:`~remotior\_sensus.tools.band\_classification` 88 | to allow for land cover classification through machine learning. 89 | The aim is to simplify the training process and development of the model. 90 | 91 | Source Code 92 | ___________ 93 | 94 | The source code of Remotior Sensus is available 95 | at https://github.com/semiautomaticgit/remotior_sensus . 96 | 97 | To report issues please visit 98 | https://github.com/semiautomaticgit/remotior_sensus/issues . 99 | 100 | License of Remotior Sensus 101 | '''''''''''''''''''''''''' 102 | Remotior Sensus is free software: you can redistribute it and/or modify it 103 | under the terms of the GNU General Public License as published by 104 | the Free Software Foundation, either version 3 of the License, 105 | or (at your option) any later version. 106 | Remotior Sensus is distributed in the hope that it will be useful, 107 | but WITHOUT ANY WARRANTY; without even the implied warranty 108 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 109 | See the GNU General Public License for more details. 110 | You should have received a copy of the GNU General Public License 111 | along with Remotior Sensus. If not, see https://www.gnu.org/licenses/. 112 | 113 | How to cite 114 | __________________________ 115 | 116 | Congedo, Luca, (2023). Remotior Sensus. https://github.com/semiautomaticgit/remotior_sensus 117 | 118 | Official site 119 | _____________ 120 | 121 | For more information and tutorials visit the official site 122 | 123 | |fromGIStoRS| `From GIS to Remote Sensing 124 | `_ 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/remotior_sensus/tools/raster_split.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | """ 18 | Raster split. 19 | 20 | This tool allows for splitting a raster to single bands. 21 | 22 | Typical usage example: 23 | 24 | >>> # import Remotior Sensus and start the session 25 | >>> import remotior_sensus 26 | >>> rs = remotior_sensus.Session() 27 | >>> # start the process 28 | >>> split = rs.raster_split(raster_path='input_path', 29 | ... output_path='output_path') 30 | """ # noqa: E501 31 | 32 | from typing import Optional 33 | 34 | from remotior_sensus.core import configurations as cfg 35 | from remotior_sensus.core.output_manager import OutputManager 36 | from remotior_sensus.util import ( 37 | files_directories, raster_vector, shared_tools 38 | ) 39 | 40 | 41 | def raster_split( 42 | raster_path: str, output_path: str = None, 43 | prefix: Optional[str] = None, extent_list: Optional[list] = None, 44 | n_processes: Optional[int] = None, 45 | virtual_output: Optional[bool] = None, 46 | progress_message: Optional[bool] = True 47 | ) -> OutputManager: 48 | """Split a multiband raster to single bands. 49 | 50 | This tool allows for splitting a multiband raster to single bands. 51 | 52 | Args: 53 | raster_path: path of raster used as input. 54 | output_path: string of output directory path. 55 | prefix: optional string for output name prefix. 56 | extent_list: list of boundary coordinates left top right bottom. 57 | n_processes: number of parallel processes. 58 | virtual_output: if True (and output_path is directory), save output 59 | as virtual raster. 60 | progress_message: if True then start progress message, if False does 61 | not start the progress message (useful if launched from other tools). 62 | 63 | Returns: 64 | object :func:`~remotior_sensus.core.output_manager.OutputManager` with 65 | - path = output path 66 | 67 | Examples: 68 | Perform the split of a raster 69 | >>> split = raster_split(raster_path='input_path', 70 | ... output_path='output_path') 71 | """ # noqa: E501 72 | cfg.logger.log.info('start') 73 | cfg.progress.update( 74 | process=__name__.split('.')[-1].replace('_', ' '), message='starting', 75 | start=progress_message 76 | ) 77 | raster_path = files_directories.input_path(raster_path) 78 | # prepare process files 79 | prepared = shared_tools.prepare_process_files( 80 | input_bands=[raster_path], output_path=output_path, 81 | n_processes=n_processes, box_coordinate_list=extent_list 82 | ) 83 | raster_info = prepared['raster_info'] 84 | output_list = [] 85 | bands = raster_info[0][5] 86 | output_path = output_path.replace('\\', '/').replace('//', '/') 87 | if output_path.endswith('/'): 88 | output_path = output_path[:-1] 89 | if prefix is None: 90 | prefix = 'band' 91 | for band in range(bands): 92 | files_directories.create_parent_directory(output_path) 93 | out_path = '%s/%s%s' % (output_path, prefix, str(band + 1)) 94 | if virtual_output is True: 95 | virtual_path = files_directories.output_path(out_path, 96 | cfg.vrt_suffix) 97 | output = virtual_path 98 | else: 99 | virtual_path = cfg.temp.temporary_file_path( 100 | name_suffix=cfg.vrt_suffix) 101 | output = files_directories.output_path(out_path, 102 | cfg.tif_suffix) 103 | raster_vector.create_virtual_raster( 104 | input_raster_list=[raster_path], output=virtual_path, 105 | band_number_list=[[band + 1]], box_coordinate_list=extent_list, 106 | relative_to_vrt=False 107 | ) 108 | if virtual_output is not True: 109 | raster_vector.gdal_copy_raster( 110 | input_raster=virtual_path, output=output 111 | ) 112 | output_list.append(output) 113 | cfg.progress.update( 114 | message='splitting', step=band, steps=bands, minimum=1, 115 | maximum=99, percentage=int(100 * band / bands) 116 | ) 117 | cfg.progress.update(end=True) 118 | cfg.logger.log.info('end; raster split: %s' % str(output_list)) 119 | return OutputManager(paths=output_list) 120 | -------------------------------------------------------------------------------- /src/remotior_sensus/core/output_manager.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | """Output manager. 18 | 19 | Core class that manages several types of output, mainly intended for tools 20 | that have several outputs. 21 | 22 | Typical usage example: 23 | 24 | >>> # process output is checked 25 | >>> OutputManager() 26 | """ 27 | 28 | from remotior_sensus.core.bandset_catalog import BandSetCatalog 29 | 30 | 31 | class OutputManager(object): 32 | """Manages output. 33 | 34 | This class manages several types of output, mainly intended for tools 35 | that have several outputs. 36 | Check argument is False if output failed. 37 | Single output raster or multiple file paths can be defined as arguments. 38 | Additional output files or tables are managed with an extra argument. 39 | The type of the extra argument can be flexible depending on the process 40 | output. 41 | 42 | Attributes: 43 | check: True if output is as expected, False if process failed. 44 | path: path of the first output. 45 | paths: list of output paths in case of multiple outputs. 46 | extra: additional output elements depending on the process. 47 | 48 | Examples: 49 | Output failed 50 | >>> OutputManager(check=False) 51 | 52 | Output is checked and file path is provided 53 | >>> OutputManager(path='file.tif') 54 | """ # noqa: E501 55 | 56 | def __init__( 57 | self, check: bool = True, path: str = None, paths: list = None, 58 | extra=None 59 | ): 60 | """Initializes an Output. 61 | 62 | Initializes an Output. 63 | 64 | Args: 65 | check: True if output is as expected, False if process failed. 66 | path: path of the first output. 67 | paths: list of output paths in case of multiple outputs. 68 | extra: additional output elements depending on the process. 69 | 70 | Examples: 71 | Create an object with a single file path 72 | >>> OutputManager(path='file.tif') 73 | 74 | Create an object with several output file paths in a list and an extra argument for a dictionary 75 | >>> OutputManager( 76 | ... paths=['file1.tif', 'file2.tif'], 77 | ... extra={'additional_output': 'file.csv'} 78 | ... ) 79 | ) 80 | """ # noqa: E501 81 | self.check = check 82 | self.paths = paths 83 | if path is None: 84 | if paths is None: 85 | self.path = None 86 | elif len(paths) == 0: 87 | self.path = None 88 | else: 89 | self.path = paths[0] 90 | else: 91 | self.path = path 92 | self.extra = extra 93 | 94 | def add_to_bandset( 95 | self, bandset_catalog: BandSetCatalog, bandset_number=None, 96 | band_number=None, raster_band=None, band_name=None, date=None, 97 | unit=None, root_directory=None, multiplicative_factor=None, 98 | additive_factor=None, wavelength=None 99 | ): 100 | """Adds output to BandSet. 101 | 102 | Adds the OutputManager.path as a band to a BandSet in a BandSetCatalog. 103 | 104 | Args: 105 | bandset_catalog: BandSetCatalog object. 106 | band_name: raster name used for identifying the bands. 107 | wavelength: center wavelengths of band. 108 | unit: wavelength unit as string 109 | multiplicative_factor: multiplicative factor for bands during calculations. 110 | additive_factor: additive factors for band during calculations. 111 | date: date string (format YYYY-MM-DD). 112 | bandset_number: number of the BandSet; if None, the band is added to the current BandSet. 113 | root_directory: root directory for relative path. 114 | raster_band: raster band number. 115 | band_number: number of band in BandSet. 116 | 117 | Examples: 118 | Add the output to BandSet 1 as band 1. 119 | >>> catalog = BandSetCatalog() 120 | >>> OutputManager.add_to_bandset( 121 | ... bandset_catalog=catalog, bandset_number=1, band_number=1 122 | ... ) 123 | """ # noqa: E501 124 | if type(bandset_catalog) is BandSetCatalog: 125 | bandset_catalog.add_band_to_bandset( 126 | path=self.path, bandset_number=bandset_number, 127 | band_number=band_number, raster_band=raster_band, 128 | band_name=band_name, date=date, unit=unit, 129 | root_directory=root_directory, 130 | multiplicative_factor=multiplicative_factor, 131 | additive_factor=additive_factor, wavelength=wavelength 132 | ) 133 | else: 134 | raise Exception('bandset catalog not found') 135 | -------------------------------------------------------------------------------- /src/remotior_sensus/core/log.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | """Logging manger. 18 | 19 | Core class that manages logs during processes. 20 | 21 | Typical usage example: 22 | 23 | >>> # create a log file in a directory 24 | >>> Log(directory='directory_path', level=10) 25 | """ 26 | 27 | import io 28 | import logging 29 | from typing import Union, Optional 30 | 31 | from remotior_sensus.core import configurations as cfg 32 | 33 | 34 | class Log(object): 35 | log = None 36 | 37 | def __init__( 38 | self, file_path: Optional[str] = None, 39 | directory: Optional[str] = None, level: Union[int, str] = None, 40 | multiprocess=False, time=True, stream_handler=True 41 | ): 42 | """Manages logs. 43 | 44 | This module allows for managing logs of processes. 45 | 46 | Attributes: 47 | file_path: path of a log file. 48 | directory: directory path where a log file is created if file_path is None. 49 | level: level of logging (10 for DEBUG, 20 for INFO). 50 | multiprocess: if True, sets logging for parallel processes. 51 | time: if True, time is saved in log file. 52 | stream_handler: if True, create stream handler. 53 | 54 | Examples: 55 | Create a log file and starts logging. 56 | >>> Log(file_path='file.txt', level=20) 57 | """ # noqa: E501 58 | if file_path is None: 59 | if directory is None: 60 | raise Exception('file path or directory missing') 61 | else: 62 | file_path = '{}/{}.log'.format(directory, cfg.root_name) 63 | # create logger 64 | logger = logging.getLogger(__name__) 65 | logging.basicConfig(level=logging.DEBUG) 66 | if level is None: 67 | level = logging.DEBUG 68 | if not multiprocess: 69 | # create file handler 70 | fh = logging.FileHandler(file_path) 71 | fh.setLevel(level) 72 | fhf = logging.Formatter( 73 | '%(levelname)s|%(asctime)s.%(msecs)03d|%(module)s|%(funcName)s' 74 | '|%(lineno)s|%(message)s', '%Y-%m-%dT%H:%M:%S' 75 | ) 76 | fh.setFormatter(fhf) 77 | if logger.hasHandlers(): 78 | # try to close handlers 79 | try: 80 | for fh in logger.handlers: 81 | fh.close() 82 | except Exception as err: 83 | str(err) 84 | logger.handlers.clear() 85 | logger.addHandler(fh) 86 | # create console handler 87 | if stream_handler: 88 | ch = logging.StreamHandler() 89 | ch.setLevel(level) 90 | if time: 91 | chf = logging.Formatter( 92 | '%(levelname)s[%(asctime)s.%(msecs)03d] ' 93 | '%(module)s.%(funcName)s[%(lineno)s] %(message)s', 94 | '%Y-%m-%dT%H:%M:%S' 95 | ) 96 | else: 97 | chf = logging.Formatter( 98 | '%(levelname)s %(module)s.%(funcName)s[%(lineno)s] ' 99 | '%(message)s' 100 | ) 101 | ch.setFormatter(chf) 102 | logger.addHandler(ch) 103 | logger.propagate = False 104 | self.log = logger 105 | self.file_path = file_path 106 | self.stream = None 107 | self.level = level 108 | # multiprocess number 109 | else: 110 | # create stream handler 111 | stream = io.StringIO() 112 | ch = logging.StreamHandler(stream) 113 | ch.setLevel(level) 114 | if time: 115 | chf = logging.Formatter( 116 | '%(levelname)s_p{}|%(asctime)s.%(msecs)03d|%(module)s' 117 | '|%(funcName)s|%(lineno)s|%(message)s'.format( 118 | multiprocess 119 | ), '%Y-%m-%dT%H:%M:%S' 120 | ) 121 | else: 122 | chf = logging.Formatter( 123 | '%(levelname)s_p{}|%(module)s|%(funcName)s|%(lineno)s' 124 | '|%(message)s'.format(multiprocess) 125 | ) 126 | ch.setFormatter(chf) 127 | if logger.hasHandlers(): 128 | # try to close handlers 129 | try: 130 | for fh in logger.handlers: 131 | fh.close() 132 | except Exception as err: 133 | str(err) 134 | logger.handlers.clear() 135 | logger.addHandler(ch) 136 | logger.propagate = False 137 | self.log = logger 138 | self.stream = stream 139 | self.file_path = None 140 | self.level = level 141 | 142 | def set_level(self, level): 143 | for h in self.log.handlers: 144 | h.setLevel(level) 145 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://img.shields.io/badge/Website-darkgreen 2 | :target: https://fromgistors.blogspot.com/p/remotior-sensus.html 3 | 4 | .. image:: https://img.shields.io/badge/Documentation-blue 5 | :target: https://remotior-sensus.readthedocs.io 6 | 7 | .. image:: https://img.shields.io/badge/Bug%20reports-red 8 | :target: https://github.com/semiautomaticgit/remotior_sensus/issues 9 | 10 | .. image:: https://img.shields.io/pypi/v/remotior-sensus?label=PyPI%20version 11 | :target: https://pypi.org/project/remotior-sensus 12 | 13 | .. image:: https://img.shields.io/pypi/dm/remotior-sensus?label=PyPI%20downloads 14 | :target: https://pypi.org/project/remotior-sensus 15 | 16 | .. image:: https://img.shields.io/conda/v/conda-forge/remotior-sensus?label=Conda%20version 17 | :target: https://anaconda.org/conda-forge/remotior-sensus 18 | 19 | .. image:: https://img.shields.io/conda/d/conda-forge/remotior-sensus?label=Conda%20downloads 20 | :target: https://anaconda.org/conda-forge/remotior-sensus 21 | 22 | .. image:: https://img.shields.io/conda/l/conda-forge/remotior-sensus 23 | :target: https://www.gnu.org/licenses/ 24 | 25 | .. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.10038132.svg 26 | :target: https://doi.org/10.5281/zenodo.10038132 27 | 28 | .. image:: https://colab.research.google.com/assets/colab-badge.svg 29 | :target: https://remotior-sensus.readthedocs.io/en/latest/quickstart.html 30 | 31 | 32 | 33 | 34 | Introduction 35 | =========================================== 36 | 37 | Remotior Sensus (which is Latin for "a more remote sense") is a Python package 38 | that allows for the processing of remote sensing images and GIS data. 39 | 40 | Remotior Sensus is developed by Luca Congedo. 41 | 42 | - **Website:** https://fromgistors.blogspot.com/p/remotior-sensus.html 43 | - **Documentation:** https://remotior-sensus.readthedocs.io 44 | - **Source code:** https://github.com/semiautomaticgit/remotior_sensus 45 | - **Bug reports:** https://github.com/semiautomaticgit/remotior_sensus/issues 46 | 47 | The main objective is to simplify the processing of remote sensing data 48 | through practical and integrated APIs that span from the download and 49 | preprocessing of satellite images to the postprocessing of classifications 50 | and GIS data. 51 | Basic dependencies are `NumPy `_, 52 | `SciPy `_ for calculations, and `GDAL `_ 53 | for managing spatial data. 54 | Optionally, `Matplotlib` is used to display spectral signature plots. 55 | 56 | The main features are: 57 | 58 | - **Search and Download** of remote sensing data such as Landsat and Sentinel-2. 59 | - **Preprocessing** of several products such as Landsat and Sentinel-2 images. 60 | - **Processing and postprocessing** tools to perform image classification through machine learning, manage GIS data and perform spatial analyses. 61 | - **Parallel processing** available for most processing tools. 62 | 63 | ``WARNING: Remotior Sensus is still in early development; 64 | new tools are going to be added, tools and APIs may change, 65 | and one may encounter issues and bugs using Remotior Sensus.`` 66 | 67 | Management of Raster Bands 68 | __________________________ 69 | 70 | Most tools accept raster bands as input, defined through the file path. 71 | 72 | In addition, raster bands can be managed through a catalog of BandSets, 73 | where each BandSet is an object that includes information about single bands 74 | (from the file path to the spatial and spectral characteristics). 75 | Bands in a BandSet can be referenced by the properties thereof, 76 | such as order number or center wavelength. 77 | 78 | Multimple BandSets can be defined and identified by their reference number. 79 | Therefore, BandSets can be used as input for operations on multiple bands 80 | such as Principal Components Analysis, classification, mosaic, 81 | or band calculation. 82 | 83 | In band calculations, alias name of bands based on center wavelength 84 | (e.g. blue, red) can be used to simplify the structure of calculation expression. 85 | 86 | Performance 87 | ___________ 88 | 89 | Most tools are designed to run in parallel processes, through a simple 90 | and effective parallelization approach based on dividing the raster input 91 | in sections that are distributed to available threads, maximizing the use 92 | of available RAM. 93 | This allows even complex algorithms to run in parallel. 94 | Optionally, the output file can be a virtual raster collecting the output 95 | rasters (corresponding to the sections) written independently by parallel 96 | processes; this avoids the time required to produce a unique raster output. 97 | Most tools allow for on the fly reprojection of input data. 98 | 99 | Machine Learning 100 | ________________ 101 | 102 | Remotior Sensus optional dependencies are `PyTorch `_ 103 | and `scikit-learn `_, which are 104 | integrated in the classification tool. 105 | to allow for land cover classification through machine learning. 106 | The aim is to simplify the training process and development of the model. 107 | 108 | Installation 109 | ______________ 110 | 111 | Remotior Sensus requires `GDAL`, `NumPy` and `SciPy` for most functionalities. 112 | Also, `scikit-learn` and `PyTorch` are optional but required for machine learning. 113 | Optionally, `Matplotlib` is used to display spectral signature plots. 114 | 115 | It is recommended to install Remotior Sensus using a `Conda` environment. 116 | 117 | .. code-block:: console 118 | 119 | $ conda install -c conda-forge remotior-sensus scikit-learn pytorch 120 | 121 | 122 | 123 | License of Remotior Sensus 124 | '''''''''''''''''''''''''' 125 | Remotior Sensus is free software: you can redistribute it and/or modify it 126 | under the terms of the GNU General Public License as published by 127 | the Free Software Foundation, either version 3 of the License, 128 | or (at your option) any later version. 129 | Remotior Sensus is distributed in the hope that it will be useful, 130 | but WITHOUT ANY WARRANTY; without even the implied warranty 131 | of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 132 | See the GNU General Public License for more details. 133 | You should have received a copy of the GNU General Public License 134 | along with Remotior Sensus. If not, see https://www.gnu.org/licenses/. 135 | 136 | 137 | Official site 138 | _____________ 139 | 140 | For more information and tutorials visit the official site 141 | 142 | `From GIS to Remote Sensing 143 | `_ 144 | -------------------------------------------------------------------------------- /src/remotior_sensus/tools/band_sieve.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | """Band sieve. 18 | 19 | This tool allows for performing the sieve of raster bands removing 20 | patches having size lower than a threshold (i.e. number of pixels). 21 | 22 | Typical usage example: 23 | 24 | >>> # import Remotior Sensus and start the session 25 | >>> import remotior_sensus 26 | >>> rs = remotior_sensus.Session() 27 | >>> # start the process 28 | >>> sieve = rs.band_sieve( 29 | ... input_bands=['file1.tif', 'file2.tif'],size=2, 30 | ... output_path='directory_path', connected=False,prefix='sieve_' 31 | ... ) 32 | """ 33 | 34 | from typing import Union, Optional 35 | 36 | from remotior_sensus.core import configurations as cfg 37 | from remotior_sensus.core.bandset_catalog import BandSet 38 | from remotior_sensus.core.bandset_catalog import BandSetCatalog 39 | from remotior_sensus.core.output_manager import OutputManager 40 | from remotior_sensus.util import shared_tools 41 | 42 | 43 | def band_sieve( 44 | input_bands: Union[list, int, BandSet], size: int, 45 | output_path: Union[list, str] = None, connected: Optional[bool] = None, 46 | overwrite: Optional[bool] = False, 47 | prefix: Optional[str] = '', extent_list: Optional[list] = None, 48 | multiple_resolution: Optional[bool] = True, 49 | n_processes: Optional[int] = None, 50 | available_ram: Optional[int] = None, 51 | bandset_catalog: Optional[BandSetCatalog] = None, 52 | virtual_output: Optional[bool] = None, 53 | progress_message: Optional[bool] = True 54 | ) -> OutputManager: 55 | """Perform band sieve. 56 | 57 | This tool allows for performing the sieve of raster bands removing 58 | patches having size lower than a threshold (i.e. number of pixels). 59 | 60 | Args: 61 | input_bands: input of type BandSet or list of paths or integer 62 | number of BandSet. 63 | output_path: string of output path directory or list of paths. 64 | overwrite: if True, output overwrites existing files. 65 | size: size of dilation in pixels. 66 | virtual_output: if True (and output_path is directory), save output 67 | as virtual raster of multiprocess parts 68 | connected: if True, consider 8 pixel connection; if False, consider 4 69 | pixel connection. 70 | prefix: optional string for output name prefix. 71 | extent_list: list of boundary coordinates left top right bottom. 72 | multiple_resolution: if True, keep the original resolution of 73 | individual raster; 74 | if False, use the resolution of the first raster for all the bands. 75 | n_processes: number of parallel processes. 76 | available_ram: number of megabytes of RAM available to processes. 77 | bandset_catalog: optional type BandSetCatalog for BandSet number. 78 | progress_message: if True then start progress message, if False does 79 | not start the progress message (useful if launched from other tools). 80 | 81 | Returns: 82 | Object :func:`~remotior_sensus.core.output_manager.OutputManager` with 83 | - paths = output list 84 | 85 | Examples: 86 | Perform the sieve of size 3 with connected pixel (8 connection) 87 | >>> sieve = band_sieve( 88 | ... input_bands=['file1.tif', 'file2.tif'], size=3, 89 | ... output_path='directory_path', connected=True, prefix='sieve_' 90 | ... ) 91 | """ # noqa: E501 92 | cfg.logger.log.info('start') 93 | cfg.progress.update( 94 | process=__name__.split('.')[-1].replace('_', ' '), message='starting', 95 | start=progress_message 96 | ) 97 | # prepare process files 98 | prepared = shared_tools.prepare_process_files( 99 | input_bands=input_bands, output_path=output_path, overwrite=overwrite, 100 | n_processes=n_processes, bandset_catalog=bandset_catalog, 101 | box_coordinate_list=extent_list, 102 | prefix=prefix, multiple_output=True, multiple_input=True, 103 | multiple_resolution=multiple_resolution, 104 | virtual_output=virtual_output 105 | ) 106 | input_raster_list = prepared['input_raster_list'] 107 | raster_info = prepared['raster_info'] 108 | n_processes = prepared['n_processes'] 109 | nodata_list = prepared['nodata_list'] 110 | output_list = prepared['output_list'] 111 | # 4 connected pixels 112 | if connected: 113 | connected = 8 114 | elif not connected: 115 | connected = 4 116 | else: 117 | connected = 4 118 | # process calculation 119 | n = 0 120 | min_p = 1 121 | max_p = int((99 - 1) / len(input_raster_list)) 122 | for i in input_raster_list: 123 | cfg.progress.update(message='processing raster %s' % (n + 1)) 124 | out = output_list[n] 125 | nd = nodata_list[n] 126 | data_type = raster_info[n][8] 127 | # perform sieve 128 | cfg.multiprocess.multiprocess_raster_sieve( 129 | raster_path=i, n_processes=n_processes, 130 | available_ram=available_ram, sieve_size=size, 131 | connected=connected, output_nodata_value=nd, output=out, 132 | output_data_type=data_type, compress=cfg.raster_compression, 133 | min_progress=min_p + max_p * n, 134 | max_progress=min_p + max_p * (n + 1) 135 | ) 136 | n += 1 137 | cfg.progress.update(end=True) 138 | cfg.logger.log.info('end; band sieve: %s' % output_list) 139 | return OutputManager(paths=output_list) 140 | -------------------------------------------------------------------------------- /src/remotior_sensus/tools/band_dilation.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | """Band dilation. 18 | 19 | This tool allows for the spatial dilation, through a moving window, 20 | of band pixels selected by values. 21 | 22 | Typical usage example: 23 | 24 | >>> # import Remotior Sensus and start the session 25 | >>> import remotior_sensus 26 | >>> rs = remotior_sensus.Session() 27 | >>> # start the process 28 | >>> dilation = rs.band_dilation(input_bands=['file1.tif', 'file2.tif'], 29 | ... value_list=[1], size=3, output_path='directory_path', 30 | ... circular_structure=True, prefix='dilation_') 31 | """ 32 | 33 | from typing import Union, Optional 34 | 35 | from remotior_sensus.core import configurations as cfg 36 | from remotior_sensus.core.bandset_catalog import BandSet 37 | from remotior_sensus.core.bandset_catalog import BandSetCatalog 38 | from remotior_sensus.core.output_manager import OutputManager 39 | from remotior_sensus.core.processor_functions import raster_dilation 40 | from remotior_sensus.util import shared_tools 41 | 42 | 43 | def band_dilation( 44 | input_bands: Union[list, int, BandSet], value_list: list, size: int, 45 | output_path: Union[list, str] = None, 46 | overwrite: Optional[bool] = False, 47 | circular_structure: Optional[bool] = None, 48 | prefix: Optional[str] = '', extent_list: Optional[list] = None, 49 | multiple_resolution: Optional[bool] = True, 50 | n_processes: Optional[int] = None, 51 | available_ram: Optional[int] = None, 52 | bandset_catalog: Optional[BandSetCatalog] = None, 53 | virtual_output: Optional[bool] = None, 54 | progress_message: Optional[bool] = True 55 | ) -> OutputManager: 56 | """Perform dilation of band pixels. 57 | 58 | This tool performs the dilation of pixels identified by a list of values. 59 | A new raster is created for each input band. 60 | 61 | Args: 62 | input_bands: input of type BandSet or list of paths or integer 63 | number of BandSet. 64 | output_path: string of output path directory or list of paths. 65 | overwrite: if True, output overwrites existing files. 66 | value_list: list of values for dilation. 67 | size: size of dilation in pixels. 68 | virtual_output: if True (and output_path is directory), save output 69 | as virtual raster of multiprocess parts 70 | circular_structure: if True, use circular structure; if False, square 71 | structure. 72 | prefix: optional string for output name prefix. 73 | extent_list: list of boundary coordinates left top right bottom. 74 | multiple_resolution: if True, keep the original resolution of 75 | individual raster; 76 | if False, use the resolution of the first raster for all the bands. 77 | n_processes: number of parallel processes. 78 | available_ram: number of megabytes of RAM available to processes. 79 | bandset_catalog: optional type BandSetCatalog for BandSet number. 80 | progress_message: if True then start progress message, if False does 81 | not start the progress message (useful if launched from other tools). 82 | 83 | Returns: 84 | Object :func:`~remotior_sensus.core.output_manager.OutputManager` with 85 | - paths = output list 86 | 87 | Examples: 88 | Perform the dilation of size 5 for value 1 and 2 89 | >>> dilation = band_dilation(input_bands=['path_1', 'path_2'],value_list=[1, 2],size=5,output_path='directory_path',circular_structure=True) 90 | """ # noqa: E501 91 | cfg.logger.log.info('start') 92 | cfg.progress.update( 93 | process=__name__.split('.')[-1].replace('_', ' '), message='starting', 94 | start=progress_message 95 | ) 96 | # prepare process files 97 | prepared = shared_tools.prepare_process_files( 98 | input_bands=input_bands, output_path=output_path, overwrite=overwrite, 99 | n_processes=n_processes, box_coordinate_list=extent_list, 100 | bandset_catalog=bandset_catalog, prefix=prefix, 101 | multiple_output=True, multiple_input=True, 102 | multiple_resolution=multiple_resolution, 103 | virtual_output=virtual_output 104 | ) 105 | input_raster_list = prepared['input_raster_list'] 106 | raster_info = prepared['raster_info'] 107 | n_processes = prepared['n_processes'] 108 | nodata_list = prepared['nodata_list'] 109 | output_list = prepared['output_list'] 110 | vrt_list = prepared['vrt_list'] 111 | if not circular_structure: 112 | structure = shared_tools.create_base_structure(size * 2 + 1) 113 | else: 114 | structure = shared_tools.create_circular_structure(size) 115 | # process calculation 116 | n = 0 117 | min_p = 1 118 | max_p = int((99 - 1) / len(input_raster_list)) 119 | # dummy bands for memory calculation as the number of values 120 | dummy_bands = len(value_list) + 4 121 | for i in input_raster_list: 122 | out = output_list[n] 123 | nd = nodata_list[n] 124 | data_type = raster_info[n][8] 125 | cfg.multiprocess.run( 126 | raster_path=i, function=raster_dilation, 127 | function_argument=structure, n_processes=n_processes, 128 | available_ram=available_ram, 129 | function_variable=value_list, output_raster_path=out, 130 | output_data_type=data_type, output_nodata_value=nd, 131 | compress=cfg.raster_compression, dummy_bands=dummy_bands, 132 | boundary_size=structure.shape[0] + 1, virtual_raster=vrt_list[n], 133 | progress_message='processing raster %s' % (n + 1), 134 | min_progress=min_p + max_p * n, 135 | max_progress=min_p + max_p * (n + 1) 136 | ) 137 | n += 1 138 | cfg.progress.update(end=True) 139 | cfg.logger.log.info('end; band dilation: %s' % output_list) 140 | return OutputManager(paths=output_list) 141 | -------------------------------------------------------------------------------- /src/remotior_sensus/tools/band_erosion.py: -------------------------------------------------------------------------------- 1 | # Remotior Sensus , software to process remote sensing and GIS data. 2 | # Copyright (C) 2022-2025 Luca Congedo. 3 | # Author: Luca Congedo 4 | # Email: ing.congedoluca@gmail.com 5 | # 6 | # This file is part of Remotior Sensus. 7 | # Remotior Sensus is free software: you can redistribute it and/or modify it 8 | # under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, 10 | # or (at your option) any later version. 11 | # Remotior Sensus is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty 13 | # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | # See the GNU General Public License for more details. 15 | # You should have received a copy of the GNU General Public License 16 | # along with Remotior Sensus. If not, see . 17 | """Band erosion. 18 | 19 | This tool allows for the spatial erosion, through a moving window, 20 | of band pixels selected by values. 21 | 22 | Typical usage example: 23 | 24 | >>> # import Remotior Sensus and start the session 25 | >>> import remotior_sensus 26 | >>> rs = remotior_sensus.Session() 27 | >>> # start the process 28 | >>> erosion = rs.band_erosion(input_bands=['file1.tif', 'file2.tif'], 29 | ... value_list=[1], size=1, output_path='directory_path', 30 | ... circular_structure=True,prefix='erosion_') 31 | """ 32 | 33 | from typing import Union, Optional 34 | 35 | from remotior_sensus.core import configurations as cfg 36 | from remotior_sensus.core.bandset_catalog import BandSet 37 | from remotior_sensus.core.bandset_catalog import BandSetCatalog 38 | from remotior_sensus.core.output_manager import OutputManager 39 | from remotior_sensus.core.processor_functions import raster_erosion 40 | from remotior_sensus.util import shared_tools 41 | 42 | 43 | def band_erosion( 44 | input_bands: Union[list, int, BandSet], value_list: list, size: int, 45 | output_path: Union[list, str] = None, 46 | overwrite: Optional[bool] = False, 47 | circular_structure: Optional[bool] = None, 48 | prefix: Optional[str] = '', extent_list: Optional[list] = None, 49 | multiple_resolution: Optional[bool] = True, 50 | n_processes: Optional[int] = None, 51 | available_ram: Optional[int] = None, 52 | bandset_catalog: Optional[BandSetCatalog] = None, 53 | virtual_output: Optional[bool] = None, 54 | progress_message: Optional[bool] = True 55 | ) -> OutputManager: 56 | """Perform erosion of band pixels. 57 | 58 | This tool performs the erosion of pixels identified by a list of values. 59 | A new raster is created for each input band. 60 | 61 | Args: 62 | input_bands: input of type BandSet or list of paths or integer 63 | number of BandSet. 64 | output_path: string of output path directory or list of paths. 65 | overwrite: if True, output overwrites existing files. 66 | value_list: list of values for dilation. 67 | size: size of dilation in pixels. 68 | virtual_output: if True (and output_path is directory), save output 69 | as virtual raster of multiprocess parts. 70 | circular_structure: if True, use circular structure; if False, square 71 | structure. 72 | prefix: optional string for output name prefix. 73 | extent_list: list of boundary coordinates left top right bottom. 74 | multiple_resolution: if True, keep the original resolution of 75 | individual raster; 76 | if False, use the resolution of the first raster for all the bands. 77 | n_processes: number of parallel processes. 78 | available_ram: number of megabytes of RAM available to processes. 79 | bandset_catalog: optional type BandSetCatalog for BandSet number. 80 | progress_message: if True then start progress message, if False does 81 | not start the progress message (useful if launched from other tools). 82 | 83 | Returns: 84 | Object :func:`~remotior_sensus.core.output_manager.OutputManager` with 85 | - paths = output list 86 | 87 | Examples: 88 | Perform the erosion of size 1 for value 1 and 2 89 | >>> erosion = band_erosion( 90 | ... input_bands=['path_1', 'path_2'], value_list=[1, 2], size=1, 91 | ... output_path='directory_path', circular_structure=True 92 | ... ) 93 | """ # noqa: E501 94 | cfg.logger.log.info('start') 95 | cfg.progress.update( 96 | process=__name__.split('.')[-1].replace('_', ' '), message='starting', 97 | start=progress_message 98 | ) 99 | # prepare process files 100 | prepared = shared_tools.prepare_process_files( 101 | input_bands=input_bands, output_path=output_path, overwrite=overwrite, 102 | n_processes=n_processes, bandset_catalog=bandset_catalog, 103 | prefix=prefix, box_coordinate_list=extent_list, 104 | multiple_output=True, multiple_input=True, 105 | multiple_resolution=multiple_resolution, 106 | virtual_output=virtual_output 107 | ) 108 | input_raster_list = prepared['input_raster_list'] 109 | raster_info = prepared['raster_info'] 110 | n_processes = prepared['n_processes'] 111 | nodata_list = prepared['nodata_list'] 112 | output_list = prepared['output_list'] 113 | vrt_list = prepared['vrt_list'] 114 | if not circular_structure: 115 | structure = shared_tools.create_base_structure(3) 116 | else: 117 | structure = shared_tools.create_circular_structure(1) 118 | # process calculation 119 | n = 0 120 | min_p = 1 121 | max_p = int((99 - 1) / len(input_raster_list)) 122 | for i in input_raster_list: 123 | cfg.progress.update(message='processing raster %s' % (n + 1)) 124 | out = output_list[n] 125 | nd = nodata_list[n] 126 | data_type = raster_info[n][8] 127 | # dummy bands for memory calculation 128 | dummy_bands = 7 129 | cfg.multiprocess.run( 130 | raster_path=i, function=raster_erosion, 131 | function_argument=structure, function_variable=[size, value_list], 132 | output_raster_path=out, output_data_type=data_type, 133 | n_processes=n_processes, available_ram=available_ram, 134 | output_nodata_value=nd, compress=cfg.raster_compression, 135 | dummy_bands=dummy_bands, boundary_size=structure.shape[0] + 1, 136 | virtual_raster=vrt_list[n], 137 | progress_message='processing raster %s' % (n + 1), 138 | min_progress=min_p + max_p * n, 139 | max_progress=min_p + max_p * (n + 1) 140 | ) 141 | n += 1 142 | cfg.progress.update(end=True) 143 | cfg.logger.log.info('end; band erosion: %s' % output_list) 144 | return OutputManager(paths=output_list) 145 | -------------------------------------------------------------------------------- /tests/test_table_manager.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from unittest import TestCase 3 | 4 | import remotior_sensus 5 | 6 | 7 | class TestTableManager(TestCase): 8 | 9 | def test_table_manager(self): 10 | rs = remotior_sensus.Session( 11 | n_processes=2, available_ram=1000, log_level=10 12 | ) 13 | cfg = rs.configurations 14 | data_path = Path(__file__).parent / 'data' 15 | file1 = str(data_path / 'files' / 'file1.csv') 16 | file2 = str(data_path / 'files' / 'file2.csv') 17 | file3 = str(data_path / 'files' / 'file1.dbf') 18 | cfg.logger.log.debug('>>> test open file') 19 | matrix_file = rs.table_manager.open_file( 20 | file1, field_names=['id', 'main', 'field1', 'field2', 'field3', 21 | 'name'] 22 | ) 23 | self.assertGreater(len(matrix_file[0]), 0) 24 | cfg.logger.log.debug('>>> test open csv') 25 | matrix_file1 = rs.table_manager._open_csv( 26 | file1, field_name_list=['id', 'main', 'field1', 'field2', 'field3', 27 | 'name'] 28 | ) 29 | self.assertGreater(len(matrix_file1[0]), 0) 30 | self.assertGreater(len(matrix_file1.id), 0) 31 | matrix_file2 = rs.table_manager._open_csv(file2) 32 | self.assertGreater(len(matrix_file2.value), 0) 33 | cfg.logger.log.debug('>>> test open dbf') 34 | matrix_file3 = rs.table_manager._open_dbf( 35 | file3, field_name_list=['id', 'main', 'field1', 'field2', 'field3', 36 | 'name'] 37 | ) 38 | self.assertGreater(len(matrix_file3[0]), 0) 39 | self.assertGreater(len(matrix_file3.id), 0) 40 | cfg.logger.log.debug('>>> test join matrices') 41 | joined_table = rs.table_manager.join_matrices( 42 | matrix1=matrix_file1, matrix2=matrix_file2, field1_name='main', 43 | field2_name='value', join_type='leftouter', matrix1_postfix='_m1', 44 | matrix2_postfix='_m2' 45 | ) 46 | self.assertGreater(len(joined_table.id_m1), 0) 47 | self.assertGreater(len(rs.table_manager.columns(joined_table)), 0) 48 | cfg.logger.log.debug('>>> test join tables') 49 | joined_table2 = rs.table_manager.join_tables( 50 | table1=matrix_file1, table2=matrix_file2, field1_name='main', 51 | field2_name='value', join_type='left' 52 | ) 53 | self.assertGreater(len(joined_table2.id2), 0) 54 | self.assertGreater(len(rs.table_manager.columns(joined_table2)), 0) 55 | joined_test_outer = rs.table_manager.join_tables( 56 | table1=matrix_file1, table2=matrix_file2, field1_name='id', 57 | field2_name='id', join_type='outer' 58 | ) 59 | self.assertGreater(len(joined_test_outer.id), 0) 60 | self.assertGreater(len(rs.table_manager.columns(joined_test_outer)), 0) 61 | joined_test_inner = rs.table_manager.join_tables( 62 | table1=matrix_file1, table2=matrix_file2, field1_name='main', 63 | field2_name='value', join_type='inner' 64 | ) 65 | self.assertGreater(len(joined_test_inner.id2), 0) 66 | self.assertGreater(len(rs.table_manager.columns(joined_test_inner)), 0) 67 | joined_test_right = rs.table_manager.join_tables( 68 | table1=matrix_file1, table2=matrix_file2, field1_name='id', 69 | field2_name='id', join_type='right' 70 | ) 71 | self.assertGreater(len(joined_test_right.id), 0) 72 | self.assertGreater(len(rs.table_manager.columns(joined_test_right)), 0) 73 | cfg.logger.log.debug('>>> test pivot_60') 74 | pivot1 = rs.table_manager.pivot_matrix( 75 | joined_table, row_field='value', 76 | column_function_list=[['field3_m1', 'sum']] 77 | ) 78 | self.assertGreater(len(pivot1.field3_m1_sum), 0) 79 | cfg.logger.log.debug('>>> test rename field') 80 | renamed_field_pivot1 = rs.table_manager.rename_field( 81 | pivot1, 'field3_m1_sum', 'sum' 82 | ) 83 | self.assertTrue( 84 | 'sum' in rs.table_manager.columns(renamed_field_pivot1) 85 | ) 86 | cfg.logger.log.debug('>>> test calculate') 87 | calculation = rs.table_manager.calculate( 88 | matrix=matrix_file3, expression_string='field.field3 * 1.5', 89 | output_field_name='calc' 90 | ) 91 | self.assertGreater(len(calculation.calc), 0) 92 | calculation_multi = rs.table_manager.calculate_multi( 93 | matrix=matrix_file3, 94 | expression_string_list=['"field1" * 1.5', '"field2" * 3.5'], 95 | output_field_name_list=['calc1', 'calc2'] 96 | ) 97 | self.assertGreater(len(calculation_multi.calc1), 0) 98 | output = cfg.temp.temporary_file_path(name_suffix=cfg.csv_suffix) 99 | cfg.logger.log.debug('>>> matrix to csv') 100 | rs.table_manager.matrix_to_csv( 101 | matrix=calculation, output_path=output, 102 | fields=['name', 'id', 'calc'], nodata_value=4, 103 | nodata_value_output='nodata', separator=';', decimal_separator=',' 104 | ) 105 | self.assertTrue(rs.files_directories.is_file(output)) 106 | cfg.logger.log.debug('>>> test pivot_60') 107 | pivot2 = rs.table_manager.pivot_matrix( 108 | joined_table, row_field='name_m1', 109 | column_function_list=[['field3_m2', 'sum']] 110 | ) 111 | cfg.logger.log.debug('>>> test get values') 112 | values = rs.table_manager.get_values( 113 | matrix=pivot2, value_field='field3_m2_sum', 114 | conditional_string='field.name_m1 == "a"' 115 | ) 116 | self.assertGreater(len(values), 0) 117 | values2 = rs.table_manager.get_values( 118 | matrix=pivot2, value_field='name_m1', 119 | conditional_string='field.field3_m2_sum >100' 120 | ) 121 | self.assertGreater(len(values2), 0) 122 | cfg.logger.log.debug('>>> test pivot_60') 123 | fields = rs.table_manager.pivot_matrix( 124 | joined_table, row_field='name_m1', 125 | secondary_row_field_list=['name_m2'], 126 | column_function_list=[['field3_m1', 'sum'], ['field3_m2', 'sum']], 127 | filter_string='matrix["field3_m1"] <= 1000', field_names=True 128 | ) 129 | self.assertGreater(len(fields), 0) 130 | pivot3 = rs.table_manager.pivot_matrix( 131 | joined_table, row_field='name_m1', 132 | secondary_row_field_list=['name_m2'], 133 | column_function_list=[['field3_m1', 'sum'], ['field3_m2', 'sum']], 134 | filter_string='matrix["field3_m1"] <= 1000' 135 | ) 136 | self.assertGreater(len(pivot3.name_m1), 0) 137 | cfg.logger.log.debug('>>> test redefine') 138 | redefined = rs.table_manager.redefine_matrix_columns( 139 | matrix=joined_table, 140 | input_field_names=['name_m1', 'field3_m1', 'name_m2'], 141 | output_field_names=['field1', 'field2', 'field3'] 142 | ) 143 | self.assertGreater(len(redefined.field1), 0) 144 | appended = rs.table_manager.append_values_to_table( 145 | matrix=redefined, value_list=[1, 2, 3] 146 | ) 147 | self.assertGreater(len(appended.field1), 0) 148 | 149 | # clear temporary directory 150 | rs.close() 151 | --------------------------------------------------------------------------------