ffi-gdal 0.0.4 → 1.0.0.beta1

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 (138) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -1
  3. data/Rakefile +12 -1
  4. data/TODO.md +11 -0
  5. data/ffi-gdal.gemspec +2 -2
  6. data/lib/ext/error_symbols.rb +59 -0
  7. data/lib/ext/float_ext.rb +15 -0
  8. data/lib/ext/narray_ext.rb +16 -0
  9. data/lib/ext/to_bool.rb +2 -0
  10. data/lib/ffi-gdal.rb +139 -4
  11. data/lib/ffi/{gdal/cpl_conv.rb → cpl/conv_h.rb} +2 -3
  12. data/lib/ffi/{gdal/cpl_error.rb → cpl/error_h.rb} +1 -25
  13. data/lib/ffi/cpl/minixml_h.rb +14 -0
  14. data/lib/ffi/{gdal/cpl_string.rb → cpl/string_h.rb} +0 -25
  15. data/lib/ffi/{gdal/cpl_vsi.rb → cpl/vsi_h.rb} +0 -0
  16. data/lib/ffi/cpl/xml_node.rb +13 -0
  17. data/lib/ffi/gdal.rb +57 -593
  18. data/lib/ffi/gdal/alg_h.rb +127 -0
  19. data/lib/ffi/gdal/gdal_grid_data_metrics_options.rb +14 -0
  20. data/lib/ffi/gdal/gdal_grid_inverse_distance_to_a_power_options.rb +19 -0
  21. data/lib/ffi/gdal/gdal_grid_moving_average_options.rb +14 -0
  22. data/lib/ffi/gdal/gdal_grid_nearest_neighbor_options.rb +13 -0
  23. data/lib/ffi/gdal/gdal_h.rb +683 -0
  24. data/lib/ffi/gdal/gdal_rpc_info.rb +27 -0
  25. data/lib/ffi/gdal/gdal_transformer_info.rb +14 -0
  26. data/lib/ffi/gdal/gdal_warp_options.rb +43 -0
  27. data/lib/ffi/gdal/grid_h.rb +51 -0
  28. data/lib/ffi/gdal/version.rb +1 -1
  29. data/lib/ffi/gdal/warper_h.rb +48 -0
  30. data/lib/ffi/ogr.rb +12 -0
  31. data/lib/ffi/ogr/api_h.rb +553 -0
  32. data/lib/ffi/ogr/core_h.rb +148 -0
  33. data/lib/ffi/ogr/featurestyle_h.rb +22 -0
  34. data/lib/ffi/ogr/geocoding_h.rb +21 -0
  35. data/lib/ffi/ogr/ogr_contour_writer_info.rb +14 -0
  36. data/lib/ffi/ogr/ogr_envelope.rb +12 -0
  37. data/lib/ffi/ogr/ogr_envelope_3d.rb +14 -0
  38. data/lib/ffi/ogr/ogr_field.rb +50 -0
  39. data/lib/ffi/ogr/ogr_style_param.rb +12 -0
  40. data/lib/ffi/ogr/ogr_style_value.rb +13 -0
  41. data/lib/ffi/ogr/srs_api_h.rb +325 -0
  42. data/lib/gdal/color_entry.rb +47 -0
  43. data/lib/gdal/color_entry_extensions.rb +30 -0
  44. data/lib/gdal/color_interpretation.rb +15 -0
  45. data/lib/gdal/color_table.rb +146 -0
  46. data/lib/gdal/color_table_extensions.rb +47 -0
  47. data/lib/gdal/color_table_types/cmyk.rb +25 -0
  48. data/lib/gdal/color_table_types/gray.rb +9 -0
  49. data/lib/gdal/color_table_types/hls.rb +21 -0
  50. data/lib/gdal/color_table_types/rgb.rb +25 -0
  51. data/lib/gdal/data_type.rb +38 -0
  52. data/lib/gdal/dataset.rb +437 -0
  53. data/lib/gdal/dataset_extensions.rb +496 -0
  54. data/lib/gdal/driver.rb +244 -0
  55. data/lib/gdal/driver_extensions.rb +56 -0
  56. data/lib/gdal/environment_methods.rb +43 -0
  57. data/lib/{ffi-gdal → gdal}/exceptions.rb +4 -1
  58. data/lib/gdal/geo_transform.rb +188 -0
  59. data/lib/gdal/geo_transform_extensions.rb +90 -0
  60. data/lib/gdal/logger.rb +7 -0
  61. data/lib/{ffi-gdal → gdal}/major_object.rb +15 -14
  62. data/lib/gdal/options.rb +49 -0
  63. data/lib/gdal/raster_attribute_table.rb +185 -0
  64. data/lib/gdal/raster_attribute_table_extensions.rb +40 -0
  65. data/lib/{ffi-gdal → gdal}/raster_band.rb +227 -99
  66. data/lib/gdal/raster_band_extensions.rb +198 -0
  67. data/lib/{ffi-gdal → gdal}/version_info.rb +8 -0
  68. data/lib/gdal/warp_operation.rb +96 -0
  69. data/lib/ogr/coordinate_transformation.rb +108 -0
  70. data/lib/ogr/data_source.rb +172 -0
  71. data/lib/ogr/data_source_extensions.rb +32 -0
  72. data/lib/ogr/driver.rb +119 -0
  73. data/lib/ogr/envelope.rb +80 -0
  74. data/lib/ogr/envelope_extensions.rb +92 -0
  75. data/lib/ogr/exceptions.rb +35 -0
  76. data/lib/ogr/feature.rb +212 -0
  77. data/lib/ogr/feature_definition.rb +120 -0
  78. data/lib/ogr/feature_definition_extensions.rb +36 -0
  79. data/lib/ogr/feature_extensions.rb +31 -0
  80. data/lib/ogr/field.rb +91 -0
  81. data/lib/ogr/field_extensions.rb +23 -0
  82. data/lib/ogr/geocoding_session.rb +84 -0
  83. data/lib/ogr/geometry.rb +617 -0
  84. data/lib/ogr/geometry_extensions.rb +60 -0
  85. data/lib/ogr/geometry_types/collection.rb +45 -0
  86. data/lib/ogr/geometry_types/curve.rb +120 -0
  87. data/lib/ogr/geometry_types/surface.rb +20 -0
  88. data/lib/ogr/layer.rb +226 -0
  89. data/lib/ogr/layer_extensions.rb +55 -0
  90. data/lib/ogr/line_string.rb +7 -0
  91. data/lib/ogr/linear_ring.rb +6 -0
  92. data/lib/ogr/multi_line_string.rb +9 -0
  93. data/lib/ogr/multi_point.rb +7 -0
  94. data/lib/ogr/multi_polygon.rb +14 -0
  95. data/lib/ogr/point.rb +89 -0
  96. data/lib/ogr/polygon.rb +9 -0
  97. data/lib/ogr/spatial_reference.rb +723 -0
  98. data/lib/ogr/spatial_reference_extensions.rb +32 -0
  99. data/lib/ogr/style_table.rb +17 -0
  100. data/lib/ogr/style_table_extensions.rb +16 -0
  101. data/spec/{ffi-gdal/integration → integration}/color_table_info_spec.rb +1 -1
  102. data/spec/{ffi-gdal/integration → integration}/dataset_info_spec.rb +0 -0
  103. data/spec/{ffi-gdal/integration → integration}/driver_info_spec.rb +1 -1
  104. data/spec/{ffi-gdal/integration → integration}/geo_transform_info_spec.rb +0 -0
  105. data/spec/{ffi-gdal/integration → integration}/raster_attribute_table_info_spec.rb +1 -1
  106. data/spec/{ffi-gdal/integration → integration}/raster_band_info_spec.rb +5 -5
  107. data/spec/spec_helper.rb +4 -1
  108. data/spec/support/shapefiles/states_21basic/states.prj +1 -0
  109. data/spec/support/shapefiles/states_21basic/states.sbn +0 -0
  110. data/spec/support/shapefiles/states_21basic/states.sbx +0 -0
  111. data/spec/support/shapefiles/states_21basic/states.shp +0 -0
  112. data/spec/support/worldfiles/SR_50M/SR_50M.VERSION.txt +1 -0
  113. data/spec/support/worldfiles/SR_50M/SR_50M.prj +1 -0
  114. data/spec/support/worldfiles/SR_50M/SR_50M.tfw +6 -0
  115. data/spec/{ext/cpl_error_symbols_spec.rb → unit/ext/error_symbols_spec.rb} +1 -1
  116. data/spec/unit/gdal/color_table_spec.rb +146 -0
  117. data/spec/unit/ogr/layer_spec.rb +97 -0
  118. data/spec/unit/ogr/linear_ring_spec.rb +111 -0
  119. data/spec/unit/ogr/point_spec.rb +321 -0
  120. data/spec/{ffi-gdal/unit → unit}/version_info_spec.rb +1 -1
  121. data/testing_gdal.rb +168 -0
  122. data/testing_gdalwarp.rb +91 -0
  123. data/testing_layer_to_layer.rb +35 -0
  124. data/testing_ndvi.rb +76 -0
  125. data/testing_nir.rb +77 -0
  126. data/testing_ogr.rb +63 -0
  127. metadata +167 -59
  128. data/lib/ext/cpl_error_symbols.rb +0 -37
  129. data/lib/ffi-gdal/color_table.rb +0 -59
  130. data/lib/ffi-gdal/dataset.rb +0 -359
  131. data/lib/ffi-gdal/driver.rb +0 -151
  132. data/lib/ffi-gdal/geo_transform.rb +0 -137
  133. data/lib/ffi-gdal/raster_attribute_table.rb +0 -78
  134. data/lib/ffi/gdal/ogr_api.rb +0 -21
  135. data/lib/ffi/gdal/ogr_core.rb +0 -195
  136. data/lib/ffi/gdal/ogr_srs_api.rb +0 -44
  137. data/meow.rb +0 -144
  138. data/rubby.rb +0 -224
@@ -0,0 +1,47 @@
1
+ require_relative 'color_entry_extensions'
2
+
3
+ module GDAL
4
+ class ColorEntry
5
+ include ColorEntryExtensions
6
+
7
+ def initialize(color_entry=nil)
8
+ @color_entry_struct = color_entry || FFI::GDAL::GDALColorEntry.new
9
+ end
10
+
11
+ def c_pointer
12
+ @color_entry_struct
13
+ end
14
+
15
+ def color1
16
+ @color_entry_struct[:c1]
17
+ end
18
+
19
+ def color1=(new_color)
20
+ @color_entry_struct[:c1] = new_color
21
+ end
22
+
23
+ def color2
24
+ @color_entry_struct[:c2]
25
+ end
26
+
27
+ def color2=(new_color)
28
+ @color_entry_struct[:c2] = new_color
29
+ end
30
+
31
+ def color3
32
+ @color_entry_struct[:c3]
33
+ end
34
+
35
+ def color3=(new_color)
36
+ @color_entry_struct[:c3] = new_color
37
+ end
38
+
39
+ def color4
40
+ @color_entry_struct[:c4]
41
+ end
42
+
43
+ def color4=(new_color)
44
+ @color_entry_struct[:c4] = new_color
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ require 'json'
2
+
3
+ module GDAL
4
+ module ColorEntryExtensions
5
+
6
+ # @param include_fourth [Boolean] Turn off in case you don't want the fourth
7
+ # color in the array.
8
+ # @return [Array]
9
+ def to_a(include_fourth=true)
10
+ if include_fourth
11
+ [color1, color2, color3, color4]
12
+ else
13
+ [color1, color2, color3]
14
+ end
15
+ end
16
+
17
+ def as_json
18
+ {
19
+ color1: color1,
20
+ color2: color2,
21
+ color3: color3,
22
+ color4: color4
23
+ }
24
+ end
25
+
26
+ def to_json
27
+ as_json.to_json
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ module GDAL
2
+ class ColorInterpretation
3
+ # @param gdal_color_interp [FFI::GDAL::GDALColorInterp]
4
+ # @return [String]
5
+ def self.name(gdal_color_interp)
6
+ FFI::GDAL.GDALGetColorInterpretationName(gdal_color_interp)
7
+ end
8
+
9
+ # @param name [String]
10
+ # @return [FFI::GDAL::GDALColorInterp]
11
+ def self.by_name(name)
12
+ FFI::GDAL.GDALGetColorInterpretationByName(name)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,146 @@
1
+ require_relative '../ffi/gdal'
2
+ require_relative 'color_table_extensions'
3
+ require_relative 'color_entry'
4
+
5
+
6
+ module GDAL
7
+ module ColorTableTypes
8
+ autoload :CMYK,
9
+ File.expand_path('color_table_types/cmyk', __dir__)
10
+ autoload :Gray,
11
+ File.expand_path('color_table_types/gray', __dir__)
12
+ autoload :HLS,
13
+ File.expand_path('color_table_types/hls', __dir__)
14
+ autoload :RGB,
15
+ File.expand_path('color_table_types/rgb', __dir__)
16
+ end
17
+
18
+ class ColorTable
19
+ include ColorTableExtensions
20
+
21
+ # @param palette_interpretation [FFI::GDAL::GDALPaletteInterp]
22
+ # @return [GDAL::ColorTable]
23
+ def self.create(palette_interpretation)
24
+ color_table_pointer = FFI::GDAL::GDALCreateColorTable(palette_interpretation)
25
+ return nil if color_table_pointer.null?
26
+
27
+ new(color_table_pointer)
28
+ end
29
+
30
+ # @param color_table
31
+ def initialize(color_table)
32
+ @color_table_pointer = GDAL._pointer(self.class, color_table)
33
+ @color_entries = []
34
+
35
+ case palette_interpretation
36
+ when :GPI_Gray then extend GDAL::ColorTableTypes::Gray
37
+ when :GPI_RGB then extend GDAL::ColorTableTypes::RGB
38
+ when :GPI_CMYK then extend GDAL::ColorTableTypes::CMYK
39
+ when :GPI_HLS then extend GDAL::ColorTableTypes::HLS
40
+ else
41
+ raise "Unknown PaletteInterpretation: #{palette_interpretation}"
42
+ end
43
+ end
44
+
45
+ def c_pointer
46
+ @color_table_pointer
47
+ end
48
+
49
+ def destroy!
50
+ FFI::GDAL.GDALDestroyColorTable(@color_table_pointer)
51
+ end
52
+
53
+ # Clones the ColorTable using the C API.
54
+ #
55
+ # @return [GDAL::ColorTable]
56
+ def clone
57
+ ct_ptr = FFI::GDAL.GDALCloneColorTable(@color_table_pointer)
58
+ return nil if ct_ptr.null?
59
+
60
+ GDAL::ColorTable.new(ct_ptr)
61
+ end
62
+
63
+ # Usually :GPI_RGB.
64
+ #
65
+ # @return [Symbol] One of FFI::GDAL::GDALPaletteInterp.
66
+ def palette_interpretation
67
+ @palette_interpretation ||= FFI::GDAL.GDALGetPaletteInterpretation(@color_table_pointer)
68
+ end
69
+
70
+ # @return [Fixnum]
71
+ def color_entry_count
72
+ FFI::GDAL.GDALGetColorEntryCount(@color_table_pointer)
73
+ end
74
+
75
+ # @param index [Fixnum]
76
+ # @return [GDAL::ColorEntry]
77
+ def color_entry(index)
78
+ @color_entries.fetch(index) do
79
+ color_entry = FFI::GDAL.GDALGetColorEntry(@color_table_pointer, index)
80
+ return nil if color_entry.null?
81
+
82
+ GDAL::ColorEntry.new(color_entry)
83
+ end
84
+ end
85
+
86
+ # @param index [Fixnum]
87
+ # @return [GDAL::ColorEntry]
88
+ def color_entry_as_rgb(index)
89
+ entry = color_entry(index)
90
+ return unless entry
91
+
92
+ FFI::GDAL.GDALGetColorEntryAsRGB(@color_table_pointer, index, entry.c_pointer)
93
+ return nil if entry.c_pointer.null?
94
+
95
+ entry
96
+ end
97
+
98
+ # Add a new ColorEntry to the ColorTable. Valid values depend on the image
99
+ # type you're working with (i.e. for Tiff, values can be between 0 and
100
+ # 65535). Values must also be relevant to the PaletteInterp type you're
101
+ # working with.
102
+ #
103
+ # @param index [Fixnum] The index of the color table's color entry to set.
104
+ # Must be between 0 and color_entry_count - 1.
105
+ # @param one [Fixnum] The `c1` value of the GDAL::ColorEntry struct
106
+ # to set.
107
+ # @param two [Fixnum] The `c2` value of the GDAL::ColorEntry struct
108
+ # to set.
109
+ # @param three [Fixnum] The `c3` value of the GDAL::ColorEntry
110
+ # struct to set.
111
+ # @param four [Fixnum] The `c4` value of the GDAL::ColorEntry
112
+ # struct to set.
113
+ # @return [GDAL::ColorEntry]
114
+ def add_color_entry(index, one=nil, two=nil, three=nil, four=nil)
115
+ entry = GDAL::ColorEntry.new
116
+ entry.color1 = one if one
117
+ entry.color2 = two if two
118
+ entry.color3 = three if three
119
+ entry.color4 = four if four
120
+
121
+ FFI::GDAL.GDALSetColorEntry(@color_table_pointer, index, entry.c_pointer)
122
+ @color_entries.insert(index, entry)
123
+
124
+ entry
125
+ end
126
+
127
+ # Automatically creates a color ramp from one color entry to another. It
128
+ # can be called several times to create multiple ramps in the same color
129
+ # table.
130
+ #
131
+ # @param start_index [Fixnum] Index to start the ramp on (0..255)
132
+ # @param start_color [GDAL::ColorEntry] Value to start the ramp.
133
+ # @param end_index [Fixnum] Index to end the ramp on (0..255)
134
+ # @param end_color [GDAL::ColorEntry] Value to end the ramp.
135
+ # @return [Fixnum] The total number of entries. nil or -1 on error.
136
+ def create_color_ramp!(start_index, start_color, end_index, end_color)
137
+ start_color_ptr = GDAL._pointer(GDAL::ColorEntry, start_color)
138
+ end_color_ptr = GDAL._pointer(GDAL::ColorEntry, end_color)
139
+
140
+ FFI::GDAL.GDALCreateColorRamp(@color_table_pointer, start_index,
141
+ start_color_ptr,
142
+ end_index,
143
+ end_color_ptr)
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,47 @@
1
+ require 'json'
2
+
3
+ module GDAL
4
+ module ColorTableExtensions
5
+
6
+ def color_entries_for(color_number)
7
+ unless (1..4).to_a.include? color_number
8
+ raise "Invalid ColorEntry number 'color#{color_number}'"
9
+ end
10
+
11
+ 0.upto(color_entry_count - 1).map do |i|
12
+ color_entry(i).send("color#{color_number}".to_sym)
13
+ end
14
+ end
15
+
16
+ # @return [Array<GDAL::ColorEntry>]
17
+ def color_entries
18
+ 0.upto(color_entry_count - 1).map do |i|
19
+ color_entry(i)
20
+ end
21
+ end
22
+
23
+ # Does the same as #color_entries, but calls #color_entry_as_rgb() instead
24
+ # of #color_entry().
25
+ #
26
+ # @return [Array<GDAL::ColorEntry>]
27
+ def color_entries_as_rgb
28
+ 0.upto(color_entry_count - 1).map do |i|
29
+ color_entry_as_rgb(i)
30
+ end
31
+ end
32
+
33
+ # @return [Hash]
34
+ def as_json
35
+ {
36
+ color_entry_count: color_entry_count,
37
+ color_entries: color_entries.map(&:as_json),
38
+ palette_interpretation: palette_interpretation,
39
+ }
40
+ end
41
+
42
+ # @return [String]
43
+ def to_json
44
+ as_json.to_json
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ module GDAL
2
+ module ColorTableTypes
3
+ module CMYK
4
+ def cyans
5
+ color_entries_for(1)
6
+ end
7
+
8
+ def magentas
9
+ color_entries_for(2)
10
+ end
11
+
12
+ def yellows
13
+ color_entries_for(3)
14
+ end
15
+
16
+ def blacks
17
+ color_entries_for(4)
18
+ end
19
+
20
+ def to_a
21
+ NMatrix[cyans, magentas, yellows, blacks].transpose.to_a
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ module GDAL
2
+ module ColorTableTypes
3
+ module Gray
4
+ def grays
5
+ color_entries_for(1)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ module GDAL
2
+ module ColorTableTypes
3
+ module HLS
4
+ def hues
5
+ color_entries_for(1)
6
+ end
7
+
8
+ def lightnesses
9
+ color_entries_for(2)
10
+ end
11
+
12
+ def saturations
13
+ color_entries_for(3)
14
+ end
15
+
16
+ def to_a
17
+ NMatrix[hues, lightnesses, saturations].transpose.to_a
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ module GDAL
2
+ module ColorTableTypes
3
+ module RGB
4
+ def reds
5
+ color_entries_for(1)
6
+ end
7
+
8
+ def greens
9
+ color_entries_for(2)
10
+ end
11
+
12
+ def blues
13
+ color_entries_for(3)
14
+ end
15
+
16
+ def alphas
17
+ color_entries_for(4)
18
+ end
19
+
20
+ def to_a
21
+ NMatrix[reds, greens, blues, alphas].transpose.to_a
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ module GDAL
2
+ class DataType
3
+ # The size in bits.
4
+ #
5
+ # @param gdal_data_type [FFI::GDAL::GDALDataType]
6
+ # @return [Fixnum]
7
+ def self.size(gdal_data_type)
8
+ FFI::GDAL.GDALGetDataTypeSize(gdal_data_type)
9
+ end
10
+
11
+ # @param gdal_data_type [FFI::GDAL::GDALDataType]
12
+ # @return [Fixnum]
13
+ def self.complex?(gdal_data_type)
14
+ FFI::GDAL.GDALDataTypeIsComplex(gdal_data_type)
15
+ end
16
+
17
+ # @param gdal_data_type [FFI::GDAL::GDALDataType]
18
+ # @return [String]
19
+ def self.name(gdal_data_type)
20
+ FFI::GDAL.GDALGetDataTypeName(gdal_data_type)
21
+ end
22
+
23
+ # The data type's symbolic name.
24
+ #
25
+ # @param name [String]
26
+ # @return [FFI::GDAL::GDALDataType]
27
+ def self.by_name(name)
28
+ FFI::GDAL.GDALGetDataTypeByName(name)
29
+ end
30
+
31
+ # @param gdal_data_type1 [FFI::GDAL::GDALDataType]
32
+ # @param gdal_data_type2 [FFI::GDAL::GDALDataType]
33
+ # @return [FFI::GDAL::GDALDataType]
34
+ def self.union(gdal_data_type1, gdal_data_type2)
35
+ FFI::GDAL.GDALDataTypeUnion(gdal_data_type1, gdal_data_type2)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,437 @@
1
+ require 'uri'
2
+ require_relative '../ffi/gdal'
3
+ require_relative 'driver'
4
+ require_relative 'geo_transform'
5
+ require_relative 'raster_band'
6
+ require_relative 'exceptions'
7
+ require_relative 'major_object'
8
+ require_relative 'dataset_extensions'
9
+ require_relative '../ogr/spatial_reference'
10
+
11
+
12
+ module GDAL
13
+
14
+ # A set of associated raster bands and info common to them all. It's also
15
+ # responsible for the georeferencing transform and coordinate system
16
+ # definition of all bands.
17
+ class Dataset
18
+ include MajorObject
19
+ include DatasetExtensions
20
+ include GDAL::Logger
21
+
22
+ ACCESS_FLAGS = {
23
+ 'r' => :GA_ReadOnly,
24
+ 'w' => :GA_Update
25
+ }
26
+
27
+ # @param path [String] Path to the file that contains the dataset. Can be
28
+ # a local file or a URL.
29
+ # @param access_flag [String] 'r' or 'w'.
30
+ def self.open(path, access_flag)
31
+ uri = URI.parse(path)
32
+ file_path = uri.scheme.nil? ? ::File.expand_path(path) : path
33
+
34
+ pointer = FFI::GDAL.GDALOpen(file_path, ACCESS_FLAGS[access_flag])
35
+ raise OpenFailure.new(file_path) if pointer.null?
36
+
37
+ new(pointer)
38
+ end
39
+
40
+ #---------------------------------------------------------------------------
41
+ # Instance methods
42
+ #---------------------------------------------------------------------------
43
+
44
+ # @param dataset_pointer [FFI::Pointer] Pointer to the dataset in memory.
45
+ def initialize(dataset_pointer)
46
+ @dataset_pointer = dataset_pointer
47
+ @last_known_file_list = []
48
+ @open = true
49
+ close_me = -> { self.close }
50
+ ObjectSpace.define_finalizer self, close_me
51
+ end
52
+
53
+ # @return [FFI::Pointer] Pointer to the GDALDatasetH that's represented by
54
+ # this Ruby object.
55
+ def c_pointer
56
+ @dataset_pointer
57
+ end
58
+
59
+ # Close the dataset.
60
+ def close
61
+ if @dataset_pointer.null?
62
+ false
63
+ else
64
+ FFI::GDAL.GDALClose(@dataset_pointer)
65
+ end
66
+ end
67
+
68
+ # @return [Symbol]
69
+ def access_flag
70
+ return nil if null?
71
+
72
+ flag = FFI::GDAL.GDALGetAccess(@dataset_pointer)
73
+
74
+ FFI::GDAL.GDALAccess[flag]
75
+ end
76
+
77
+ # @return [GDAL::Driver] The driver to be used for working with this
78
+ # dataset.
79
+ def driver
80
+ return @driver if @driver
81
+ driver_ptr = FFI::GDAL.GDALGetDatasetDriver(@dataset_pointer)
82
+
83
+ @driver = Driver.new(driver_ptr)
84
+ end
85
+
86
+ # Fetches all files that form the dataset.
87
+ # @return [Array<String>]
88
+ def file_list
89
+ list_pointer = FFI::GDAL.GDALGetFileList(@dataset_pointer)
90
+ return [] if list_pointer.null?
91
+ file_list = list_pointer.get_array_of_string(0)
92
+ FFI::GDAL.CSLDestroy(list_pointer)
93
+
94
+ file_list
95
+ end
96
+
97
+ # Flushes all write-cached data to disk.
98
+ def flush_cache
99
+ FFI::GDAL.GDALFlushCache(@dataset_pointer)
100
+ end
101
+
102
+ # @return [Fixnum]
103
+ def raster_x_size
104
+ return nil if null?
105
+
106
+ FFI::GDAL.GDALGetRasterXSize(@dataset_pointer)
107
+ end
108
+
109
+ # @return [Fixnum]
110
+ def raster_y_size
111
+ return nil if null?
112
+
113
+ FFI::GDAL.GDALGetRasterYSize(@dataset_pointer)
114
+ end
115
+
116
+ # @return [Fixnum]
117
+ def raster_count
118
+ return 0 if null?
119
+
120
+ FFI::GDAL.GDALGetRasterCount(@dataset_pointer)
121
+ end
122
+
123
+ # @param raster_index [Fixnum]
124
+ # @return [GDAL::RasterBand]
125
+ def raster_band(raster_index)
126
+ @raster_bands ||= Array.new(raster_count)
127
+ zero_index = raster_index - 1
128
+
129
+ if @raster_bands[zero_index] && !@raster_bands[zero_index].null?
130
+ return @raster_bands[zero_index]
131
+ end
132
+
133
+ raster_band_ptr = FFI::GDAL.GDALGetRasterBand(@dataset_pointer, raster_index)
134
+ @raster_bands[zero_index] = GDAL::RasterBand.new(raster_band_ptr)
135
+ @raster_bands.compact!
136
+
137
+ @raster_bands[zero_index]
138
+ end
139
+
140
+ # @param type [FFI::GDAL::GDALDataType]
141
+ # @param options [Hash]
142
+ # @return [GDAL::RasterBand, nil]
143
+ def add_band(type, **options)
144
+ options_ptr = GDAL::Options.pointer(options)
145
+ cpl_err = FFI::GDAL.GDALAddBand(@dataset_pointer, type, options_ptr)
146
+ cpl_err.to_bool
147
+
148
+ raster_band(raster_count)
149
+ end
150
+
151
+ # Adds a mask band to the dataset
152
+ #
153
+ # @return [Boolean]
154
+ def create_mask_band
155
+ cpl_err = FFI::GDAL.GDALCreateDatasetMaskBand(@dataset_pointer, 0)
156
+
157
+ cpl_err.to_bool
158
+ end
159
+
160
+ # @return [String]
161
+ def projection
162
+ FFI::GDAL.GDALGetProjectionRef(@dataset_pointer)
163
+ end
164
+
165
+ # @param new_projection [String]
166
+ # @return [Boolean]
167
+ def projection=(new_projection)
168
+ cpl_err = FFI::GDAL.GDALSetProjection(@dataset_pointer, new_projection)
169
+
170
+ cpl_err.to_bool
171
+ end
172
+
173
+ # @return [GDAL::GeoTransform]
174
+ def geo_transform
175
+ return @geo_transform if @geo_transform
176
+
177
+ geo_transform_pointer = FFI::MemoryPointer.new(:double, 6)
178
+ cpl_err = FFI::GDAL.GDALGetGeoTransform(@dataset_pointer, geo_transform_pointer)
179
+ cpl_err.to_ruby
180
+
181
+ @geo_transform = GeoTransform.new(geo_transform_pointer)
182
+ end
183
+
184
+ # @param new_transform [GDAL::GeoTransform]
185
+ # @return [GDAL::GeoTransform]
186
+ def geo_transform=(new_transform)
187
+ new_pointer = FFI::Pointer.new(new_transform.c_pointer)
188
+ cpl_err = FFI::GDAL.GDALSetGeoTransform(@dataset_pointer, new_pointer)
189
+ cpl_err.to_bool
190
+
191
+ @geo_transform = GeoTransform.new(new_pointer)
192
+ end
193
+
194
+ # @return [Fixnum]
195
+ def gcp_count
196
+ return 0 if null?
197
+
198
+ FFI::GDAL.GDALGetGCPCount(@dataset_pointer)
199
+ end
200
+
201
+ # @return [String]
202
+ def gcp_projection
203
+ return '' if null?
204
+
205
+ FFI::GDAL.GDALGetGCPProjection(@dataset_pointer)
206
+ end
207
+
208
+ # @return [FFI::GDAL::GDALGCP]
209
+ def gcps
210
+ return FFI::GDAL.GDALGCP.new if null?
211
+
212
+ gcp_array_pointer = FFI::GDAL.GDALGetGCPs(@dataset_pointer)
213
+
214
+ if gcp_array_pointer.null?
215
+ FFI::GDAL.GDALGCP.new
216
+ else
217
+ FFI::GDAL.GDALGCP.new(gcp_array_pointer)
218
+ end
219
+ end
220
+
221
+ # @return [Fixnum]
222
+ def layer_count
223
+ if GDAL._supported?(:GDALDatasetGetLayerCount)
224
+ FFI::GDAL.GDALDatasetGetLayerCount(@dataset_pointer)
225
+ end
226
+ end
227
+
228
+ # @param resampling [String, Symbol] One of:
229
+ # * :nearest
230
+ # * :gauss
231
+ # * :cubic
232
+ # * :average
233
+ # * :mode
234
+ # * :average_magphase
235
+ # * :none
236
+ # @param overview_levels [Array<Fixnum>] The list of overview decimation
237
+ # factors to build.
238
+ # @param band_numbers [Array<Fixnum>] The numbers of the bands to build
239
+ # overviews from.
240
+ def build_overviews(resampling, overview_levels, band_numbers=nil, &progress)
241
+ resampling_string = if resampling.is_a? String
242
+ resampling.upcase
243
+ elsif resampling.is_a? Symbol
244
+ resampling.to_s.upcase
245
+ end
246
+
247
+ overview_levels_ptr = FFI::MemoryPointer.new(:int, overview_levels.size)
248
+ overview_levels_ptr.write_array_of_int(overview_levels)
249
+
250
+ if band_numbers
251
+ band_count = band_numbers.size
252
+ band_numbers_ptr = FFI::MemoryPointer.new(:int, band_count)
253
+ band_numbers_ptr.write_array_of_int(band_numbers)
254
+ else
255
+ band_numbers_ptr = nil
256
+ band_count = nil
257
+ end
258
+
259
+ cpl_err = FFI::GDAL.GDALBuildOverviews(@dataset_pointer,
260
+ resampling_string,
261
+ overview_levels.size,
262
+ overview_levels_ptr,
263
+ band_count,
264
+ band_numbers_ptr,
265
+ progress,
266
+ nil
267
+ )
268
+
269
+ cpl_err.to_bool
270
+ end
271
+
272
+ # Rasterizes the geometric objects +geometries+ into this raster dataset.
273
+ # +transformer+ can be nil as long as the +geometries+ are within the
274
+ # georeferenced coordinates of this raster's dataset.
275
+ #
276
+ # @param band_numbers [Array<Fixnum>, Fixnum]
277
+ # @param geometries [Array<OGR::Geometry>, OGR::Geometry]
278
+ # @param burn_values [Array<Float>, Float]
279
+ # @param transformer [Proc]
280
+ # @param options [Hash]
281
+ # @option options all_touched [Boolean] If +true+, sets all pixels touched
282
+ # by the line or polygons, not just those whose center is within the
283
+ # polygon or that are selected by Brezenham's line algorithm. Defaults to
284
+ # +false+.
285
+ # @option options burn_value_from ["Z"] Use the Z values of the geometries.
286
+ # @option @options merge_alg [String] "REPLACE" or "ADD". REPLACE results
287
+ # in overwriting of value, while ADD adds the new value to the existing
288
+ # raster, suitable for heatmaps for instance.
289
+ def rasterize_geometries!(band_numbers, geometries, burn_values,
290
+ transformer: nil, transform_arg: nil, **options, &progress_block)
291
+
292
+ if geo_transform.nil? && gcp_count.zero?
293
+ raise "Can't rasterize geometries--no geo_transform or GCPs have been defined on the dataset."
294
+ end
295
+
296
+ gdal_options = GDAL::Options.pointer(options)
297
+ band_numbers = band_numbers.is_a?(Array) ? band_numbers : [band_numbers]
298
+ geometries = geometries.is_a?(Array) ? geometries : [geometries]
299
+ burn_values = burn_values.is_a?(Array) ? burn_values : [burn_values]
300
+
301
+ band_numbers_ptr = FFI::MemoryPointer.new(:pointer, band_numbers.size)
302
+ band_numbers_ptr.write_array_of_int(band_numbers)
303
+
304
+ geometries_ptr = FFI::MemoryPointer.new(:pointer, geometries.size)
305
+ geometries_ptr.write_array_of_pointer(geometries.map(&:c_pointer))
306
+
307
+ burn_values_ptr = FFI::MemoryPointer.new(:pointer, burn_values.size)
308
+ burn_values_ptr.write_array_of_double(burn_values)
309
+
310
+ # not allowing for now
311
+ progress_callback_data = nil
312
+
313
+ cpl_err = FFI::GDAL.GDALRasterizeGeometries(@dataset_pointer,
314
+ band_numbers.size,
315
+ band_numbers_ptr,
316
+ geometries.size,
317
+ geometries_ptr,
318
+ transformer,
319
+ transform_arg,
320
+ burn_values_ptr,
321
+ gdal_options,
322
+ progress_block,
323
+ progress_callback_data)
324
+
325
+ cpl_err.to_bool
326
+ end
327
+
328
+ # @param band_numbers [Array<Fixnum>, Fixnum]
329
+ # @param layers [Array<OGR::Layer>, OGR::Layer]
330
+ # @param burn_values [Array<Float>, Float]
331
+ # @param transformer [Proc]
332
+ # @param options [Hash]
333
+ # @option options attribute [String] An attribute field on features to be
334
+ # used for a burn-in value, which will be burned into all output bands.
335
+ # @option options chunkysize [Fixnum] The height in lines of the chunk to
336
+ # operate on.
337
+ # @option options all_touched [Boolean] If +true+, sets all pixels touched
338
+ # by the line or polygons, not just those whose center is within the
339
+ # polygon or that are selected by Brezenham's line algorithm. Defaults to
340
+ # +false+.
341
+ # @option options burn_value_from ["Z"] Use the Z values of the geometries.
342
+ # @option @options merge_alg [String] "REPLACE" or "ADD". REPLACE results
343
+ # in overwriting of value, while ADD adds the new value to the existing
344
+ # raster, suitable for heatmaps for instance.
345
+ def rasterize_layers!(band_numbers, layers, burn_values,
346
+ transformer: nil, transform_arg: nil, **options, &progress_block)
347
+ gdal_options = GDAL::Options.pointer(options)
348
+ band_numbers = band_numbers.is_a?(Array) ? band_numbers : [band_numbers]
349
+ log "band numbers: #{band_numbers}"
350
+
351
+ layers = layers.is_a?(Array) ? layers : [layers]
352
+ log "layers: #{layers}"
353
+
354
+ burn_values = burn_values.is_a?(Array) ? burn_values : [burn_values]
355
+ log "burn values: #{burn_values}"
356
+
357
+ band_numbers_ptr = FFI::MemoryPointer.new(:pointer, band_numbers.size)
358
+ band_numbers_ptr.write_array_of_int(band_numbers)
359
+ log "band numbers ptr null? #{band_numbers_ptr.null?}"
360
+
361
+ layers_ptr = FFI::MemoryPointer.new(:pointer, layers.size)
362
+ layers_ptr.write_array_of_pointer(layers.map(&:c_pointer))
363
+ log "layers ptr null? #{layers_ptr.null?}"
364
+
365
+ burn_values_ptr = FFI::MemoryPointer.new(:pointer, burn_values.size)
366
+ burn_values_ptr.write_array_of_double(burn_values)
367
+ log "burn value ptr null? #{burn_values_ptr.null?}"
368
+
369
+ cpl_err = FFI::GDAL.GDALRasterizeLayers(@dataset_pointer, # hDS
370
+ band_numbers.size, # nBandCount
371
+ band_numbers_ptr, # panBandList
372
+ layers.size, # nLayerCount
373
+ layers_ptr, # pahLayers
374
+ transformer, # pfnTransformer
375
+ transform_arg, # pTransformerArg
376
+ burn_values_ptr, # padfLayerBurnValues
377
+ gdal_options, # papszOptions
378
+ progress_block, # pfnProgress
379
+ nil) # pProgressArg
380
+
381
+ cpl_err.to_bool
382
+ end
383
+
384
+ # @todo Implement
385
+ def simple_image_warp(destination_file, driver, band_numbers,
386
+ transformer, transformer_arg, **options)
387
+ raise NotImplementedError, '#simple_image_warp not yet implemented.'
388
+
389
+ options_ptr = GDAL::Options.pointer(options)
390
+ driver = GDAL::Driver.by_name(driver)
391
+ destination_dataset_ptr = driver.open(destination_file, 'w')
392
+
393
+ band_numbers = band_numbers.is_a?(Array) ? band_numbers : [band_numbers]
394
+ log "band numbers: #{band_numbers}"
395
+
396
+ bands_ptr = FFI::MemoryPointer.new(:pointer, band_numbers.size)
397
+ bands_ptr.write_array_of_int(band_numbers)
398
+ log "band numbers ptr null? #{bands_ptr.null?}"
399
+
400
+ log "transformer: #{transformer}"
401
+
402
+ success = FFI::GDAL.GDALSimpleImageWarp(@dataset_pointer,
403
+ destination_dataset_ptr,
404
+ band_numbers.size,
405
+ bands_ptr,
406
+ transformer,
407
+ nil,
408
+ nil,
409
+ nil,
410
+ options_ptr)
411
+
412
+ raise 'Image warp failed!' unless success
413
+
414
+ GDAL::Dataset.new(destination_dataset_ptr)
415
+ end
416
+
417
+ # @return [FFI::Pointer] Pointer to be used for transformations.
418
+ def create_general_image_projection_transformer(destination_dataset,
419
+ source_projection: nil, destination_projection: nil, use_gcps: false)
420
+ log "destination dataset: #{destination_dataset}"
421
+
422
+ error_threshold = 0.0
423
+ order = 0
424
+
425
+ transformer_ptr = FFI::GDAL.GDALCreateGenImgProjTransformer(@dataset_pointer,
426
+ source_projection,
427
+ destination_dataset.c_pointer,
428
+ destination_projection,
429
+ use_gcps,
430
+ error_threshold,
431
+ order)
432
+
433
+ log "transformer pointer: #{transformer_ptr}"
434
+ transformer_ptr
435
+ end
436
+ end
437
+ end