cars 1.0.0rc1__cp313-cp313-musllinux_1_2_i686.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.

Potentially problematic release.


This version of cars might be problematic. Click here for more details.

Files changed (202) hide show
  1. cars/__init__.py +74 -0
  2. cars/applications/__init__.py +37 -0
  3. cars/applications/application.py +117 -0
  4. cars/applications/application_constants.py +29 -0
  5. cars/applications/application_template.py +146 -0
  6. cars/applications/auxiliary_filling/__init__.py +29 -0
  7. cars/applications/auxiliary_filling/abstract_auxiliary_filling_app.py +104 -0
  8. cars/applications/auxiliary_filling/auxiliary_filling_algo.py +475 -0
  9. cars/applications/auxiliary_filling/auxiliary_filling_from_sensors_app.py +630 -0
  10. cars/applications/auxiliary_filling/auxiliary_filling_wrappers.py +90 -0
  11. cars/applications/dem_generation/__init__.py +30 -0
  12. cars/applications/dem_generation/abstract_dem_generation_app.py +116 -0
  13. cars/applications/dem_generation/bulldozer_config/base_config.yaml +42 -0
  14. cars/applications/dem_generation/bulldozer_dem_app.py +655 -0
  15. cars/applications/dem_generation/bulldozer_memory.py +55 -0
  16. cars/applications/dem_generation/dem_generation_algo.py +107 -0
  17. cars/applications/dem_generation/dem_generation_constants.py +32 -0
  18. cars/applications/dem_generation/dem_generation_wrappers.py +323 -0
  19. cars/applications/dense_match_filling/__init__.py +30 -0
  20. cars/applications/dense_match_filling/abstract_dense_match_filling_app.py +242 -0
  21. cars/applications/dense_match_filling/fill_disp_algo.py +113 -0
  22. cars/applications/dense_match_filling/fill_disp_constants.py +39 -0
  23. cars/applications/dense_match_filling/fill_disp_wrappers.py +83 -0
  24. cars/applications/dense_match_filling/zero_padding_app.py +302 -0
  25. cars/applications/dense_matching/__init__.py +30 -0
  26. cars/applications/dense_matching/abstract_dense_matching_app.py +261 -0
  27. cars/applications/dense_matching/census_mccnn_sgm_app.py +1460 -0
  28. cars/applications/dense_matching/cpp/__init__.py +0 -0
  29. cars/applications/dense_matching/cpp/dense_matching_cpp.cpython-313-i386-linux-musl.so +0 -0
  30. cars/applications/dense_matching/cpp/dense_matching_cpp.py +94 -0
  31. cars/applications/dense_matching/cpp/includes/dense_matching.hpp +58 -0
  32. cars/applications/dense_matching/cpp/meson.build +9 -0
  33. cars/applications/dense_matching/cpp/src/bindings.cpp +13 -0
  34. cars/applications/dense_matching/cpp/src/dense_matching.cpp +207 -0
  35. cars/applications/dense_matching/dense_matching_algo.py +401 -0
  36. cars/applications/dense_matching/dense_matching_constants.py +89 -0
  37. cars/applications/dense_matching/dense_matching_wrappers.py +951 -0
  38. cars/applications/dense_matching/disparity_grid_algo.py +588 -0
  39. cars/applications/dense_matching/loaders/__init__.py +23 -0
  40. cars/applications/dense_matching/loaders/config_census_sgm_default.json +31 -0
  41. cars/applications/dense_matching/loaders/config_census_sgm_homogeneous.json +30 -0
  42. cars/applications/dense_matching/loaders/config_census_sgm_mountain_and_vegetation.json +30 -0
  43. cars/applications/dense_matching/loaders/config_census_sgm_shadow.json +30 -0
  44. cars/applications/dense_matching/loaders/config_census_sgm_sparse.json +36 -0
  45. cars/applications/dense_matching/loaders/config_census_sgm_urban.json +30 -0
  46. cars/applications/dense_matching/loaders/config_mapping.json +13 -0
  47. cars/applications/dense_matching/loaders/config_mccnn.json +28 -0
  48. cars/applications/dense_matching/loaders/global_land_cover_map.tif +0 -0
  49. cars/applications/dense_matching/loaders/pandora_loader.py +593 -0
  50. cars/applications/dsm_filling/__init__.py +32 -0
  51. cars/applications/dsm_filling/abstract_dsm_filling_app.py +101 -0
  52. cars/applications/dsm_filling/border_interpolation_app.py +270 -0
  53. cars/applications/dsm_filling/bulldozer_config/base_config.yaml +44 -0
  54. cars/applications/dsm_filling/bulldozer_filling_app.py +279 -0
  55. cars/applications/dsm_filling/exogenous_filling_app.py +333 -0
  56. cars/applications/grid_generation/__init__.py +30 -0
  57. cars/applications/grid_generation/abstract_grid_generation_app.py +142 -0
  58. cars/applications/grid_generation/epipolar_grid_generation_app.py +327 -0
  59. cars/applications/grid_generation/grid_correction_app.py +496 -0
  60. cars/applications/grid_generation/grid_generation_algo.py +388 -0
  61. cars/applications/grid_generation/grid_generation_constants.py +46 -0
  62. cars/applications/grid_generation/transform_grid.py +88 -0
  63. cars/applications/ground_truth_reprojection/__init__.py +30 -0
  64. cars/applications/ground_truth_reprojection/abstract_ground_truth_reprojection_app.py +137 -0
  65. cars/applications/ground_truth_reprojection/direct_localization_app.py +629 -0
  66. cars/applications/ground_truth_reprojection/ground_truth_reprojection_algo.py +275 -0
  67. cars/applications/point_cloud_outlier_removal/__init__.py +30 -0
  68. cars/applications/point_cloud_outlier_removal/abstract_outlier_removal_app.py +385 -0
  69. cars/applications/point_cloud_outlier_removal/outlier_removal_algo.py +392 -0
  70. cars/applications/point_cloud_outlier_removal/outlier_removal_constants.py +43 -0
  71. cars/applications/point_cloud_outlier_removal/small_components_app.py +527 -0
  72. cars/applications/point_cloud_outlier_removal/statistical_app.py +531 -0
  73. cars/applications/rasterization/__init__.py +30 -0
  74. cars/applications/rasterization/abstract_pc_rasterization_app.py +183 -0
  75. cars/applications/rasterization/rasterization_algo.py +534 -0
  76. cars/applications/rasterization/rasterization_constants.py +38 -0
  77. cars/applications/rasterization/rasterization_wrappers.py +634 -0
  78. cars/applications/rasterization/simple_gaussian_app.py +1152 -0
  79. cars/applications/resampling/__init__.py +28 -0
  80. cars/applications/resampling/abstract_resampling_app.py +187 -0
  81. cars/applications/resampling/bicubic_resampling_app.py +762 -0
  82. cars/applications/resampling/resampling_algo.py +614 -0
  83. cars/applications/resampling/resampling_constants.py +36 -0
  84. cars/applications/resampling/resampling_wrappers.py +309 -0
  85. cars/applications/sparse_matching/__init__.py +30 -0
  86. cars/applications/sparse_matching/abstract_sparse_matching_app.py +498 -0
  87. cars/applications/sparse_matching/sift_app.py +735 -0
  88. cars/applications/sparse_matching/sparse_matching_algo.py +360 -0
  89. cars/applications/sparse_matching/sparse_matching_constants.py +68 -0
  90. cars/applications/sparse_matching/sparse_matching_wrappers.py +238 -0
  91. cars/applications/triangulation/__init__.py +32 -0
  92. cars/applications/triangulation/abstract_triangulation_app.py +227 -0
  93. cars/applications/triangulation/line_of_sight_intersection_app.py +1243 -0
  94. cars/applications/triangulation/pc_transform.py +552 -0
  95. cars/applications/triangulation/triangulation_algo.py +371 -0
  96. cars/applications/triangulation/triangulation_constants.py +38 -0
  97. cars/applications/triangulation/triangulation_wrappers.py +259 -0
  98. cars/bundleadjustment.py +757 -0
  99. cars/cars.py +177 -0
  100. cars/conf/__init__.py +23 -0
  101. cars/conf/geoid/egm96.grd +0 -0
  102. cars/conf/geoid/egm96.grd.hdr +15 -0
  103. cars/conf/input_parameters.py +156 -0
  104. cars/conf/mask_cst.py +35 -0
  105. cars/core/__init__.py +23 -0
  106. cars/core/cars_logging.py +402 -0
  107. cars/core/constants.py +191 -0
  108. cars/core/constants_disparity.py +50 -0
  109. cars/core/datasets.py +140 -0
  110. cars/core/geometry/__init__.py +27 -0
  111. cars/core/geometry/abstract_geometry.py +1119 -0
  112. cars/core/geometry/shareloc_geometry.py +598 -0
  113. cars/core/inputs.py +568 -0
  114. cars/core/outputs.py +176 -0
  115. cars/core/preprocessing.py +722 -0
  116. cars/core/projection.py +843 -0
  117. cars/core/roi_tools.py +215 -0
  118. cars/core/tiling.py +774 -0
  119. cars/core/utils.py +164 -0
  120. cars/data_structures/__init__.py +23 -0
  121. cars/data_structures/cars_dataset.py +1541 -0
  122. cars/data_structures/cars_dict.py +74 -0
  123. cars/data_structures/corresponding_tiles_tools.py +186 -0
  124. cars/data_structures/dataframe_converter.py +185 -0
  125. cars/data_structures/format_transformation.py +297 -0
  126. cars/devibrate.py +689 -0
  127. cars/extractroi.py +264 -0
  128. cars/orchestrator/__init__.py +23 -0
  129. cars/orchestrator/achievement_tracker.py +125 -0
  130. cars/orchestrator/cluster/__init__.py +37 -0
  131. cars/orchestrator/cluster/abstract_cluster.py +244 -0
  132. cars/orchestrator/cluster/abstract_dask_cluster.py +375 -0
  133. cars/orchestrator/cluster/dask_cluster_tools.py +103 -0
  134. cars/orchestrator/cluster/dask_config/README.md +94 -0
  135. cars/orchestrator/cluster/dask_config/dask.yaml +21 -0
  136. cars/orchestrator/cluster/dask_config/distributed.yaml +70 -0
  137. cars/orchestrator/cluster/dask_config/jobqueue.yaml +26 -0
  138. cars/orchestrator/cluster/dask_config/reference_confs/dask-schema.yaml +137 -0
  139. cars/orchestrator/cluster/dask_config/reference_confs/dask.yaml +26 -0
  140. cars/orchestrator/cluster/dask_config/reference_confs/distributed-schema.yaml +1009 -0
  141. cars/orchestrator/cluster/dask_config/reference_confs/distributed.yaml +273 -0
  142. cars/orchestrator/cluster/dask_config/reference_confs/jobqueue.yaml +212 -0
  143. cars/orchestrator/cluster/dask_jobqueue_utils.py +204 -0
  144. cars/orchestrator/cluster/local_dask_cluster.py +116 -0
  145. cars/orchestrator/cluster/log_wrapper.py +1075 -0
  146. cars/orchestrator/cluster/mp_cluster/__init__.py +27 -0
  147. cars/orchestrator/cluster/mp_cluster/mp_factorizer.py +212 -0
  148. cars/orchestrator/cluster/mp_cluster/mp_objects.py +535 -0
  149. cars/orchestrator/cluster/mp_cluster/mp_tools.py +93 -0
  150. cars/orchestrator/cluster/mp_cluster/mp_wrapper.py +505 -0
  151. cars/orchestrator/cluster/mp_cluster/multiprocessing_cluster.py +873 -0
  152. cars/orchestrator/cluster/mp_cluster/multiprocessing_profiler.py +399 -0
  153. cars/orchestrator/cluster/pbs_dask_cluster.py +207 -0
  154. cars/orchestrator/cluster/sequential_cluster.py +139 -0
  155. cars/orchestrator/cluster/slurm_dask_cluster.py +234 -0
  156. cars/orchestrator/orchestrator.py +905 -0
  157. cars/orchestrator/orchestrator_constants.py +29 -0
  158. cars/orchestrator/registry/__init__.py +23 -0
  159. cars/orchestrator/registry/abstract_registry.py +143 -0
  160. cars/orchestrator/registry/compute_registry.py +106 -0
  161. cars/orchestrator/registry/id_generator.py +116 -0
  162. cars/orchestrator/registry/replacer_registry.py +213 -0
  163. cars/orchestrator/registry/saver_registry.py +363 -0
  164. cars/orchestrator/registry/unseen_registry.py +118 -0
  165. cars/orchestrator/tiles_profiler.py +279 -0
  166. cars/pipelines/__init__.py +26 -0
  167. cars/pipelines/conf_resolution/conf_final_resolution.yaml +5 -0
  168. cars/pipelines/conf_resolution/conf_first_resolution.yaml +2 -0
  169. cars/pipelines/conf_resolution/conf_intermediate_resolution.yaml +2 -0
  170. cars/pipelines/default/__init__.py +26 -0
  171. cars/pipelines/default/default_pipeline.py +786 -0
  172. cars/pipelines/parameters/__init__.py +0 -0
  173. cars/pipelines/parameters/advanced_parameters.py +417 -0
  174. cars/pipelines/parameters/advanced_parameters_constants.py +69 -0
  175. cars/pipelines/parameters/application_parameters.py +71 -0
  176. cars/pipelines/parameters/depth_map_inputs.py +0 -0
  177. cars/pipelines/parameters/dsm_inputs.py +918 -0
  178. cars/pipelines/parameters/dsm_inputs_constants.py +25 -0
  179. cars/pipelines/parameters/output_constants.py +52 -0
  180. cars/pipelines/parameters/output_parameters.py +454 -0
  181. cars/pipelines/parameters/sensor_inputs.py +842 -0
  182. cars/pipelines/parameters/sensor_inputs_constants.py +49 -0
  183. cars/pipelines/parameters/sensor_loaders/__init__.py +29 -0
  184. cars/pipelines/parameters/sensor_loaders/basic_classif_loader.py +86 -0
  185. cars/pipelines/parameters/sensor_loaders/basic_image_loader.py +98 -0
  186. cars/pipelines/parameters/sensor_loaders/pivot_classif_loader.py +90 -0
  187. cars/pipelines/parameters/sensor_loaders/pivot_image_loader.py +105 -0
  188. cars/pipelines/parameters/sensor_loaders/sensor_loader.py +93 -0
  189. cars/pipelines/parameters/sensor_loaders/sensor_loader_template.py +71 -0
  190. cars/pipelines/parameters/sensor_loaders/slurp_classif_loader.py +86 -0
  191. cars/pipelines/pipeline.py +119 -0
  192. cars/pipelines/pipeline_constants.py +31 -0
  193. cars/pipelines/pipeline_template.py +139 -0
  194. cars/pipelines/unit/__init__.py +26 -0
  195. cars/pipelines/unit/unit_pipeline.py +2850 -0
  196. cars/starter.py +167 -0
  197. cars-1.0.0rc1.dist-info/METADATA +292 -0
  198. cars-1.0.0rc1.dist-info/RECORD +202 -0
  199. cars-1.0.0rc1.dist-info/WHEEL +5 -0
  200. cars-1.0.0rc1.dist-info/entry_points.txt +8 -0
  201. cars.libs/libgcc_s-1257a076.so.1 +0 -0
  202. cars.libs/libstdc++-0530927c.so.6.0.32 +0 -0
@@ -0,0 +1,918 @@
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
+ # pylint: disable=too-many-lines
21
+ """
22
+ CARS dsm inputs
23
+ """
24
+
25
+ import collections
26
+ import logging
27
+ import os
28
+
29
+ import numpy as np
30
+ import rasterio
31
+ import xarray as xr
32
+ from affine import Affine
33
+ from json_checker import Checker, Or
34
+ from rasterio.windows import from_bounds
35
+
36
+ # CARS imports
37
+ import cars.orchestrator.orchestrator as ocht
38
+ import cars.pipelines.parameters.dsm_inputs_constants as dsm_cst
39
+ from cars.applications.rasterization.rasterization_wrappers import (
40
+ update_data,
41
+ update_weights,
42
+ )
43
+ from cars.core import constants as cst
44
+ from cars.core import inputs, preprocessing, tiling
45
+ from cars.core.geometry.abstract_geometry import AbstractGeometry
46
+ from cars.core.utils import make_relative_path_absolute, safe_makedirs
47
+ from cars.data_structures import cars_dataset
48
+ from cars.pipelines.parameters import sensor_inputs as sens_inp
49
+ from cars.pipelines.parameters import sensor_inputs_constants as sens_cst
50
+
51
+
52
+ def check_dsm_inputs(conf, config_dir=None):
53
+ """
54
+ Check the inputs given
55
+
56
+ :param conf: configuration of inputs
57
+ :type conf: dict
58
+ :param config_dir: directory of used json/yaml, if
59
+ user filled paths with relative paths
60
+ :type config_dir: str
61
+
62
+ :return: overloader inputs
63
+ :rtype: dict
64
+ """
65
+
66
+ overloaded_conf = {}
67
+
68
+ # Overload some optional parameters
69
+ overloaded_conf[dsm_cst.DSMS] = {}
70
+
71
+ overloaded_conf[sens_cst.ROI] = conf.get(sens_cst.ROI, None)
72
+
73
+ overloaded_conf[sens_cst.INITIAL_ELEVATION] = (
74
+ sens_inp.get_initial_elevation(
75
+ conf.get(sens_cst.INITIAL_ELEVATION, None)
76
+ )
77
+ )
78
+
79
+ overloaded_conf[sens_cst.SENSORS] = conf.get(sens_cst.SENSORS, None)
80
+
81
+ overloaded_conf[sens_cst.PAIRING] = conf.get(sens_cst.PAIRING, None)
82
+
83
+ # Validate inputs
84
+ inputs_schema = {
85
+ dsm_cst.DSMS: dict,
86
+ sens_cst.ROI: Or(str, dict, None),
87
+ sens_cst.INITIAL_ELEVATION: Or(dict, None),
88
+ sens_cst.SENSORS: Or(dict, None),
89
+ sens_cst.PAIRING: Or([[str]], None),
90
+ }
91
+
92
+ checker_inputs = Checker(inputs_schema)
93
+ checker_inputs.validate(overloaded_conf)
94
+
95
+ # Validate depth maps
96
+
97
+ dsm_schema = {
98
+ cst.DSM_CLASSIF: Or(str, None),
99
+ cst.DSM_ALT: Or(str, None),
100
+ cst.DSM_ALT_INF: Or(str, None),
101
+ cst.DSM_ALT_SUP: Or(str, None),
102
+ cst.DSM_WEIGHTS_SUM: Or(str, None),
103
+ cst.DSM_MSK: Or(str, None),
104
+ cst.DSM_NB_PTS: Or(str, None),
105
+ cst.DSM_NB_PTS_IN_CELL: Or(str, None),
106
+ cst.DSM_MEAN: Or(str, None),
107
+ cst.DSM_STD_DEV: Or(str, None),
108
+ cst.DSM_INF_MEAN: Or(str, None),
109
+ cst.DSM_INF_STD: Or(str, None),
110
+ cst.DSM_SUP_MEAN: Or(str, None),
111
+ cst.DSM_SUP_STD: Or(str, None),
112
+ cst.DSM_AMBIGUITY: Or(str, None),
113
+ cst.DSM_PERFORMANCE_MAP: Or(str, None),
114
+ cst.DSM_SOURCE_PC: Or(str, None),
115
+ cst.DSM_FILLING: Or(str, None),
116
+ cst.DSM_COLOR: Or(str, None),
117
+ }
118
+
119
+ checker_pc = Checker(dsm_schema)
120
+ for dsm_key in conf[dsm_cst.DSMS]:
121
+ # Get depth maps with default
122
+ overloaded_conf[dsm_cst.DSMS][dsm_key] = {}
123
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_ALT] = conf[
124
+ dsm_cst.DSMS
125
+ ][dsm_key].get("dsm", None)
126
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_CLASSIF] = conf[
127
+ dsm_cst.DSMS
128
+ ][dsm_key].get("classification", None)
129
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_COLOR] = conf[
130
+ dsm_cst.DSMS
131
+ ][dsm_key].get("image", None)
132
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_MSK] = conf[
133
+ dsm_cst.DSMS
134
+ ][dsm_key].get("mask", None)
135
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_ALT_INF] = conf[
136
+ dsm_cst.DSMS
137
+ ][dsm_key].get("dsm_inf", None)
138
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_ALT_SUP] = conf[
139
+ dsm_cst.DSMS
140
+ ][dsm_key].get("dsm_sup", None)
141
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_WEIGHTS_SUM] = conf[
142
+ dsm_cst.DSMS
143
+ ][dsm_key].get("weights", None)
144
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_NB_PTS] = conf[
145
+ dsm_cst.DSMS
146
+ ][dsm_key].get("dsm_n_pts", None)
147
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_NB_PTS_IN_CELL] = conf[
148
+ dsm_cst.DSMS
149
+ ][dsm_key].get("dsm_pts_in_cell", None)
150
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_MEAN] = conf[
151
+ dsm_cst.DSMS
152
+ ][dsm_key].get("dsm_mean", None)
153
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_STD_DEV] = conf[
154
+ dsm_cst.DSMS
155
+ ][dsm_key].get("dsm_std", None)
156
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_INF_MEAN] = conf[
157
+ dsm_cst.DSMS
158
+ ][dsm_key].get("dsm_inf_mean", None)
159
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_INF_STD] = conf[
160
+ dsm_cst.DSMS
161
+ ][dsm_key].get("dsm_inf_std", None)
162
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_SUP_MEAN] = conf[
163
+ dsm_cst.DSMS
164
+ ][dsm_key].get("dsm_sup_mean", None)
165
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_SUP_STD] = conf[
166
+ dsm_cst.DSMS
167
+ ][dsm_key].get("dsm_sup_std", None)
168
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_AMBIGUITY] = conf[
169
+ dsm_cst.DSMS
170
+ ][dsm_key].get("ambiguity", None)
171
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_PERFORMANCE_MAP] = conf[
172
+ dsm_cst.DSMS
173
+ ][dsm_key].get("performance_map", None)
174
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_SOURCE_PC] = conf[
175
+ dsm_cst.DSMS
176
+ ][dsm_key].get("contributing_pair", None)
177
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.DSM_FILLING] = conf[
178
+ dsm_cst.DSMS
179
+ ][dsm_key].get("filling", None)
180
+
181
+ # validate
182
+ checker_pc.validate(overloaded_conf[dsm_cst.DSMS][dsm_key])
183
+
184
+ # Modify to absolute path
185
+ if config_dir is not None:
186
+ modify_to_absolute_path(config_dir, overloaded_conf)
187
+ else:
188
+ logging.debug(
189
+ "path of config file was not given,"
190
+ "relative path are not transformed to absolute paths"
191
+ )
192
+
193
+ for dsm_key in conf[dsm_cst.DSMS]:
194
+ # check sizes
195
+ check_input_size(
196
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.INDEX_DSM_ALT],
197
+ overloaded_conf[dsm_cst.DSMS][dsm_key][
198
+ cst.INDEX_DSM_CLASSIFICATION
199
+ ],
200
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.INDEX_DSM_COLOR],
201
+ overloaded_conf[dsm_cst.DSMS][dsm_key][cst.INDEX_DSM_MASK],
202
+ )
203
+
204
+ # Check srtm dir
205
+ sens_inp.check_srtm(
206
+ overloaded_conf[sens_cst.INITIAL_ELEVATION][sens_cst.DEM_PATH]
207
+ )
208
+
209
+ check_phasing(conf[dsm_cst.DSMS])
210
+
211
+ overloaded_conf[sens_cst.LOADERS] = sens_inp.check_loaders(
212
+ conf.get(sens_cst.LOADERS, {})
213
+ )
214
+
215
+ classif_loader = overloaded_conf[sens_cst.LOADERS][
216
+ sens_cst.INPUT_CLASSIFICATION
217
+ ]
218
+
219
+ overloaded_conf[sens_cst.FILLING] = sens_inp.check_filling(
220
+ conf.get(sens_cst.FILLING, {}), classif_loader
221
+ )
222
+
223
+ if sens_cst.SENSORS in conf and conf[sens_cst.SENSORS] is not None:
224
+ sens_inp.check_sensors(conf, overloaded_conf, config_dir)
225
+
226
+ return overloaded_conf
227
+
228
+
229
+ def check_geometry_plugin(conf_inputs, conf_geom_plugin):
230
+ """
231
+ Check the geometry plugin with inputs
232
+ :param conf_geom_plugin: name of geometry plugin
233
+ :type conf_geom_plugin: str
234
+ :param conf_inputs: checked configuration of inputs
235
+ :type conf_inputs: type
236
+
237
+ :return: geometry plugin with dem
238
+ """
239
+ if conf_geom_plugin is None:
240
+ conf_geom_plugin = "SharelocGeometry"
241
+
242
+ dem_path = conf_inputs[sens_cst.INITIAL_ELEVATION][sens_cst.DEM_PATH]
243
+
244
+ if dem_path is None:
245
+ return conf_geom_plugin, None
246
+
247
+ # Initialize a geometry plugin with elevation information
248
+ geom_plugin_with_dem_and_geoid = (
249
+ AbstractGeometry( # pylint: disable=abstract-class-instantiated
250
+ conf_geom_plugin,
251
+ dem=dem_path,
252
+ geoid=conf_inputs[sens_cst.INITIAL_ELEVATION][sens_cst.GEOID],
253
+ default_alt=sens_cst.CARS_DEFAULT_ALT,
254
+ )
255
+ )
256
+
257
+ return conf_geom_plugin, geom_plugin_with_dem_and_geoid
258
+
259
+
260
+ def check_input_size(dsm, classif, color, mask):
261
+ """
262
+ Check dsm, mask, color, classif given
263
+
264
+ Images must have same size
265
+
266
+ :param dsm: phased dsm path
267
+ :type dsm: str
268
+ :param classif: classif path
269
+ :type classif: str
270
+ :param color: color path
271
+ :type color: str
272
+ :param mask: mask path
273
+ :type mask: str
274
+ """
275
+
276
+ if inputs.rasterio_get_nb_bands(dsm) != 1:
277
+ raise RuntimeError("{} is not mono-band image".format(dsm))
278
+
279
+ for path in [mask, color, classif]:
280
+ if path is not None:
281
+ if inputs.rasterio_get_size(dsm) != inputs.rasterio_get_size(path):
282
+ raise RuntimeError(
283
+ "The image {} and {} "
284
+ "do not have the same size".format(dsm, path)
285
+ )
286
+
287
+
288
+ def modify_to_absolute_path(config_dir, overloaded_conf):
289
+ """
290
+ Modify input file path to absolute path
291
+
292
+ :param config_dir: directory of the json configuration
293
+ :type config_dir: str
294
+ :param overloaded_conf: overloaded configuration json
295
+ :dict overloaded_conf: dict
296
+ """
297
+ for dsm_key in overloaded_conf[dsm_cst.DSMS]:
298
+ dsms = overloaded_conf[dsm_cst.DSMS][dsm_key]
299
+ for tag in [
300
+ cst.INDEX_DSM_ALT,
301
+ cst.INDEX_DSM_CLASSIFICATION,
302
+ cst.INDEX_DSM_COLOR,
303
+ cst.INDEX_DSM_MASK,
304
+ ]:
305
+ if dsms[tag] is not None:
306
+ dsms[tag] = make_relative_path_absolute(dsms[tag], config_dir)
307
+
308
+ if overloaded_conf[sens_cst.ROI] is not None:
309
+ if isinstance(overloaded_conf[sens_cst.ROI], str):
310
+ overloaded_conf[sens_cst.ROI] = make_relative_path_absolute(
311
+ overloaded_conf[sens_cst.ROI], config_dir
312
+ )
313
+
314
+
315
+ def check_phasing(dsm_dict):
316
+ """
317
+ Check if the dsm are phased, and if resolution and epsg code are equivalent
318
+
319
+ :param dsm_dict: list of phased dsm
320
+ :type dsm_dict: dict
321
+ """
322
+
323
+ ref_key = next(iter(dsm_dict))
324
+ ref_epsg = inputs.rasterio_get_epsg_code(dsm_dict[ref_key]["dsm"])
325
+ ref_profile = inputs.rasterio_get_profile(dsm_dict[ref_key]["dsm"])
326
+ ref_transform = list(ref_profile["transform"])
327
+ ref_res_x = ref_transform[0]
328
+ ref_res_y = ref_transform[4]
329
+ ref_bounds = inputs.rasterio_get_bounds(dsm_dict[ref_key]["dsm"])
330
+
331
+ for dsm_key in dsm_dict:
332
+ if dsm_key == ref_key:
333
+ continue
334
+
335
+ epsg = inputs.rasterio_get_epsg_code(dsm_dict[dsm_key]["dsm"])
336
+ profile = inputs.rasterio_get_profile(dsm_dict[ref_key]["dsm"])
337
+ transform = list(profile["transform"])
338
+ res_x = transform[0]
339
+ res_y = transform[4]
340
+ bounds = inputs.rasterio_get_bounds(dsm_dict[dsm_key]["dsm"])
341
+
342
+ if epsg != ref_epsg:
343
+ raise RuntimeError(
344
+ f"EPSG mismatch: DSM {dsm_key} has EPSG {epsg}, "
345
+ f"expected {ref_epsg}."
346
+ )
347
+
348
+ if ref_res_x != res_x or ref_res_y != res_y:
349
+ raise RuntimeError(
350
+ f"Resolution mismatch: DSM {dsm_key} has resolution "
351
+ f"{(res_x, res_y)}, expected {(ref_res_x, ref_res_y)}."
352
+ )
353
+
354
+ # Compare the left_bottom corner
355
+ diff = ref_bounds[0:2] - bounds[0:2]
356
+ resolution = np.array([ref_res_x, -ref_res_y])
357
+ res_ratio = diff / resolution
358
+
359
+ if ~np.all(np.equal(res_ratio, res_ratio.astype(int))) and ~np.all(
360
+ np.equal(1 / res_ratio, (1 / res_ratio).astype(int))
361
+ ):
362
+ raise RuntimeError(f"DSM {dsm_key} and {ref_key} are not phased")
363
+
364
+
365
+ # pylint: disable=too-many-positional-arguments
366
+ def merge_dsm_infos( # noqa: C901 function is too complex
367
+ dict_path,
368
+ orchestrator,
369
+ roi_poly,
370
+ terrain_tile_size,
371
+ dump_dir=None,
372
+ dsm_file_name=None,
373
+ color_file_name=None,
374
+ classif_file_name=None,
375
+ filling_file_name=None,
376
+ performance_map_file_name=None,
377
+ ambiguity_file_name=None,
378
+ contributing_pair_file_name=None,
379
+ ):
380
+ """
381
+ Merge all the dsms
382
+
383
+ :param dict_path: path of all variables from all dsms
384
+ :type dict_path: dict
385
+ :param orchestrator: orchestrator used
386
+ :param terrain_tile_size: tile size to use
387
+ :type terrain_tile_size: int
388
+ :param dump_dir: output path
389
+ :type dump_dir: str
390
+ :param dsm_file_name: name of the dsm output file
391
+ :type dsm_file_name: str
392
+ :param color_file_name: name of the color output file
393
+ :type color_file_name: str
394
+ :param classif_file_name: name of the classif output file
395
+ :type classif_file_name: str
396
+ :param filling_file_name: name of the filling output file
397
+ :type filling_file_name: str
398
+ :param performance_map_file_name: name of the performance_map output file
399
+ :type performance_map_file_name: str
400
+ :param ambiguity_file_name: name of the ambiguity output file
401
+ :type ambiguity_file_name: str
402
+ :param contributing_pair_file_name: name of contributing_pair output file
403
+ :type contributing_pair_file_name: str
404
+
405
+ :return: raster DSM. CarsDataset contains:
406
+
407
+ - Z x W Delayed tiles. \
408
+ Each tile will be a future xarray Dataset containing:
409
+
410
+ - data : with keys : "hgt", "img", "raster_msk",optional : \
411
+ "n_pts", "pts_in_cell", "hgt_mean", "hgt_stdev",\
412
+ "hgt_inf", "hgt_sup"
413
+ - attrs with keys: "epsg"
414
+ - attributes containing: None
415
+
416
+ :rtype : CarsDataset filled with xr.Dataset
417
+ """
418
+
419
+ # Create CarsDataset
420
+ terrain_raster = cars_dataset.CarsDataset("arrays", name="rasterization")
421
+
422
+ # find the global bounds of the dataset
423
+ dsm_nodata = None
424
+ epsg = None
425
+ resolution = None
426
+ for index, path in enumerate(dict_path["dsm"]):
427
+ with rasterio.open(path) as src:
428
+ if index == 0:
429
+ bounds = src.bounds
430
+ global_bounds = bounds
431
+ profile = src.profile
432
+ transform = list(profile["transform"])
433
+ res_x = transform[0]
434
+ res_y = transform[4]
435
+ resolution = (res_y, res_x)
436
+
437
+ epsg = src.crs
438
+
439
+ dsm_nodata = src.nodata
440
+ else:
441
+ bounds = src.bounds
442
+ global_bounds = (
443
+ min(bounds[0], global_bounds[0]), # xmin
444
+ min(bounds[1], global_bounds[1]), # ymin
445
+ max(bounds[2], global_bounds[2]), # xmax
446
+ max(bounds[3], global_bounds[3]), # ymax
447
+ )
448
+
449
+ if roi_poly is not None:
450
+ global_bounds = preprocessing.crop_terrain_bounds_with_roi(
451
+ roi_poly,
452
+ global_bounds[0],
453
+ global_bounds[1],
454
+ global_bounds[2],
455
+ global_bounds[3],
456
+ )
457
+
458
+ # Tiling of the dataset
459
+ [xmin, ymin, xmax, ymax] = global_bounds
460
+
461
+ terrain_raster.tiling_grid = tiling.generate_tiling_grid(
462
+ xmin,
463
+ ymin,
464
+ xmax,
465
+ ymax,
466
+ terrain_tile_size,
467
+ terrain_tile_size,
468
+ )
469
+
470
+ xsize, ysize = tiling.roi_to_start_and_size(global_bounds, resolution[1])[
471
+ 2:
472
+ ]
473
+
474
+ # build the tranform of the dataset
475
+ # Generate profile
476
+ geotransform = (
477
+ global_bounds[0],
478
+ resolution[1],
479
+ 0.0,
480
+ global_bounds[3],
481
+ 0.0,
482
+ -resolution[1],
483
+ )
484
+
485
+ transform = Affine.from_gdal(*geotransform)
486
+ raster_profile = collections.OrderedDict(
487
+ {
488
+ "height": ysize,
489
+ "width": xsize,
490
+ "driver": "GTiff",
491
+ "dtype": "float32",
492
+ "transform": transform,
493
+ "crs": "EPSG:{}".format(epsg),
494
+ "tiled": True,
495
+ "no_data": dsm_nodata,
496
+ }
497
+ )
498
+
499
+ # Get sources pc
500
+ full_sources_band_descriptions = None
501
+ if cst.DSM_SOURCE_PC in list(dict_path.keys()):
502
+ full_sources_band_descriptions = []
503
+ for source_pc in dict_path[cst.DSM_SOURCE_PC]:
504
+ full_sources_band_descriptions += list(
505
+ inputs.get_descriptions_bands(source_pc)
506
+ )
507
+
508
+ # remove copies
509
+ full_sources_band_descriptions = list(
510
+ dict.fromkeys(full_sources_band_descriptions)
511
+ )
512
+
513
+ # Setup dump directory
514
+ if dump_dir is not None:
515
+ out_dump_dir = dump_dir
516
+ safe_makedirs(out_dump_dir)
517
+ else:
518
+ out_dump_dir = orchestrator.out_dir
519
+
520
+ if dsm_file_name is not None:
521
+ safe_makedirs(os.path.dirname(dsm_file_name))
522
+
523
+ # Save all file that are in inputs
524
+ for key in dict_path.keys():
525
+ if key in (cst.DSM_ALT, cst.DSM_COLOR, cst.DSM_WEIGHTS_SUM):
526
+ option = False
527
+ else:
528
+ option = True
529
+
530
+ if key == cst.DSM_ALT and dsm_file_name is not None:
531
+ out_file_name = dsm_file_name
532
+ elif key == cst.DSM_COLOR and color_file_name is not None:
533
+ out_file_name = color_file_name
534
+ elif key == cst.DSM_CLASSIF and classif_file_name is not None:
535
+ out_file_name = classif_file_name
536
+ elif key == cst.DSM_FILLING and filling_file_name is not None:
537
+ out_file_name = filling_file_name
538
+ elif (
539
+ key == cst.DSM_PERFORMANCE_MAP
540
+ and performance_map_file_name is not None
541
+ ):
542
+ out_file_name = performance_map_file_name
543
+ elif key == cst.DSM_AMBIGUITY and ambiguity_file_name is not None:
544
+ out_file_name = ambiguity_file_name
545
+ elif (
546
+ key == cst.DSM_SOURCE_PC and contributing_pair_file_name is not None
547
+ ):
548
+ out_file_name = contributing_pair_file_name
549
+ else:
550
+ out_file_name = os.path.join(out_dump_dir, key + ".tif")
551
+
552
+ orchestrator.add_to_save_lists(
553
+ out_file_name,
554
+ key,
555
+ terrain_raster,
556
+ dtype=inputs.rasterio_get_dtype(dict_path[key][0]),
557
+ nodata=inputs.rasterio_get_nodata(dict_path[key][0]),
558
+ cars_ds_name=key,
559
+ optional_data=option,
560
+ )
561
+
562
+ [saving_info] = orchestrator.get_saving_infos([terrain_raster])
563
+ logging.info(
564
+ "Merge DSM info in {} x {} tiles".format(
565
+ terrain_raster.shape[0], terrain_raster.shape[1]
566
+ )
567
+ )
568
+ for col in range(terrain_raster.shape[1]):
569
+ for row in range(terrain_raster.shape[0]):
570
+ # update saving infos for potential replacement
571
+ full_saving_info = ocht.update_saving_infos(
572
+ saving_info, row=row, col=col
573
+ )
574
+
575
+ # Delayed call to dsm merging operations using all
576
+ terrain_raster[row, col] = orchestrator.cluster.create_task(
577
+ dsm_merging_wrapper, nout=1
578
+ )(
579
+ dict_path,
580
+ terrain_raster.tiling_grid[row, col],
581
+ resolution,
582
+ raster_profile,
583
+ full_saving_info,
584
+ full_sources_band_descriptions,
585
+ )
586
+
587
+ return terrain_raster
588
+
589
+
590
+ def dsm_merging_wrapper( # pylint: disable=too-many-positional-arguments # noqa C901
591
+ dict_path,
592
+ tile_bounds,
593
+ resolution,
594
+ profile,
595
+ saving_info=None,
596
+ full_sources_band_descriptions=None,
597
+ ):
598
+ """
599
+ Merge all the variables
600
+
601
+ :param dict_path: path of all variables from all dsms
602
+ :type dict_path: dict
603
+ :param tile_bounds: list of tiles coordinates
604
+ :type tile_bounds: list
605
+ :param resolution: resolution of the dsms
606
+ :type resolution: list
607
+ :param profile: profile of the global dsm
608
+ :type profile: OrderedDict
609
+ :saving_info: the saving infos
610
+ """
611
+
612
+ # create the tile dataset
613
+ x_value = np.arange(tile_bounds[0], tile_bounds[1], resolution[1])
614
+ y_value = np.arange(tile_bounds[2], tile_bounds[3], resolution[1])
615
+ height = len(y_value)
616
+ width = len(x_value)
617
+
618
+ dataset = xr.Dataset(
619
+ data_vars={},
620
+ coords={
621
+ "y": y_value,
622
+ "x": x_value,
623
+ },
624
+ )
625
+
626
+ # calculate the bounds intersection between each path
627
+ list_intersection = []
628
+
629
+ for path in dict_path["dsm"]:
630
+ with rasterio.open(path) as src:
631
+ intersect_bounds = (
632
+ max(tile_bounds[0], src.bounds.left), # xmin
633
+ max(tile_bounds[2], src.bounds.bottom), # ymin
634
+ min(tile_bounds[1], src.bounds.right), # xmax
635
+ min(tile_bounds[3], src.bounds.top), # ymax
636
+ )
637
+
638
+ if (
639
+ intersect_bounds[0] < intersect_bounds[2]
640
+ and intersect_bounds[1] < intersect_bounds[3]
641
+ ):
642
+ list_intersection.append(intersect_bounds)
643
+ else:
644
+ list_intersection.append("no intersection")
645
+
646
+ # Update the data
647
+ for key in dict_path.keys():
648
+ # Choose the method regarding the variable
649
+ if key in [cst.DSM_NB_PTS, cst.DSM_NB_PTS_IN_CELL]:
650
+ method = "sum"
651
+ elif key in [
652
+ cst.DSM_FILLING,
653
+ cst.DSM_CLASSIF,
654
+ cst.DSM_SOURCE_PC,
655
+ ]:
656
+ method = "bool"
657
+ else:
658
+ method = "basic"
659
+
660
+ # take band description information
661
+ band_descriptions = list(
662
+ inputs.get_descriptions_bands(dict_path[key][0])
663
+ )
664
+ nb_bands = inputs.rasterio_get_nb_bands(dict_path[key][0])
665
+ if len(band_descriptions) == 0:
666
+ band_descriptions = []
667
+ elif (
668
+ key
669
+ in [
670
+ cst.DSM_COLOR,
671
+ cst.DSM_SOURCE_PC,
672
+ cst.DSM_CLASSIF,
673
+ cst.DSM_FILLING,
674
+ ]
675
+ and None in band_descriptions
676
+ ):
677
+ band_descriptions = [
678
+ str(current_band) for current_band in range(nb_bands)
679
+ ]
680
+
681
+ # Define the dimension of the data in the dataset
682
+ if key == cst.DSM_COLOR:
683
+ dataset.coords[cst.BAND_IM] = (cst.BAND_IM, band_descriptions)
684
+ dim = [cst.BAND_IM, cst.Y, cst.X]
685
+ elif key == cst.DSM_SOURCE_PC:
686
+ dataset.coords[cst.BAND_SOURCE_PC] = (
687
+ cst.BAND_SOURCE_PC,
688
+ full_sources_band_descriptions,
689
+ )
690
+ dim = [cst.BAND_SOURCE_PC, cst.Y, cst.X]
691
+ elif key == cst.DSM_CLASSIF:
692
+ dataset.coords[cst.BAND_CLASSIF] = (
693
+ cst.BAND_CLASSIF,
694
+ band_descriptions,
695
+ )
696
+ dim = [cst.BAND_CLASSIF, cst.Y, cst.X]
697
+ elif key == cst.DSM_FILLING:
698
+ dataset.coords[cst.BAND_FILLING] = (
699
+ cst.BAND_FILLING,
700
+ band_descriptions,
701
+ )
702
+ dim = [cst.BAND_FILLING, cst.Y, cst.X]
703
+ else:
704
+ dim = [cst.Y, cst.X]
705
+
706
+ # Update data
707
+ if key == cst.DSM_ALT:
708
+ # Update dsm_value and weights once
709
+ value, weights = assemblage(
710
+ dict_path[key],
711
+ dict_path[cst.DSM_WEIGHTS_SUM],
712
+ method,
713
+ list_intersection,
714
+ tile_bounds,
715
+ height,
716
+ width,
717
+ band_descriptions,
718
+ )
719
+
720
+ dataset[key] = (dim, value)
721
+ dataset[cst.DSM_WEIGHTS_SUM] = (dim, weights)
722
+ elif key == cst.DSM_SOURCE_PC:
723
+ value, _ = assemblage(
724
+ dict_path[key],
725
+ dict_path[cst.DSM_WEIGHTS_SUM],
726
+ method,
727
+ list_intersection,
728
+ tile_bounds,
729
+ height,
730
+ width,
731
+ full_sources_band_descriptions,
732
+ merge_sources=True,
733
+ )
734
+ dataset[key] = (dim, value)
735
+ elif key != cst.DSM_WEIGHTS_SUM:
736
+ # Update other variables
737
+ value, _ = assemblage(
738
+ dict_path[key],
739
+ dict_path[cst.DSM_WEIGHTS_SUM],
740
+ method,
741
+ list_intersection,
742
+ tile_bounds,
743
+ height,
744
+ width,
745
+ band_descriptions,
746
+ )
747
+
748
+ dataset[key] = (dim, value)
749
+
750
+ # add performance map classes
751
+ if key == cst.DSM_PERFORMANCE_MAP:
752
+ perf_map_classes = inputs.rasterio_get_tags(dict_path[key][0])[
753
+ "CLASSES"
754
+ ]
755
+ dataset.attrs[cst.RIO_TAG_PERFORMANCE_MAP_CLASSES] = (
756
+ perf_map_classes
757
+ )
758
+
759
+ # Define the tile transform
760
+ bounds = [tile_bounds[0], tile_bounds[2], tile_bounds[1], tile_bounds[3]]
761
+ xstart, ystart, xsize, ysize = tiling.roi_to_start_and_size(
762
+ bounds, resolution[1]
763
+ )
764
+ transform = rasterio.Affine(*profile["transform"][0:6])
765
+
766
+ row_pix_pos, col_pix_pos = rasterio.transform.AffineTransformer(
767
+ transform
768
+ ).rowcol(xstart, ystart)
769
+ window = [
770
+ row_pix_pos,
771
+ row_pix_pos + ysize,
772
+ col_pix_pos,
773
+ col_pix_pos + xsize,
774
+ ]
775
+
776
+ window = cars_dataset.window_array_to_dict(window)
777
+
778
+ # Fill dataset
779
+ cars_dataset.fill_dataset(
780
+ dataset,
781
+ saving_info=saving_info,
782
+ window=window,
783
+ profile=profile,
784
+ overlaps=None,
785
+ )
786
+
787
+ return dataset
788
+
789
+
790
+ def assemblage( # pylint: disable=too-many-positional-arguments
791
+ out,
792
+ current_weights,
793
+ method,
794
+ intersect_bounds,
795
+ tile_bounds,
796
+ height,
797
+ width,
798
+ band_descriptions=None,
799
+ merge_sources=False,
800
+ ):
801
+ """
802
+ Update data
803
+
804
+ :param out: the data to update
805
+ :type out: list of path
806
+ :param current_weights: the current weights of the data
807
+ :type current_weights: list of path
808
+ :param method: the method used to update the data
809
+ :type method: str
810
+ :param intersect_bounds: the bounds intersection
811
+ :type intersect_bounds: list of bounds
812
+ :param height: the height of the tile
813
+ :type height: int
814
+ :param width: the width of the tile
815
+ :type width: int
816
+ :param band_descriptions: the band description of the data
817
+ :type band_descriptions: str of list
818
+ :param merge_sources: merge source pc, using full band_description
819
+ :type merge_sources: bool
820
+
821
+ """
822
+ # Initialize the tile
823
+ if merge_sources:
824
+ nb_bands = len(band_descriptions)
825
+ else:
826
+ nb_bands = inputs.rasterio_get_nb_bands(out[0])
827
+
828
+ dtype = inputs.rasterio_get_dtype(out[0])
829
+ nodata = inputs.rasterio_get_nodata(out[0])
830
+
831
+ if band_descriptions[0] is not None:
832
+ tile = np.full((nb_bands, height, width), nodata, dtype=dtype)
833
+ else:
834
+ tile = np.full((height, width), nodata, dtype=dtype)
835
+
836
+ # Initialize the weights
837
+ weights = np.full((height, width), 0, dtype=dtype)
838
+
839
+ for idx, path in enumerate(out):
840
+ with (
841
+ rasterio.open(path) as src,
842
+ rasterio.open(current_weights[idx]) as drt,
843
+ ):
844
+ if intersect_bounds[idx] != "no intersection":
845
+ # Build the window
846
+ window = from_bounds(
847
+ *intersect_bounds[idx], transform=src.transform
848
+ )
849
+
850
+ # Extract the data
851
+ current_nb_bands = src.count
852
+ if current_nb_bands > 1:
853
+ data = src.read(window=window)
854
+ _, rows, cols = data.shape
855
+ else:
856
+ data = src.read(1, window=window)
857
+ rows, cols = data.shape
858
+
859
+ indexes = list(range(current_nb_bands))
860
+ if merge_sources:
861
+ # Extract current band description
862
+ current_band_descriptions = list(src.descriptions)
863
+ # Get position
864
+ indexes = []
865
+ for current_band in current_band_descriptions:
866
+ indexes.append(band_descriptions.index(current_band))
867
+
868
+ current_weights_window = drt.read(1, window=window)
869
+
870
+ # Calculate the x and y offset because the current_data
871
+ # doesn't equal to the entire tile
872
+ x_offset = int(
873
+ (intersect_bounds[idx][0] - tile_bounds[0])
874
+ / np.abs(src.res[0])
875
+ )
876
+ y_offset = int(
877
+ (tile_bounds[3] - intersect_bounds[idx][3])
878
+ / np.abs(src.res[1])
879
+ )
880
+
881
+ if cols > 0 and rows > 0:
882
+ tab_x = np.arange(x_offset, x_offset + cols)
883
+
884
+ tab_y = np.arange(y_offset, y_offset + rows)
885
+
886
+ # Update data
887
+ if band_descriptions[0] is not None:
888
+
889
+ tile[np.ix_(indexes, tab_y, tab_x)] = np.reshape(
890
+ update_data(
891
+ tile[np.ix_(indexes, tab_y, tab_x)],
892
+ data,
893
+ current_weights_window,
894
+ weights[np.ix_(tab_y, tab_x)],
895
+ nodata,
896
+ method=method,
897
+ ),
898
+ tile[np.ix_(indexes, tab_y, tab_x)].shape,
899
+ )
900
+ else:
901
+ tile[np.ix_(tab_y, tab_x)] = np.reshape(
902
+ update_data(
903
+ tile[np.ix_(tab_y, tab_x)],
904
+ data,
905
+ current_weights_window,
906
+ weights[np.ix_(tab_y, tab_x)],
907
+ nodata,
908
+ method=method,
909
+ ),
910
+ tile[np.ix_(tab_y, tab_x)].shape,
911
+ )
912
+
913
+ # Update weights
914
+ weights[np.ix_(tab_y, tab_x)] = update_weights(
915
+ weights[np.ix_(tab_y, tab_x)], current_weights_window
916
+ )
917
+
918
+ return tile, weights