1use crate::blocks::RasterBlock;
6use crate::core_types::RasterType;
7use crate::gdal_utils::create_rayon_pool;
8use crate::parallel_writer;
9use crate::rasterdataset::RasterDataset;
10
11use anyhow::Result as GdalResult;
12use gdal::raster::RasterizeOptions;
13use gdal::spatial_ref::CoordTransform;
14use gdal::spatial_ref::SpatialRef;
15use gdal::vector::{Layer, LayerAccess};
16use gdal::{Dataset, DriverManager};
17use kdam::par_tqdm;
18use ndarray::Array3;
19use num_traits::NumCast;
20use rayon::prelude::*;
21use std::collections::HashMap;
22use std::path::Path;
23
24fn get_layer<'a>(dataset: &'a Dataset, layer_name: Option<&str>) -> GdalResult<Layer<'a>> {
25 match layer_name {
26 Some(name) => dataset.layer_by_name(name).map_err(|e| anyhow::anyhow!(e)),
27 None => dataset.layer(0).map_err(|e| anyhow::anyhow!(e)),
28 }
29}
30
31impl<R> RasterDataset<R>
32where
33 R: RasterType,
34{
35 pub fn rasterize(
41 &self,
42 vector_fn: &Path,
43 column_name: Option<&str>,
44 map: Option<HashMap<String, i32>>,
45 layer_name: Option<&str>,
46 n_cpus: usize,
47 rasterize_options: Option<RasterizeOptions>,
48 out_file: &Path,
49 ) {
50 log::debug!("Starting rasterize.");
51 gdal::config::set_config_option("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", "TRADITIONAL_GIS_ORDER").unwrap();
52
53 if column_name.is_none() {
54 log::warn!("No --column specified, using feature FID as burn value");
55 }
56
57 let n_bands = self.metadata.shape.layers * self.metadata.shape.times;
58
59 parallel_writer::create_output_geotiff::<R>(
61 out_file,
62 &self.metadata.geo_transform,
63 self.metadata.epsg_code as u32,
64 self.metadata.shape.cols,
65 self.metadata.shape.rows,
66 n_bands,
67 R::zero(),
68 )
69 .expect("Failed to create output GeoTIFF");
70
71 let writer = parallel_writer::ParallelGeoTiffWriter::new(
72 out_file.to_path_buf(),
73 self.metadata.geo_transform,
74 self.metadata.epsg_code as u32,
75 self.metadata.shape.cols,
76 self.metadata.shape.rows,
77 n_bands,
78 );
79
80 let pool = create_rayon_pool(n_cpus);
81
82 pool.install(|| {
83 par_tqdm!(self.blocks.to_owned().into_par_iter()).for_each(
84 |raster_block: RasterBlock<R>| {
85 let dataset = Dataset::open(vector_fn).expect("unable to open dataset");
86 let mut layer = get_layer(&dataset, layer_name).unwrap();
87
88 let x_ul = raster_block.geo_transform.x_ul;
90 let y_ul = raster_block.geo_transform.y_ul;
91 let x2 = x_ul
92 + (raster_block.geo_transform.x_res
93 * raster_block.read_window.size.cols as f64);
94 let y2 = y_ul
95 + (raster_block.geo_transform.y_res
96 * raster_block.read_window.size.rows as f64);
97 let (xmin, xmax) = if x_ul < x2 { (x_ul, x2) } else { (x2, x_ul) };
98 let (ymin, ymax) = if y_ul < y2 { (y_ul, y2) } else { (y2, y_ul) };
99
100 let src = SpatialRef::from_epsg(raster_block.epsg_code as u32)
101 .expect("Invalid raster EPSG code");
102 let dst = layer
103 .spatial_ref()
104 .expect("Vector layer has no CRS defined");
105
106 let mut filter_geom = gdal::vector::Geometry::from_wkt(&format!(
108 "POLYGON (({} {}, {} {}, {} {}, {} {}, {} {}))",
109 xmin, ymin, xmin, ymax, xmax, ymax, xmax, ymin, xmin, ymin
110 ))
111 .unwrap();
112 filter_geom.set_spatial_ref(src.clone());
113 let filter_geom = if src != dst {
114 let tx = CoordTransform::new(&src, &dst)
115 .expect("Failed to create coordinate transform");
116 filter_geom
117 .transform(&tx)
118 .expect("Failed to transform bbox into layer CRS")
119 } else {
120 filter_geom
121 };
122 layer.set_spatial_filter(&filter_geom);
123
124 let mut geometries = Vec::new();
126 let mut burn_values: Vec<R> = Vec::new();
127 let available_fields: Vec<String> = if column_name.is_some() {
128 layer.defn().fields().map(|f| f.name()).collect()
129 } else {
130 Vec::new()
131 };
132
133 for feature in layer.features() {
134 let g = feature.geometry().unwrap().to_owned();
135 let g = g.buffer(0., 8).unwrap();
136 let g = if src != dst {
137 let tx = CoordTransform::new(&dst, &src)
138 .expect("Failed to create coordinate transform");
139 g.transform(&tx)
140 .expect("Failed to transform geom into raster CRS")
141 } else {
142 g
143 };
144
145 if let Some(col) = column_name {
146 let field_index = match feature.field_index(col) {
147 Ok(idx) => idx,
148 Err(_) => panic!(
149 "Column '{}' not found in vector layer. Available fields: {}",
150 col,
151 available_fields.join(", ")
152 ),
153 };
154 if let Some(field_value) =
155 feature.field(field_index).expect("Invalid column name!")
156 {
157 let v = match field_value {
158 gdal::vector::FieldValue::IntegerValue(i) => {
159 NumCast::from(i).unwrap_or(0)
160 }
161 gdal::vector::FieldValue::RealValue(r) => {
162 NumCast::from(r).unwrap()
163 }
164 gdal::vector::FieldValue::StringValue(s) => match &map {
165 Some(hmap) => match hmap.get(&s) {
166 Some(v) => *v,
167 None => panic!(
168 "String '{}' not found in lookup table",
169 s
170 ),
171 },
172 None => panic!(
173 "Field '{}' is a string ('{}'), but no string→number map was supplied",
174 col, s
175 ),
176 },
177 _ => panic!("The field to rasterize has to be numeric"),
178 };
179 burn_values.push(NumCast::from(v).expect("Cant cast!"));
180 } else {
181 panic!("{}", format!("Field {col} does not exist"));
182 }
183 } else {
184 let fid = feature.fid().expect("Feature has no FID") as i32;
185 burn_values.push(NumCast::from(fid).expect("Cant cast!"));
186 }
187 geometries.push(g);
188 }
189
190 let block_rows = raster_block.read_window.size.rows as usize;
192 let block_cols = raster_block.read_window.size.cols as usize;
193
194 let mem_driver = DriverManager::get_driver_by_name("MEM")
195 .expect("GDAL MEM driver not available");
196 let mut mem_dataset = mem_driver
197 .create_with_band_type::<R, _>("", block_cols, block_rows, 1)
198 .expect("Failed to create in-memory dataset");
199 mem_dataset
200 .set_geo_transform(&raster_block.geo_transform.to_array())
201 .expect("Failed to set geo transform");
202 let srs = SpatialRef::from_epsg(raster_block.epsg_code as u32)
203 .expect("Invalid EPSG code");
204 mem_dataset
205 .set_spatial_ref(&srs)
206 .expect("Failed to set CRS");
207
208 let opts = rasterize_options.unwrap_or_else(|| RasterizeOptions::default());
209 let burn_values_f64: Vec<f64> = burn_values
210 .iter()
211 .map(|v| NumCast::from(*v).unwrap_or(0.0))
212 .collect();
213 gdal::raster::rasterize(
214 &mut mem_dataset,
215 &[1],
216 &geometries,
217 burn_values_f64.as_slice(),
218 Some(opts),
219 )
220 .expect("Rasterize failed");
221
222 let band = mem_dataset.rasterband(1).expect("No band 1");
224 let buffer: gdal::raster::Buffer<R> = band
225 .read_as::<R>(
226 (0, 0),
227 (block_cols, block_rows),
228 (block_cols, block_rows),
229 None,
230 )
231 .expect("Failed to read rasterized data");
232 let (_shape, data) = buffer.into_shape_and_vec();
233
234 let array = Array3::from_shape_vec((1, block_rows, block_cols), data)
235 .expect("Shape mismatch reading rasterized data");
236
237 parallel_writer::write_block(
239 &writer,
240 array.view(),
241 raster_block.read_window.clone(),
242 )
243 .expect("Failed to write block to output");
244 },
245 )
246 });
247 }
248
249 pub fn rasterize_cog(
254 &self,
255 vector_fn: &Path,
256 column_name: Option<&str>,
257 map: Option<HashMap<String, i32>>,
258 layer_name: Option<&str>,
259 n_cpus: usize,
260 rasterize_options: Option<RasterizeOptions>,
261 out_file: &Path,
262 config: &crate::types::OutputConfig,
263 ) {
264 use crate::types::OutputFormat;
265
266 match config.format {
267 OutputFormat::GeoTiff => {
268 self.rasterize(vector_fn, column_name, map, layer_name, n_cpus, rasterize_options, out_file);
269 }
270 OutputFormat::GeoTiffOverviews => {
271 self.rasterize(vector_fn, column_name, map, layer_name, n_cpus, rasterize_options, out_file);
272
273 let n_bands = self.metadata.shape.layers * self.metadata.shape.times;
274 let writer = parallel_writer::ParallelGeoTiffWriter::new(
275 out_file.to_path_buf(),
276 self.metadata.geo_transform,
277 self.metadata.epsg_code as u32,
278 self.metadata.shape.cols,
279 self.metadata.shape.rows,
280 n_bands,
281 );
282 writer.build_overviews(&config.overview_resampling, &config.overview_levels).ok();
283 }
284 OutputFormat::COG => {
285 let intermediate = std::path::PathBuf::from(crate::gdal_utils::create_temp_file("tif"));
286
287 self.rasterize(vector_fn, column_name, map, layer_name, n_cpus, rasterize_options, &intermediate);
288
289 crate::gdal_utils::translate_to_cog(
290 &intermediate, out_file,
291 &config.compression,
292 &config.overview_resampling,
293 ).ok();
294 std::fs::remove_file(&intermediate).ok();
295 }
296 }
297 }
298}