ffi-gdal 1.0.0.beta3 → 1.0.0.beta4

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 (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,542 @@
1
+ require 'json'
2
+ require 'narray'
3
+ require_relative '../raster_band'
4
+ require_relative '../warp_operation'
5
+ require_relative '../../ogr/driver'
6
+ require_relative '../../ogr/layer'
7
+ require_relative '../../ogr/spatial_reference'
8
+ require_relative '../../ogr/coordinate_transformation'
9
+
10
+ module GDAL
11
+ module DatasetMixins
12
+ # Methods not originally supplied with GDAL, but enhance it.
13
+ module Extensions
14
+ # Computes NDVI from the red and near-infrared bands in the dataset. Raises
15
+ # a GDAL::RequiredBandNotFound if one of those band types isn't found.
16
+ #
17
+ # @param destination [String] Path to output the new dataset to.
18
+ # @param driver_name [String] The type of dataset to create.
19
+ # @param band_order [Array<String>] The list of band types, i.e. ['red',
20
+ # 'green', 'blue'].
21
+ # @param output_data_type [FFI::GDAL::DataType] Resulting dataset will be
22
+ # in this data type.
23
+ # @param remove_negatives [Boolean] Remove negative values after
24
+ # calculating NDVI.
25
+ # @param no_data_value [Float]
26
+ # @param options [Hash] Options that get used for creating the new NDVI
27
+ # dataset. See docs for GDAL::Driver#create_dataset.
28
+ # @return [GDAL::Dataset] The new NDVI dataset. *Be sure to call #close on
29
+ # this object or the data may not persist!*
30
+ def extract_ndvi(destination, driver_name: 'GTiff', band_order: nil,
31
+ output_data_type: :GDT_Byte, remove_negatives: false,
32
+ no_data_value: -9999.0,
33
+ **options)
34
+ original_bands =
35
+ if band_order
36
+ bands_with_labels(band_order)
37
+ else
38
+ {
39
+ red: red_band,
40
+ green: green_band,
41
+ blue: blue_band,
42
+ nir: undefined_band
43
+ }
44
+ end
45
+
46
+ red = original_bands[:red]
47
+ nir = original_bands[:nir]
48
+
49
+ if red.nil?
50
+ fail RequiredBandNotFound, 'Red band not found.'
51
+ elsif nir.nil?
52
+ fail RequiredBandNotFound, 'Near-infrared'
53
+ end
54
+
55
+ the_array = calculate_ndvi(red.to_na,
56
+ nir.to_na,
57
+ no_data_value,
58
+ remove_negatives,
59
+ output_data_type)
60
+ driver = GDAL::Driver.by_name(driver_name)
61
+
62
+ driver.create_dataset(destination, raster_x_size, raster_y_size,
63
+ data_type: output_data_type, **options) do |ndvi_dataset|
64
+ ndvi_dataset.geo_transform = geo_transform
65
+ ndvi_dataset.projection = projection
66
+
67
+ ndvi_band = ndvi_dataset.raster_band(1)
68
+ ndvi_band.write_array(the_array, data_type: output_data_type)
69
+ ndvi_band.no_data_value = no_data_value
70
+ end
71
+ end
72
+
73
+ # Computes GNDVI from the green and near-infrared bands in the dataset.
74
+ # Raises a GDAL::RequiredBandNotFound if one of those band types isn't
75
+ # found.
76
+ #
77
+ # @param destination [String] Path to output the new dataset to.
78
+ # @param driver_name [String] The type of dataset to create.
79
+ # @param band_order [Array<String>] The list of band types, i.e. ['red',
80
+ # 'green', 'blue'].
81
+ # @param output_data_type [FFI::GDAL::DataType] Resulting dataset will be
82
+ # in this data type.
83
+ # @param remove_negatives [Boolean] Remove negative values after
84
+ # calculating NDVI.
85
+ # @param no_data_value [Float]
86
+ # @param options [Hash] Options that get used for creating the new NDVI
87
+ # dataset. See docs for GDAL::Driver#create_dataset.
88
+ # @return [GDAL::Dataset] The new NDVI dataset. *Be sure to call #close on
89
+ # this object or the data may not persist!*
90
+ def extract_gndvi(destination, driver_name: 'GTiff', band_order: nil,
91
+ output_data_type: :GDT_Byte, remove_negatives: false, no_data_value: -9999.0,
92
+ **options)
93
+ original_bands =
94
+ if band_order
95
+ bands_with_labels(band_order)
96
+ else
97
+ {
98
+ red: red_band,
99
+ green: green_band,
100
+ blue: blue_band,
101
+ nir: undefined_band
102
+ }
103
+ end
104
+
105
+ green = original_bands[:green]
106
+ nir = original_bands[:nir]
107
+
108
+ if green.nil?
109
+ fail RequiredBandNotFound, 'Green band not found.'
110
+ elsif nir.nil?
111
+ fail RequiredBandNotFound, 'Near-infrared'
112
+ end
113
+
114
+ the_array = calculate_ndvi(green.to_na, nir.to_na,
115
+ no_data_value, remove_negatives, output_data_type)
116
+ driver = GDAL::Driver.by_name(driver_name)
117
+
118
+ driver.create_dataset(destination, raster_x_size, raster_y_size,
119
+ data_type: output_data_type, **options) do |gndvi_dataset|
120
+ gndvi_dataset.geo_transform = geo_transform
121
+ gndvi_dataset.projection = projection
122
+
123
+ gndvi_band = gndvi_dataset.raster_band(1)
124
+ gndvi_band.write_array(the_array, data_type: output_data_type)
125
+ gndvi_band.no_data_value = no_data_value
126
+ end
127
+ end
128
+
129
+ # Extracts the NIR band and writes to a new file. NOTE: be sure to close
130
+ # the dataset object that gets returned or your data will not get written
131
+ # to the file.
132
+ #
133
+ # @param destination [String] The destination file path.
134
+ # @param band_number [Fixnum] The number of the band that is the NIR band.
135
+ # Remember that raster bands are 1-indexed, not 0-indexed.
136
+ # @param driver_name [String] the GDAL::Driver short name to use for the
137
+ # new dataset.
138
+ # @param output_data_type [FFI::GDAL::DataType] Resulting dataset will be
139
+ # in this data type.
140
+ # @param options [Hash] Options that get used for creating the new NDVI
141
+ # dataset. See docs for GDAL::Driver#create_dataset.
142
+ # @return [GDAL::Dataset] The new NIR dataset. *Be sure to call #close on
143
+ # this object or the data may not persist!*
144
+ def extract_nir(destination, band_number, driver_name: 'GTiff', output_data_type: :GDT_Byte, **options)
145
+ driver = GDAL::Driver.by_name(driver_name)
146
+ original_nir_band = raster_band(band_number)
147
+
148
+ if original_nir_band.nil?
149
+ fail InvalidBandNumber, "Band #{band_number} found but was nil."
150
+ end
151
+
152
+ driver.create_dataset(destination, raster_x_size, raster_y_size,
153
+ data_type: output_data_type, **options) do |nir_dataset|
154
+ nir_dataset.geo_transform = geo_transform
155
+ nir_dataset.projection = projection
156
+
157
+ nir_band = nir_dataset.raster_band(1)
158
+ original_nir_band.copy_whole_raster(nir_band)
159
+ end
160
+ end
161
+
162
+ # Extracts the RGB bands and writes to a new file. NOTE: be sure to close
163
+ # the dataset object that gets returned or your data will not get written
164
+ # to the file.
165
+ #
166
+ # @param destination [String] The destination file path.
167
+ # @param driver_name [String] the GDAL::Driver short name to use for the
168
+ # new dataset.
169
+ # @param band_order [Array<String>] The list of band types, i.e. ['red',
170
+ # 'green', 'blue'].
171
+ # @param options [Hash] Options that get used for creating the new NDVI
172
+ # dataset. See docs for GDAL::Driver#create_dataset.
173
+ # @return [GDAL::Dataset]
174
+ def extract_natural_color(destination, driver_name: 'GTiff',
175
+ band_order: nil, output_data_type: :GDT_Byte, **options)
176
+ rows = raster_y_size
177
+ columns = raster_x_size
178
+ driver = GDAL::Driver.by_name(driver_name)
179
+
180
+ original_bands = if band_order
181
+ bands_with_labels(band_order)
182
+ else
183
+ {
184
+ red: red_band,
185
+ green: green_band,
186
+ blue: blue_band
187
+ }
188
+ end
189
+
190
+ driver.create_dataset(destination, columns, rows,
191
+ band_count: 3, data_type: output_data_type, **options) do |new_dataset|
192
+ new_dataset.geo_transform = geo_transform
193
+ new_dataset.projection = projection
194
+
195
+ new_red_band = new_dataset.raster_band(1)
196
+ original_bands[:red].copy_whole_raster(new_red_band)
197
+
198
+ new_green_band = new_dataset.raster_band(2)
199
+ original_bands[:green].copy_whole_raster(new_green_band)
200
+
201
+ new_blue_band = new_dataset.raster_band(3)
202
+ original_bands[:blue].copy_whole_raster(new_blue_band)
203
+ end
204
+ end
205
+
206
+ # @param red_band_array [NArray]
207
+ # @param nir_band_array [NArray]
208
+ # @param remove_negatives [Fixnum] Value to replace negative values with.
209
+ # @return [NArray]
210
+ def calculate_ndvi(red_band_array, nir_band_array, no_data_value,
211
+ remove_negatives = false, output_data_type = nil)
212
+
213
+ # convert to float32 for calculating
214
+ nir_band_array = nir_band_array.to_type(NArray::DFLOAT)
215
+ red_band_array = red_band_array.to_type(NArray::DFLOAT)
216
+
217
+ numerator = nir_band_array - red_band_array
218
+ denominator = (nir_band_array + red_band_array)
219
+ ndvi = numerator / denominator
220
+
221
+ # Remove NaNs
222
+ 0.upto(ndvi.size - 1) do |i|
223
+ ndvi[i] = no_data_value if ndvi[i].is_a?(Float) && ndvi[i].nan?
224
+ end
225
+
226
+ # Convert to output data type
227
+ final_array = case output_data_type
228
+ when :GDT_Byte
229
+ calculate_ndvi_byte(ndvi)
230
+ when :GDT_UInt16
231
+ calculate_ndvi_uint16(ndvi)
232
+ else
233
+ ndvi
234
+ end
235
+
236
+ remove_negatives ? remove_negatives_from(final_array) : final_array
237
+ end
238
+
239
+ # Map raster bands to a label, as a hash. Useful for when bands don't match
240
+ # the color_interpretation that's returned from GDAL. This simply maps the
241
+ # list of labels you pass in to the raster bands.
242
+ #
243
+ # Valid labels:
244
+ # * Near-infrared: 'N', :nir
245
+ # * Red: 'R', :red
246
+ # * Green: 'G', :green
247
+ # * Blue: 'B', :blue
248
+ # * Alpha: 'A', :alpha
249
+ #
250
+ # @param order [Array<Object>]
251
+ # @return [Hash{<Object> => GDAL::RasterBand}]
252
+ def bands_with_labels(order)
253
+ order.each_with_object({}).each_with_index do |(band_label, obj), i|
254
+ label = case band_label.to_s
255
+ when 'N', 'nir' then :nir
256
+ when 'R', 'red' then :red
257
+ when 'G', 'green' then :green
258
+ when 'B', 'blue' then :blue
259
+ else
260
+ band_label
261
+ end
262
+
263
+ obj[label] = raster_band(i + 1)
264
+ end
265
+ end
266
+
267
+ # @return [Array<GDAL::RasterBand>]
268
+ def raster_bands
269
+ 1.upto(raster_count).map do |i|
270
+ raster_band(i)
271
+ end
272
+ end
273
+
274
+ # Iterates raster bands from 1 to #raster_count and yields them to the given
275
+ # block.
276
+ def each_band
277
+ 1.upto(raster_count) do |i|
278
+ yield(raster_band(i))
279
+ end
280
+ end
281
+
282
+ # Returns the first raster band for which the block returns true. Ex.
283
+ #
284
+ # dataset.find_band do |band|
285
+ # band.color_interpretation == :GCI_RedBand
286
+ # end
287
+ #
288
+ # @return [GDAL::RasterBand]
289
+ def find_band
290
+ each_band do |band|
291
+ result = yield(band)
292
+ return band if result
293
+ end
294
+ end
295
+
296
+ # @return [GDAL::RasterBand]
297
+ def red_band
298
+ band = find_band do |b|
299
+ b.color_interpretation == :GCI_RedBand
300
+ end
301
+
302
+ band.is_a?(GDAL::RasterBand) ? band : nil
303
+ end
304
+
305
+ # @return [GDAL::RasterBand]
306
+ def green_band
307
+ band = find_band do |b|
308
+ b.color_interpretation == :GCI_GreenBand
309
+ end
310
+
311
+ band.is_a?(GDAL::RasterBand) ? band : nil
312
+ end
313
+
314
+ # @return [GDAL::RasterBand]
315
+ def blue_band
316
+ band = find_band do |b|
317
+ b.color_interpretation == :GCI_BlueBand
318
+ end
319
+
320
+ band.is_a?(GDAL::RasterBand) ? band : nil
321
+ end
322
+
323
+ # @return [GDAL::RasterBand]
324
+ def undefined_band
325
+ band = find_band do |b|
326
+ b.color_interpretation == :GCI_Undefined
327
+ end
328
+
329
+ band.is_a?(GDAL::RasterBand) ? band : nil
330
+ end
331
+
332
+ # Creates a OGR::SpatialReference object from the dataset's projection.
333
+ #
334
+ # @return [OGR::SpatialReference]
335
+ def spatial_reference
336
+ return @spatial_reference if @spatial_reference
337
+
338
+ return nil if projection.empty?
339
+
340
+ @spatial_reference = OGR::SpatialReference.new(projection)
341
+ end
342
+
343
+ # Converts raster band number +band_number+ to the vector format
344
+ # +vector_driver_name+. Similar to gdal_polygonize.py. If block format is
345
+ # used, the new DataSource will be closed/flushed when the block returns. If
346
+ # the non-block format is used, you need to call #close on the DataSource.
347
+ #
348
+ # @param file_name [String] Path to write the vector file to.
349
+ # @param vector_driver_name [String] One of OGR::Driver.names.
350
+ # @param geometry_type [FFI::GDAL::OGRwkbGeometryType] The type of geometry
351
+ # to use when turning the raster into a vector image.
352
+ # @param layer_name_prefix [String] Prefix of the name to give the new
353
+ # vector layer.
354
+ # @param band_numbers [Array<Fixnum>,Fixnum] Number of the raster band or
355
+ # bands from this dataset to vectorize. Can be a single Fixnum or array
356
+ # of Fixnums.
357
+ # @return [OGR::DataSource]
358
+ def to_vector(file_name, vector_driver_name, geometry_type: :wkbUnknown,
359
+ layer_name_prefix: 'band_number', band_numbers: [1],
360
+ field_name_prefix: 'field', use_band_masks: true)
361
+ band_numbers = band_numbers.is_a?(Array) ? band_numbers : [band_numbers]
362
+ ogr_driver = OGR::Driver.by_name(vector_driver_name)
363
+
364
+ if projection.empty?
365
+ spatial_ref = nil
366
+ else
367
+ spatial_ref = OGR::SpatialReference.new(projection)
368
+ spatial_ref.auto_identify_epsg! rescue OGR::UnsupportedSRS
369
+ end
370
+
371
+ data_source = ogr_driver.create_data_source(file_name)
372
+
373
+ band_numbers.each_with_index do |band_number, i|
374
+ log "Starting to polygonize raster band #{band_number}..."
375
+ layer_name = "#{layer_name_prefix}-#{band_number}"
376
+ layer = data_source.create_layer(layer_name, geometry_type: geometry_type,
377
+ spatial_reference: spatial_ref)
378
+
379
+ field_name = "#{field_name_prefix}#{i}"
380
+ layer.create_field(OGR::FieldDefinition.new(field_name, :OFTInteger))
381
+ band = raster_band(band_number)
382
+
383
+ unless band
384
+ fail GDAL::InvalidBandNumber, "Unknown band number: #{band_number}"
385
+ end
386
+
387
+ pixel_value_field = layer.feature_definition.field_index(field_name)
388
+ options = { pixel_value_field: pixel_value_field }
389
+ options.merge!(mask_band: band.mask_band) if use_band_masks
390
+ band.polygonize(layer, options)
391
+ end
392
+
393
+ if block_given?
394
+ yield data_source
395
+ data_source.close
396
+ end
397
+
398
+ data_source
399
+ end
400
+
401
+ # Gets the OGR::Geometry that represents the extent of the dataset.
402
+ #
403
+ # @return [OGR::Polygon]
404
+ def extent
405
+ raster_data_source = to_vector('memory data source', 'Memory', geometry_type: :wkbLinearRing)
406
+
407
+ raster_data_source.layer(0).geometry_from_extent
408
+ end
409
+
410
+ # @param wkt_geometry_string [String]
411
+ # @param wkt_srid [Fixnum]
412
+ # @return [Boolean]
413
+ def contains_geometry?(wkt_geometry_string, wkt_srid = 4326)
414
+ source_srs = OGR::SpatialReference.new_from_epsg(wkt_srid)
415
+ source_geometry = OGR::Geometry.create_from_wkt(wkt_geometry_string, source_srs)
416
+ @raster_geometry ||= extent
417
+
418
+ coordinate_transformation = OGR::CoordinateTransformation.new(source_srs,
419
+ @raster_geometry.spatial_reference)
420
+ source_geometry.transform!(coordinate_transformation)
421
+
422
+ @raster_geometry.contains? source_geometry
423
+ end
424
+
425
+ def image_warp(destination_file, driver, band_numbers, **warp_options)
426
+ fail NotImplementedError, '#image_warp not yet implemented.'
427
+
428
+ _options_ptr = GDAL::Options.pointer(warp_options)
429
+ driver = GDAL::Driver.by_name(driver)
430
+ destination_dataset = driver.create_dataset(destination_file, raster_x_size, raster_y_size)
431
+
432
+ band_numbers = band_numbers.is_a?(Array) ? band_numbers : [band_numbers]
433
+ log "band numbers: #{band_numbers}"
434
+
435
+ bands_ptr = FFI::MemoryPointer.new(:pointer, band_numbers.size)
436
+ bands_ptr.write_array_of_int(band_numbers)
437
+ log "band numbers ptr null? #{bands_ptr.null?}"
438
+
439
+ warp_options_struct = FFI::GDAL::WarpOptions.new
440
+
441
+ warp_options.each do |k, _|
442
+ warp_options_struct[k] = warp_options[k]
443
+ end
444
+
445
+ warp_options[:source_dataset] = c_pointer
446
+ warp_options[:destination_dataset] = destination_dataset.c_pointer
447
+ warp_options[:band_count] = band_numbers.size
448
+ warp_options[:source_bands] = bands_ptr
449
+ warp_options[:transformer] = transformer
450
+ warp_options[:transformer_arg] = transformer_arg
451
+
452
+ log "transformer: #{transformer}"
453
+ error_threshold = 0.0
454
+ order = 0
455
+
456
+ _transformer_ptr = FFI::GDAL::Alg.GDALCreateGenImgProjTransformer(@c_pointer,
457
+ projection,
458
+ destination_dataset.c_pointer,
459
+ destination.projection,
460
+ false,
461
+ error_threshold,
462
+ order)
463
+
464
+ warp_operation = GDAL::WarpOperation.new(warp_options)
465
+ warp_operation.chunk_and_warp_image(0, 0, raster_x_size, raster_y_size)
466
+ transformer.destroy!
467
+ warp_operation.destroy!
468
+
469
+ destination = GDAL::Dataset.new(destination_dataset_ptr)
470
+ destination.close
471
+
472
+ destination
473
+ end
474
+
475
+ # Retrieves pixels from each raster band and converts this to an array of
476
+ # points per pixel. For example:
477
+ #
478
+ # # If the arrays for each band look like:
479
+ # red_band_array = [0, 0, 0]
480
+ # green_band_array = [10, 10, 10]
481
+ # blue_band_array = [99, 99, 99]
482
+ # alpha_band_array = [250, 150, 2]
483
+ #
484
+ # # This array would look like:
485
+ # [[0, 10, 99, 2], [0, 10, 99, 150], [0, 10, 99, 250]]
486
+ # @return NArray
487
+ def to_na(to_data_type = nil)
488
+ na = NMatrix.to_na(raster_bands.map { |r| r.to_na(to_data_type) })
489
+
490
+ NArray[*na.transpose]
491
+ end
492
+
493
+ def as_json(options = nil)
494
+ {
495
+ dataset: {
496
+ driver: driver.long_name,
497
+ file_list: file_list,
498
+ gcp_count: gcp_count,
499
+ gcp_projection: gcp_projection,
500
+ geo_transform: geo_transform.as_json(options),
501
+ projection: projection,
502
+ raster_count: raster_count,
503
+ raster_bands: raster_bands.map(&:as_json),
504
+ spatial_reference: spatial_reference.as_json(options)
505
+ },
506
+ metadata: all_metadata
507
+ }
508
+ end
509
+
510
+ # @return [String]
511
+ def to_json(options = nil)
512
+ as_json(options).to_json
513
+ end
514
+
515
+ private
516
+
517
+ # @param ndvi [NArray]
518
+ # @return [NArray]
519
+ def calculate_ndvi_byte(ndvi)
520
+ ((ndvi + 1) * (255.0 / 2)).to_type(NArray::BYTE)
521
+ end
522
+
523
+ # @param ndvi [NArray]
524
+ # @return [NArray]
525
+ def calculate_ndvi_uint16(ndvi)
526
+ ((ndvi + 1) * (65_535.0 / 2)).to_type(NArray::INT)
527
+ end
528
+
529
+ # Sets any negative values in the NArray to 0.
530
+ #
531
+ # @param narray [NArray]
532
+ # @return [NArray]
533
+ def remove_negatives_from(narray, replace_with = 0)
534
+ 0.upto(narray.size - 1) do |i|
535
+ narray[i] = replace_with if narray[i] < 0
536
+ end
537
+
538
+ narray
539
+ end
540
+ end
541
+ end
542
+ end
@@ -0,0 +1,26 @@
1
+ require_relative '../../ffi/gdal/matching'
2
+
3
+ module GDAL
4
+ module DatasetMixins
5
+ module Matching
6
+ # @param other_dataset [GDAL::Dataset]
7
+ # @param options [Hash]
8
+ # @return [Hash{count => Fixnum, gcp: => FFI::GDAL::GCP}] Not sure why,
9
+ # but the C function seems to return a single GCP instead of an Array of
10
+ # them.
11
+ def compute_matching_points(other_dataset, **options)
12
+ other_dataset_ptr = GDAL._pointer(GDAL::Dataset, other_dataset)
13
+ options_ptr = GDAL::Options.pointer(options)
14
+ gcp_count_ptr = FFI::MemoryPointer.new(:int)
15
+
16
+ gcp = FFI::GDAL::Matching.GDALComputeMatchingPoints(
17
+ @c_pointer,
18
+ other_dataset_ptr,
19
+ options_ptr,
20
+ gcp_count_ptr)
21
+
22
+ { count: gcp_count_ptr.read_int, gcp: gcp }
23
+ end
24
+ end
25
+ end
26
+ end