1
1
use ptr:: null_mut;
2
2
use std:: convert:: TryInto ;
3
- use std:: mem:: MaybeUninit ;
3
+ use std:: mem:: { size_of , MaybeUninit } ;
4
4
use std:: {
5
5
ffi:: NulError ,
6
6
ffi:: { CStr , CString } ,
@@ -12,6 +12,7 @@ use std::{
12
12
use crate :: cpl:: CslStringList ;
13
13
use crate :: errors:: * ;
14
14
use crate :: raster:: RasterCreationOption ;
15
+ use crate :: raster:: { Buffer3D , GdalType , RasterIOExtraArg , ResampleAlg } ;
15
16
use crate :: utils:: { _last_cpl_err, _last_null_pointer_err, _path_to_c_string, _string} ;
16
17
use crate :: vector:: { sql, Geometry , OwnedLayer } ;
17
18
use crate :: {
@@ -20,10 +21,10 @@ use crate::{
20
21
} ;
21
22
22
23
use gdal_sys:: {
23
- self , CPLErr , GDALAccess , GDALDatasetH , GDALMajorObjectH , OGRErr , OGRGeometryH , OGRLayerH ,
24
- OGRwkbGeometryType ,
24
+ self , CPLErr , GDALAccess , GDALDatasetH , GDALMajorObjectH , GDALRWFlag , GDALRasterIOExtraArg ,
25
+ OGRErr , OGRGeometryH , OGRLayerH , OGRwkbGeometryType ,
25
26
} ;
26
- use libc:: { c_double, c_int, c_uint} ;
27
+ use libc:: { c_double, c_int, c_uint, c_void } ;
27
28
28
29
#[ cfg( all( major_ge_3, minor_ge_1) ) ]
29
30
use crate :: raster:: Group ;
@@ -282,6 +283,23 @@ impl<'a> Default for LayerOptions<'a> {
282
283
}
283
284
}
284
285
286
+ // This defines multiple ways to layout an image in memory, based on GDAL Python bindings
287
+ // which have either 'band' or 'pixel' interleave:
288
+ // https://github.com/OSGeo/gdal/blob/301f31b9b74cd67edcdc555f7e7a58db87cbadb2/swig/include/gdal_array.i#L2300
289
+ pub enum ImageInterleaving {
290
+ /// This means the image is stored in memory with first the first band,
291
+ /// then second band and so on
292
+ Band ,
293
+ /// This means the image is stored in memory with first the value of all bands
294
+ /// for the first pixel, then the same for second pixel and so on
295
+ Pixel ,
296
+ }
297
+
298
+ pub enum BandSelection {
299
+ Subset ( Vec < i32 > ) ,
300
+ All ,
301
+ }
302
+
285
303
// GDAL Docs state: The returned dataset should only be accessed by one thread at a time.
286
304
// See: https://gdal.org/api/raster_c_api.html#_CPPv48GDALOpenPKc10GDALAccess
287
305
// Additionally, VRT Datasets are not safe before GDAL 2.3.
@@ -741,6 +759,115 @@ impl Dataset {
741
759
Ok ( self . child_layer ( c_layer) )
742
760
}
743
761
762
+ /// Read a [`Buffer<T>`] from this dataset, where `T` implements [`GdalType`].
763
+ ///
764
+ /// # Arguments
765
+ /// * `window` - the window position from top left
766
+ /// * `window_size` - the window size (width, height). GDAL will interpolate data if `window_size` != `buffer_size`
767
+ /// * `buffer_size` - the desired size of the 'Buffer' (width, height)
768
+ /// * `e_resample_alg` - the resample algorithm used for the interpolation. Default: `NearestNeighbor`.
769
+ /// * `interleaving`- The output buffer image layout (see `ImageInterleaving`)
770
+ /// * `bands` - A subset of bands to select or BandSelection::All to read all bands
771
+ ///
772
+ /// # Example
773
+ ///
774
+ /// ```rust
775
+ /// # fn main() -> gdal::errors::Result<()> {
776
+ /// use gdal::{Dataset, ImageInterleaving, BandSelection};
777
+ /// use gdal::raster::ResampleAlg;
778
+ /// let dataset = Dataset::open("fixtures/m_3607824_se_17_1_20160620_sub.tif")?;
779
+ /// let size = 2;
780
+ /// let buf = dataset.read_as::<u8>((0, 0), dataset.raster_size(), (size, size), Some(ResampleAlg::Bilinear), ImageInterleaving::Pixel, BandSelection::All)?;
781
+ /// assert_eq!(buf.size, (size, size, dataset.raster_count() as usize));
782
+ /// assert_eq!(buf.data, [103, 116, 101, 169, 92, 108, 94, 163, 92, 112, 93, 179, 89, 109, 91, 181]);
783
+ /// let buf = dataset.read_as::<u8>((0, 0), dataset.raster_size(), (size, size), Some(ResampleAlg::Bilinear), ImageInterleaving::Band, BandSelection::All)?;
784
+ /// assert_eq!(buf.data, [103, 92, 92, 89, 116, 108, 112, 109, 101, 94, 93, 91, 169, 163, 179, 181]);
785
+ /// # Ok(())
786
+ /// # }
787
+ /// ```
788
+ pub fn read_as < T : Copy + GdalType > (
789
+ & self ,
790
+ window : ( isize , isize ) ,
791
+ window_size : ( usize , usize ) ,
792
+ buffer_size : ( usize , usize ) ,
793
+ e_resample_alg : Option < ResampleAlg > ,
794
+ interleaving : ImageInterleaving ,
795
+ bands : BandSelection ,
796
+ ) -> Result < Buffer3D < T > > {
797
+ let resample_alg = e_resample_alg. unwrap_or ( ResampleAlg :: NearestNeighbour ) ;
798
+
799
+ let mut options: GDALRasterIOExtraArg = RasterIOExtraArg {
800
+ e_resample_alg : resample_alg,
801
+ ..Default :: default ( )
802
+ }
803
+ . into ( ) ;
804
+
805
+ let options_ptr: * mut GDALRasterIOExtraArg = & mut options;
806
+
807
+ let ( mut bands, band_count) = match bands {
808
+ BandSelection :: Subset ( bands) => {
809
+ let band_count = bands. len ( ) ;
810
+ ( bands, band_count)
811
+ }
812
+ BandSelection :: All => {
813
+ let band_count = self . raster_count ( ) as usize ;
814
+ let bands = ( 1_i32 ..band_count as i32 + 1_i32 ) . collect ( ) ;
815
+ ( bands, band_count)
816
+ }
817
+ } ;
818
+
819
+ let pixels = buffer_size. 0 * buffer_size. 1 * band_count;
820
+ let mut data: Vec < T > = Vec :: with_capacity ( pixels) ;
821
+ let size_t = size_of :: < T > ( ) as i64 ;
822
+
823
+ let ( pixel_space, line_space, band_space) = match interleaving {
824
+ ImageInterleaving :: Band => ( 0 , 0 , 0 ) ,
825
+ ImageInterleaving :: Pixel => (
826
+ size_t * band_count as i64 ,
827
+ buffer_size. 0 as i64 * size_t * band_count as i64 ,
828
+ size_t,
829
+ ) ,
830
+ } ;
831
+
832
+ // Safety: the GDALRasterIOEx writes
833
+ // exactly pixel elements into the slice, before we
834
+ // read from this slice. This paradigm is suggested
835
+ // in the rust std docs
836
+ // (https://doc.rust-lang.org/std/vec/struct.Vec.html#examples-18)
837
+ let rv = unsafe {
838
+ gdal_sys:: GDALDatasetRasterIOEx (
839
+ self . c_dataset ,
840
+ GDALRWFlag :: GF_Read ,
841
+ window. 0 as c_int ,
842
+ window. 1 as c_int ,
843
+ window_size. 0 as c_int ,
844
+ window_size. 1 as c_int ,
845
+ data. as_mut_ptr ( ) as * mut c_void ,
846
+ buffer_size. 0 as c_int ,
847
+ buffer_size. 1 as c_int ,
848
+ T :: gdal_ordinal ( ) ,
849
+ band_count as i32 ,
850
+ bands. as_mut_ptr ( ) as * mut c_int ,
851
+ pixel_space,
852
+ line_space,
853
+ band_space,
854
+ options_ptr,
855
+ )
856
+ } ;
857
+ if rv != CPLErr :: CE_None {
858
+ return Err ( _last_cpl_err ( rv) ) ;
859
+ }
860
+
861
+ unsafe {
862
+ data. set_len ( pixels) ;
863
+ } ;
864
+
865
+ Ok ( Buffer3D {
866
+ size : ( buffer_size. 0 , buffer_size. 1 , band_count) ,
867
+ data,
868
+ } )
869
+ }
870
+
744
871
/// Set the [`Dataset`]'s affine transformation; also called a _geo-transformation_.
745
872
///
746
873
/// This is like a linear transformation preserves points, straight lines and planes.
@@ -1327,4 +1454,131 @@ mod tests {
1327
1454
let mut ds = Dataset :: open ( fixture ( "roads.geojson" ) ) . unwrap ( ) ;
1328
1455
assert ! ( ds. start_transaction( ) . is_err( ) ) ;
1329
1456
}
1457
+
1458
+ #[ test]
1459
+ fn test_dataset_read_as_pixel_interleaving ( ) {
1460
+ let ds = Dataset :: open ( fixture ( "m_3607824_se_17_1_20160620_sub.tif" ) ) . unwrap ( ) ;
1461
+ print ! ( "{:?}" , ds. raster_size( ) ) ;
1462
+ let ( width, height) = ( 4 , 7 ) ;
1463
+ let band_count = ds. raster_count ( ) as usize ;
1464
+
1465
+ // We compare a single dataset.read_as() to reading band-by-band using
1466
+ // band.read_as()
1467
+ let ds_buf = ds
1468
+ . read_as :: < u8 > (
1469
+ ( 0 , 0 ) ,
1470
+ ( width, height) ,
1471
+ ( width, height) ,
1472
+ Some ( ResampleAlg :: Bilinear ) ,
1473
+ ImageInterleaving :: Pixel ,
1474
+ BandSelection :: All ,
1475
+ )
1476
+ . unwrap ( ) ;
1477
+ assert_eq ! ( ds_buf. size, ( width, height, band_count) ) ;
1478
+
1479
+ for band_index in 0 ..band_count {
1480
+ let band = ds. rasterband ( band_index as isize + 1 ) . unwrap ( ) ;
1481
+ let band_buf = band
1482
+ . read_as :: < u8 > (
1483
+ ( 0 , 0 ) ,
1484
+ ( width, height) ,
1485
+ ( width, height) ,
1486
+ Some ( ResampleAlg :: Bilinear ) ,
1487
+ )
1488
+ . unwrap ( ) ;
1489
+ assert_eq ! ( band_buf. size, ( width, height) ) ;
1490
+ for i in 0 ..height {
1491
+ for j in 0 ..width {
1492
+ assert_eq ! (
1493
+ band_buf. data[ i * width + j] ,
1494
+ ds_buf. data[ i * width * band_count + j * band_count + band_index] ,
1495
+ ) ;
1496
+ }
1497
+ }
1498
+ }
1499
+ }
1500
+
1501
+ #[ test]
1502
+ fn test_dataset_read_as_band_interleaving ( ) {
1503
+ let ds = Dataset :: open ( fixture ( "m_3607824_se_17_1_20160620_sub.tif" ) ) . unwrap ( ) ;
1504
+ let size: ( usize , usize ) = ( 4 , 7 ) ;
1505
+ let band_count = ds. raster_count ( ) as usize ;
1506
+ // We compare a single dataset.read_as() to reading band-by-band using
1507
+ // band.read_as()
1508
+ let ds_buf = ds
1509
+ . read_as :: < u8 > (
1510
+ ( 0 , 0 ) ,
1511
+ size,
1512
+ size,
1513
+ Some ( ResampleAlg :: Bilinear ) ,
1514
+ ImageInterleaving :: Band ,
1515
+ BandSelection :: All ,
1516
+ )
1517
+ . unwrap ( ) ;
1518
+ assert_eq ! ( ds_buf. size, ( size. 0 , size. 1 , band_count) ) ;
1519
+
1520
+ for band_index in 0 ..band_count {
1521
+ let band = ds. rasterband ( band_index as isize + 1 ) . unwrap ( ) ;
1522
+ let band_buf = band
1523
+ . read_as :: < u8 > ( ( 0 , 0 ) , size, size, Some ( ResampleAlg :: Bilinear ) )
1524
+ . unwrap ( ) ;
1525
+ assert_eq ! ( band_buf. size, size) ;
1526
+ assert_eq ! (
1527
+ band_buf. data,
1528
+ ds_buf. data[ band_index * size. 0 * size. 1 ..( band_index + 1 ) * size. 0 * size. 1 ]
1529
+ ) ;
1530
+ }
1531
+ }
1532
+
1533
+ #[ test]
1534
+ fn test_dataset_read_as_band_selection ( ) {
1535
+ let ds = Dataset :: open ( fixture ( "m_3607824_se_17_1_20160620_sub.tif" ) ) . unwrap ( ) ;
1536
+ let size: ( usize , usize ) = ( 4 , 7 ) ;
1537
+ // We compare a single dataset.read_as() to reading band-by-band using
1538
+ // band.read_as()
1539
+ let ds_buf = ds
1540
+ . read_as :: < u8 > (
1541
+ ( 0 , 0 ) ,
1542
+ size,
1543
+ size,
1544
+ Some ( ResampleAlg :: Bilinear ) ,
1545
+ ImageInterleaving :: Band ,
1546
+ BandSelection :: Subset ( vec ! [ 1 , 3 ] ) ,
1547
+ )
1548
+ . unwrap ( ) ;
1549
+ assert_eq ! ( ds_buf. size, ( size. 0 , size. 1 , 2 ) ) ;
1550
+
1551
+ for ( i, band_index) in vec ! [ 1 , 3 ] . iter ( ) . enumerate ( ) {
1552
+ let band = ds. rasterband ( * band_index as isize ) . unwrap ( ) ;
1553
+ let band_buf = band
1554
+ . read_as :: < u8 > ( ( 0 , 0 ) , size, size, Some ( ResampleAlg :: Bilinear ) )
1555
+ . unwrap ( ) ;
1556
+ assert_eq ! ( band_buf. size, size) ;
1557
+ assert_eq ! (
1558
+ band_buf. data,
1559
+ ds_buf. data[ i * size. 0 * size. 1 ..( i + 1 ) * size. 0 * size. 1 ]
1560
+ ) ;
1561
+ }
1562
+ }
1563
+
1564
+ #[ test]
1565
+ fn test_dataset_read_as_buffer_size ( ) {
1566
+ let ds = Dataset :: open ( fixture ( "m_3607824_se_17_1_20160620_sub.tif" ) ) . unwrap ( ) ;
1567
+ let size: ( usize , usize ) = ( 4 , 7 ) ;
1568
+ let buffer_size: ( usize , usize ) = ( 8 , 14 ) ;
1569
+ let band_count = ds. raster_count ( ) as usize ;
1570
+ let ds_buf = ds
1571
+ . read_as :: < u8 > (
1572
+ ( 0 , 0 ) ,
1573
+ size,
1574
+ buffer_size,
1575
+ Some ( ResampleAlg :: Bilinear ) ,
1576
+ ImageInterleaving :: Band ,
1577
+ BandSelection :: All ,
1578
+ )
1579
+ . unwrap ( ) ;
1580
+ // We only assert that we get the right buffer size back because checking for explicit
1581
+ // values is convoluted since GDAL handles the decimation by doing some interpolation
1582
+ assert_eq ! ( ds_buf. size, ( buffer_size. 0 , buffer_size. 1 , band_count) ) ;
1583
+ }
1330
1584
}
0 commit comments