ffi-gdal 0.0.4 → 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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,244 @@
1
+ require_relative '../ffi/gdal'
2
+ require_relative 'major_object'
3
+ require_relative 'driver_extensions'
4
+ require 'multi_xml'
5
+ require 'log_switch'
6
+
7
+
8
+ module GDAL
9
+ class Driver
10
+ include MajorObject
11
+ include GDAL::Logger
12
+ include DriverExtensions
13
+
14
+ GDAL_DOCS_URL = 'http://gdal.org'
15
+
16
+ # @return [Fixnum]
17
+ def self.count
18
+ FFI::GDAL.GDALGetDriverCount
19
+ end
20
+
21
+ # @param name [String] Short name of the registered GDALDriver.
22
+ # @return [GDAL::Driver]
23
+ def self.by_name(name)
24
+ driver_ptr = FFI::GDAL.GDALGetDriverByName(name)
25
+ return nil if driver_ptr.null?
26
+
27
+ new(driver_ptr)
28
+ end
29
+
30
+ # @return [Array<String>]
31
+ def self.short_names
32
+ return @short_names if @short_names
33
+
34
+ names = 0.upto(count - 1).map do |i|
35
+ driver = at_index(i)
36
+ driver.short_name
37
+ end
38
+
39
+ @short_names = names.compact.sort
40
+ end
41
+
42
+ # @return [Array<String>]
43
+ def self.long_names
44
+ return @long_names if @long_names
45
+
46
+ names = 0.upto(count - 1).map do |i|
47
+ at_index(i).long_name
48
+ end
49
+
50
+ @long_names = names.compact.sort
51
+ end
52
+
53
+ # @return [Hash{String => String}] Keys are driver short names, values are
54
+ # driver long names.
55
+ def self.names
56
+ return @names if @names
57
+
58
+ names = 0.upto(count - 1).each_with_object({}) do |i, obj|
59
+ driver = at_index(i)
60
+ obj[driver.short_name] = driver.long_name
61
+ end
62
+
63
+ @names = Hash[names.sort]
64
+ end
65
+
66
+ # @param index [Fixnum] Index of the registered driver. Must be less than
67
+ # GDAL::Driver.count.
68
+ # @return [GDAL::Driver]
69
+ def self.at_index(index)
70
+ if index > count
71
+ raise "index must be between 0 and #{count - 1}."
72
+ end
73
+
74
+ driver_ptr = FFI::GDAL.GDALGetDriver(index)
75
+
76
+ new(driver_ptr)
77
+ end
78
+
79
+ # @param file_path [String] File to get the driver for.
80
+ # @return [GDAL::Driver]
81
+ def self.identify_driver(file_path)
82
+ driver_ptr = FFI::GDAL.GDALIdentifyDriver(::File.expand_path(file_path), nil)
83
+
84
+ new(driver_ptr)
85
+ end
86
+
87
+ # @param driver [GDAL::Driver, FFI::Pointer]
88
+ def initialize(driver)
89
+ @driver_pointer = GDAL._pointer(GDAL::Driver, driver)
90
+ end
91
+
92
+ def c_pointer
93
+ @driver_pointer
94
+ end
95
+
96
+ # @return [String]
97
+ def short_name
98
+ FFI::GDAL.GDALGetDriverShortName(@driver_pointer)
99
+ end
100
+
101
+ # @return [String]
102
+ def long_name
103
+ FFI::GDAL.GDALGetDriverLongName(@driver_pointer)
104
+ end
105
+
106
+ # @return [String]
107
+ def help_topic
108
+ "#{GDAL_DOCS_URL}/#{FFI::GDAL.GDALGetDriverHelpTopic(@driver_pointer)}"
109
+ end
110
+
111
+ # Lists and describes the options that can be used when calling
112
+ # GDAL::Dataset.create or GDAL::Dataset.create_copy.
113
+ #
114
+ # @return [Array]
115
+ def creation_option_list
116
+ return [] unless @driver_pointer
117
+
118
+ creation_option_list_xml = FFI::GDAL.GDALGetDriverCreationOptionList(@driver_pointer)
119
+ root = MultiXml.parse(creation_option_list_xml)
120
+ return [] if root.nil? || root.empty?
121
+
122
+ list = root['CreationOptionList']
123
+ return [] if list.nil? || list.empty?
124
+
125
+ list['Option']
126
+ end
127
+
128
+ # @param options [Hash]
129
+ # @return [Boolean]
130
+ def validate_creation_options(options)
131
+ options_pointer = if options.is_a? GDAL::Options
132
+ options.c_pointer
133
+ else
134
+ GDAL::Options.pointer(options)
135
+ end
136
+
137
+ FFI::GDAL.GDALValidateCreationOptions(@driver_pointer, options_pointer).to_bool
138
+ end
139
+
140
+ # Copy all of the associated files of a dataset from one file to another.
141
+ #
142
+ # @param new_name [String]
143
+ # @param old_name [String]
144
+ # @return true on success, false on warning.
145
+ # @raise [GDAL::CPLErrFailure] If failures.
146
+ def copy_dataset_files(new_name, old_name)
147
+ cpl_err = FFI::GDAL.GDALCopyDatasetFiles(@driver_pointer, new_name, old_name)
148
+
149
+ cpl_err.to_bool
150
+ end
151
+
152
+ # Create a new Dataset with this driver. Legal arguments depend on the
153
+ # driver and can't be retrieved programmatically. NOTE: In order to write
154
+ # out all data to the destination, you must call #close on the dataset!
155
+ #
156
+ # @param filename [String]
157
+ # @param x_size [Fixnum] Width of created raster in pixels.
158
+ # @param y_size [Fixnum] Height of created raster in pixels.
159
+ # @param bands [Fixnum]
160
+ # @param data_type [FFI::GDAL::GDALDataType]
161
+ # @return [GDAL::Dataset] Returns the *closed* dataset. You'll need to
162
+ # reopen it if you with to continue working with it.
163
+ # @todo Implement options.
164
+ def create_dataset(filename, x_size, y_size, bands: 1, data_type: :GDT_Byte, **options)
165
+ options_pointer = GDAL::Options.pointer(options)
166
+
167
+ dataset_pointer = FFI::GDAL.GDALCreate(@driver_pointer,
168
+ filename,
169
+ x_size,
170
+ y_size,
171
+ bands,
172
+ data_type,
173
+ options_pointer
174
+ )
175
+
176
+ raise CreateFail if dataset_pointer.null?
177
+
178
+ dataset = Dataset.new(dataset_pointer)
179
+ yield(dataset) if block_given?
180
+
181
+ dataset
182
+ end
183
+
184
+ # @param filename [String] The name for the new dataset file.
185
+ # @param source_dataset [GDAL::Dataset, FFI::Pointer] The dataset to copy.
186
+ # @param strict [Boolean] +false+ indicates the copy may adapt as needed for
187
+ # the output format.
188
+ # @param options [Hash]
189
+ # @param progress [Proc] For outputting copy progress. Conforms to the
190
+ # FFI::GDAL::GDALProgressFunc signature.
191
+ def copy_dataset(filename, source_dataset, strict: true, **options, &progress)
192
+ options_ptr = GDAL::Options.pointer(options)
193
+
194
+ source_dataset_ptr = if source_dataset.is_a? GDAL::Dataset
195
+ source_dataset.c_pointer
196
+ elsif source_dataset.is_a? String
197
+ GDAL::Dataset.open(source_dataset, 'r').c_pointer
198
+ else
199
+ source_dataset
200
+ end
201
+
202
+ raise "Source dataset couldn't be read" if source_dataset_ptr.null?
203
+
204
+ destination_dataset_ptr = FFI::GDAL.GDALCreateCopy(@driver_pointer,
205
+ filename,
206
+ source_dataset_ptr,
207
+ strict,
208
+ options_ptr,
209
+ progress,
210
+ nil
211
+ )
212
+
213
+ raise CreateFail if destination_dataset_ptr.null?
214
+
215
+ dataset = Dataset.new(destination_dataset_ptr)
216
+ yield(dataset) if block_given?
217
+ dataset.close
218
+
219
+ dataset
220
+ end
221
+
222
+ # Delete the dataset represented by +file_name+. Depending on the driver,
223
+ # this could mean deleting associated files, database objects, etc.
224
+ #
225
+ # @param file_name [String]
226
+ # @return true on success, false on warning.
227
+ # @raise [GDAL::CPLErrFailure] If failures.
228
+ def delete_dataset(file_name)
229
+ cpl_err = FFI::GDAL.GDALDeleteDataset(@driver_pointer, file_name)
230
+
231
+ cpl_err.to_bool
232
+ end
233
+
234
+ # @param new_name [String]
235
+ # @param old_name [String]
236
+ # @return true on success, false on warning.
237
+ # @raise [GDAL::CPLErrFailure] If failures.
238
+ def rename_dataset(new_name, old_name)
239
+ cpl_err = FFI::GDAL.GDALRenameDataset(@driver_pointer, new_name, old_name)
240
+
241
+ cpl_err.to_bool
242
+ end
243
+ end
244
+ end
@@ -0,0 +1,56 @@
1
+ module GDAL
2
+ module DriverExtensions
3
+
4
+ # The things that this driver can do, as reported by its metadata.
5
+ # Possibilities include:
6
+ # * :open
7
+ # * :create
8
+ # * :copy
9
+ # * :virtual_io
10
+ # * :rasters
11
+ # * :vectors
12
+ #
13
+ # @return [Array<Symbol>]
14
+ def capabilities
15
+ caps = []
16
+ caps << :open if can_open_datasets?
17
+ caps << :create if can_create_datasets?
18
+ caps << :copy if can_copy_datasets?
19
+ caps << :virtual_io if can_do_virtual_io?
20
+ caps << :rasters if can_do_rasters?
21
+ caps << :vectors if can_do_vectors?
22
+
23
+ caps
24
+ end
25
+
26
+ # @return [Boolean]
27
+ def can_open_datasets?
28
+ metadata_item('DCAP_OPEN') == 'YES'
29
+ end
30
+
31
+ # @return [Boolean]
32
+ def can_create_datasets?
33
+ metadata_item('DCAP_CREATE') == 'YES'
34
+ end
35
+
36
+ # @return [Boolean]
37
+ def can_copy_datasets?
38
+ metadata_item('DCAP_CREATECOPY') == 'YES'
39
+ end
40
+
41
+ # @return [Boolean]
42
+ def can_do_virtual_io?
43
+ metadata_item('DCAP_VIRTUALIO') == 'YES'
44
+ end
45
+
46
+ # @return [Boolean]
47
+ def can_do_rasters?
48
+ metadata_item('DCAP_RASTER') == 'YES'
49
+ end
50
+
51
+ # @return [Boolean]
52
+ def can_do_vectors?
53
+ metadata_item('DCAP_VECTOR') == 'YES'
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,43 @@
1
+ module GDAL
2
+ module EnvironmentMethods
3
+ # @return [Fixnum] The maximum cache memory.
4
+ def cache_max
5
+ FFI::GDAL.GDALGetCacheMax
6
+ end
7
+
8
+ # @param bytes [Fixnum]
9
+ def cache_max=(bytes)
10
+ FFI::GDAL.GDALSetCacheMax(bytes)
11
+ end
12
+
13
+ # @return [Fixnum] The maximum cache memory.
14
+ def cache_max64
15
+ FFI::GDAL.GDALGetCacheMax64
16
+ end
17
+
18
+ # @param bytes [Fixnum]
19
+ def cache_max64=(bytes)
20
+ FFI::GDAL.GDALSetCacheMax64(bytes)
21
+ end
22
+
23
+ # @return [Fixnum] The amount of used cache memory.
24
+ def cache_used
25
+ FFI::GDAL.GDALGetCacheUsed
26
+ end
27
+
28
+ # @return [Fixnum] The amount of used cache memory.
29
+ def cache_used64
30
+ FFI::GDAL.GDALGetCacheUsed64
31
+ end
32
+
33
+ # @return [Boolean]
34
+ def flush_cache_block
35
+ FFI::GDAL.GDALFlushCacheBlock
36
+ end
37
+
38
+ # @param file_name [String]
39
+ def dump_open_datasets(file_name)
40
+ FFI::GDAL.GDALDumpOpenDatasets(file_name)
41
+ end
42
+ end
43
+ end
@@ -1,7 +1,7 @@
1
1
  module GDAL
2
2
  class OpenFailure < StandardError
3
3
  def initialize(file, msg=nil)
4
- message = msg || "Unabled to open file '#{file}'. Perhaps an unsupported file format?"
4
+ message = msg || "Unable to open file '#{file}'. Perhaps an unsupported file format?"
5
5
  super(message)
6
6
  end
7
7
  end
@@ -14,4 +14,7 @@ module GDAL
14
14
 
15
15
  class RequiredBandNotFound < StandardError
16
16
  end
17
+
18
+ class InvalidBandNumber < StandardError
19
+ end
17
20
  end
@@ -0,0 +1,188 @@
1
+ require_relative '../ffi/gdal'
2
+ require_relative 'geo_transform_extensions'
3
+
4
+ module GDAL
5
+ class GeoTransform
6
+ include GeoTransformExtensions
7
+
8
+ def self.new_pointer
9
+ FFI::MemoryPointer.new(:double, 6)
10
+ end
11
+
12
+ # @param filename [String]
13
+ # @return [GDAL::GeoTransform]
14
+ def self.from_world_file(filename, extension=nil)
15
+ gt_ptr = new_pointer
16
+
17
+ result = if extension
18
+ FFI::GDAL.GDALReadWorldFile(filename, extension, gt_ptr)
19
+ else
20
+ FFI::GDAL.GDALLoadWorldFile(filename, gt_ptr)
21
+ end
22
+
23
+ return nil unless result
24
+
25
+ new(gt_ptr)
26
+ end
27
+
28
+ # @param geo_transform [FFI::Pointer]
29
+ def initialize(geo_transform=nil)
30
+ @geo_transform_pointer = if geo_transform.is_a? GDAL::GeoTransform
31
+ geo_transform.c_pointer
32
+ elsif geo_transform
33
+ geo_transform
34
+ else
35
+ self.class.new_pointer
36
+ end
37
+
38
+ to_a
39
+ end
40
+
41
+ def c_pointer
42
+ @geo_transform_pointer
43
+ end
44
+
45
+ def null?
46
+ @geo_transform_pointer.null?
47
+ end
48
+
49
+ # X-coordinate of the center of the upper left pixel.
50
+ # In wikipedia's World Map definition, this is "C".
51
+ #
52
+ # @return [Float]
53
+ def x_origin
54
+ return nil if null?
55
+
56
+ @geo_transform_pointer[0].read_double
57
+ end
58
+
59
+ # @param new_x_origin [Float]
60
+ def x_origin=(new_x_origin)
61
+ @geo_transform_pointer[0].write_double(new_x_origin)
62
+ end
63
+
64
+ # AKA X-pixel size.
65
+ # In wikipedia's World Map definition, this is "A".
66
+ #
67
+ # @return [Float]
68
+ def pixel_width
69
+ return nil if null?
70
+
71
+ @geo_transform_pointer[1].read_double
72
+ end
73
+
74
+ # @param new_pixel_width [Float]
75
+ def pixel_width=(new_pixel_width)
76
+ @geo_transform_pointer[1].write_double(new_pixel_width)
77
+ end
78
+
79
+ # Rotation about the x-axis.
80
+ # In wikipedia's World File definition, this is "B".
81
+ #
82
+ # @return [Float]
83
+ def x_rotation
84
+ return nil if null?
85
+
86
+ @geo_transform_pointer[2].read_double
87
+ end
88
+
89
+ # @param new_x_rotation [Float]
90
+ def x_rotation=(new_x_rotation)
91
+ @geo_transform_pointer[2].write_double(new_x_rotation)
92
+ end
93
+
94
+ # Y-coordinate of the center of the upper left pixel.
95
+ # In wikipedia's World Map definition, this is "F".
96
+ #
97
+ # @return [Float]
98
+ def y_origin
99
+ return nil if null?
100
+
101
+ @geo_transform_pointer[3].read_double
102
+ end
103
+
104
+ # @param new_y_origin [Float]
105
+ def y_origin=(new_y_origin)
106
+ @geo_transform_pointer[3].write_double(new_y_origin)
107
+ end
108
+
109
+ # Rotation about the y-axis.
110
+ # In wikipedia's World Map definition, this is "D".
111
+ #
112
+ # @return [Float]
113
+ def y_rotation
114
+ return nil if null?
115
+
116
+ @geo_transform_pointer[4].read_double
117
+ end
118
+
119
+ # @param new_y_rotation [Float]
120
+ def y_rotation=(new_y_rotation)
121
+ @geo_transform_pointer[4].write_double(new_y_rotation)
122
+ end
123
+
124
+ # AKA Y-pixel size.
125
+ # In wikipedia's World Map definition, this is "E".
126
+ #
127
+ # @return [Float]
128
+ def pixel_height
129
+ return nil if null?
130
+
131
+ @geo_transform_pointer[5].read_double
132
+ end
133
+
134
+ # @param new_pixel_height [Float]
135
+ def pixel_height=(new_pixel_height)
136
+ @geo_transform_pointer[5].write_double(new_pixel_height)
137
+ end
138
+
139
+ # Converts a (pixel, line) coordinate to a georeferenced (geo_x, geo_y)
140
+ # location.
141
+ #
142
+ # @param pixel [Float] Input pixel position.
143
+ # @param line [Float] Input line position.
144
+ # @return [Hash{x_location: Float, y_location: Float}] longitude, latitude.
145
+ def apply_geo_transform(pixel, line)
146
+ geo_x_ptr = FFI::MemoryPointer.new(:double)
147
+ geo_y_ptr = FFI::MemoryPointer.new(:double)
148
+ FFI::GDAL.GDALApplyGeoTransform(@geo_transform_pointer, pixel, line, geo_x_ptr, geo_y_ptr)
149
+
150
+ { longitude: geo_x_ptr.read_double, latitude: geo_y_ptr.read_double }
151
+ end
152
+
153
+ # Composes this and the give geo_transform. The result is equivalent to
154
+ # applying both geotransforms to a point.
155
+ #
156
+ # @param other_geo_transform [GDAL::GeoTransform, FFI::Pointer]
157
+ # @return [GDAL::GeoTransform]
158
+ def compose(other_geo_transform)
159
+ other_ptr = GDAL._pointer(GDAL::GeoTransform, other_geo_transform)
160
+
161
+ new_gt_ptr = self.class.new_pointer
162
+ FFI::GDAL.GDALComposeGeoTransforms(@geo_transform_pointer, other_ptr, new_gt_ptr)
163
+ return nil if new_gt_ptr.null?
164
+
165
+ GDAL::GeoTransform.new(new_gt_ptr)
166
+ end
167
+
168
+ # Inverts the current 3x2 set of coefficients and returns a new GeoTransform.
169
+ # Useful for converting from the geotransform equation from pixel to geo to
170
+ # being geo to pixel.
171
+ #
172
+ # @return [GDAL::GeoTransform]
173
+ def invert
174
+ new_geo_transform_ptr = self.class.new_pointer
175
+ success = FFI::GDAL.GDALInvGeoTransform(@geo_transform_pointer, new_geo_transform_ptr)
176
+ return nil unless success
177
+
178
+ self.class.new(new_geo_transform_ptr)
179
+ end
180
+
181
+ # @param raster_filename [String] The target raster file.
182
+ # @param world_extension [String]
183
+ # @return [Boolean]
184
+ def to_world_file(raster_filename, world_extension)
185
+ FFI::GDAL.GDALWriteWorldFile(raster_filename, world_extension, @geo_transform_pointer)
186
+ end
187
+ end
188
+ end