cars 1.0.0rc3__cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cars/__init__.py +74 -0
- cars/applications/__init__.py +40 -0
- cars/applications/application.py +117 -0
- cars/applications/application_constants.py +29 -0
- cars/applications/application_template.py +146 -0
- cars/applications/auxiliary_filling/__init__.py +29 -0
- cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +105 -0
- cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
- cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +632 -0
- cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +90 -0
- cars/applications/dem_generation/__init__.py +30 -0
- cars/applications/dem_generation/abstract_dem_generation_app.py +116 -0
- cars/applications/dem_generation/bulldozer_config/base_config.yaml +46 -0
- cars/applications/dem_generation/bulldozer_dem_app.py +641 -0
- cars/applications/dem_generation/bulldozer_memory.py +55 -0
- cars/applications/dem_generation/dem_generation_algo.py +107 -0
- cars/applications/dem_generation/dem_generation_constants.py +32 -0
- cars/applications/dem_generation/dem_generation_wrappers.py +323 -0
- cars/applications/dense_match_filling/__init__.py +30 -0
- cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +242 -0
- cars/applications/dense_match_filling/fill_disp_algo.py +113 -0
- cars/applications/dense_match_filling/fill_disp_constants.py +39 -0
- cars/applications/dense_match_filling/fill_disp_wrappers.py +83 -0
- cars/applications/dense_match_filling/zero_padding_app.py +302 -0
- cars/applications/dense_matching/__init__.py +30 -0
- cars/applications/dense_matching/abstract_dense_matching_app.py +261 -0
- cars/applications/dense_matching/census_mccnn_sgm_app.py +1461 -0
- cars/applications/dense_matching/cpp/__init__.py +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.cpython-313-x86_64-linux-gnu.so +0 -0
- cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
- cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
- cars/applications/dense_matching/cpp/meson.build +9 -0
- cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
- cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
- cars/applications/dense_matching/dense_matching_algo.py +401 -0
- cars/applications/dense_matching/dense_matching_constants.py +89 -0
- cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
- cars/applications/dense_matching/disparity_grid_algo.py +597 -0
- cars/applications/dense_matching/loaders/__init__.py +23 -0
- cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
- cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
- cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
- cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
- cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
- cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
- cars/applications/dense_matching/loaders/config_mapping.json +13 -0
- cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
- cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
- cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
- cars/applications/dsm_filling/__init__.py +32 -0
- cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
- cars/applications/dsm_filling/border_interpolation_app.py +278 -0
- cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
- cars/applications/dsm_filling/bulldozer_filling_app.py +288 -0
- cars/applications/dsm_filling/exogenous_filling_app.py +341 -0
- cars/applications/dsm_merging/__init__.py +28 -0
- cars/applications/dsm_merging/abstract_dsm_merging_app.py +101 -0
- cars/applications/dsm_merging/weighted_fusion_app.py +639 -0
- cars/applications/grid_correction/__init__.py +30 -0
- cars/applications/grid_correction/abstract_grid_correction_app.py +103 -0
- cars/applications/grid_correction/grid_correction_app.py +557 -0
- cars/applications/grid_generation/__init__.py +30 -0
- cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
- cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
- cars/applications/grid_generation/grid_generation_algo.py +388 -0
- cars/applications/grid_generation/grid_generation_constants.py +46 -0
- cars/applications/grid_generation/transform_grid.py +88 -0
- cars/applications/ground_truth_reprojection/__init__.py +30 -0
- cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
- cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
- cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
- cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
- cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
- cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
- cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
- cars/applications/point_cloud_outlier_removal/small_components_app.py +522 -0
- cars/applications/point_cloud_outlier_removal/statistical_app.py +528 -0
- cars/applications/rasterization/__init__.py +30 -0
- cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
- cars/applications/rasterization/rasterization_algo.py +534 -0
- cars/applications/rasterization/rasterization_constants.py +38 -0
- cars/applications/rasterization/rasterization_wrappers.py +639 -0
- cars/applications/rasterization/simple_gaussian_app.py +1152 -0
- cars/applications/resampling/__init__.py +28 -0
- cars/applications/resampling/abstract_resampling_app.py +187 -0
- cars/applications/resampling/bicubic_resampling_app.py +760 -0
- cars/applications/resampling/resampling_algo.py +590 -0
- cars/applications/resampling/resampling_constants.py +36 -0
- cars/applications/resampling/resampling_wrappers.py +309 -0
- cars/applications/sensors_subsampling/__init__.py +32 -0
- cars/applications/sensors_subsampling/abstract_subsampling_app.py +109 -0
- cars/applications/sensors_subsampling/rasterio_subsampling_app.py +420 -0
- cars/applications/sensors_subsampling/subsampling_algo.py +108 -0
- cars/applications/sparse_matching/__init__.py +30 -0
- cars/applications/sparse_matching/abstract_sparse_matching_app.py +599 -0
- cars/applications/sparse_matching/sift_app.py +724 -0
- cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
- cars/applications/sparse_matching/sparse_matching_constants.py +66 -0
- cars/applications/sparse_matching/sparse_matching_wrappers.py +282 -0
- cars/applications/triangulation/__init__.py +32 -0
- cars/applications/triangulation/abstract_triangulation_app.py +227 -0
- cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
- cars/applications/triangulation/pc_transform.py +552 -0
- cars/applications/triangulation/triangulation_algo.py +371 -0
- cars/applications/triangulation/triangulation_constants.py +38 -0
- cars/applications/triangulation/triangulation_wrappers.py +259 -0
- cars/bundleadjustment.py +750 -0
- cars/cars.py +179 -0
- cars/conf/__init__.py +23 -0
- cars/conf/geoid/egm96.grd +0 -0
- cars/conf/geoid/egm96.grd.hdr +15 -0
- cars/conf/input_parameters.py +156 -0
- cars/conf/mask_cst.py +35 -0
- cars/core/__init__.py +23 -0
- cars/core/cars_logging.py +402 -0
- cars/core/constants.py +191 -0
- cars/core/constants_disparity.py +50 -0
- cars/core/datasets.py +140 -0
- cars/core/geometry/__init__.py +27 -0
- cars/core/geometry/abstract_geometry.py +1130 -0
- cars/core/geometry/shareloc_geometry.py +604 -0
- cars/core/inputs.py +568 -0
- cars/core/outputs.py +176 -0
- cars/core/preprocessing.py +722 -0
- cars/core/projection.py +843 -0
- cars/core/roi_tools.py +215 -0
- cars/core/tiling.py +774 -0
- cars/core/utils.py +164 -0
- cars/data_structures/__init__.py +23 -0
- cars/data_structures/cars_dataset.py +1544 -0
- cars/data_structures/cars_dict.py +74 -0
- cars/data_structures/corresponding_tiles_tools.py +186 -0
- cars/data_structures/dataframe_converter.py +185 -0
- cars/data_structures/format_transformation.py +297 -0
- cars/devibrate.py +689 -0
- cars/extractroi.py +264 -0
- cars/orchestrator/__init__.py +23 -0
- cars/orchestrator/achievement_tracker.py +125 -0
- cars/orchestrator/cluster/__init__.py +37 -0
- cars/orchestrator/cluster/abstract_cluster.py +250 -0
- cars/orchestrator/cluster/abstract_dask_cluster.py +381 -0
- cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
- cars/orchestrator/cluster/dask_config/README.md +94 -0
- cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
- cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
- cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
- cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
- cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
- cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
- cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
- cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
- cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
- cars/orchestrator/cluster/local_dask_cluster.py +116 -0
- cars/orchestrator/cluster/log_wrapper.py +728 -0
- cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
- cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
- cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
- cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
- cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
- cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +986 -0
- cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
- cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
- cars/orchestrator/cluster/sequential_cluster.py +139 -0
- cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
- cars/orchestrator/memory_tools.py +47 -0
- cars/orchestrator/orchestrator.py +755 -0
- cars/orchestrator/orchestrator_constants.py +29 -0
- cars/orchestrator/registry/__init__.py +23 -0
- cars/orchestrator/registry/abstract_registry.py +143 -0
- cars/orchestrator/registry/compute_registry.py +106 -0
- cars/orchestrator/registry/id_generator.py +116 -0
- cars/orchestrator/registry/replacer_registry.py +213 -0
- cars/orchestrator/registry/saver_registry.py +363 -0
- cars/orchestrator/registry/unseen_registry.py +118 -0
- cars/orchestrator/tiles_profiler.py +279 -0
- cars/pipelines/__init__.py +26 -0
- cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
- cars/pipelines/conf_resolution/conf_first_resolution.yaml +4 -0
- cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
- cars/pipelines/default/__init__.py +26 -0
- cars/pipelines/default/default_pipeline.py +1095 -0
- cars/pipelines/filling/__init__.py +26 -0
- cars/pipelines/filling/filling.py +981 -0
- cars/pipelines/formatting/__init__.py +26 -0
- cars/pipelines/formatting/formatting.py +190 -0
- cars/pipelines/merging/__init__.py +26 -0
- cars/pipelines/merging/merging.py +439 -0
- cars/pipelines/parameters/__init__.py +0 -0
- cars/pipelines/parameters/advanced_parameters.py +256 -0
- cars/pipelines/parameters/advanced_parameters_constants.py +68 -0
- cars/pipelines/parameters/application_parameters.py +72 -0
- cars/pipelines/parameters/depth_map_inputs.py +0 -0
- cars/pipelines/parameters/dsm_inputs.py +349 -0
- cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
- cars/pipelines/parameters/output_constants.py +52 -0
- cars/pipelines/parameters/output_parameters.py +435 -0
- cars/pipelines/parameters/sensor_inputs.py +859 -0
- cars/pipelines/parameters/sensor_inputs_constants.py +51 -0
- cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
- cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
- cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
- cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
- cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
- cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
- cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
- cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
- cars/pipelines/pipeline.py +119 -0
- cars/pipelines/pipeline_constants.py +38 -0
- cars/pipelines/pipeline_template.py +135 -0
- cars/pipelines/subsampling/__init__.py +26 -0
- cars/pipelines/subsampling/subsampling.py +358 -0
- cars/pipelines/surface_modeling/__init__.py +26 -0
- cars/pipelines/surface_modeling/surface_modeling.py +2098 -0
- cars/pipelines/tie_points/__init__.py +26 -0
- cars/pipelines/tie_points/tie_points.py +536 -0
- cars/starter.py +167 -0
- cars-1.0.0rc3.dist-info/METADATA +289 -0
- cars-1.0.0rc3.dist-info/RECORD +220 -0
- cars-1.0.0rc3.dist-info/WHEEL +6 -0
- cars-1.0.0rc3.dist-info/entry_points.txt +8 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# coding: utf8
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2020 Centre National d'Etudes Spatiales (CNES).
|
|
5
|
+
#
|
|
6
|
+
# This file is part of CARS
|
|
7
|
+
# (see https://github.com/CNES/cars).
|
|
8
|
+
#
|
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
# you may not use this file except in compliance with the License.
|
|
11
|
+
# You may obtain a copy of the License at
|
|
12
|
+
#
|
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
#
|
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
# See the License for the specific language governing permissions and
|
|
19
|
+
# limitations under the License.
|
|
20
|
+
#
|
|
21
|
+
"""
|
|
22
|
+
This module contains the bulldozer dsm filling application class.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import contextlib
|
|
26
|
+
import logging
|
|
27
|
+
import os
|
|
28
|
+
import shutil
|
|
29
|
+
import warnings
|
|
30
|
+
|
|
31
|
+
import numpy as np
|
|
32
|
+
import rasterio as rio
|
|
33
|
+
import yaml
|
|
34
|
+
from bulldozer.pipeline.bulldozer_pipeline import dsm_to_dtm
|
|
35
|
+
from json_checker import Checker, Or
|
|
36
|
+
from pyproj import CRS
|
|
37
|
+
from rasterio.errors import NodataShadowWarning
|
|
38
|
+
from shapely import Polygon
|
|
39
|
+
|
|
40
|
+
from cars.applications.dem_generation.bulldozer_memory import (
|
|
41
|
+
can_allocate_shared_memory,
|
|
42
|
+
)
|
|
43
|
+
from cars.core import inputs, projection
|
|
44
|
+
from cars.orchestrator.cluster.log_wrapper import cars_profile
|
|
45
|
+
|
|
46
|
+
from .abstract_dsm_filling_app import DsmFilling
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class BulldozerFilling(DsmFilling, short_name="bulldozer"):
|
|
50
|
+
"""
|
|
51
|
+
Bulldozer filling
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(self, conf=None):
|
|
55
|
+
"""
|
|
56
|
+
Init function of BulldozerFilling
|
|
57
|
+
|
|
58
|
+
:param conf: configuration for BulldozerFilling
|
|
59
|
+
:return: an application_to_use object
|
|
60
|
+
"""
|
|
61
|
+
super().__init__(conf=conf)
|
|
62
|
+
|
|
63
|
+
# check conf
|
|
64
|
+
self.used_method = self.used_config["method"]
|
|
65
|
+
self.classification = self.used_config["classification"]
|
|
66
|
+
self.save_intermediate_data = self.used_config["save_intermediate_data"]
|
|
67
|
+
|
|
68
|
+
def check_conf(self, conf):
|
|
69
|
+
|
|
70
|
+
# init conf
|
|
71
|
+
if conf is not None:
|
|
72
|
+
overloaded_conf = conf.copy()
|
|
73
|
+
else:
|
|
74
|
+
conf = {}
|
|
75
|
+
overloaded_conf = {}
|
|
76
|
+
|
|
77
|
+
# Check if can use bulldozer
|
|
78
|
+
can_allocate_sm, log_message = can_allocate_shared_memory()
|
|
79
|
+
if not can_allocate_sm:
|
|
80
|
+
logging.error(log_message)
|
|
81
|
+
logging.error("DSM filling with bulldozer might crash")
|
|
82
|
+
|
|
83
|
+
# Overload conf
|
|
84
|
+
overloaded_conf["method"] = conf.get("method", "bulldozer")
|
|
85
|
+
overloaded_conf["classification"] = conf.get("classification", "nodata")
|
|
86
|
+
|
|
87
|
+
if isinstance(overloaded_conf["classification"], str):
|
|
88
|
+
overloaded_conf["classification"] = [
|
|
89
|
+
overloaded_conf["classification"]
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
overloaded_conf["save_intermediate_data"] = conf.get(
|
|
93
|
+
"save_intermediate_data", False
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
rectification_schema = {
|
|
97
|
+
"method": str,
|
|
98
|
+
"classification": Or(None, [str]),
|
|
99
|
+
"save_intermediate_data": bool,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Check conf
|
|
103
|
+
checker = Checker(rectification_schema)
|
|
104
|
+
checker.validate(overloaded_conf)
|
|
105
|
+
|
|
106
|
+
return overloaded_conf
|
|
107
|
+
|
|
108
|
+
@cars_profile(name="Bulldozer filling")
|
|
109
|
+
def run( # pylint: disable=too-many-positional-arguments # noqa C901
|
|
110
|
+
self,
|
|
111
|
+
dsm_file,
|
|
112
|
+
classif_file,
|
|
113
|
+
filling_file,
|
|
114
|
+
dump_dir,
|
|
115
|
+
roi_polys,
|
|
116
|
+
roi_epsg,
|
|
117
|
+
orchestrator,
|
|
118
|
+
dsm_dir=None,
|
|
119
|
+
):
|
|
120
|
+
"""
|
|
121
|
+
Run dsm filling using initial elevation and the current dsm
|
|
122
|
+
Replaces dsm.tif by the filled dsm. Adds a new band
|
|
123
|
+
to filling.tif if it exists.
|
|
124
|
+
The old dsm is saved in dump_dir.
|
|
125
|
+
|
|
126
|
+
roi_poly can any of these objects :
|
|
127
|
+
- a list of Shapely Polygons
|
|
128
|
+
- a Shapely Polygon
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
if self.classification is None:
|
|
132
|
+
self.classification = ["nodata"]
|
|
133
|
+
|
|
134
|
+
if not os.path.exists(dump_dir):
|
|
135
|
+
os.makedirs(dump_dir)
|
|
136
|
+
|
|
137
|
+
old_dsm_path = os.path.join(dump_dir, "dsm_not_filled.tif")
|
|
138
|
+
new_dsm_path = os.path.join(dump_dir, "dsm_filled.tif")
|
|
139
|
+
|
|
140
|
+
if dsm_dir is not None:
|
|
141
|
+
dsm_path_out = os.path.join(dsm_dir, "dsm.tif")
|
|
142
|
+
filling_path_out = os.path.join(dsm_dir, "filling.tif")
|
|
143
|
+
else:
|
|
144
|
+
dsm_path_out = dsm_file
|
|
145
|
+
filling_path_out = filling_file
|
|
146
|
+
|
|
147
|
+
# create the config for the bulldozer execution
|
|
148
|
+
bull_conf_path = os.path.join(
|
|
149
|
+
os.path.dirname(__file__), "bulldozer_config/base_config.yaml"
|
|
150
|
+
)
|
|
151
|
+
with open(bull_conf_path, "r", encoding="utf8") as bull_conf_file:
|
|
152
|
+
bull_conf = yaml.safe_load(bull_conf_file)
|
|
153
|
+
|
|
154
|
+
bull_conf["dsm_path"] = dsm_file
|
|
155
|
+
bull_conf["output_dir"] = os.path.join(dump_dir, "bulldozer")
|
|
156
|
+
|
|
157
|
+
if orchestrator is not None:
|
|
158
|
+
if (
|
|
159
|
+
orchestrator.get_conf()["mode"] == "multiprocessing"
|
|
160
|
+
or orchestrator.get_conf()["mode"] == "local_dask"
|
|
161
|
+
):
|
|
162
|
+
bull_conf["nb_max_workers"] = orchestrator.get_conf()[
|
|
163
|
+
"nb_workers"
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
bull_conf_path = os.path.join(dump_dir, "bulldozer_config.yaml")
|
|
167
|
+
with open(bull_conf_path, "w", encoding="utf8") as bull_conf_file:
|
|
168
|
+
yaml.dump(bull_conf, bull_conf_file)
|
|
169
|
+
|
|
170
|
+
dtm_path = os.path.join(bull_conf["output_dir"], "dtm.tif")
|
|
171
|
+
|
|
172
|
+
# get dsm to be filled and its metadata
|
|
173
|
+
with rio.open(dsm_file) as in_dsm:
|
|
174
|
+
dsm = in_dsm.read(1)
|
|
175
|
+
dsm_tr = in_dsm.transform
|
|
176
|
+
dsm_crs = in_dsm.crs
|
|
177
|
+
dsm_meta = in_dsm.meta
|
|
178
|
+
|
|
179
|
+
roi_raster = np.ones(dsm.shape)
|
|
180
|
+
|
|
181
|
+
if isinstance(roi_polys, list):
|
|
182
|
+
roi_polys_outepsg = []
|
|
183
|
+
for poly in roi_polys:
|
|
184
|
+
if isinstance(poly, Polygon):
|
|
185
|
+
roi_poly_outepsg = projection.polygon_projection_crs(
|
|
186
|
+
poly, CRS(roi_epsg), dsm_crs
|
|
187
|
+
)
|
|
188
|
+
roi_polys_outepsg.append(roi_poly_outepsg)
|
|
189
|
+
|
|
190
|
+
roi_raster = rio.features.rasterize(
|
|
191
|
+
roi_polys_outepsg, out_shape=roi_raster.shape, transform=dsm_tr
|
|
192
|
+
)
|
|
193
|
+
elif isinstance(roi_polys, Polygon):
|
|
194
|
+
roi_poly_outepsg = projection.polygon_projection_crs(
|
|
195
|
+
roi_polys, CRS(roi_epsg), dsm_crs
|
|
196
|
+
)
|
|
197
|
+
roi_raster = rio.features.rasterize(
|
|
198
|
+
[roi_poly_outepsg], out_shape=roi_raster.shape, transform=dsm_tr
|
|
199
|
+
)
|
|
200
|
+
try:
|
|
201
|
+
try:
|
|
202
|
+
# suppress prints in bulldozer by redirecting stdout&stderr
|
|
203
|
+
with open(os.devnull, "w", encoding="utf8") as devnull:
|
|
204
|
+
with (
|
|
205
|
+
contextlib.redirect_stdout(devnull),
|
|
206
|
+
contextlib.redirect_stderr(devnull),
|
|
207
|
+
):
|
|
208
|
+
dsm_to_dtm(bull_conf_path)
|
|
209
|
+
except Exception:
|
|
210
|
+
logging.info(
|
|
211
|
+
"Bulldozer failed on its first execution. Retrying"
|
|
212
|
+
)
|
|
213
|
+
# suppress prints in bulldozer by redirecting stdout&stderr
|
|
214
|
+
with open(os.devnull, "w", encoding="utf8") as devnull:
|
|
215
|
+
with (
|
|
216
|
+
contextlib.redirect_stdout(devnull),
|
|
217
|
+
contextlib.redirect_stderr(devnull),
|
|
218
|
+
):
|
|
219
|
+
dsm_to_dtm(bull_conf_path)
|
|
220
|
+
except Exception:
|
|
221
|
+
logging.warning(
|
|
222
|
+
"Bulldozer failed on its second execution."
|
|
223
|
+
+ " The DSM could not be filled."
|
|
224
|
+
)
|
|
225
|
+
return None
|
|
226
|
+
with rio.open(dtm_path) as in_dtm:
|
|
227
|
+
dtm = in_dtm.read(1)
|
|
228
|
+
|
|
229
|
+
if self.save_intermediate_data:
|
|
230
|
+
with rio.open(old_dsm_path, "w", **dsm_meta) as out_dsm:
|
|
231
|
+
out_dsm.write(dsm, 1)
|
|
232
|
+
|
|
233
|
+
if classif_file is not None and os.path.exists(classif_file):
|
|
234
|
+
classif_descriptions = inputs.get_descriptions_bands(classif_file)
|
|
235
|
+
else:
|
|
236
|
+
classif_descriptions = []
|
|
237
|
+
combined_mask = np.zeros_like(dsm).astype(np.uint8)
|
|
238
|
+
for label in self.classification:
|
|
239
|
+
if label in classif_descriptions:
|
|
240
|
+
index_classif = classif_descriptions.index(label) + 1
|
|
241
|
+
with rio.open(classif_file) as in_classif:
|
|
242
|
+
classif = in_classif.read(index_classif)
|
|
243
|
+
|
|
244
|
+
with warnings.catch_warnings():
|
|
245
|
+
warnings.simplefilter("ignore", NodataShadowWarning)
|
|
246
|
+
classif_msk = in_classif.read_masks(1)
|
|
247
|
+
classif[classif_msk == 0] = 0
|
|
248
|
+
|
|
249
|
+
filling_mask = np.logical_and(classif, roi_raster > 0)
|
|
250
|
+
elif label == "nodata":
|
|
251
|
+
if classif_file is not None and os.path.exists(classif_file):
|
|
252
|
+
with rio.open(classif_file) as in_classif:
|
|
253
|
+
classif_msk = in_classif.read_masks(1)
|
|
254
|
+
classif = ~classif_msk
|
|
255
|
+
else:
|
|
256
|
+
with rio.open(dsm_file) as in_dsm:
|
|
257
|
+
dsm_msk = in_dsm.read_masks(1)
|
|
258
|
+
classif = ~dsm_msk
|
|
259
|
+
filling_mask = np.logical_and(classif, roi_raster > 0)
|
|
260
|
+
else:
|
|
261
|
+
logging.error(
|
|
262
|
+
"Label {} not found in classification "
|
|
263
|
+
"descriptions {}".format(label, classif_descriptions)
|
|
264
|
+
)
|
|
265
|
+
continue
|
|
266
|
+
logging.info("Filling of {} with Bulldozer DTM".format(label))
|
|
267
|
+
dsm[filling_mask] = dtm[filling_mask]
|
|
268
|
+
combined_mask = np.logical_or(combined_mask, filling_mask)
|
|
269
|
+
|
|
270
|
+
with rio.open(dsm_path_out, "w", **dsm_meta) as out_dsm:
|
|
271
|
+
out_dsm.write(dsm, 1)
|
|
272
|
+
if self.save_intermediate_data:
|
|
273
|
+
shutil.copy2(dsm_path_out, new_dsm_path)
|
|
274
|
+
|
|
275
|
+
if filling_file is not None:
|
|
276
|
+
with rio.open(filling_file, "r") as src:
|
|
277
|
+
fill_meta = src.meta
|
|
278
|
+
bands = [src.read(i + 1) for i in range(src.count)]
|
|
279
|
+
bands_desc = [src.descriptions[i] for i in range(src.count)]
|
|
280
|
+
fill_meta["count"] += 1
|
|
281
|
+
bands.append(combined_mask.astype(np.uint8))
|
|
282
|
+
bands_desc.append("bulldozer")
|
|
283
|
+
|
|
284
|
+
with rio.open(filling_path_out, "w", **fill_meta) as out:
|
|
285
|
+
for i, band in enumerate(bands):
|
|
286
|
+
out.write(band, i + 1)
|
|
287
|
+
out.set_band_description(i + 1, bands_desc[i])
|
|
288
|
+
return dtm_path
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# coding: utf8
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2020 Centre National d'Etudes Spatiales (CNES).
|
|
5
|
+
#
|
|
6
|
+
# This file is part of CARS
|
|
7
|
+
# (see https://github.com/CNES/cars).
|
|
8
|
+
#
|
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
# you may not use this file except in compliance with the License.
|
|
11
|
+
# You may obtain a copy of the License at
|
|
12
|
+
#
|
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
#
|
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
# See the License for the specific language governing permissions and
|
|
19
|
+
# limitations under the License.
|
|
20
|
+
#
|
|
21
|
+
"""
|
|
22
|
+
This module contains the exogenous dsm filling application class.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import logging
|
|
26
|
+
import os
|
|
27
|
+
import shutil
|
|
28
|
+
import warnings
|
|
29
|
+
|
|
30
|
+
import numpy as np
|
|
31
|
+
import rasterio as rio
|
|
32
|
+
from json_checker import Checker, Or
|
|
33
|
+
from pyproj import CRS
|
|
34
|
+
from rasterio.enums import Resampling
|
|
35
|
+
from rasterio.errors import NodataShadowWarning
|
|
36
|
+
from rasterio.warp import reproject
|
|
37
|
+
from shapely import Polygon
|
|
38
|
+
|
|
39
|
+
from cars.core import inputs, projection
|
|
40
|
+
from cars.orchestrator.cluster.log_wrapper import cars_profile
|
|
41
|
+
|
|
42
|
+
from .abstract_dsm_filling_app import DsmFilling
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ExogenousFilling(DsmFilling, short_name="exogenous_filling"):
|
|
46
|
+
"""
|
|
47
|
+
Exogenous filling
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(self, conf=None):
|
|
51
|
+
"""
|
|
52
|
+
Init function of ExogenousFilling
|
|
53
|
+
|
|
54
|
+
:param conf: configuration for ExogenousFilling
|
|
55
|
+
:return: an application_to_use object
|
|
56
|
+
"""
|
|
57
|
+
super().__init__(conf=conf)
|
|
58
|
+
|
|
59
|
+
# check conf
|
|
60
|
+
self.used_method = self.used_config["method"]
|
|
61
|
+
self.classification = self.used_config["classification"]
|
|
62
|
+
self.fill_with_geoid = self.used_config["fill_with_geoid"]
|
|
63
|
+
self.interpolation_method = self.used_config["interpolation_method"]
|
|
64
|
+
self.save_intermediate_data = self.used_config["save_intermediate_data"]
|
|
65
|
+
|
|
66
|
+
def check_conf(self, conf):
|
|
67
|
+
|
|
68
|
+
# init conf
|
|
69
|
+
if conf is not None:
|
|
70
|
+
overloaded_conf = conf.copy()
|
|
71
|
+
else:
|
|
72
|
+
conf = {}
|
|
73
|
+
overloaded_conf = {}
|
|
74
|
+
|
|
75
|
+
# Overload conf
|
|
76
|
+
overloaded_conf["method"] = conf.get("method", "exogenous_filling")
|
|
77
|
+
overloaded_conf["classification"] = conf.get("classification", "nodata")
|
|
78
|
+
if isinstance(overloaded_conf["classification"], str):
|
|
79
|
+
overloaded_conf["classification"] = [
|
|
80
|
+
overloaded_conf["classification"]
|
|
81
|
+
]
|
|
82
|
+
overloaded_conf["fill_with_geoid"] = conf.get("fill_with_geoid", None)
|
|
83
|
+
overloaded_conf["interpolation_method"] = conf.get(
|
|
84
|
+
"interpolation_method", "bilinear"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if overloaded_conf["interpolation_method"] not in ["bilinear", "cubic"]:
|
|
88
|
+
# pylint: disable=inconsistent-quotes
|
|
89
|
+
raise RuntimeError(
|
|
90
|
+
f"Invalid interpolation method"
|
|
91
|
+
f"{overloaded_conf['interpolation_method']}, "
|
|
92
|
+
f"supported modes are bilinear and cubic."
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
overloaded_conf["save_intermediate_data"] = conf.get(
|
|
96
|
+
"save_intermediate_data", False
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
rectification_schema = {
|
|
100
|
+
"method": str,
|
|
101
|
+
"classification": Or(None, [str]),
|
|
102
|
+
"fill_with_geoid": Or(None, [str]),
|
|
103
|
+
"interpolation_method": str,
|
|
104
|
+
"save_intermediate_data": bool,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# Check conf
|
|
108
|
+
checker = Checker(rectification_schema)
|
|
109
|
+
checker.validate(overloaded_conf)
|
|
110
|
+
|
|
111
|
+
return overloaded_conf
|
|
112
|
+
|
|
113
|
+
@cars_profile(name="Exogeneous filling")
|
|
114
|
+
def run( # pylint: disable=too-many-positional-arguments # noqa C901
|
|
115
|
+
self,
|
|
116
|
+
dsm_file,
|
|
117
|
+
classif_file,
|
|
118
|
+
filling_file,
|
|
119
|
+
dump_dir,
|
|
120
|
+
roi_polys,
|
|
121
|
+
roi_epsg,
|
|
122
|
+
output_geoid,
|
|
123
|
+
geom_plugin,
|
|
124
|
+
dsm_dir=None,
|
|
125
|
+
):
|
|
126
|
+
"""
|
|
127
|
+
Run dsm filling using initial elevation and the current dsm
|
|
128
|
+
Replaces dsm.tif by the filled dsm. Adds a new band
|
|
129
|
+
to filling.tif if it exists.
|
|
130
|
+
The old dsm is saved in dump_dir.
|
|
131
|
+
|
|
132
|
+
roi_poly can any of these objects :
|
|
133
|
+
- a list of Shapely Polygons
|
|
134
|
+
- a Shapely Polygon
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
if dsm_dir is not None:
|
|
138
|
+
dsm_path_out = os.path.join(dsm_dir, "dsm.tif")
|
|
139
|
+
filling_path_out = os.path.join(dsm_dir, "filling.tif")
|
|
140
|
+
else:
|
|
141
|
+
dsm_path_out = dsm_file
|
|
142
|
+
filling_path_out = filling_file
|
|
143
|
+
|
|
144
|
+
if self.classification is None:
|
|
145
|
+
self.classification = ["nodata"]
|
|
146
|
+
|
|
147
|
+
if self.fill_with_geoid is None:
|
|
148
|
+
self.fill_with_geoid = []
|
|
149
|
+
|
|
150
|
+
interpolation_methods_dict = {
|
|
151
|
+
"bilinear": Resampling.bilinear,
|
|
152
|
+
"cubic": Resampling.cubic,
|
|
153
|
+
}
|
|
154
|
+
interpolation_method = interpolation_methods_dict.get(
|
|
155
|
+
self.interpolation_method, Resampling.bilinear
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
if geom_plugin is None:
|
|
159
|
+
logging.error(
|
|
160
|
+
"No DEM was provided, exogenous_filling will not run."
|
|
161
|
+
)
|
|
162
|
+
return
|
|
163
|
+
|
|
164
|
+
if not os.path.exists(dump_dir):
|
|
165
|
+
os.makedirs(dump_dir)
|
|
166
|
+
|
|
167
|
+
old_dsm_path = os.path.join(dump_dir, "dsm_not_filled.tif")
|
|
168
|
+
new_dsm_path = os.path.join(dump_dir, "dsm_filled.tif")
|
|
169
|
+
|
|
170
|
+
# get dsm to be filled and its metadata
|
|
171
|
+
with rio.open(dsm_file) as in_dsm:
|
|
172
|
+
dsm = in_dsm.read(1)
|
|
173
|
+
dsm_tr = in_dsm.transform
|
|
174
|
+
dsm_crs = in_dsm.crs
|
|
175
|
+
dsm_meta = in_dsm.meta
|
|
176
|
+
|
|
177
|
+
roi_raster = np.ones(dsm.shape)
|
|
178
|
+
|
|
179
|
+
if isinstance(roi_polys, list):
|
|
180
|
+
roi_polys_outepsg = []
|
|
181
|
+
for poly in roi_polys:
|
|
182
|
+
if isinstance(poly, Polygon):
|
|
183
|
+
roi_poly_outepsg = projection.polygon_projection_crs(
|
|
184
|
+
poly, CRS(roi_epsg), dsm_crs
|
|
185
|
+
)
|
|
186
|
+
roi_polys_outepsg.append(roi_poly_outepsg)
|
|
187
|
+
|
|
188
|
+
roi_raster = rio.features.rasterize(
|
|
189
|
+
roi_polys_outepsg, out_shape=roi_raster.shape, transform=dsm_tr
|
|
190
|
+
)
|
|
191
|
+
elif isinstance(roi_polys, Polygon):
|
|
192
|
+
roi_poly_outepsg = projection.polygon_projection_crs(
|
|
193
|
+
roi_polys, CRS(roi_epsg), dsm_crs
|
|
194
|
+
)
|
|
195
|
+
roi_raster = rio.features.rasterize(
|
|
196
|
+
[roi_poly_outepsg], out_shape=roi_raster.shape, transform=dsm_tr
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Get the initial elevation
|
|
200
|
+
with rio.open(geom_plugin.dem) as in_elev:
|
|
201
|
+
# Reproject the elevation data to match the DSM
|
|
202
|
+
elev_data = np.empty(dsm.shape, dtype=in_elev.dtypes[0])
|
|
203
|
+
|
|
204
|
+
reproject(
|
|
205
|
+
source=rio.band(in_elev, 1),
|
|
206
|
+
destination=elev_data,
|
|
207
|
+
src_transform=in_elev.transform,
|
|
208
|
+
src_crs=in_elev.crs,
|
|
209
|
+
dst_transform=dsm_tr,
|
|
210
|
+
dst_crs=dsm_crs,
|
|
211
|
+
resampling=interpolation_method,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
if self.save_intermediate_data:
|
|
215
|
+
reprojected_dem_path = os.path.join(dump_dir, "reprojected_dem.tif")
|
|
216
|
+
with rio.open(reprojected_dem_path, "w", **dsm_meta) as out_elev:
|
|
217
|
+
out_elev.write(elev_data, 1)
|
|
218
|
+
|
|
219
|
+
with rio.open(geom_plugin.geoid) as in_geoid:
|
|
220
|
+
# Reproject the geoid data to match the DSM
|
|
221
|
+
input_geoid_data = np.empty(dsm.shape, dtype=in_geoid.dtypes[0])
|
|
222
|
+
|
|
223
|
+
reproject(
|
|
224
|
+
source=rio.band(in_geoid, 1),
|
|
225
|
+
destination=input_geoid_data,
|
|
226
|
+
src_transform=in_geoid.transform,
|
|
227
|
+
src_crs=in_geoid.crs,
|
|
228
|
+
dst_transform=dsm_tr,
|
|
229
|
+
dst_crs=dsm_crs,
|
|
230
|
+
resampling=interpolation_method,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
if self.save_intermediate_data:
|
|
234
|
+
reprojected_geoid_path = os.path.join(
|
|
235
|
+
dump_dir, "reprojected_input_geoid.tif"
|
|
236
|
+
)
|
|
237
|
+
with rio.open(reprojected_geoid_path, "w", **dsm_meta) as out_geoid:
|
|
238
|
+
out_geoid.write(input_geoid_data, 1)
|
|
239
|
+
|
|
240
|
+
if isinstance(output_geoid, str):
|
|
241
|
+
with rio.open(output_geoid) as in_geoid:
|
|
242
|
+
# Reproject the geoid data to match the DSM
|
|
243
|
+
output_geoid_data = np.empty(
|
|
244
|
+
dsm.shape, dtype=in_geoid.dtypes[0]
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
reproject(
|
|
248
|
+
source=rio.band(in_geoid, 1),
|
|
249
|
+
destination=output_geoid_data,
|
|
250
|
+
src_transform=in_geoid.transform,
|
|
251
|
+
src_crs=in_geoid.crs,
|
|
252
|
+
dst_transform=dsm_tr,
|
|
253
|
+
dst_crs=dsm_crs,
|
|
254
|
+
resampling=interpolation_method,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
if self.save_intermediate_data:
|
|
258
|
+
reprojected_geoid_path = os.path.join(
|
|
259
|
+
dump_dir, "reprojected_output_geoid.tif"
|
|
260
|
+
)
|
|
261
|
+
with rio.open(
|
|
262
|
+
reprojected_geoid_path, "w", **dsm_meta
|
|
263
|
+
) as out_geoid:
|
|
264
|
+
out_geoid.write(input_geoid_data, 1)
|
|
265
|
+
|
|
266
|
+
# Save old dsm
|
|
267
|
+
if self.save_intermediate_data:
|
|
268
|
+
with rio.open(old_dsm_path, "w", **dsm_meta) as out_dsm:
|
|
269
|
+
out_dsm.write(dsm, 1)
|
|
270
|
+
|
|
271
|
+
# Fill DSM for every label
|
|
272
|
+
combined_mask = np.zeros_like(dsm).astype(np.uint8)
|
|
273
|
+
if classif_file is not None:
|
|
274
|
+
classif_descriptions = inputs.get_descriptions_bands(classif_file)
|
|
275
|
+
else:
|
|
276
|
+
classif_descriptions = []
|
|
277
|
+
for label in self.classification:
|
|
278
|
+
if label in classif_descriptions:
|
|
279
|
+
index_classif = classif_descriptions.index(label) + 1
|
|
280
|
+
with rio.open(classif_file) as in_classif:
|
|
281
|
+
classif = in_classif.read(index_classif)
|
|
282
|
+
classif_msk = in_classif.read_masks(1)
|
|
283
|
+
classif[classif_msk == 0] = 0
|
|
284
|
+
filling_mask = np.logical_and(classif, roi_raster > 0)
|
|
285
|
+
elif label == "nodata":
|
|
286
|
+
if classif_file is not None:
|
|
287
|
+
with rio.open(classif_file) as in_classif:
|
|
288
|
+
with warnings.catch_warnings():
|
|
289
|
+
warnings.simplefilter("ignore", NodataShadowWarning)
|
|
290
|
+
classif_msk = in_classif.read_masks(1)
|
|
291
|
+
classif = ~classif_msk
|
|
292
|
+
else:
|
|
293
|
+
with rio.open(dsm_file) as in_dsm:
|
|
294
|
+
dsm_msk = in_dsm.read_masks(1)
|
|
295
|
+
classif = ~dsm_msk
|
|
296
|
+
filling_mask = np.logical_and(classif, roi_raster > 0)
|
|
297
|
+
else:
|
|
298
|
+
logging.error(
|
|
299
|
+
"Label {} not found in classification "
|
|
300
|
+
"descriptions {}".format(label, classif_descriptions)
|
|
301
|
+
)
|
|
302
|
+
continue
|
|
303
|
+
|
|
304
|
+
if label in self.fill_with_geoid:
|
|
305
|
+
logging.info("Filling of {} with geoid".format(label))
|
|
306
|
+
dsm[filling_mask] = 0
|
|
307
|
+
else:
|
|
308
|
+
logging.info("Filling of {} with DEM and geoid".format(label))
|
|
309
|
+
dsm[filling_mask] = elev_data[filling_mask]
|
|
310
|
+
|
|
311
|
+
# apply offset to project on geoid if needed
|
|
312
|
+
if output_geoid is not True:
|
|
313
|
+
if isinstance(output_geoid, bool) and output_geoid is False:
|
|
314
|
+
# out geoid is ellipsoid: add geoid-ellipsoid distance
|
|
315
|
+
dsm[filling_mask] += input_geoid_data[filling_mask]
|
|
316
|
+
elif isinstance(output_geoid, str):
|
|
317
|
+
# out geoid is a new geoid whose path is in output_geoid:
|
|
318
|
+
# add carsgeoid-ellipsoid then add ellipsoid-outgeoid
|
|
319
|
+
dsm[filling_mask] += input_geoid_data[filling_mask]
|
|
320
|
+
dsm[filling_mask] -= output_geoid_data[filling_mask]
|
|
321
|
+
|
|
322
|
+
combined_mask = np.logical_or(combined_mask, filling_mask)
|
|
323
|
+
|
|
324
|
+
with rio.open(dsm_path_out, "w", **dsm_meta) as out_dsm:
|
|
325
|
+
out_dsm.write(dsm, 1)
|
|
326
|
+
if self.save_intermediate_data:
|
|
327
|
+
shutil.copy2(dsm_path_out, new_dsm_path)
|
|
328
|
+
|
|
329
|
+
if filling_file is not None:
|
|
330
|
+
with rio.open(filling_file, "r") as src:
|
|
331
|
+
fill_meta = src.meta
|
|
332
|
+
bands = [src.read(i + 1) for i in range(src.count)]
|
|
333
|
+
bands_desc = [src.descriptions[i] for i in range(src.count)]
|
|
334
|
+
fill_meta["count"] += 1
|
|
335
|
+
bands.append(combined_mask)
|
|
336
|
+
bands_desc.append("filling_exogenous")
|
|
337
|
+
|
|
338
|
+
with rio.open(filling_path_out, "w", **fill_meta) as out:
|
|
339
|
+
for i, band in enumerate(bands):
|
|
340
|
+
out.write(band, i + 1)
|
|
341
|
+
out.set_band_description(i + 1, bands_desc[i])
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# !/usr/bin/env python
|
|
2
|
+
# coding: utf8
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2025 Centre National d'Etudes Spatiales (CNES).
|
|
5
|
+
#
|
|
6
|
+
# This file is part of CARS
|
|
7
|
+
# (see https://github.com/CNES/cars).
|
|
8
|
+
#
|
|
9
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
+
# you may not use this file except in compliance with the License.
|
|
11
|
+
# You may obtain a copy of the License at
|
|
12
|
+
#
|
|
13
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
+
#
|
|
15
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
16
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
+
# See the License for the specific language governing permissions and
|
|
19
|
+
# limitations under the License.
|
|
20
|
+
#
|
|
21
|
+
"""
|
|
22
|
+
CARS dsm mergingmodule init file
|
|
23
|
+
"""
|
|
24
|
+
# flake8: noqa: F401
|
|
25
|
+
|
|
26
|
+
from cars.applications.dsm_merging.abstract_dsm_merging_app import DsmMerging
|
|
27
|
+
|
|
28
|
+
from . import weighted_fusion_app
|