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.
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'