ffi-gdal 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +25 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +60 -0
- data/Rakefile +57 -0
- data/ffi-gdal.gemspec +28 -0
- data/lib/ext/cpl_error_symbols.rb +37 -0
- data/lib/ext/to_bool.rb +13 -0
- data/lib/ffi/gdal/cpl_conv.rb +151 -0
- data/lib/ffi/gdal/cpl_error.rb +91 -0
- data/lib/ffi/gdal/cpl_string.rb +113 -0
- data/lib/ffi/gdal/cpl_vsi.rb +119 -0
- data/lib/ffi/gdal/gdal_color_entry.rb +13 -0
- data/lib/ffi/gdal/gdal_gcp.rb +18 -0
- data/lib/ffi/gdal/ogr_api.rb +28 -0
- data/lib/ffi/gdal/ogr_core.rb +199 -0
- data/lib/ffi/gdal/ogr_srs_api.rb +48 -0
- data/lib/ffi/gdal/version.rb +5 -0
- data/lib/ffi/gdal.rb +607 -0
- data/lib/ffi-gdal/color_table.rb +59 -0
- data/lib/ffi-gdal/dataset.rb +347 -0
- data/lib/ffi-gdal/driver.rb +151 -0
- data/lib/ffi-gdal/exceptions.rb +17 -0
- data/lib/ffi-gdal/geo_transform.rb +137 -0
- data/lib/ffi-gdal/major_object.rb +71 -0
- data/lib/ffi-gdal/raster_attribute_table.rb +78 -0
- data/lib/ffi-gdal/raster_band.rb +571 -0
- data/lib/ffi-gdal/version_info.rb +48 -0
- data/lib/ffi-gdal.rb +12 -0
- data/linkies.rb +35 -0
- data/meow.rb +144 -0
- data/readie.rb +90 -0
- data/rubby.rb +224 -0
- data/spec/ext/cpl_error_symbols_spec.rb +79 -0
- data/spec/ffi-gdal/integration/color_table_info_spec.rb +60 -0
- data/spec/ffi-gdal/integration/dataset_info_spec.rb +95 -0
- data/spec/ffi-gdal/integration/driver_info_spec.rb +60 -0
- data/spec/ffi-gdal/integration/geo_transform_info_spec.rb +66 -0
- data/spec/ffi-gdal/integration/raster_attribute_table_info_spec.rb +23 -0
- data/spec/ffi-gdal/integration/raster_band_info_spec.rb +333 -0
- data/spec/ffi-gdal/unit/version_info_spec.rb +48 -0
- data/spec/ffi-gdal_spec.rb +6 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/integration_help.rb +1 -0
- data/spec/support/shared_examples/major_object_examples.rb +68 -0
- data/things.rb +84 -0
- metadata +216 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
require_relative '../ffi/gdal'
|
2
|
+
|
3
|
+
|
4
|
+
module GDAL
|
5
|
+
class RasterAttributeTable
|
6
|
+
include FFI::GDAL
|
7
|
+
|
8
|
+
# @param raster_band [GDAL::RasterBand, FFI::Pointer]
|
9
|
+
# @param raster_attribute_table_pointer [FFI::Pointer]
|
10
|
+
def initialize(raster_band, raster_attribute_table_pointer: nil)
|
11
|
+
@raster_band = if raster_band.is_a? GDAL::RasterBand
|
12
|
+
raster_band.c_pointer
|
13
|
+
else
|
14
|
+
raster_band
|
15
|
+
end
|
16
|
+
|
17
|
+
@gdal_raster_attribute_table = if raster_attribute_table_pointer
|
18
|
+
raster_attribute_table_pointer
|
19
|
+
else
|
20
|
+
GDALGetDefaultRAT(@raster_band)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def c_pointer
|
25
|
+
@gdal_raster_attribute_table
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param index [Fixnum] The column number.
|
29
|
+
# @return [Fixnum]
|
30
|
+
def column_count
|
31
|
+
GDALRATGetColumnCount(c_pointer)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param index [Fixnum] The column number.
|
35
|
+
# @return [String]
|
36
|
+
def columnn_name(index)
|
37
|
+
GDALRATGetNameOfCol(c_pointer, index)
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param index [Fixnum] The column number.
|
41
|
+
# @return [GDALRATFieldUsage]
|
42
|
+
def column_usage(index)
|
43
|
+
GDALRATGetUsageOfCol(c_pointer, index)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @param index [Fixnum] The column number.
|
47
|
+
# @return [GDALRATFieldType]
|
48
|
+
def column_type(index)
|
49
|
+
GDALRATGetTypeOfCol(c_pointer, index)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param field_usage [GDALRATFieldUsage]
|
53
|
+
# @return [Fixnum] The column number.
|
54
|
+
def column_of_usage(field_usage)
|
55
|
+
GDALRATGetColOfUsage(c_pointer, index)
|
56
|
+
end
|
57
|
+
|
58
|
+
# @param field_usage [GDALRATFieldUsage]
|
59
|
+
# @return [Fixnum] The column number.
|
60
|
+
def row_count
|
61
|
+
GDALRATGetRowCount(c_pointer)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param entry_count [Fixnum] The number of entries to produce. The default
|
65
|
+
# will try to auto-determine the number.
|
66
|
+
# @return [GDAL::ColorTable]
|
67
|
+
def to_color_table(entry_count = -1)
|
68
|
+
color_table_pointer = GDALRATTranslateToColorTable(c_pointer, entry_count)
|
69
|
+
|
70
|
+
GDAL::ColorTable.new(@raster_band, color_table_pointer: color_table_pointer)
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param file_path [String]
|
74
|
+
def dump_readable(file_path = 'stdout')
|
75
|
+
GDALRATDumpReadable(c_pointer, file_path)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,571 @@
|
|
1
|
+
require_relative '../ffi/gdal'
|
2
|
+
require_relative 'color_table'
|
3
|
+
require_relative 'major_object'
|
4
|
+
require_relative 'raster_attribute_table'
|
5
|
+
require 'narray'
|
6
|
+
|
7
|
+
module GDAL
|
8
|
+
class RasterBand
|
9
|
+
include FFI::GDAL
|
10
|
+
include MajorObject
|
11
|
+
|
12
|
+
attr_reader :dataset
|
13
|
+
|
14
|
+
# @param dataset [GDAL::Dataset, FFI::Pointer]
|
15
|
+
# @param band_id [Fixnum] Requried if not passing in +raster_band_pointer+.
|
16
|
+
# @param raster_band_pointer [FFI::Pointer] Requried if not passing in
|
17
|
+
# +band_id+.
|
18
|
+
def initialize(dataset, band_id: nil, raster_band_pointer: nil)
|
19
|
+
if dataset.is_a? GDAL::Dataset
|
20
|
+
@dataset = dataset.c_pointer
|
21
|
+
else
|
22
|
+
@dataset = dataset
|
23
|
+
end
|
24
|
+
|
25
|
+
@gdal_raster_band = if raster_band_pointer
|
26
|
+
raster_band_pointer
|
27
|
+
elsif band_id
|
28
|
+
GDALGetRasterBand(@dataset, band_id)
|
29
|
+
else
|
30
|
+
raise 'Must pass in band_id or the raster_band_pointer.'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def c_pointer
|
35
|
+
@gdal_raster_band
|
36
|
+
end
|
37
|
+
|
38
|
+
# The raster width in pixels.
|
39
|
+
#
|
40
|
+
# @return [Fixnum]
|
41
|
+
def x_size
|
42
|
+
return nil if null?
|
43
|
+
|
44
|
+
GDALGetRasterBandXSize(@gdal_raster_band)
|
45
|
+
end
|
46
|
+
|
47
|
+
# The raster height in pixels.
|
48
|
+
#
|
49
|
+
# @return [Fixnum]
|
50
|
+
def y_size
|
51
|
+
return nil if null?
|
52
|
+
|
53
|
+
GDALGetRasterBandYSize(@gdal_raster_band)
|
54
|
+
end
|
55
|
+
|
56
|
+
# The type of access to the raster band this object currently has.
|
57
|
+
#
|
58
|
+
# @return [Symbol] Either :GA_Update or :GA_ReadOnly.
|
59
|
+
def access_flag
|
60
|
+
return nil if null?
|
61
|
+
|
62
|
+
GDALGetRasterAccess(@gdal_raster_band)
|
63
|
+
end
|
64
|
+
|
65
|
+
# The number of band within the associated dataset that this band
|
66
|
+
# represents.
|
67
|
+
#
|
68
|
+
# @return [Fixnum]
|
69
|
+
def band_number
|
70
|
+
return nil if null?
|
71
|
+
|
72
|
+
GDALGetBandNumber(@gdal_raster_band)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [Symbol] One of FFI::GDAL::GDALColorInterp.
|
76
|
+
def color_interpretation
|
77
|
+
GDALGetRasterColorInterpretation(@gdal_raster_band)
|
78
|
+
end
|
79
|
+
|
80
|
+
# @return [GDAL::ColorTable]
|
81
|
+
def color_table
|
82
|
+
gdal_color_table = GDALGetRasterColorTable(@gdal_raster_band)
|
83
|
+
return nil if gdal_color_table.null?
|
84
|
+
|
85
|
+
@color_table ||= ColorTable.new(@gdal_raster_band,
|
86
|
+
color_table_pointer: gdal_color_table)
|
87
|
+
end
|
88
|
+
|
89
|
+
# The pixel data type for this band.
|
90
|
+
#
|
91
|
+
# @return [Symbol] One of FFI::GDAL::GDALDataType.
|
92
|
+
def data_type
|
93
|
+
GDALGetRasterDataType(@gdal_raster_band)
|
94
|
+
end
|
95
|
+
|
96
|
+
# The natural block size is the block size that is most efficient for
|
97
|
+
# accessing the format. For many formats this is simply a whole scanline
|
98
|
+
# in which case x is set to #x_size, and y is set to 1.
|
99
|
+
#
|
100
|
+
# @return [Hash{x => Fixnum, y => Fixnum}]
|
101
|
+
def block_size
|
102
|
+
x_pointer = FFI::MemoryPointer.new(:int)
|
103
|
+
y_pointer = FFI::MemoryPointer.new(:int)
|
104
|
+
GDALGetBlockSize(@gdal_raster_band, x_pointer, y_pointer)
|
105
|
+
|
106
|
+
{ x: x_pointer.read_int, y: y_pointer.read_int }
|
107
|
+
end
|
108
|
+
|
109
|
+
# @return [Array<String>]
|
110
|
+
def category_names
|
111
|
+
names = GDALGetRasterCategoryNames(@gdal_raster_band)
|
112
|
+
return [] if names.null?
|
113
|
+
|
114
|
+
names.get_array_of_string(0)
|
115
|
+
end
|
116
|
+
|
117
|
+
# @param names [Array<String>]
|
118
|
+
# @return [Array<String>]
|
119
|
+
def category_names=(names)
|
120
|
+
str_pointers = names.map do |name|
|
121
|
+
FFI::MemoryPointer.from_string(name.to_s)
|
122
|
+
end
|
123
|
+
|
124
|
+
str_pointers << nil
|
125
|
+
names_pointer = FFI::MemoryPointer.new(:pointer, str_pointers.length)
|
126
|
+
|
127
|
+
str_pointers.each_with_index do |ptr, i|
|
128
|
+
names_pointer[i].put_pointer(0, ptr)
|
129
|
+
end
|
130
|
+
|
131
|
+
cpl_err = GDALSetRasterCategoryNames(@gdal_raster_band, names_pointer)
|
132
|
+
|
133
|
+
cpl_err.to_ruby(warning: [])
|
134
|
+
end
|
135
|
+
|
136
|
+
# The no data value for a band is generally a special marker value used to
|
137
|
+
# mark pixels that are not valid data. Such pixels should generally not be
|
138
|
+
# displayed, nor contribute to analysis operations.
|
139
|
+
#
|
140
|
+
# @return [Hash{value => Float, is_associated => Boolean}]
|
141
|
+
def no_data_value
|
142
|
+
associated = FFI::MemoryPointer.new(:bool)
|
143
|
+
value = GDALGetRasterNoDataValue(@gdal_raster_band, associated)
|
144
|
+
|
145
|
+
{ value: value, is_associated: associated.read_bytes(1).to_bool }
|
146
|
+
end
|
147
|
+
|
148
|
+
# @return [Fixnum]
|
149
|
+
def overview_count
|
150
|
+
GDALGetOverviewCount(@gdal_raster_band)
|
151
|
+
end
|
152
|
+
|
153
|
+
# @return [Boolean]
|
154
|
+
def arbitrary_overviews?
|
155
|
+
GDALHasArbitraryOverviews(@gdal_raster_band).zero? ? false : true
|
156
|
+
end
|
157
|
+
|
158
|
+
# @param index [Fixnum] Must be between 0 and (#overview_count - 1).
|
159
|
+
# @return [GDAL::RasterBand]
|
160
|
+
def overview(index)
|
161
|
+
return nil if overview_count.zero?
|
162
|
+
|
163
|
+
overview_pointer = GDALGetOverview(@gdal_raster_band, index)
|
164
|
+
return nil if overview_pointer.null?
|
165
|
+
|
166
|
+
self.class.new(dataset, raster_band_pointer: overview_pointer)
|
167
|
+
end
|
168
|
+
|
169
|
+
# @param desired_samples [Fixnum] The returned band will have at least this
|
170
|
+
# many pixels.
|
171
|
+
# @return [GDAL::RasterBand] An optimal overview or the same raster band if
|
172
|
+
# the raster band has no overviews.
|
173
|
+
def raster_sample_overview(desired_samples=0)
|
174
|
+
band_pointer = GDALGetRasterSampleOverview(@gdal_raster_band, desired_samples)
|
175
|
+
return nil if band_pointer.null?
|
176
|
+
|
177
|
+
self.class.new(dataset, raster_band_pointer: band_pointer)
|
178
|
+
end
|
179
|
+
|
180
|
+
# @return [GDAL::RasterBand]
|
181
|
+
def mask_band
|
182
|
+
band_pointer = GDALGetMaskBand(@gdal_raster_band)
|
183
|
+
return nil if band_pointer.null?
|
184
|
+
|
185
|
+
self.class.new(dataset, raster_band_pointer: band_pointer)
|
186
|
+
end
|
187
|
+
|
188
|
+
# @return [Array<Symbol>]
|
189
|
+
def mask_flags
|
190
|
+
flag_list = GDALGetMaskFlags(@gdal_raster_band).to_s(2).scan(/\d/)
|
191
|
+
flags = []
|
192
|
+
|
193
|
+
flag_list.reverse.each_with_index do |flag, i|
|
194
|
+
if i == 0 && flag.to_i == 1
|
195
|
+
flags << :GMF_ALL_VALID
|
196
|
+
elsif i == 1 && flag.to_i == 1
|
197
|
+
flags << :GMF_PER_DATASET
|
198
|
+
elsif i == 2 && flag.to_i == 1
|
199
|
+
flags << :GMF_ALPHA
|
200
|
+
elsif i == 3 && flag.to_i == 1
|
201
|
+
flags << :GMF_NODATA
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
flags
|
206
|
+
end
|
207
|
+
|
208
|
+
# Returns minimum, maximum, mean, and standard deviation of all pixel values
|
209
|
+
# in this band.
|
210
|
+
#
|
211
|
+
# @param approx_ok [Boolean] If +true+, stats may be computed based on
|
212
|
+
# overviews or a subset of all tiles.
|
213
|
+
# @param force [Boolean] If +false+, stats will only be returned if the
|
214
|
+
# calculating can be done without rescanning the image.
|
215
|
+
# @return [Hash{mininum: Float, maximum: Float, mean: Float,
|
216
|
+
# standard_deviation: Float}]
|
217
|
+
def statistics(approx_ok=true, force=true)
|
218
|
+
min = FFI::MemoryPointer.new(:double)
|
219
|
+
max = FFI::MemoryPointer.new(:double)
|
220
|
+
mean = FFI::MemoryPointer.new(:double)
|
221
|
+
standard_deviation = FFI::MemoryPointer.new(:double)
|
222
|
+
|
223
|
+
cpl_err = GDALGetRasterStatistics(@gdal_raster_band,
|
224
|
+
approx_ok,
|
225
|
+
force,
|
226
|
+
min,
|
227
|
+
max,
|
228
|
+
mean,
|
229
|
+
standard_deviation)
|
230
|
+
|
231
|
+
minimum = min.null? ? 0.0 : min.read_double
|
232
|
+
|
233
|
+
case cpl_err.to_ruby
|
234
|
+
when :none, :debug
|
235
|
+
{
|
236
|
+
minimum: min.read_double,
|
237
|
+
maximum: max.read_double,
|
238
|
+
mean: mean.read_double,
|
239
|
+
standard_deviation: standard_deviation.read_double
|
240
|
+
}
|
241
|
+
when :warning then {}
|
242
|
+
when :failure, :fatal then raise CPLErrFailure
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# The raster value scale. This value (in combination with the #offset
|
247
|
+
# value) is used to transform raw pixel values into the units returned by
|
248
|
+
# #units. For example this might be used to store elevations in GUInt16
|
249
|
+
# bands with a precision of 0.1, and starting from -100.
|
250
|
+
#
|
251
|
+
# Units value = (raw value * scale) + offset
|
252
|
+
#
|
253
|
+
# For file formats that don't know this intrinsically a value of one is
|
254
|
+
# returned.
|
255
|
+
#
|
256
|
+
# @return [Hash{value => Float, is_meaningful => Boolean}]
|
257
|
+
def scale
|
258
|
+
meaningful = FFI::MemoryPointer.new(:bool)
|
259
|
+
result = GDALGetRasterScale(@gdal_raster_band, meaningful)
|
260
|
+
|
261
|
+
{ value: result, is_meaningful: meaningful.read_bytes(1).to_bool }
|
262
|
+
end
|
263
|
+
|
264
|
+
# @param new_scale [Float]
|
265
|
+
# @return [FFI::GDAL::CPLErr]
|
266
|
+
def scale=(new_scale)
|
267
|
+
GDALSetRasterScale(@gdal_raster_band, new_scale.to_f)
|
268
|
+
end
|
269
|
+
|
270
|
+
# This value (in combination with the GetScale() value) is used to
|
271
|
+
# transform raw pixel values into the units returned by #units. For example
|
272
|
+
# this might be used to store elevations in GUInt16 bands with a precision
|
273
|
+
# of 0.1, and starting from -100.
|
274
|
+
#
|
275
|
+
# Units value = (raw value * scale) + offset.
|
276
|
+
#
|
277
|
+
# For file formats that don't know this intrinsically a value of 0.0 is
|
278
|
+
# returned.
|
279
|
+
#
|
280
|
+
# @return [Hash{value => Float, is_meaningful => Boolean}]
|
281
|
+
def offset
|
282
|
+
meaningful = FFI::MemoryPointer.new(:bool)
|
283
|
+
result = GDALGetRasterOffset(@gdal_raster_band, meaningful)
|
284
|
+
|
285
|
+
{ value: result, is_meaningful: meaningful.read_bytes(1).to_bool }
|
286
|
+
end
|
287
|
+
|
288
|
+
# @param new_offset [Float]
|
289
|
+
# @return [FFI::GDAL::CPLErr]
|
290
|
+
def offset=(new_offset)
|
291
|
+
GDALSetRasterOffset(@gdal_raster_band, new_offset)
|
292
|
+
end
|
293
|
+
|
294
|
+
# @return [String]
|
295
|
+
def unit_type
|
296
|
+
GDALGetRasterUnitType(@gdal_raster_band)
|
297
|
+
end
|
298
|
+
|
299
|
+
# @param new_unit_type [String] "" indicates unknown, "m" is meters, "ft"
|
300
|
+
# is feet; other non-standard values are allowed.
|
301
|
+
# @return [FFI::GDAL::CPLErr]
|
302
|
+
def unit_type=(new_unit_type)
|
303
|
+
GDALSetRasterUnitType(@gdal_raster_band, new_unit_type)
|
304
|
+
end
|
305
|
+
|
306
|
+
# Gets the default raster histogram. Results are returned as a Hash so some
|
307
|
+
# metadata about the histogram can be returned. Example:
|
308
|
+
#
|
309
|
+
# {
|
310
|
+
# :mininum => -0.9,
|
311
|
+
# :maximum => 255.9,
|
312
|
+
# :buckets => 256,
|
313
|
+
# :totals => [
|
314
|
+
# 3954, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
315
|
+
# 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0,
|
316
|
+
# 0, 0, 10, 27, 201, 699, 1766, 3472, 5013, 6464, 7698, 8352,
|
317
|
+
# 9039, 10054, 11378, 13132, 14377, 14371, 14221, 14963, 14740,
|
318
|
+
# 14379, 13724, 12938, 11318, 9828, 8504, 7040, 5700, 4890,
|
319
|
+
# 4128, 3276, 2749, 2322, 1944, 1596, 1266, 1050, 784, 663,
|
320
|
+
# 547, 518, 367, 331, 309, 279, 178, 169, 162, 149, 109, 98,
|
321
|
+
# 90, 89, 82, 85, 74, 75, 42, 40, 39, 35, 39, 36, 36, 27, 20,
|
322
|
+
# 12, 13, 19, 16, 12, 11, 6, 6, 8, 12, 6, 8, 11, 3, 7, 9, 2,
|
323
|
+
# 5, 2, 5, 1, 4, 0, 0, 1, 0, 1, 2, 1, 0, 2, 1, 0, 0, 1, 0, 1,
|
324
|
+
# 1, 1, 0, 2, 1, 2, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
|
325
|
+
# 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
326
|
+
# 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
327
|
+
# 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
328
|
+
# 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
329
|
+
# 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
330
|
+
# ]
|
331
|
+
# }
|
332
|
+
#
|
333
|
+
# @param force [Boolean] Forces the computation of the histogram. If
|
334
|
+
# +false+ and the default histogram isn't available, this returns nil.
|
335
|
+
# @param block [Proc] No required, but can be used to output progess info
|
336
|
+
# during processing.
|
337
|
+
# @yieldparam completion [Float] The ration completed as a decimal.
|
338
|
+
# @yieldparam message [String] Message string to display.
|
339
|
+
# @return [Hash{minimum => Float, maximum => Float, buckets => Fixnum,
|
340
|
+
# totals => Array<Fixnum>}]
|
341
|
+
def default_histogram(force=false, &block)
|
342
|
+
min_pointer = FFI::MemoryPointer.new(:double)
|
343
|
+
max_pointer = FFI::MemoryPointer.new(:double)
|
344
|
+
buckets_pointer = FFI::MemoryPointer.new(:int)
|
345
|
+
histogram_pointer = FFI::MemoryPointer.new(:pointer)
|
346
|
+
|
347
|
+
progress_proc = block || Proc.new do |completion, message, progress_arg|
|
348
|
+
puts "completion: #{completion * 100}"
|
349
|
+
puts "message: #{message}"
|
350
|
+
true
|
351
|
+
end
|
352
|
+
|
353
|
+
cpl_err = GDALGetDefaultHistogram(@gdal_raster_band,
|
354
|
+
min_pointer,
|
355
|
+
max_pointer,
|
356
|
+
buckets_pointer,
|
357
|
+
histogram_pointer,
|
358
|
+
force,
|
359
|
+
progress_proc,
|
360
|
+
nil
|
361
|
+
)
|
362
|
+
|
363
|
+
min = min_pointer.read_double
|
364
|
+
max = max_pointer.read_double
|
365
|
+
buckets = buckets_pointer.read_int
|
366
|
+
totals = histogram_pointer.get_pointer(0).read_array_of_int(buckets)
|
367
|
+
|
368
|
+
case cpl_err.to_ruby
|
369
|
+
when :none, :debug
|
370
|
+
{
|
371
|
+
minimum: min,
|
372
|
+
maximum: max,
|
373
|
+
buckets: buckets,
|
374
|
+
totals: totals
|
375
|
+
}
|
376
|
+
when :warning then return nil
|
377
|
+
when :failure, :fatal then raise CPLError
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
# @return [GDAL::RasterAttributeTable]
|
382
|
+
def default_raster_attribute_table
|
383
|
+
rat_pointer = GDALGetDefaultRAT(c_pointer)
|
384
|
+
return nil if rat_pointer.null?
|
385
|
+
|
386
|
+
GDAL::RasterAttributeTable.new(c_pointer,
|
387
|
+
raster_attribute_table_pointer: rat_pointer)
|
388
|
+
end
|
389
|
+
|
390
|
+
# Computes a histogram using the given inputs. If you just want the default
|
391
|
+
# histogram, use #default_histogram.
|
392
|
+
#
|
393
|
+
# @param min [Float]
|
394
|
+
# @param max [Float]
|
395
|
+
# @param buckets [Fixnum]
|
396
|
+
# @param include_out_of_range [Boolean]
|
397
|
+
# @param approx_ok [Boolean]
|
398
|
+
# @param block [Proc] No required, but can be used to output progess info
|
399
|
+
# during processing.
|
400
|
+
# @yieldparam completion [Float] The ration completed as a decimal.
|
401
|
+
# @yieldparam message [String] Message string to display.
|
402
|
+
# @return [Hash{minimum => Float, maximum => Float, buckets => Fixnum,
|
403
|
+
# totals => Array<Fixnum>}]
|
404
|
+
def histogram(min, max, buckets, include_out_of_range: false,
|
405
|
+
approx_ok: true, &block)
|
406
|
+
histogram_pointer = FFI::MemoryPointer.new(:int, buckets)
|
407
|
+
|
408
|
+
progress_proc = block || Proc.new do |completion, message, progress_arg|
|
409
|
+
puts "progress: #{completion * 100}"
|
410
|
+
true
|
411
|
+
end
|
412
|
+
|
413
|
+
cpl_err = GDALGetRasterHistogram(@gdal_raster_band,
|
414
|
+
min.to_f,
|
415
|
+
max.to_f,
|
416
|
+
buckets,
|
417
|
+
histogram_pointer,
|
418
|
+
include_out_of_range,
|
419
|
+
approx_ok,
|
420
|
+
progress_proc,
|
421
|
+
'doing things'
|
422
|
+
)
|
423
|
+
totals = histogram_pointer.read_array_of_int(0)
|
424
|
+
|
425
|
+
case cpl_err.to_ruby
|
426
|
+
when :none
|
427
|
+
{
|
428
|
+
minimum: min,
|
429
|
+
maximum: max,
|
430
|
+
buckets: buckets,
|
431
|
+
totals: totals
|
432
|
+
}
|
433
|
+
when :warning then return nil
|
434
|
+
when :failure then raise CPLError
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
# TODO: Something about the pointer allocation smells here...
|
439
|
+
#def read(x_offset: 0, y_offset: 0, x_size: x_size, y_size: 1, pixel_space: 0, line_space: 0)
|
440
|
+
def readlines
|
441
|
+
x_offset = 0
|
442
|
+
line_size = 1
|
443
|
+
pixel_space = 0
|
444
|
+
line_space = 0
|
445
|
+
scan_line = FFI::MemoryPointer.new(:float, x_size)
|
446
|
+
|
447
|
+
0.upto(y_size - 1) do |y|
|
448
|
+
GDALRasterIO(@gdal_raster_band,
|
449
|
+
:GF_Read,
|
450
|
+
x_offset,
|
451
|
+
y,
|
452
|
+
x_size,
|
453
|
+
line_size,
|
454
|
+
scan_line,
|
455
|
+
x_size,
|
456
|
+
line_size,
|
457
|
+
data_type,
|
458
|
+
pixel_space,
|
459
|
+
line_space
|
460
|
+
)
|
461
|
+
|
462
|
+
yield scan_line.read_array_of_float(x_size).dup
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
# @param pixel_array [NArray] The NArray of pixels.
|
467
|
+
# @param x_offset [Fixnum] The left-most pixel to start writing.
|
468
|
+
# @param y_offset [Fixnum] The top-most pixel to start writing.
|
469
|
+
# @param data_type [FFI::GDAL::GDALDataType] The type of pixel contained in
|
470
|
+
# the +pixel_array+.
|
471
|
+
# @param line_space [Fixnum]
|
472
|
+
# @param pixel_space [Fixnum]
|
473
|
+
# TODO: Write using #buffer_size to write most efficiently.
|
474
|
+
# TODO: Return a value!
|
475
|
+
def write_array(pixel_array, x_offset: 0, y_offset: 0, data_type: :GDT_Byte,
|
476
|
+
line_space: 0, pixel_space: 0)
|
477
|
+
line_size = 1
|
478
|
+
x_size = pixel_array.sizes.first
|
479
|
+
y_size = pixel_array.sizes.last
|
480
|
+
|
481
|
+
columns_to_write = x_size - x_offset
|
482
|
+
lines_to_write = y_size - y_offset
|
483
|
+
scan_line = FFI::MemoryPointer.new(:float, columns_to_write)
|
484
|
+
|
485
|
+
(y_offset).upto(lines_to_write - 1) do |y|
|
486
|
+
pixels = pixel_array[true, y]
|
487
|
+
scan_line.write_array_of_float(pixels.to_a)
|
488
|
+
|
489
|
+
GDALRasterIO(@gdal_raster_band,
|
490
|
+
:GF_Write,
|
491
|
+
x_offset, # nXOff
|
492
|
+
y,
|
493
|
+
x_size, # nXSize
|
494
|
+
line_size, # nYSize
|
495
|
+
scan_line, # pData
|
496
|
+
x_size, # nBufXSize
|
497
|
+
line_size, # nBufYSize
|
498
|
+
data_type, # eBufType
|
499
|
+
pixel_space, # nPixelSpace
|
500
|
+
line_space # nLineSpace
|
501
|
+
)
|
502
|
+
end
|
503
|
+
|
504
|
+
GDALFlushRasterCache(@gdal_raster_band)
|
505
|
+
end
|
506
|
+
|
507
|
+
# Read a block of image data, more efficiently than #read. Doesn't
|
508
|
+
# resample or do data type conversion.
|
509
|
+
#
|
510
|
+
# @param x_offset [Fixnum] The horizontal block offset, with 0 indicating
|
511
|
+
# the left-most block, 1 the next block, etc.
|
512
|
+
# @param y_offset [Fixnum] The vertical block offset, with 0 indicating the
|
513
|
+
# top-most block, 1 the next block, etc.
|
514
|
+
def read_block(x_offset, y_offset, image_buffer=nil)
|
515
|
+
image_buffer ||= FFI::MemoryPointer.new(:void)
|
516
|
+
#puts "x offset: #{x_offset}"
|
517
|
+
#puts "y offset: #{y_offset}"
|
518
|
+
|
519
|
+
result = GDALReadBlock(@gdal_raster_band, x_offset, y_offset, image_buffer)
|
520
|
+
|
521
|
+
if result == :none
|
522
|
+
elsif result == :failure
|
523
|
+
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
# The minimum and maximum values for this band.
|
528
|
+
#
|
529
|
+
# @return [Array{min => Float, max => Float}]
|
530
|
+
def compute_min_max
|
531
|
+
@min_max = if minimum_value[:value] && maximum_value[:value]
|
532
|
+
min_max = FFI::MemoryPointer.new(:double, 2)
|
533
|
+
min_max.put_array_of_double 0, [minimum_value[:value], maximum_value[:value]]
|
534
|
+
GDALComputeRasterMinMax(@gdal_raster_band, 1, min_max)
|
535
|
+
|
536
|
+
[min_max[0].read_double, min_max[1].read_double]
|
537
|
+
else
|
538
|
+
[0.0, 0.0]
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
# @return [Hash{value => Float, it_tight => Boolean}]
|
543
|
+
def minimum_value
|
544
|
+
is_tight = FFI::MemoryPointer.new(:bool)
|
545
|
+
value = GDALGetRasterMinimum(@gdal_raster_band, is_tight)
|
546
|
+
|
547
|
+
{ value: value, is_tight: is_tight.read_bytes(1).to_bool }
|
548
|
+
end
|
549
|
+
|
550
|
+
# @return [Hash{value => Float, it_tight => Boolean}]
|
551
|
+
def maximum_value
|
552
|
+
is_tight = FFI::MemoryPointer.new(:double)
|
553
|
+
value = GDALGetRasterMaximum(@gdal_raster_band, is_tight)
|
554
|
+
|
555
|
+
{ value: value, is_tight: is_tight.read_bytes(1).to_bool }
|
556
|
+
end
|
557
|
+
|
558
|
+
# Iterates through all lines and builds an NArray of pixels.
|
559
|
+
#
|
560
|
+
# @return [NArray]
|
561
|
+
def to_a
|
562
|
+
lines = []
|
563
|
+
|
564
|
+
readlines do |line|
|
565
|
+
lines << line
|
566
|
+
end
|
567
|
+
|
568
|
+
NArray.to_na(lines)
|
569
|
+
end
|
570
|
+
end
|
571
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative '../ffi/gdal'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module GDAL
|
5
|
+
module VersionInfo
|
6
|
+
# Version in the form "1170".
|
7
|
+
#
|
8
|
+
# @return [String]
|
9
|
+
def version_num
|
10
|
+
FFI::GDAL.GDALVersionInfo('VERSION_NUM')
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Date]
|
14
|
+
def release_date
|
15
|
+
Date.parse(FFI::GDAL.GDALVersionInfo('RELEASE_DATE'))
|
16
|
+
end
|
17
|
+
|
18
|
+
# Version in the form "1.1.7".
|
19
|
+
#
|
20
|
+
# @return [String]
|
21
|
+
def release_name
|
22
|
+
FFI::GDAL.GDALVersionInfo('RELEASE_NAME')
|
23
|
+
end
|
24
|
+
|
25
|
+
# The long licensing info.
|
26
|
+
#
|
27
|
+
# @return [String]
|
28
|
+
def license
|
29
|
+
FFI::GDAL.GDALVersionInfo('LICENSE')
|
30
|
+
end
|
31
|
+
|
32
|
+
# Options used when building GDAL.
|
33
|
+
#
|
34
|
+
# @return [Hash{String => String}]
|
35
|
+
def build_info
|
36
|
+
key_value_pairs = FFI::GDAL.GDALVersionInfo('BUILD_INFO')
|
37
|
+
key_value_pairs.split.each_with_object({}) do |kv, obj|
|
38
|
+
key, value = kv.split('=', 2)
|
39
|
+
obj[key] = value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [String]
|
44
|
+
def long_version
|
45
|
+
FFI::GDAL.GDALVersionInfo('--version')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|