File Coverage

blib/lib/Geo/GDAL/FFI/Band.pm
Criterion Covered Total %
statement 180 199 90.4
branch 17 46 36.9
condition 35 60 58.3
subroutine 22 26 84.6
pod 15 20 75.0
total 269 351 76.6


line stmt bran cond sub pod time code
1             package Geo::GDAL::FFI::Band;
2 5     5   62 use v5.10;
  5         17  
3 5     5   175 use strict;
  5         16  
  5         157  
4 5     5   31 use warnings;
  5         7  
  5         180  
5 5     5   27 use Carp;
  5         18  
  5         366  
6 5     5   42 use FFI::Platypus::Buffer;
  5         8  
  5         11594  
7              
8             our $VERSION = 0.0900;
9              
10             sub DESTROY {
11 3     3   1409 my $self = shift;
12 3         19 Geo::GDAL::FFI::_deregister_parent_ref ($$self);
13             }
14              
15             sub GetDataType {
16 0     0 1 0 my $self = shift;
17 0         0 return $Geo::GDAL::FFI::data_types_reverse{Geo::GDAL::FFI::GDALGetRasterDataType($$self)};
18             }
19              
20             sub GetWidth {
21 0     0 0 0 my $self = shift;
22 0         0 Geo::GDAL::FFI::GDALGetRasterBandXSize($$self);
23             }
24              
25             sub GetHeight {
26 0     0 0 0 my $self = shift;
27 0         0 Geo::GDAL::FFI::GDALGetRasterBandYSize($$self);
28             }
29              
30             sub GetSize {
31 5     5 1 9 my $self = shift;
32             return (
33 5         36 Geo::GDAL::FFI::GDALGetRasterBandXSize($$self),
34             Geo::GDAL::FFI::GDALGetRasterBandYSize($$self)
35             );
36             }
37              
38             sub GetCategoryNames {
39 1     1 0 8 my $self = shift;
40 1         6 my $csl = Geo::GDAL::FFI::GDALGetRasterCategoryNames($$self);
41 1         2 my @names;
42 1         8 for my $i (0..Geo::GDAL::FFI::CSLCount($csl)-1) {
43 2         12 push @names, Geo::GDAL::FFI::CSLGetField($csl, $i);
44             }
45 1         6 return @names;
46             }
47              
48             sub SetCategoryNames {
49 1     1 0 385 my ($self, @names) = @_;
50 1         4 my $csl = 0;
51 1         3 for my $n (@names) {
52 2         15 $csl = Geo::GDAL::FFI::CSLAddString($csl, $n);
53             }
54 1         28 Geo::GDAL::FFI::GDALSetRasterCategoryNames($$self, $csl);
55 1         7 Geo::GDAL::FFI::CSLDestroy($csl);
56             }
57              
58             sub GetNoDataValue {
59 5     5 1 631 my $self = shift;
60 5         12 my $b = 0;
61 5         31 my $v = Geo::GDAL::FFI::GDALGetRasterNoDataValue($$self, \$b);
62 5 100       27 return unless $b;
63 1         8 return $v;
64             }
65              
66             sub SetNoDataValue {
67 2     2 1 696 my $self = shift;
68 2 100       8 unless (@_) {
69 1         16 Geo::GDAL::FFI::GDALDeleteRasterNoDataValue($$self);
70 1         4 return;
71             }
72 1         3 my $v = shift;
73 1         9 my $e = Geo::GDAL::FFI::GDALSetRasterNoDataValue($$self, $v);
74 1 50       5 return unless $e;
75 0   0     0 confess Geo::GDAL::FFI::error_msg() // "SetNoDataValue not supported by the driver.";
76             }
77              
78             sub GetBlockSize {
79 1     1 1 19 my $self = shift;
80 1         3 my ($w, $h);
81 1         16 Geo::GDAL::FFI::GDALGetBlockSize($$self, \$w, \$h);
82 1         5 return ($w, $h);
83             }
84              
85             sub pack_char {
86 15     15 0 26 my $t = shift;
87 15         76 my $is_big_endian = unpack("h*", pack("s", 1)) =~ /01/; # from Programming Perl
88 15 50       66 return ('C', 1) if $t == 1;
89 0 0       0 return ($is_big_endian ? ('n', 2) : ('v', 2)) if $t == 2;
    0          
90 0 0       0 return ('s', 2) if $t == 3;
91 0 0       0 return ($is_big_endian ? ('N', 4) : ('V', 4)) if $t == 4;
    0          
92 0 0       0 return ('l', 4) if $t == 5;
93 0 0       0 return ('f', 4) if $t == 6;
94 0 0       0 return ('d', 8) if $t == 7;
95             # CInt16 => 8,
96             # CInt32 => 9,
97             # CFloat32 => 10,
98             # CFloat64 => 11
99             }
100              
101             sub Read {
102 5     5 1 40 my ($self, $xoff, $yoff, $xsize, $ysize, $bufxsize, $bufysize) = @_;
103 5   100     37 $xoff //= 0;
104 5   100     23 $yoff //= 0;
105 5         35 my $t = Geo::GDAL::FFI::GDALGetRasterDataType($$self);
106 5         15 my ($pc, $bytes_per_cell) = pack_char($t);
107 5         12 my $w;
108 5   66     30 $xsize //= Geo::GDAL::FFI::GDALGetRasterBandXSize($$self);
109 5   66     28 $ysize //= Geo::GDAL::FFI::GDALGetRasterBandYSize($$self);
110 5   33     28 $bufxsize //= $xsize;
111 5   33     24 $bufysize //= $ysize;
112 5         8 $w = $bufxsize * $bytes_per_cell;
113 5         21 my $buf = ' ' x ($bufysize * $w);
114 5         18 my ($pointer, $size) = scalar_to_buffer $buf;
115 5         156 Geo::GDAL::FFI::GDALRasterIO($$self, $Geo::GDAL::FFI::Read, $xoff, $yoff, $xsize, $ysize, $pointer, $bufxsize, $bufysize, $t, 0, 0);
116 5         17 my $offset = 0;
117 5         9 my @data;
118 5         22 for my $y (0..$bufysize-1) {
119 62         170 my @d = unpack($pc."[$bufxsize]", substr($buf, $offset, $w));
120 62         112 push @data, \@d;
121 62         98 $offset += $w;
122             }
123 5         30 return \@data;
124             }
125              
126             sub ReadBlock {
127 2     2 1 20 my ($self, $xoff, $yoff, $xsize, $ysize, $t) = @_;
128 2   50     24 $xoff //= 0;
129 2   50     13 $yoff //= 0;
130 2 50       26 Geo::GDAL::FFI::GDALGetBlockSize($$self, \$xsize, \$ysize) unless defined $xsize;
131 2 50       11 $t = Geo::GDAL::FFI::GDALGetRasterDataType($$self) unless defined $t;
132 2         5 my $buf;
133 2         7 my ($pc, $bytes_per_cell) = pack_char($t);
134 2         5 my $w = $xsize * $bytes_per_cell;
135 2         24 $buf = ' ' x ($ysize * $w);
136 2         10 my ($pointer, $size) = scalar_to_buffer $buf;
137 2         84 Geo::GDAL::FFI::GDALReadBlock($$self, $xoff, $yoff, $pointer);
138 2         6 my $offset = 0;
139 2         6 my @data;
140 2         9 for my $y (0..$ysize-1) {
141 64         1236 my @d = unpack($pc."[$xsize]", substr($buf, $offset, $w));
142 64         206 push @data, \@d;
143 64         127 $offset += $w;
144             }
145 2         107 return \@data;
146             }
147              
148             sub Write {
149 5     5 1 1213 my ($self, $data, $xoff, $yoff, $xsize, $ysize) = @_;
150 5   50     49 $xoff //= 0;
151 5   50     25 $yoff //= 0;
152 5         10 my $bufxsize = @{$data->[0]};
  5         13  
153 5         10 my $bufysize = @$data;
154 5   33     28 $xsize //= $bufxsize;
155 5   33     22 $ysize //= $bufysize;
156 5         28 my $t = Geo::GDAL::FFI::GDALGetRasterDataType($$self);
157 5         15 my ($pc, $bytes_per_cell) = pack_char($t);
158 5         14 my $buf = '';
159 5         30 for my $i (0..$bufysize-1) {
160 62         114 $buf .= pack($pc."[$bufxsize]", @{$data->[$i]});
  62         131  
161             }
162 5         22 my ($pointer, $size) = scalar_to_buffer $buf;
163 5         437 Geo::GDAL::FFI::GDALRasterIO($$self, $Geo::GDAL::FFI::Write, $xoff, $yoff, $xsize, $ysize, $pointer, $bufxsize, $bufysize, $t, 0, 0);
164             }
165              
166             sub WriteBlock {
167 1     1 1 392 my ($self, $data, $xoff, $yoff) = @_;
168 1         3 my ($xsize, $ysize);
169 1         9 Geo::GDAL::FFI::GDALGetBlockSize($$self, \$xsize, \$ysize);
170 1         7 my $t = Geo::GDAL::FFI::GDALGetRasterDataType($$self);
171 1         4 my ($pc, $bytes_per_cell) = pack_char($t);
172 1         4 my $buf = '';
173 1         5 for my $i (0..$ysize-1) {
174 32         54 $buf .= pack($pc."[$xsize]", @{$data->[$i]});
  32         242  
175             }
176 1         7 my ($pointer, $size) = scalar_to_buffer $buf;
177 1         52 Geo::GDAL::FFI::GDALWriteBlock($$self, $xoff, $yoff, $pointer);
178             }
179              
180             sub GetColorInterpretation {
181 0     0 1 0 my $self = shift;
182             return $Geo::GDAL::FFI::color_interpretations_reverse{
183 0         0 Geo::GDAL::FFI::GDALGetRasterColorInterpretation($$self)
184             };
185             }
186              
187             sub SetColorInterpretation {
188 1     1 1 1011 my ($self, $i) = @_;
189 1         9 my $tmp = $Geo::GDAL::FFI::color_interpretations{$i};
190 1 50       5 confess "Unknown color interpretation: $i." unless defined $tmp;
191 1         3 $i = $tmp;
192 1         8 Geo::GDAL::FFI::GDALSetRasterColorInterpretation($$self, $i);
193             }
194              
195             sub GetColorTable {
196 1     1 1 8 my $self = shift;
197 1         22 my $ct = Geo::GDAL::FFI::GDALGetRasterColorTable($$self);
198 1 50       5 return unless $ct;
199             # color table is a table of [c1...c4]
200             # the interpretation of colors is from next method
201 1         2 my @table;
202 1         10 for my $i (0..Geo::GDAL::FFI::GDALGetColorEntryCount($ct)-1) {
203 2         20 my $c = Geo::GDAL::FFI::GDALGetColorEntry($ct, $i);
204 2         13 push @table, $c;
205             }
206 1 50       12 return wantarray ? @table : \@table;
207             }
208              
209             sub SetColorTable {
210 1     1 1 20 my ($self, $table) = @_;
211 1         19 my $ct = Geo::GDAL::FFI::GDALCreateColorTable();
212 1         5 for my $i (0..$#$table) {
213 2         95 Geo::GDAL::FFI::GDALSetColorEntry($ct, $i, $table->[$i]);
214             }
215 1         63 Geo::GDAL::FFI::GDALSetRasterColorTable($$self, $ct);
216 1         16 Geo::GDAL::FFI::GDALDestroyColorTable($ct);
217             }
218              
219             sub GetPiddle {
220 2     2 1 1480 require PDL::Lite; # minimal load
221 2         146551 my ($self, $xoff, $yoff, $xsize, $ysize, $xdim, $ydim, $alg) = @_;
222 2   100     13 $xoff //= 0;
223 2   100     9 $yoff //= 0;
224 2         7 my ($w, $h) = $self->GetSize;
225 2   66     10 $xsize //= $w - $xoff;
226 2   66     7 $ysize //= $h - $yoff;
227 2         9 my $t = Geo::GDAL::FFI::GDALGetRasterDataType($$self);
228 2         8 my $pdl_t = $Geo::GDAL::FFI::data_type2pdl_data_type{$Geo::GDAL::FFI::data_types_reverse{$t}};
229 2 50       6 confess "The Piddle data_type is unsuitable." unless defined $pdl_t;
230 2   33     11 $xdim //= $xsize;
231 2   33     8 $ydim //= $ysize;
232 2   50     8 $alg //= 'NearestNeighbour';
233 2         5 my $tmp = $Geo::GDAL::FFI::resampling{$alg};
234 2 50       6 confess "Unknown resampling scheme: $alg." unless defined $tmp;
235 2         3 $alg = $tmp;
236 2         4 my $bufxsize = $xsize;
237 2         5 my $bufysize = $ysize;
238 2         7 my ($pc, $bytes_per_cell) = pack_char($t);
239 2         8 my $buf = ' ' x ($bufysize * $bufxsize * $bytes_per_cell);
240 2         57 my ($pointer, $size) = scalar_to_buffer $buf;
241 2         54 Geo::GDAL::FFI::GDALRasterIO($$self, $Geo::GDAL::FFI::Read, $xoff, $yoff, $xsize, $ysize, $pointer, $bufxsize, $bufysize, $t, 0, 0);
242 2         13 my $pdl = PDL->new;
243 2         236 $pdl->set_datatype($pdl_t);
244 2         10 $pdl->setdims([$xdim, $ydim]);
245 2         8 my $data = $pdl->get_dataref();
246             # FIXME: see http://pdl.perl.org/PDLdocs/API.html how to wrap $buf into a piddle
247 2         5 $$data = $buf;
248 2         6 $pdl->upd_data;
249             # FIXME: we want approximate equality since no data value can be very large floating point value
250 2         6 my $bad = GetNoDataValue($self);
251 2 50       6 return $pdl->setbadif($pdl == $bad) if defined $bad;
252 2         15 return $pdl;
253             }
254              
255             sub SetPiddle {
256 3     3 1 13 my ($self, $pdl, $xoff, $yoff, $xsize, $ysize) = @_;
257 3   100     14 $xoff //= 0;
258 3   100     10 $yoff //= 0;
259 3         8 my ($w, $h) = $self->GetSize;
260 3         16 my $t = $Geo::GDAL::FFI::pdl_data_type2data_type{$pdl->get_datatype};
261 3 50       8 confess "The Piddle data_type '".$pdl->get_datatype."' is unsuitable." unless defined $t;
262 3         9 $t = $Geo::GDAL::FFI::data_types{$t};
263 3         10 my ($xdim, $ydim) = $pdl->dims();
264 3   66     93 $xsize //= $xdim;
265 3   66     15 $ysize //= $ydim;
266 3 50       9 if ($xdim > $w - $xoff) {
267 0         0 warn "Piddle too wide ($xdim) for this raster band (width = $w, offset = $xoff).";
268 0         0 $xdim = $w - $xoff;
269             }
270 3 50       8 if ($ydim > $h - $yoff) {
271 0         0 $ydim = $h - $yoff;
272 0         0 warn "Piddle too tall ($ydim) for this raster band (height = $h, offset = $yoff).";
273             }
274 3         10 my $data = $pdl->get_dataref();
275 3         11 my ($pointer, $size) = scalar_to_buffer $$data;
276 3         382 Geo::GDAL::FFI::GDALRasterIO($$self, $Geo::GDAL::FFI::Write, $xoff, $yoff, $xsize, $ysize, $pointer, $xdim, $ydim, $t, 0, 0);
277             }
278              
279             1;
280              
281             =pod
282              
283             =encoding UTF-8
284              
285             =head1 NAME
286              
287             Geo::GDAL::FFI::Band - A GDAL raster band
288              
289             =head1 SYNOPSIS
290              
291             =head1 DESCRIPTION
292              
293             A band (channel) in a raster dataset. Use the Band method of a dataset
294             object to obtain a band object.
295              
296             =head1 METHODS
297              
298             =head2 GetDataType
299              
300             my $datatype = $band->GetDataType;
301              
302             =head2 GetSize
303              
304             my @size = $band->GetSize;
305              
306             =head2 GetBlockSize
307              
308             my @size = $band->GetBlockSize;
309              
310             =head2 GetNoDataValue
311              
312             my $nodata = $band->GetNoDataValue;
313              
314             =head2 SetNoDataValue
315              
316             $band->SetNoDataValue($value);
317              
318             Calling the method without arguments deletes the nodata value.
319              
320             $band->SetNoDataValue;
321              
322             =head2 Read
323              
324             my $data = $band->Read($xoff, $yoff, $xsize, $ysize, $bufxsize, $bufysize);
325              
326             All arguments are optional. If no arguments are given, reads the whole
327             raster band into a 2D Perl array. The returned array is an array of
328             references to arrays of row values.
329              
330             =head2 ReadBlock
331              
332             my $data = $band->ReadBlock($xoff, $yoff, @blocksize, $datatype);
333              
334             Reads a block of data from the band and returns it as a Perl 2D
335             array. C<@blocksize> and C<$datatype> (an integer) are optional and
336             obtained from the GDAL raster object if not given.
337              
338             =head2 Write
339              
340             $band->Write($data, $xoff, $yoff, $xsize, $ysize);
341              
342             =head2 WriteBlock
343              
344             $band->WriteBlock($data, $xoff, $yoff);
345              
346             =head2 SetPiddle
347              
348             $band->SetPiddle($pdl, $xoff, $yoff, $xsize, $ysize);
349              
350             Read data from a piddle into this Band.
351              
352             =head2 GetPiddle
353              
354             $band->GetPiddle($xoff, $yoff, $xsize, $ysize, $xdim, $ydim);
355              
356             Read data from this Band into a piddle.
357              
358             =head2 GetColorInterpretation
359              
360             my $ci = $band->GetColorInterpretation;
361              
362             =head2 SetColorInterpretation
363              
364             $band->SetColorInterpretation($ci);
365              
366             =head2 GetColorTable
367              
368             my $color_table = $band->GetColorTable;
369              
370             Returns the color table as an array of arrays. The inner tables are
371             colors [c1...c4].
372              
373             =head2 SetColorTable
374              
375             $band->SetColorTable($color_table);
376              
377             =head1 LICENSE
378              
379             This software is released under the Artistic License. See
380             L.
381              
382             =head1 AUTHOR
383              
384             Ari Jolma - Ari.Jolma at gmail.com
385              
386             =head1 SEE ALSO
387              
388             L
389              
390             L, L, L
391              
392             =cut
393              
394             __END__;