ffi-gdal 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|