ffi-gdal 1.0.0.beta3 → 1.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (286) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +62 -0
  4. data/Gemfile +1 -1
  5. data/History.md +53 -28
  6. data/README.md +6 -0
  7. data/Rakefile +23 -1
  8. data/examples/extract_and_colorize.rb +21 -22
  9. data/examples/geometries.rb +2 -2
  10. data/examples/gridding.rb +106 -0
  11. data/examples/ogr_layer_to_layer.rb +1 -1
  12. data/examples/points.txt +127 -0
  13. data/examples/testing_gdal.rb +3 -4
  14. data/ffi-gdal.gemspec +3 -2
  15. data/lib/ext/error_symbols.rb +2 -57
  16. data/lib/ext/ffi_library_function_checks.rb +26 -0
  17. data/lib/ext/float_ext.rb +2 -2
  18. data/lib/ext/narray_ext.rb +2 -0
  19. data/lib/ext/numeric_as_data_type.rb +19 -0
  20. data/lib/ext/to_bool.rb +4 -4
  21. data/lib/ffi/cpl/conv.rb +132 -0
  22. data/lib/ffi/cpl/error.rb +67 -0
  23. data/lib/ffi/cpl/hash_set.rb +39 -0
  24. data/lib/ffi/cpl/http.rb +25 -0
  25. data/lib/ffi/cpl/http_result.rb +18 -0
  26. data/lib/ffi/cpl/list.rb +38 -0
  27. data/lib/ffi/cpl/mime_part.rb +11 -0
  28. data/lib/ffi/cpl/minixml.rb +47 -0
  29. data/lib/ffi/cpl/port.rb +23 -0
  30. data/lib/ffi/cpl/quad_tree.rb +51 -0
  31. data/lib/ffi/{ogr/ogr_envelope.rb → cpl/rect_obj.rb} +3 -3
  32. data/lib/ffi/cpl/string.rb +95 -0
  33. data/lib/ffi/cpl/vsi.rb +115 -0
  34. data/lib/ffi/cpl/xml_node.rb +6 -6
  35. data/lib/ffi/gdal/alg.rb +385 -0
  36. data/lib/ffi/gdal/{gdal_color_entry.rb → color_entry.rb} +1 -2
  37. data/lib/ffi/gdal/exceptions.rb +6 -0
  38. data/lib/ffi/gdal/{gdal_gcp.rb → gcp.rb} +1 -3
  39. data/lib/ffi/gdal/{gdal_h.rb → gdal.rb} +253 -252
  40. data/lib/ffi/gdal/grid.rb +58 -0
  41. data/lib/ffi/gdal/{gdal_grid_data_metrics_options.rb → grid_data_metrics_options.rb} +1 -2
  42. data/lib/ffi/gdal/{gdal_grid_inverse_distance_to_a_power_options.rb → grid_inverse_distance_to_a_power_options.rb} +1 -2
  43. data/lib/ffi/gdal/{gdal_grid_moving_average_options.rb → grid_moving_average_options.rb} +1 -2
  44. data/lib/ffi/gdal/{gdal_grid_nearest_neighbor_options.rb → grid_nearest_neighbor_options.rb} +1 -2
  45. data/lib/ffi/gdal/matching.rb +20 -0
  46. data/lib/ffi/gdal/{gdal_rpc_info.rb → rpc_info.rb} +1 -2
  47. data/lib/ffi/gdal/{gdal_transformer_info.rb → transformer_info.rb} +1 -2
  48. data/lib/ffi/gdal/version.rb +1 -1
  49. data/lib/ffi/gdal/vrt.rb +92 -0
  50. data/lib/ffi/gdal/{gdal_warp_options.rb → warp_options.rb} +8 -4
  51. data/lib/ffi/gdal/warper.rb +70 -0
  52. data/lib/ffi/gdal.rb +56 -69
  53. data/lib/ffi/ogr/api.rb +567 -0
  54. data/lib/ffi/ogr/{ogr_contour_writer_info.rb → contour_writer_info.rb} +2 -3
  55. data/lib/ffi/ogr/core.rb +181 -0
  56. data/lib/ffi/ogr/envelope.rb +12 -0
  57. data/lib/ffi/ogr/{ogr_envelope_3d.rb → envelope_3d.rb} +2 -2
  58. data/lib/ffi/ogr/featurestyle.rb +29 -0
  59. data/lib/ffi/ogr/field.rb +63 -0
  60. data/lib/ffi/ogr/geocoding.rb +30 -0
  61. data/lib/ffi/ogr/srs_api.rb +407 -0
  62. data/lib/ffi/ogr/style_param.rb +13 -0
  63. data/lib/ffi/ogr/{ogr_style_value.rb → style_value.rb} +4 -3
  64. data/lib/ffi/ogr.rb +7 -7
  65. data/lib/ffi-gdal.rb +7 -145
  66. data/lib/gdal/color_entry.rb +18 -14
  67. data/lib/gdal/color_entry_mixins/extensions.rb +32 -0
  68. data/lib/gdal/color_interpretation.rb +2 -2
  69. data/lib/gdal/color_table.rb +37 -32
  70. data/lib/gdal/color_table_mixins/extensions.rb +48 -0
  71. data/lib/gdal/cpl_error_handler.rb +119 -0
  72. data/lib/gdal/data_type.rb +7 -7
  73. data/lib/gdal/dataset.rb +131 -238
  74. data/lib/gdal/dataset_mixins/algorithm_methods.rb +182 -0
  75. data/lib/gdal/dataset_mixins/extensions.rb +542 -0
  76. data/lib/gdal/dataset_mixins/matching.rb +26 -0
  77. data/lib/gdal/driver.rb +68 -92
  78. data/lib/gdal/driver_mixins/extensions.rb +93 -0
  79. data/lib/gdal/exceptions.rb +32 -4
  80. data/lib/gdal/geo_transform.rb +63 -43
  81. data/lib/gdal/geo_transform_mixins/extensions.rb +57 -0
  82. data/lib/gdal/grid.rb +144 -0
  83. data/lib/gdal/grid_types/data_metrics_base.rb +14 -0
  84. data/lib/gdal/grid_types/inverse_distance_to_a_power.rb +19 -0
  85. data/lib/gdal/grid_types/metric_average_distance.rb +12 -0
  86. data/lib/gdal/grid_types/metric_average_distance_pts.rb +12 -0
  87. data/lib/gdal/grid_types/metric_count.rb +12 -0
  88. data/lib/gdal/grid_types/metric_maximum.rb +12 -0
  89. data/lib/gdal/grid_types/metric_minimum.rb +12 -0
  90. data/lib/gdal/grid_types/metric_range.rb +12 -0
  91. data/lib/gdal/grid_types/moving_average.rb +19 -0
  92. data/lib/gdal/grid_types/nearest_neighbor.rb +19 -0
  93. data/lib/gdal/grid_types.rb +22 -0
  94. data/lib/gdal/internal_helpers.rb +94 -0
  95. data/lib/gdal/major_object.rb +4 -7
  96. data/lib/gdal/options.rb +14 -7
  97. data/lib/gdal/raster_attribute_table.rb +38 -47
  98. data/lib/gdal/raster_attribute_table_mixins/extensions.rb +41 -0
  99. data/lib/gdal/raster_band.rb +193 -227
  100. data/lib/gdal/raster_band_classifier.rb +107 -0
  101. data/lib/gdal/raster_band_mixins/algorithm_methods.rb +292 -0
  102. data/lib/gdal/raster_band_mixins/extensions.rb +238 -0
  103. data/lib/gdal/rpc_info.rb +35 -0
  104. data/lib/gdal/transformer.rb +15 -0
  105. data/lib/gdal/transformers/approximate_transformer.rb +48 -0
  106. data/lib/gdal/transformers/base_general_image_projection_transformer.rb +45 -0
  107. data/lib/gdal/transformers/gcp_transformer.rb +55 -0
  108. data/lib/gdal/transformers/general_image_projection_transformer.rb +31 -0
  109. data/lib/gdal/transformers/general_image_projection_transformer2.rb +52 -0
  110. data/lib/gdal/transformers/general_image_projection_transformer3.rb +25 -0
  111. data/lib/gdal/transformers/geolocation_transformer.rb +42 -0
  112. data/lib/gdal/transformers/reprojection_transformer.rb +39 -0
  113. data/lib/gdal/transformers/rpc_transformer.rb +56 -0
  114. data/lib/gdal/transformers/tps_transformer.rb +40 -0
  115. data/lib/gdal/virtual_dataset.rb +96 -0
  116. data/lib/gdal/warp_operation.rb +20 -23
  117. data/lib/gdal.rb +17 -0
  118. data/lib/ogr/coordinate_transformation.rb +16 -41
  119. data/lib/ogr/data_source.rb +103 -58
  120. data/lib/ogr/data_source_extensions.rb +5 -6
  121. data/lib/ogr/data_source_mixins/capability_methods.rb +27 -0
  122. data/lib/ogr/driver.rb +61 -33
  123. data/lib/ogr/driver_mixins/capability_methods.rb +16 -0
  124. data/lib/ogr/envelope.rb +29 -18
  125. data/lib/ogr/envelope_extensions.rb +70 -49
  126. data/lib/ogr/error_handling.rb +46 -0
  127. data/lib/ogr/exceptions.rb +58 -12
  128. data/lib/ogr/feature.rb +334 -86
  129. data/lib/ogr/feature_definition.rb +94 -51
  130. data/lib/ogr/feature_definition_extensions.rb +36 -13
  131. data/lib/ogr/feature_extensions.rb +62 -11
  132. data/lib/ogr/field.rb +175 -54
  133. data/lib/ogr/field_definition.rb +110 -0
  134. data/lib/ogr/{field_extensions.rb → field_definition_extensions.rb} +4 -5
  135. data/lib/ogr/{geocoding_session.rb → geocoder.rb} +14 -13
  136. data/lib/ogr/geometries/geometry_collection.rb +13 -0
  137. data/lib/ogr/geometries/line_string.rb +35 -0
  138. data/lib/ogr/geometries/linear_ring.rb +11 -0
  139. data/lib/ogr/geometries/multi_line_string.rb +16 -0
  140. data/lib/ogr/geometries/multi_point.rb +14 -0
  141. data/lib/ogr/geometries/multi_polygon.rb +21 -0
  142. data/lib/ogr/geometries/none_geometry.rb +13 -0
  143. data/lib/ogr/geometries/point.rb +65 -0
  144. data/lib/ogr/geometries/point_extensions.rb +32 -0
  145. data/lib/ogr/geometries/polygon.rb +14 -0
  146. data/lib/ogr/geometries/unknown_geometry.rb +10 -0
  147. data/lib/ogr/geometry.rb +270 -242
  148. data/lib/ogr/geometry_extensions.rb +8 -9
  149. data/lib/ogr/geometry_field_definition.rb +99 -0
  150. data/lib/ogr/geometry_field_definition_extensions.rb +19 -0
  151. data/lib/ogr/geometry_types/container.rb +75 -0
  152. data/lib/ogr/geometry_types/curve.rb +25 -28
  153. data/lib/ogr/geometry_types/surface.rb +13 -4
  154. data/lib/ogr/internal_helpers.rb +57 -0
  155. data/lib/ogr/layer.rb +60 -181
  156. data/lib/ogr/layer_mixins/capability_methods.rb +102 -0
  157. data/lib/ogr/layer_mixins/extensions.rb +59 -0
  158. data/lib/ogr/layer_mixins/ogr_feature_methods.rb +127 -0
  159. data/lib/ogr/layer_mixins/ogr_field_methods.rb +143 -0
  160. data/lib/ogr/layer_mixins/ogr_layer_method_methods.rb +163 -0
  161. data/lib/ogr/layer_mixins/ogr_query_filter_methods.rb +89 -0
  162. data/lib/ogr/layer_mixins/ogr_sql_methods.rb +48 -0
  163. data/lib/ogr/spatial_reference.rb +108 -589
  164. data/lib/ogr/spatial_reference_extensions.rb +29 -3
  165. data/lib/ogr/spatial_reference_mixins/coordinate_system_getter_setters.rb +494 -0
  166. data/lib/ogr/spatial_reference_mixins/exporters.rb +134 -0
  167. data/lib/ogr/spatial_reference_mixins/importers.rb +243 -0
  168. data/lib/ogr/spatial_reference_mixins/morphers.rb +25 -0
  169. data/lib/ogr/spatial_reference_mixins/parameter_getter_setters.rb +122 -0
  170. data/lib/ogr/spatial_reference_mixins/type_checks.rb +62 -0
  171. data/lib/ogr/style_table.rb +53 -5
  172. data/lib/ogr/style_table_extensions.rb +21 -5
  173. data/lib/ogr/style_tool.rb +118 -0
  174. data/lib/ogr.rb +8 -0
  175. data/spec/ffi-gdal_spec.rb +1 -2
  176. data/spec/integration/{color_table_info_spec.rb → gdal/color_table_info_spec.rb} +12 -12
  177. data/spec/integration/{dataset_info_spec.rb → gdal/dataset_info_spec.rb} +2 -6
  178. data/spec/integration/{driver_info_spec.rb → gdal/driver_info_spec.rb} +5 -5
  179. data/spec/integration/{geo_transform_info_spec.rb → gdal/geo_transform_info_spec.rb} +1 -14
  180. data/spec/integration/{raster_attribute_table_info_spec.rb → gdal/raster_attribute_table_info_spec.rb} +1 -2
  181. data/spec/integration/{raster_band_info_spec.rb → gdal/raster_band_info_spec.rb} +30 -42
  182. data/spec/integration/ogr/layer_spec.rb +97 -0
  183. data/spec/spec_helper.rb +94 -5
  184. data/spec/support/integration_help.rb +1 -0
  185. data/spec/support/shared_contexts.rb +26 -0
  186. data/spec/support/shared_examples/{major_object_examples.rb → gdal/major_object_examples.rb} +5 -5
  187. data/spec/support/shared_examples/ogr/a_25D_geometry.rb +7 -0
  188. data/spec/support/shared_examples/ogr/a_container_geometry.rb +47 -0
  189. data/spec/support/shared_examples/ogr/a_geometry.rb +404 -0
  190. data/spec/support/shared_examples/ogr/a_line_string.rb +16 -0
  191. data/spec/support/test_style_table.txt +3 -0
  192. data/spec/unit/ext/error_symbols_spec.rb +41 -53
  193. data/spec/unit/ext/numeric_as_data_type_spec.rb +113 -0
  194. data/spec/unit/ffi/gdal_spec.rb +70 -0
  195. data/spec/unit/gdal/color_entry_spec.rb +5 -0
  196. data/spec/unit/gdal/color_interpretation_spec.rb +5 -0
  197. data/spec/unit/gdal/{color_table_extensions_spec.rb → color_table_mixins/extensions_spec.rb} +3 -3
  198. data/spec/unit/gdal/color_table_spec.rb +14 -16
  199. data/spec/unit/gdal/data_type_spec.rb +10 -17
  200. data/spec/unit/gdal/dataset_spec.rb +4 -7
  201. data/spec/unit/gdal/driver_mixins/extensions_spec.rb +22 -0
  202. data/spec/unit/gdal/driver_spec.rb +49 -0
  203. data/spec/unit/gdal/environment_methods_spec.rb +6 -0
  204. data/spec/unit/gdal/geo_transform_spec.rb +276 -0
  205. data/spec/unit/gdal/grid_spec.rb +5 -0
  206. data/spec/unit/gdal/internal_helpers_spec.rb +112 -0
  207. data/spec/unit/gdal/major_object_spec.rb +6 -0
  208. data/spec/unit/gdal/options_spec.rb +5 -0
  209. data/spec/unit/gdal/raster_attribute_table_spec.rb +5 -0
  210. data/spec/unit/gdal/raster_band_classifier_spec.rb +134 -0
  211. data/spec/unit/gdal/raster_band_spec.rb +5 -0
  212. data/spec/unit/gdal/rpc_info_spec.rb +5 -0
  213. data/spec/unit/gdal/version_info_spec.rb +6 -0
  214. data/spec/unit/gdal/virtual_dataset_spec.rb +32 -0
  215. data/spec/unit/gdal/warp_operation_spec.rb +5 -0
  216. data/spec/unit/ogr/data_source_mixins/capability_methods_spec.rb +30 -0
  217. data/spec/unit/ogr/data_source_spec.rb +209 -0
  218. data/spec/unit/ogr/driver_mixins/capability_methods_spec.rb +18 -0
  219. data/spec/unit/ogr/driver_spec.rb +150 -0
  220. data/spec/unit/ogr/envelope_spec.rb +322 -0
  221. data/spec/unit/ogr/feature_definition_spec.rb +313 -0
  222. data/spec/unit/ogr/feature_spec.rb +379 -0
  223. data/spec/unit/ogr/field_definition_spec.rb +135 -0
  224. data/spec/unit/ogr/field_spec.rb +193 -0
  225. data/spec/unit/ogr/geometries/geometry_collection_spec.rb +186 -0
  226. data/spec/unit/ogr/geometries/line_string_spec.rb +105 -0
  227. data/spec/unit/ogr/{linear_ring_spec.rb → geometries/linear_ring_spec.rb} +10 -31
  228. data/spec/unit/ogr/geometries/multi_line_string_spec.rb +14 -0
  229. data/spec/unit/ogr/geometries/multi_point_spec.rb +14 -0
  230. data/spec/unit/ogr/geometries/multi_polygon_spec.rb +41 -0
  231. data/spec/unit/ogr/geometries/none_geometry_spec.rb +12 -0
  232. data/spec/unit/ogr/{point_spec.rb → geometries/point_spec.rb} +19 -25
  233. data/spec/unit/ogr/geometries/polygon_spec.rb +17 -0
  234. data/spec/unit/ogr/geometries/unknown_geometry_spec.rb +10 -0
  235. data/spec/unit/ogr/geometry_field_definition_spec.rb +87 -0
  236. data/spec/unit/ogr/geometry_spec.rb +542 -0
  237. data/spec/unit/ogr/internal_helpers_spec.rb +57 -0
  238. data/spec/unit/ogr/layer_mixins/capability_methods_spec.rb +88 -0
  239. data/spec/unit/ogr/layer_mixins/ogr_feature_methods_spec.rb +145 -0
  240. data/spec/unit/ogr/layer_mixins/ogr_field_methods_spec.rb +432 -0
  241. data/spec/unit/ogr/layer_mixins/ogr_layer_method_methods_spec.rb +20 -0
  242. data/spec/unit/ogr/layer_mixins/ogr_query_filter_methods_spec.rb +42 -0
  243. data/spec/unit/ogr/layer_mixins/ogr_sql_methods_spec.rb +12 -0
  244. data/spec/unit/ogr/layer_spec.rb +66 -67
  245. data/spec/unit/ogr/spatial_reference_mixins/coordinate_system_getter_setters_spec.rb +46 -0
  246. data/spec/unit/ogr/spatial_reference_mixins/exporters_spec.rb +139 -0
  247. data/spec/unit/ogr/spatial_reference_mixins/importers_spec.rb +38 -0
  248. data/spec/unit/ogr/spatial_reference_mixins/morphers_spec.rb +36 -0
  249. data/spec/unit/ogr/spatial_reference_mixins/parameter_getter_setters_spec.rb +102 -0
  250. data/spec/unit/ogr/spatial_reference_mixins/type_checks_spec.rb +157 -0
  251. data/spec/unit/ogr/spatial_reference_spec.rb +42 -0
  252. data/spec/unit/ogr/style_table_spec.rb +132 -0
  253. data/spec/unit/ogr/style_tool_spec.rb +157 -0
  254. data/spec/unit/version_info_spec.rb +1 -1
  255. metadata +285 -75
  256. data/lib/ffi/cpl/conv_h.rb +0 -143
  257. data/lib/ffi/cpl/error_h.rb +0 -63
  258. data/lib/ffi/cpl/minixml_h.rb +0 -14
  259. data/lib/ffi/cpl/string_h.rb +0 -81
  260. data/lib/ffi/cpl/vsi_h.rb +0 -112
  261. data/lib/ffi/gdal/alg_h.rb +0 -127
  262. data/lib/ffi/gdal/grid_h.rb +0 -51
  263. data/lib/ffi/gdal/warper_h.rb +0 -48
  264. data/lib/ffi/ogr/api_h.rb +0 -553
  265. data/lib/ffi/ogr/core_h.rb +0 -148
  266. data/lib/ffi/ogr/featurestyle_h.rb +0 -22
  267. data/lib/ffi/ogr/geocoding_h.rb +0 -21
  268. data/lib/ffi/ogr/ogr_field.rb +0 -50
  269. data/lib/ffi/ogr/ogr_style_param.rb +0 -12
  270. data/lib/ffi/ogr/srs_api_h.rb +0 -325
  271. data/lib/gdal/color_entry_extensions.rb +0 -30
  272. data/lib/gdal/color_table_extensions.rb +0 -47
  273. data/lib/gdal/dataset_extensions.rb +0 -496
  274. data/lib/gdal/driver_extensions.rb +0 -56
  275. data/lib/gdal/geo_transform_extensions.rb +0 -90
  276. data/lib/gdal/raster_attribute_table_extensions.rb +0 -40
  277. data/lib/gdal/raster_band_extensions.rb +0 -198
  278. data/lib/ogr/geometry_types/collection.rb +0 -45
  279. data/lib/ogr/layer_extensions.rb +0 -55
  280. data/lib/ogr/line_string.rb +0 -7
  281. data/lib/ogr/linear_ring.rb +0 -6
  282. data/lib/ogr/multi_line_string.rb +0 -9
  283. data/lib/ogr/multi_point.rb +0 -7
  284. data/lib/ogr/multi_polygon.rb +0 -14
  285. data/lib/ogr/point.rb +0 -89
  286. data/lib/ogr/polygon.rb +0 -9
@@ -0,0 +1,107 @@
1
+ module GDAL
2
+ # Takes a list of Ranges of color values and remaps them. Note that these
3
+ # values are directly written to the raster band, overwriting all existing
4
+ # values.
5
+ #
6
+ # @example
7
+ # classifier = GDAL::RasterBandClassifier.new(raster_band)
8
+ # ranges = [
9
+ # { range: 0...20, map_to: 1 },
10
+ # { range: 20...50, map_to: 2 },
11
+ # { range: 50...250, map_to: 3 }
12
+ # ]
13
+ # classifier.add_ranges(ranges)
14
+ # classifier.classify!(ranges)
15
+ #
16
+ # @param ranges [Array<Hash{range => Range, map_to => Number}>]
17
+ class RasterBandClassifier
18
+ include GDAL::Logger
19
+
20
+ attr_reader :ranges
21
+
22
+ # @param raster_band [GDAL::RasterBand]
23
+ def initialize(raster_band)
24
+ @raster_band = raster_band
25
+ @ranges = []
26
+ end
27
+
28
+ # @param range [Range] The range of values to map to a new value.
29
+ # @param map_to_value [Number]
30
+ def add_range(range, map_to_value)
31
+ fail 'range must be a Ruby Range' unless range.is_a? Range
32
+
33
+ @ranges << { range: range, map_to: map_to_value }
34
+ end
35
+
36
+ # @param range_array [Array<Hash{range => Range, map_to => Number}>]
37
+ def add_ranges(range_array)
38
+ range_array.each do |range|
39
+ add_range range[:range], range[:map_to]
40
+ end
41
+ end
42
+
43
+ # Uses the max value of the associated RasterBand and +range_count+ to
44
+ # calculate evenly-weighted ranges. If there are remainder values at the
45
+ # max end of the values, those get lumped in with the last range.
46
+ #
47
+ # @param range_count [Fixnum] The number of ranges to create.
48
+ def equal_count_ranges(range_count)
49
+ sorted_pixels = @raster_band.to_na.sort
50
+ sorted_and_masked_pixels = sorted_pixels[sorted_pixels.ne(@raster_band.no_data_value[:value])]
51
+ range_size = (sorted_and_masked_pixels.size / range_count).to_i
52
+ log "Pixel count: #{sorted_and_masked_pixels.size}"
53
+ log "Min pixel value: #{sorted_and_masked_pixels.min}"
54
+ log "Max pixel value: #{sorted_and_masked_pixels.max}"
55
+ log "Range size: #{range_size}"
56
+ log 'Break offsets:'
57
+ break_values = range_count.times.map { |i| sorted_and_masked_pixels[range_size * i] }
58
+ log "Break values: #{break_values}"
59
+
60
+ breakpoint_calculator = lambda do |range_number|
61
+ min = break_values[range_number]
62
+ max = break_values[range_number + 1] || sorted_and_masked_pixels.max
63
+
64
+ range_for_type(min, max)
65
+ end
66
+
67
+ range_count.times.each_with_object([]) do |i, ranges|
68
+ range = breakpoint_calculator.call(i)
69
+
70
+ ranges << {
71
+ range: range,
72
+ map_to: (i + 1).to_data_type(@raster_band.data_type)
73
+ }
74
+ end
75
+ end
76
+
77
+ # Uses the ranges that have been added to remap ranges to map_to values.
78
+ # Note that this *will* overwrite the associated RasterBand with these
79
+ # values, so if you don't want to overwrite the Dataset you're working with,
80
+ # you should copy it first.
81
+ def classify!
82
+ narray = @raster_band.to_na.dup
83
+
84
+ 0.upto(narray.size - 1) do |pixel_number|
85
+ next if narray[pixel_number] == @raster_band.no_data_value[:value]
86
+
87
+ range = @ranges.find do |r|
88
+ r[:range].member? narray[pixel_number]
89
+ end
90
+
91
+ if range
92
+ narray[pixel_number] = range[:map_to]
93
+ else
94
+ log "pixel #{pixel_number} (value: #{narray[pixel_number]}) not in any given range"
95
+ end
96
+ end
97
+
98
+ @raster_band.write_array(narray, data_type: @raster_band.data_type)
99
+ end
100
+
101
+ private
102
+
103
+ def range_for_type(min, max)
104
+ min.to_data_type(@raster_band.data_type)..max.to_data_type(@raster_band.data_type)
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,292 @@
1
+ require_relative '../../ffi/gdal/alg'
2
+
3
+ module GDAL
4
+ module RasterBandMixins
5
+ module AlgorithmMethods
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ # Compute the optimal PCT for RGB image. Implements a median cut
12
+ # algorithm to compute an "optimal" pseudo-color table for representing
13
+ # an input RGB image. This PCT could then be used with
14
+ # +dither_rgb_to_pct+ to convert a 24-bit RGB image into an 8-bit
15
+ # pseudo-colored image.
16
+ #
17
+ # @param red_band [GDAL::RasterBand, FFI::Pointer]
18
+ # @param green_band [GDAL::RasterBand, FFI::Pointer]
19
+ # @param blue_band [GDAL::RasterBand, FFI::Pointer]
20
+ # @param colors [Fixnum] Number of colors to return; 2-256.
21
+ # @param color_interpretation [FFI::GDAL::PaletteInterp] The type
22
+ # of ColorTable to return.
23
+ # @return [GDAL::ColorTable]
24
+ def compute_median_cut_pct(red_band, green_band, blue_band,
25
+ colors, color_interpretation, &progress)
26
+ color_table = GDAL::ColorTable.new(color_interpretation)
27
+
28
+ FFI::GDAL::Alg.GDALComputeMedianCutPCT(
29
+ red_band,
30
+ green_band,
31
+ blue_band,
32
+ nil, # This isn't yet supported in GDAL.
33
+ colors,
34
+ color_table.c_pointer,
35
+ progress,
36
+ nil)
37
+
38
+ color_table
39
+ end
40
+
41
+ # 24-bit to 8-bit conversion with dithering. Utilizes Floyd-Steinberg
42
+ # dithering, using the provided color table.
43
+ #
44
+ # The red, green, and blue input bands do not necessarily need to come
45
+ # from the same file, but they must be the same width and height. They
46
+ # will be clipped to 8-bit during reading, so non-eight bit bands are
47
+ # generally inappropriate. Likewise, +output_band+ will be written with
48
+ # 8-bit values and must match the width and height of the source bands.
49
+ #
50
+ # The ColorTable cannot have more than 256 entries.
51
+ #
52
+ # @param red_band [GDAL::RasterBand, FFI::Pointer]
53
+ # @param green_band [GDAL::RasterBand, FFI::Pointer]
54
+ # @param blue_band [GDAL::RasterBand, FFI::Pointer]
55
+ # @param output_band [GDAL::RasterBand, FFI::Pointer]
56
+ # @param color_table [GDAL::ColorTable, FFI::Pointer]
57
+ # @return [GDAL::RasterBand] +output_band+ with the dithering algorithm
58
+ # applied.
59
+ def dither_rgb_to_pct(red_band, green_band, blue_band, output_band,
60
+ color_table, &progress)
61
+ red_ptr = GDAL._pointer(GDAL::RasterBand, red_band)
62
+ green_ptr = GDAL._pointer(GDAL::RasterBand, green_band)
63
+ blue_ptr = GDAL._pointer(GDAL::RasterBand, blue_band)
64
+ output_ptr = GDAL._pointer(GDAL::RasterBand, output_band)
65
+ color_table_ptr = GDAL._pointer(GDAL::ColorTable, color_table)
66
+
67
+ FFI::GDAL::Alg.GDALDitherRGB2PCT(
68
+ red_ptr,
69
+ green_ptr,
70
+ blue_ptr,
71
+ output_ptr,
72
+ color_table_ptr,
73
+ progress,
74
+ nil)
75
+
76
+ output_band
77
+ end
78
+ end
79
+
80
+ # Computes a 16-bit (0-65535) checksum from a region of raster data.
81
+ # Floating point data is converted to 32-bit integers so decimal portions
82
+ # of the raster data won't affect the checksum. Real and imaginary
83
+ # components of complex bands influence the result.
84
+ #
85
+ # @param x_offset [Fixnum]
86
+ # @param y_offset [Fixnum]
87
+ # @param x_size [Fixnum]
88
+ # @param y_size [Fixnum]
89
+ # @return [Fixnum] The checksum value.
90
+ def checksum_image(x_offset, y_offset, x_size, y_size)
91
+ !!FFI::GDAL::Alg.GDALChecksumImage(
92
+ @c_pointer,
93
+ x_offset,
94
+ y_offset,
95
+ x_size,
96
+ y_size)
97
+ end
98
+
99
+ # Computes the proximity of all pixels in the proximity_band to those in
100
+ # this band. By default all non-zero pixels in this band will be
101
+ # considered as "target", and all proximities will be computed in pixels.
102
+ # Target pixels are set to the value corresponding to a distance of zero.
103
+ #
104
+ # Note that this modifies the source band in place with the computed
105
+ # values.
106
+ #
107
+ # @param proximity_band [GDAL::RasterBand, FFI::Pointer]
108
+ # @param options [Hash]
109
+ # @option options [String] values A list of target pixel values to measure
110
+ # the distance from. If this isn't provided, proximity will be computed
111
+ # from non-zero pixel values.
112
+ # @option options [String] distunits (PIXEL) Indicates what unit type to
113
+ # use for computing.
114
+ # @option options [Fixnum] maxdist The maximum distance to search.
115
+ # @option options [Fixnum] nodata If not given, it will try to use the
116
+ # nodata value on the +proximity_band+. If not found there, will use
117
+ # 65535.
118
+ # @option options [Fixnum] fixed_buf_val If set, all pixels within the
119
+ # +maxdist+ threshold are set to this fixed value instead of to a
120
+ # proximity distance.
121
+ def compute_proximity!(proximity_band, **options, &progress)
122
+ proximity_band_ptr = GDAL._pointer(GDAL::RasterBand, proximity_band)
123
+ options_ptr = GDAL::Options.pointer(options)
124
+
125
+ FFI::GDAL::Alg.GDALComputeProximity(
126
+ @c_pointer,
127
+ proximity_band_ptr,
128
+ options_ptr,
129
+ progress,
130
+ nil)
131
+ end
132
+
133
+ # Fill selected raster regions by interpolation from the edges. It
134
+ # interpolates values for all designated nodata pixels (marked by zeroes
135
+ # in +mask_band+). For each pixel, a four-direction conic search is done
136
+ # to find values to interpolate from (using inverse distance weighting).
137
+ # Once all values are interpolated, zero or more smoothing iterations
138
+ # (3x3 average filters on interpolated pixels) are applied to smooth out
139
+ # artifacts.
140
+ #
141
+ # This is generally suitable for interpolating missing regions of fairly
142
+ # continuously varying rasters (such as elevation models, for instance).
143
+ # It is also suitable for filling small holes and cracks in more
144
+ # irregularly varying images (like aerial photos). Its is generally not so
145
+ # great for interpolating a raster from sparse point data. See GDAL::Grid
146
+ # for that case.
147
+ #
148
+ # Note that this alters values of the current raster band.
149
+ #
150
+ # @param mask_band [GDAL::RasterBand] Band that indicates which pixels to
151
+ # be interpolated (it does so using 0-valued pixels).
152
+ # @param max_search_distance [Float] Max number of pixels to search in all
153
+ # directions to find values to interpolate from.
154
+ # @param smoothing_iterations [Fixnum] The number of 3x3 smoothing filter
155
+ # passes to run. Can be 0.
156
+ # @param options [Hash]
157
+ # TODO: document what valid options are.
158
+ def fill_nodata!(mask_band, max_search_distance, smoothing_iterations, **options, &progress)
159
+ mask_band_ptr = GDAL._pointer(GDAL::RasterBand, mask_band)
160
+ options_ptr = GDAL::Options.pointer(options)
161
+
162
+ !!FFI::GDAL.GDALFillNodata(@c_pointer,
163
+ mask_band_ptr,
164
+ max_search_distance,
165
+ 0, # deprecated option in GDAL
166
+ smoothing_iterations,
167
+ options_ptr,
168
+ progress,
169
+ nil)
170
+ end
171
+
172
+ # Creates vector polygons for all connected regions of pixels in the raster
173
+ # that share a common pixel value. Optionally, each polygon may be
174
+ # labeled with the pixel value in an attribute. Optionally, a mask band
175
+ # can be provided to determine which pixels are eligible for processing.
176
+ #
177
+ # The C API implements two functions for this: +GDALPolygonize+ and
178
+ # +GDALFPolygonize+, where the former uses a 32-bit Integer buffer and the
179
+ # latter uses a 32-bit Float buffer. The Integer version may be quicker,
180
+ # but the Float version more accurate. As such, calling +polygonize+
181
+ # defaults to use the Float version internally, but you can tell it to use
182
+ # the Integer version by using the +use_integer_function+ flag.
183
+ #
184
+ # Polygon features will be created on +layer+ with Polygon geometries
185
+ # representing the polygons. The geometries will be in the georeferenced
186
+ # coordinate system of the image (based on the GeoTransform) of the source
187
+ # Dataset). It is acceptable for +layer+ to already have other features.
188
+ #
189
+ # Note that this does not set the coordinate system on the output
190
+ # layer--the application is responsible for doing so.
191
+ #
192
+ # @param layer [OGR::Layer, FFI::Pointer] The layer to write the polygons
193
+ # to.
194
+ # @param mask_band [GDAL::RasterBand, FFI::Pointer] Optional band, where all
195
+ # pixels in the mask with a value other than zero will be considered
196
+ # suitable for collection as polygons.
197
+ # @param pixel_value_field [Fixnum] Index of the feature attribute into
198
+ # which the pixel value of the polygon should be written.
199
+ # @param options [Hash]
200
+ # @option options [Fixnum] '8CONNECTED' (4) Set to 8 to use 8
201
+ # connectedness.
202
+ # @param progress [Proc]
203
+ # @return [OGR::Layer]
204
+ def polygonize(layer, mask_band: nil, pixel_value_field: -1, use_integer_function: false,
205
+ **options, &progress)
206
+ mask_band_ptr = GDAL._pointer(GDAL::RasterBand, mask_band, false)
207
+ layer_ptr = GDAL._pointer(OGR::Layer, layer)
208
+ fail OGR::InvalidLayer, "Invalid layer: #{layer.inspect}" if layer_ptr.null?
209
+ log "Pixel value field: #{pixel_value_field}"
210
+
211
+ options_ptr = GDAL::Options.pointer(options)
212
+
213
+ function = use_integer_function ? :GDALPolygonize : :GDALFPolygonize
214
+
215
+ FFI::GDAL::Alg.send(
216
+ function,
217
+ @c_pointer, # hSrcBand
218
+ mask_band_ptr, # hMaskBand
219
+ layer_ptr, # hOutLayer
220
+ pixel_value_field, # iPixValField
221
+ options_ptr, # papszOptions
222
+ progress, # pfnProgress
223
+ nil # pProgressArg
224
+ )
225
+
226
+ layer_ptr.instance_of?(OGR::Layer) ? layer_ptr : OGR::Layer.new(layer_ptr)
227
+ end
228
+
229
+ # Removes raster polygons that are smaller than the given threshold (in
230
+ # pixels) and replaces them with the pixel value of the largest neighbor
231
+ # polygon. Polygons are determined as regions of the raster where the
232
+ # pixels all have the same value, and that are contiguous (connected).
233
+ #
234
+ # If +mask_band+ is given, "nodata" pixels in the band will not be treated
235
+ # as part of a polygon, regardless of their pixel values.
236
+ #
237
+ # @param size_threshold [Fixnum] Polygons found in the raster with sizes
238
+ # smaller than this will be merged into their largest neighbor.
239
+ # @param connectedness [Fixnum] 4 or 8. 4 indicates that diagonal pixels
240
+ # are not considered directly adjacent for polygon membership purposes;
241
+ # 8 indicates they are.
242
+ # @param mask_band [GDAL::RasterBand] [description] All pixels in this
243
+ # band with a value other than 0 will be considered suitable for
244
+ # inclusion in polygons.
245
+ # @param options [Hash] None supported in GDAL as of this writing.
246
+ def sieve_filter!(size_threshold, connectedness, mask_band: nil, **options, &progress)
247
+ _sieve_filter(size_threshold, connectedness, @c_pointer,
248
+ mask_band: mask_band, **options, &progress)
249
+ end
250
+
251
+ # The same as +sieve_filter!+, but returns a new GDAL::RasterBand as the
252
+ # result.
253
+ #
254
+ # @see +sieve_filter!
255
+ # @param destination_band [GDAL::RasterBand]
256
+ def sieve_filter(size_threshold, connectedness, destination_band,
257
+ mask_band: nil, **options, &progress)
258
+ destination_band_ptr = GDAL._pointer(GDAL::RasterBand, destination_band)
259
+ if destination_band.nil? || destination_band.null?
260
+ fail GDAL::InvalidRasterBand, "destination_band isn't a valid GDAL::RasterBand: #{destination_band}"
261
+ end
262
+
263
+ _sieve_filter(size_threshold, connectedness, destination_band_ptr,
264
+ mask_band: mask_band, **options, &progress)
265
+
266
+ if destination_band.is_a? GDAL::RasterBand
267
+ destination_band
268
+ else
269
+ GDAL::RasterBand.new(destination_band)
270
+ end
271
+ end
272
+
273
+ private
274
+
275
+ def _sieve_filter(size_threshold, connectedness, destination_band_ptr = nil,
276
+ mask_band: nil, **options, &progress)
277
+ mask_band_ptr = GDAL._pointer(GDAL::RasterBand, mask_band, false)
278
+ options_ptr = GDAL::Options.pointer(options)
279
+
280
+ FFI::GDAL::Alg.GDALSieveFilter(
281
+ @c_pointer,
282
+ mask_band_ptr,
283
+ destination_band_ptr,
284
+ size_threshold,
285
+ connectedness,
286
+ options_ptr,
287
+ progress,
288
+ nil)
289
+ end
290
+ end
291
+ end
292
+ end
@@ -0,0 +1,238 @@
1
+ require 'json'
2
+
3
+ module GDAL
4
+ module RasterBandMixins
5
+ module Extensions
6
+ def overviews
7
+ overview_count.times.map do |i|
8
+ overview(i)
9
+ end
10
+ end
11
+
12
+ # @return [Hash{x => Fixnum, y => Fixnum}]
13
+ def block_count
14
+ x_blocks = (x_size + block_size[:x]).divmod(block_size[:x])
15
+ y_blocks = (y_size + block_size[:y]).divmod(block_size[:y])
16
+
17
+ {
18
+ x: x_blocks.first - 1,
19
+ x_remainder: x_blocks.last,
20
+ y: y_blocks.first - 1,
21
+ y_remainder: y_blocks.last
22
+ }
23
+ end
24
+
25
+ # The buffer size to use for block-based IO, based on #block_size.
26
+ #
27
+ # @return [Fixnum]
28
+ def block_buffer_size
29
+ block_size[:x] * block_size[:y]
30
+ end
31
+
32
+ # Reads through the raster, block-by-block and yields the pixel data that
33
+ # it gathered.
34
+ #
35
+ # @param to_data_type [FFI::GDAL::DataType]
36
+ # @return [Enumerator, nil] Returns an Enumerable if no block is given,
37
+ # allowing to chain with other Enumerable methods. Returns nil if a
38
+ # block is given.
39
+ def each_by_block(to_data_type = nil)
40
+ return enum_for(:each_by_block) unless block_given?
41
+
42
+ data_type = to_data_type || self.data_type
43
+ data_pointer = GDAL._pointer_from_data_type(data_type, block_buffer_size)
44
+
45
+ 0.upto(block_count[:y]).each do |y_block_number|
46
+ 0.upto(block_count[:x] - 1).each do |x_block_number|
47
+ y_block_size = if y_block_number == block_count[:y] && !block_count[:y_remainder].zero?
48
+ block_count[:y_remainder]
49
+ elsif y_block_number == block_count[:y]
50
+ 0
51
+ else
52
+ block_size[:y]
53
+ end
54
+
55
+ next if y_block_size.zero?
56
+
57
+ read_block(x_block_number, y_block_number, data_pointer)
58
+
59
+ 0.upto(y_block_size - 1).each do |block_index|
60
+ x_read_offset = block_size[:x] * block_index
61
+
62
+ pixels = if data_type == :GDT_Byte
63
+ data_pointer.get_array_of_uint8(x_read_offset, block_size[:x])
64
+ elsif data_type == :GDT_UInt16
65
+ data_pointer.get_array_of_uint16(x_read_offset, block_size[:x])
66
+ else
67
+ data_pointer.get_array_of_float(x_read_offset, block_size[:x])
68
+ end
69
+
70
+ yield(pixels)
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ # Iterates through all lines and builds an NArray of pixels.
77
+ #
78
+ # @return [NArray]
79
+ def to_na(to_data_type = nil)
80
+ values = each_by_block.map do |pixels|
81
+ pixels
82
+ end
83
+
84
+ if to_data_type
85
+ case to_data_type
86
+ when :GDT_Byte then NArray.to_na(values).to_type(NArray::BYTE)
87
+ when :GDT_Int16 then NArray.to_na(values).to_type(NArray::SINT)
88
+ when :GDT_UInt16 then NArray.to_na(values).to_type(NArray::INT)
89
+ when :GDT_Int32 then NArray.to_na(values).to_type(NArray::INT)
90
+ when :GDT_UInt32 then NArray.to_na(values).to_type(NArray::INT)
91
+ when :GDT_Float32 then NArray.to_na(values).to_type(NArray::SFLOAT)
92
+ when :GDT_Float64 then NArray.to_na(values).to_type(NArray::DFLOAT)
93
+ when :GDT_CInt16 then NArray.to_na(values).to_type(NArray::SCOMPLEX)
94
+ when :GDT_CInt32 then NArray.to_na(values).to_type(NArray::DCOMPLEX)
95
+ when :GDT_CFloat32 then NArray.to_na(values).to_type(NArray::SCOMPLEX)
96
+ when :GDT_CFloat64 then NArray.to_na(values).to_type(NArray::DCOMPLEX)
97
+ else
98
+ fail "Unknown data type: #{to_data_type}"
99
+ end
100
+ else
101
+ NArray.to_na(values)
102
+ end
103
+ end
104
+
105
+ # Sets the band to be a Palette band, then applies an RGB color table using
106
+ # the given colors. Colors are distributed evenly across the table based
107
+ # on the number of colors given. Note that this will overwrite any existing
108
+ # color table that may be set on this band.
109
+ #
110
+ # @param colors [Array<Fixnum, String>, Fixnum, String] Can be a single or
111
+ # many colors, given as either R, G, and B integers (0-255) or as strings
112
+ # of Hex.
113
+ #
114
+ # @example Colors as RGB values
115
+ # # This will make the first 128 values black, and the last 128, red.
116
+ # my_band.colorize!([[0, 0, 0], [255, 0, 0]])
117
+ #
118
+ # @example Colors as Hex values
119
+ # # Same as above...
120
+ # my_band.colorize!(%w[#000000 #FF0000])
121
+ def colorize!(*colors)
122
+ return if colors.empty?
123
+
124
+ table = GDAL::ColorTable.new(:GPI_RGB)
125
+
126
+ color_entry_index_count =
127
+ if data_type == :GDT_Byte
128
+ 256
129
+ elsif data_type == :GDT_UInt16
130
+ 65_536
131
+ else
132
+ fail "Can't colorize a #{data_type} band--must be :GDT_Byte or :GDT_UInt16"
133
+ end
134
+
135
+ self.color_interpretation ||= :GCI_PaletteIndex
136
+ table.add_color_entry(0, 0, 0, 0, 255)
137
+ bin_count = color_entry_index_count / colors.size.to_f
138
+
139
+ 1.upto(color_entry_index_count - 1) do |color_entry_index|
140
+ color_number = (color_entry_index / bin_count).to_i
141
+
142
+ color = colors[color_number]
143
+ # TODO: Fix possible uninitialized rgb_array
144
+ rgb_array = hex_to_rgb(color) unless color.is_a?(Array)
145
+ table.add_color_entry(color_entry_index,
146
+ rgb_array[0], rgb_array[1], rgb_array[2], 255)
147
+ end
148
+
149
+ self.color_table = table
150
+ end
151
+
152
+ # Gets the colors from the associated ColorTable and returns an Array of
153
+ # those, where each ColorEntry is [R, G, B, A].
154
+ #
155
+ # @return [Array<Array<Fixnum>>]
156
+ def colors_as_rgb
157
+ return [] unless color_table
158
+
159
+ color_table.color_entries_as_rgb.map(&:to_a)
160
+ end
161
+
162
+ # Gets the colors from the associated ColorTable and returns an Array of
163
+ # Strings, where the RGB color for each ColorEntry has been converted to
164
+ # Hex.
165
+ #
166
+ # @return [Array<String>]
167
+ def colors_as_hex
168
+ colors_as_rgb.map do |rgba|
169
+ rgb = rgba.to_a[0..2]
170
+
171
+ "##{rgb[0].to_s(16)}#{rgb[1].to_s(16)}#{rgb[2].to_s(16)}"
172
+ end
173
+ end
174
+
175
+ # @param hex [String]
176
+ def hex_to_rgb(hex)
177
+ hex.sub!(/^#/, '')
178
+ matches = hex.match(/(?<red>[a-zA-Z0-9]{2})(?<green>[a-zA-Z0-9]{2})(?<blue>[a-zA-Z0-9]{2})/)
179
+
180
+ [matches[:red].to_i(16), matches[:green].to_i(16), matches[:blue].to_i(16)]
181
+ end
182
+
183
+ # Each pixel of the raster projected using the dataset's geo_transform.
184
+ #
185
+ # @return [NArray]
186
+ def projected_points
187
+ point_count = (y_size) * (x_size)
188
+ narray = NArray.object(2, point_count)
189
+
190
+ 0.upto(y_size - 1).each do |y_point|
191
+ 0.upto(x_size - 1).each do |x_point|
192
+ hash = dataset.geo_transform.apply_geo_transform(y_point, x_point)
193
+ point_number = y_point * x_point
194
+ narray[0, point_number] = hash[:y_geo] || 0
195
+ narray[1, point_number] = hash[:x_geo] || 0
196
+ end
197
+ end
198
+
199
+ narray
200
+ end
201
+
202
+ # @return [Hash]
203
+ def as_json(_options = nil)
204
+ {
205
+ raster_band: {
206
+ block_size: block_size,
207
+ category_names: category_names,
208
+ color_interpretation: color_interpretation,
209
+ color_table: color_table,
210
+ data_type: data_type,
211
+ default_histogram: default_histogram(true),
212
+ default_raster_attribute_table: default_raster_attribute_table,
213
+ has_arbitrary_overviews: arbitrary_overviews?,
214
+ mask_flags: mask_flags,
215
+ maximum_value: maximum_value,
216
+ minimum_value: minimum_value,
217
+ no_data_value: no_data_value,
218
+ number: number,
219
+ offset: offset,
220
+ overview_count: overview_count,
221
+ overviews: overviews,
222
+ scale: scale,
223
+ statistics: statistics,
224
+ unit_type: unit_type,
225
+ x_size: x_size,
226
+ y_size: y_size
227
+ },
228
+ metadata: all_metadata
229
+ }
230
+ end
231
+
232
+ # @return [String]
233
+ def to_json(options = nil)
234
+ as_json(options).to_json
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,35 @@
1
+ require_relative '../ffi/gdal/rpc_info'
2
+ require_relative '../ffi/gdal/alg'
3
+ require 'forwardable'
4
+
5
+ module GDAL
6
+ # @return [FFI::GDAL::RPCInfo]
7
+ attr_reader :c_struct
8
+
9
+ # Wrapper for FFI::GDAL::RPCInfo.
10
+ class RPCInfo
11
+ extend Forwardable
12
+ def_delegator :@c_struct, :[]
13
+
14
+ # @param struct_or_ptr [FFI::GDAL::RPCInfo, FFI::Pointer]
15
+ def initialize(struct_or_ptr = nil)
16
+ @c_struct = if struct_or_ptr.is_a? FFI::GDAL::RPCInfo
17
+ struct_or_ptr
18
+ elsif struct_or_ptr.is_a? FFI::Pointer
19
+ FFI::GDAL::RPCInfo.new(struct_or_ptr)
20
+ else
21
+ FFI::GDAL::RPCInfo.new
22
+ end
23
+ end
24
+
25
+ # @return [FFI::Pointer]
26
+ def c_pointer
27
+ @c_struct.to_ptr
28
+ end
29
+
30
+ # @return [Array<String>]
31
+ def to_metadata
32
+ FFI::GDAL::Alg.RPCInfoToMD(@c_struct).read_array_of_string(0)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,15 @@
1
+ require_relative '../ffi/gdal/alg'
2
+
3
+ module GDAL
4
+ class Transformer
5
+ # @return [FFI::Pointer]
6
+ def self.create_similar_transformer(transformer_arg_ptr, source_ratio_x, source_ratio_y)
7
+ FFI::GDAL::Alg.GDALCreateSimilarTransformer(transformer_arg_ptr, source_ratio_x, source_ratio_y)
8
+ end
9
+
10
+ # @param transformer_arg [FFI::Pointer]
11
+ def self.destroy_transformer(transformer_arg)
12
+ FFI::GDAL::Alg.GDALDestroyTransformer(transformer_arg)
13
+ end
14
+ end
15
+ end