ffi-gdal 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +25 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +60 -0
  7. data/Rakefile +57 -0
  8. data/ffi-gdal.gemspec +28 -0
  9. data/lib/ext/cpl_error_symbols.rb +37 -0
  10. data/lib/ext/to_bool.rb +13 -0
  11. data/lib/ffi/gdal/cpl_conv.rb +151 -0
  12. data/lib/ffi/gdal/cpl_error.rb +91 -0
  13. data/lib/ffi/gdal/cpl_string.rb +113 -0
  14. data/lib/ffi/gdal/cpl_vsi.rb +119 -0
  15. data/lib/ffi/gdal/gdal_color_entry.rb +13 -0
  16. data/lib/ffi/gdal/gdal_gcp.rb +18 -0
  17. data/lib/ffi/gdal/ogr_api.rb +28 -0
  18. data/lib/ffi/gdal/ogr_core.rb +199 -0
  19. data/lib/ffi/gdal/ogr_srs_api.rb +48 -0
  20. data/lib/ffi/gdal/version.rb +5 -0
  21. data/lib/ffi/gdal.rb +607 -0
  22. data/lib/ffi-gdal/color_table.rb +59 -0
  23. data/lib/ffi-gdal/dataset.rb +347 -0
  24. data/lib/ffi-gdal/driver.rb +151 -0
  25. data/lib/ffi-gdal/exceptions.rb +17 -0
  26. data/lib/ffi-gdal/geo_transform.rb +137 -0
  27. data/lib/ffi-gdal/major_object.rb +71 -0
  28. data/lib/ffi-gdal/raster_attribute_table.rb +78 -0
  29. data/lib/ffi-gdal/raster_band.rb +571 -0
  30. data/lib/ffi-gdal/version_info.rb +48 -0
  31. data/lib/ffi-gdal.rb +12 -0
  32. data/linkies.rb +35 -0
  33. data/meow.rb +144 -0
  34. data/readie.rb +90 -0
  35. data/rubby.rb +224 -0
  36. data/spec/ext/cpl_error_symbols_spec.rb +79 -0
  37. data/spec/ffi-gdal/integration/color_table_info_spec.rb +60 -0
  38. data/spec/ffi-gdal/integration/dataset_info_spec.rb +95 -0
  39. data/spec/ffi-gdal/integration/driver_info_spec.rb +60 -0
  40. data/spec/ffi-gdal/integration/geo_transform_info_spec.rb +66 -0
  41. data/spec/ffi-gdal/integration/raster_attribute_table_info_spec.rb +23 -0
  42. data/spec/ffi-gdal/integration/raster_band_info_spec.rb +333 -0
  43. data/spec/ffi-gdal/unit/version_info_spec.rb +48 -0
  44. data/spec/ffi-gdal_spec.rb +6 -0
  45. data/spec/spec_helper.rb +13 -0
  46. data/spec/support/integration_help.rb +1 -0
  47. data/spec/support/shared_examples/major_object_examples.rb +68 -0
  48. data/things.rb +84 -0
  49. 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
data/lib/ffi-gdal.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'log_switch'
2
+ require_relative 'ffi-gdal/version_info'
3
+
4
+ module GDAL
5
+ extend VersionInfo
6
+ extend LogSwitch
7
+ end
8
+
9
+ GDAL.log_class_name = true
10
+
11
+ require_relative 'ffi/gdal'
12
+ require_relative 'ffi-gdal/dataset'