File Coverage

blib/lib/Geo/GDAL/FFI/Band.pm
Criterion Covered Total %
statement 179 198 90.4
branch 17 46 36.9
condition 35 60 58.3
subroutine 22 26 84.6
pod 15 20 75.0
total 268 350 76.5


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