ffi-gdal 1.0.0.beta5 → 1.0.0.beta6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (237) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -3
  3. data/.rubocop.yml +7 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +1 -1
  6. data/History.md +143 -1
  7. data/README.md +5 -11
  8. data/Rakefile +2 -60
  9. data/TODO.md +10 -0
  10. data/examples/geometries.rb +4 -6
  11. data/examples/gridding.rb +99 -98
  12. data/examples/ogr_layer_to_layer.rb +0 -2
  13. data/examples/raster_erasing.rb +47 -0
  14. data/examples/remove_small_polygons.rb +62 -0
  15. data/examples/testing_gdal.rb +0 -3
  16. data/examples/warping.rb +140 -0
  17. data/ffi-gdal.gemspec +5 -2
  18. data/lib/ext/error_symbols.rb +1 -1
  19. data/lib/ext/ffi_library_function_checks.rb +3 -2
  20. data/lib/ext/float_ext.rb +2 -2
  21. data/lib/ext/narray_ext.rb +1 -1
  22. data/lib/ext/numeric_as_data_type.rb +1 -1
  23. data/lib/ext/to_bool.rb +2 -2
  24. data/lib/ffi/cpl/conv.rb +1 -3
  25. data/lib/ffi/cpl/error.rb +0 -3
  26. data/lib/ffi/cpl/minixml.rb +17 -21
  27. data/lib/ffi/cpl/progress.rb +27 -0
  28. data/lib/ffi/cpl/string.rb +0 -8
  29. data/lib/ffi/cpl/vsi.rb +0 -1
  30. data/lib/ffi/cpl/xml_node.rb +0 -1
  31. data/lib/ffi/cpl.rb +15 -0
  32. data/lib/ffi/gdal/alg.rb +72 -54
  33. data/lib/ffi/gdal/gdal.rb +669 -672
  34. data/lib/ffi/gdal/grid.rb +141 -24
  35. data/lib/ffi/gdal/grid_data_metrics_options.rb +1 -1
  36. data/lib/ffi/gdal/grid_moving_average_options.rb +1 -1
  37. data/lib/ffi/gdal/matching.rb +0 -2
  38. data/lib/ffi/gdal/transformer_info.rb +1 -1
  39. data/lib/ffi/gdal/version.rb +1 -1
  40. data/lib/ffi/gdal/vrt.rb +0 -2
  41. data/lib/ffi/gdal/warp_options.rb +12 -14
  42. data/lib/ffi/gdal/warper.rb +61 -6
  43. data/lib/ffi/gdal.rb +18 -3
  44. data/lib/ffi/ogr/api.rb +10 -21
  45. data/lib/ffi/ogr/core.rb +9 -12
  46. data/lib/ffi/ogr/featurestyle.rb +0 -5
  47. data/lib/ffi/ogr/geocoding.rb +0 -1
  48. data/lib/ffi/ogr/srs_api.rb +0 -4
  49. data/lib/ffi/ogr/style_value.rb +1 -2
  50. data/lib/ffi/ogr.rb +15 -12
  51. data/lib/ffi-gdal.rb +5 -3
  52. data/lib/gdal/color_entry.rb +1 -0
  53. data/lib/gdal/color_interpretation.rb +2 -2
  54. data/lib/gdal/color_table.rb +14 -14
  55. data/lib/gdal/color_table_mixins/extensions.rb +4 -4
  56. data/lib/gdal/cpl_error_handler.rb +12 -14
  57. data/lib/gdal/data_type.rb +13 -12
  58. data/lib/gdal/dataset.rb +170 -94
  59. data/lib/gdal/dataset_mixins/algorithm_methods.rb +47 -21
  60. data/lib/gdal/dataset_mixins/extensions.rb +32 -61
  61. data/lib/gdal/dataset_mixins/matching.rb +0 -2
  62. data/lib/gdal/dataset_mixins/warp_methods.rb +42 -0
  63. data/lib/gdal/driver.rb +62 -47
  64. data/lib/gdal/driver_mixins/extensions.rb +2 -7
  65. data/lib/gdal/environment_methods.rb +13 -10
  66. data/lib/gdal/exceptions.rb +24 -2
  67. data/lib/gdal/geo_transform.rb +10 -16
  68. data/lib/gdal/geo_transform_mixins/extensions.rb +58 -3
  69. data/lib/gdal/grid.rb +62 -109
  70. data/lib/gdal/{grid_types → grid_algorithms}/data_metrics_base.rb +1 -3
  71. data/lib/gdal/{grid_types → grid_algorithms}/inverse_distance_to_a_power.rb +2 -4
  72. data/lib/gdal/{grid_types → grid_algorithms}/metric_average_distance.rb +2 -2
  73. data/lib/gdal/{grid_types → grid_algorithms}/metric_average_distance_pts.rb +2 -2
  74. data/lib/gdal/{grid_types → grid_algorithms}/metric_count.rb +2 -2
  75. data/lib/gdal/{grid_types → grid_algorithms}/metric_maximum.rb +2 -2
  76. data/lib/gdal/{grid_types → grid_algorithms}/metric_minimum.rb +2 -2
  77. data/lib/gdal/{grid_types → grid_algorithms}/metric_range.rb +2 -2
  78. data/lib/gdal/{grid_types → grid_algorithms}/moving_average.rb +2 -4
  79. data/lib/gdal/{grid_types → grid_algorithms}/nearest_neighbor.rb +2 -4
  80. data/lib/gdal/grid_algorithms.rb +22 -0
  81. data/lib/gdal/gridder/point_extracting.rb +89 -0
  82. data/lib/gdal/gridder.rb +294 -0
  83. data/lib/gdal/gridder_options.rb +273 -0
  84. data/lib/gdal/internal_helpers.rb +132 -23
  85. data/lib/gdal/major_object.rb +13 -10
  86. data/lib/gdal/merger.rb +130 -0
  87. data/lib/gdal/options.rb +3 -2
  88. data/lib/gdal/raster_attribute_table.rb +74 -51
  89. data/lib/gdal/raster_attribute_table_mixins/extensions.rb +21 -3
  90. data/lib/gdal/raster_band.rb +139 -167
  91. data/lib/gdal/raster_band_classifier.rb +19 -18
  92. data/lib/gdal/raster_band_mixins/algorithm_extensions.rb +107 -0
  93. data/lib/gdal/raster_band_mixins/algorithm_methods.rb +79 -40
  94. data/lib/gdal/raster_band_mixins/coloring_extensions.rb +84 -0
  95. data/lib/gdal/raster_band_mixins/extensions.rb +34 -169
  96. data/lib/gdal/raster_band_mixins/io_extensions.rb +180 -0
  97. data/lib/gdal/rpc_info.rb +1 -2
  98. data/lib/gdal/transformer.rb +1 -6
  99. data/lib/gdal/transformers/approximate_transformer.rb +0 -4
  100. data/lib/gdal/transformers/base_general_image_projection_transformer.rb +0 -6
  101. data/lib/gdal/transformers/gcp_transformer.rb +2 -6
  102. data/lib/gdal/transformers/general_image_projection_transformer.rb +8 -7
  103. data/lib/gdal/transformers/general_image_projection_transformer2.rb +1 -1
  104. data/lib/gdal/transformers/geolocation_transformer.rb +0 -4
  105. data/lib/gdal/transformers/reprojection_transformer.rb +0 -8
  106. data/lib/gdal/transformers/rpc_transformer.rb +0 -4
  107. data/lib/gdal/transformers/tps_transformer.rb +1 -3
  108. data/lib/gdal/version_info.rb +7 -8
  109. data/lib/gdal/virtual_dataset.rb +2 -4
  110. data/lib/gdal/warp_operation.rb +17 -14
  111. data/lib/gdal/warp_options.rb +132 -0
  112. data/lib/gdal.rb +41 -2
  113. data/lib/ogr/coordinate_transformation.rb +79 -32
  114. data/lib/ogr/data_source.rb +17 -14
  115. data/lib/ogr/data_source_extensions.rb +1 -5
  116. data/lib/ogr/driver.rb +11 -14
  117. data/lib/ogr/envelope.rb +1 -1
  118. data/lib/ogr/envelope_extensions.rb +23 -6
  119. data/lib/ogr/error_handling.rb +3 -3
  120. data/lib/ogr/exceptions.rb +6 -0
  121. data/lib/ogr/feature.rb +25 -38
  122. data/lib/ogr/feature_definition.rb +6 -8
  123. data/lib/ogr/feature_definition_extensions.rb +2 -6
  124. data/lib/ogr/feature_extensions.rb +71 -41
  125. data/lib/ogr/field.rb +16 -15
  126. data/lib/ogr/field_definition.rb +4 -4
  127. data/lib/ogr/geocoder.rb +5 -5
  128. data/lib/ogr/geometries/geometry_collection.rb +4 -1
  129. data/lib/ogr/geometries/geometry_collection_25d.rb +12 -0
  130. data/lib/ogr/geometries/line_string.rb +30 -8
  131. data/lib/ogr/geometries/line_string_25d.rb +21 -0
  132. data/lib/ogr/geometries/linear_ring.rb +10 -1
  133. data/lib/ogr/geometries/multi_line_string.rb +2 -1
  134. data/lib/ogr/geometries/multi_line_string_25d.rb +13 -0
  135. data/lib/ogr/geometries/multi_point.rb +2 -1
  136. data/lib/ogr/geometries/multi_point_25d.rb +14 -0
  137. data/lib/ogr/geometries/multi_polygon.rb +3 -2
  138. data/lib/ogr/geometries/multi_polygon_25d.rb +13 -0
  139. data/lib/ogr/geometries/point.rb +20 -23
  140. data/lib/ogr/geometries/point_25d.rb +48 -0
  141. data/lib/ogr/geometries/polygon.rb +4 -1
  142. data/lib/ogr/geometries/polygon_25d.rb +14 -0
  143. data/lib/ogr/geometry.rb +125 -93
  144. data/lib/ogr/geometry_field_definition.rb +7 -5
  145. data/lib/ogr/geometry_mixins/container_mixins.rb +23 -0
  146. data/lib/ogr/geometry_mixins/extensions.rb +111 -0
  147. data/lib/ogr/geometry_types/container.rb +10 -3
  148. data/lib/ogr/geometry_types/curve.rb +68 -23
  149. data/lib/ogr/geometry_types/surface.rb +0 -9
  150. data/lib/ogr/internal_helpers.rb +3 -3
  151. data/lib/ogr/layer.rb +4 -5
  152. data/lib/ogr/layer_mixins/extensions.rb +242 -17
  153. data/lib/ogr/layer_mixins/ogr_feature_methods.rb +11 -11
  154. data/lib/ogr/layer_mixins/ogr_field_methods.rb +6 -11
  155. data/lib/ogr/layer_mixins/ogr_layer_method_methods.rb +18 -18
  156. data/lib/ogr/layer_mixins/ogr_query_filter_methods.rb +0 -2
  157. data/lib/ogr/layer_mixins/ogr_sql_methods.rb +1 -1
  158. data/lib/ogr/spatial_reference.rb +12 -37
  159. data/lib/ogr/spatial_reference_mixins/coordinate_system_getter_setters.rb +53 -55
  160. data/lib/ogr/spatial_reference_mixins/exporters.rb +18 -49
  161. data/lib/ogr/spatial_reference_mixins/parameter_getter_setters.rb +10 -29
  162. data/lib/ogr/style_table.rb +2 -2
  163. data/lib/ogr/style_table_extensions.rb +3 -1
  164. data/lib/ogr/style_tool.rb +8 -14
  165. data/lib/ogr.rb +39 -1
  166. data/spec/ffi-gdal_spec.rb +18 -1
  167. data/spec/integration/gdal/color_table_info_spec.rb +49 -33
  168. data/spec/integration/gdal/dataset_info_spec.rb +294 -45
  169. data/spec/integration/gdal/driver_info_spec.rb +139 -31
  170. data/spec/integration/gdal/geo_transform_info_spec.rb +197 -26
  171. data/spec/integration/gdal/gridder_spec.rb +329 -0
  172. data/spec/integration/gdal/raster_attribute_table_info_spec.rb +216 -11
  173. data/spec/integration/gdal/raster_band_algorithms_spec.rb +33 -0
  174. data/spec/integration/gdal/raster_band_info_spec.rb +240 -271
  175. data/spec/integration/ogr/layer_spec.rb +3 -1
  176. data/spec/spec_helper.rb +15 -6
  177. data/spec/support/images/osgeo/gdal/data/hfa/float-rle.img +0 -0
  178. data/spec/support/images/osgeo/geotiff/GeogToWGS84GeoKey/GeogToWGS84GeoKey5.lgo +31 -0
  179. data/spec/support/images/osgeo/geotiff/GeogToWGS84GeoKey/GeogToWGS84GeoKey5.tif +0 -0
  180. data/spec/support/images/osgeo/geotiff/GeogToWGS84GeoKey/GeogToWGS84GeoKey5.tif.msk +0 -0
  181. data/spec/support/images/osgeo/geotiff/GeogToWGS84GeoKey/GeogToWGS84GeoKey5.txt +10 -0
  182. data/spec/support/images/osgeo/geotiff/gdal_eg/cea.tif +0 -0
  183. data/spec/support/images/osgeo/geotiff/gdal_eg/cea.txt +84 -0
  184. data/spec/support/images/osgeo/geotiff/zi_imaging/image0.lgo +45 -0
  185. data/spec/support/images/osgeo/geotiff/zi_imaging/image0.tif +0 -0
  186. data/spec/support/integration_help.rb +32 -2
  187. data/spec/support/shared_examples/gdal/major_object_examples.rb +0 -6
  188. data/spec/support/shared_examples/ogr/a_geometry.rb +1 -1
  189. data/spec/unit/ffi/gdal_spec.rb +1 -1
  190. data/spec/unit/gdal/color_entry_spec.rb +1 -0
  191. data/spec/unit/gdal/color_interpretation_spec.rb +1 -0
  192. data/spec/unit/gdal/dataset_spec.rb +53 -2
  193. data/spec/unit/gdal/geo_transform_mixins/extensions_spec.rb +67 -0
  194. data/spec/unit/gdal/geo_transform_spec.rb +1 -1
  195. data/spec/unit/gdal/grid_spec.rb +83 -0
  196. data/spec/unit/gdal/gridder/point_extracting_spec.rb +99 -0
  197. data/spec/unit/gdal/gridder_options_spec.rb +183 -0
  198. data/spec/unit/gdal/gridder_spec.rb +140 -0
  199. data/spec/unit/gdal/internal_helpers_spec.rb +166 -2
  200. data/spec/unit/gdal/major_object_spec.rb +2 -0
  201. data/spec/unit/gdal/options_spec.rb +1 -0
  202. data/spec/unit/gdal/raster_band_classifier_spec.rb +70 -12
  203. data/spec/unit/gdal/raster_band_mixins/extensions_spec.rb +71 -0
  204. data/spec/unit/gdal/raster_band_mixins/io_extensions_spec.rb +133 -0
  205. data/spec/unit/gdal/raster_band_spec.rb +1 -0
  206. data/spec/unit/gdal/rpc_info_spec.rb +1 -0
  207. data/spec/unit/gdal/version_info_spec.rb +2 -0
  208. data/spec/unit/gdal/warp_operation_spec.rb +1 -0
  209. data/spec/unit/ogr/coordinate_transformation_spec.rb +102 -0
  210. data/spec/unit/ogr/data_source_spec.rb +12 -0
  211. data/spec/unit/ogr/feature_extensions_spec.rb +88 -0
  212. data/spec/unit/ogr/feature_spec.rb +30 -46
  213. data/spec/unit/ogr/geometries/geometry_collection_25d_spec.rb +23 -0
  214. data/spec/unit/ogr/geometries/geometry_collection_spec.rb +3 -3
  215. data/spec/unit/ogr/geometries/line_string_25d_spec.rb +23 -0
  216. data/spec/unit/ogr/geometries/line_string_spec.rb +2 -2
  217. data/spec/unit/ogr/geometries/linear_ring_spec.rb +2 -2
  218. data/spec/unit/ogr/geometries/multi_line_string_25d_spec.rb +23 -0
  219. data/spec/unit/ogr/geometries/multi_point_25d_spec.rb +23 -0
  220. data/spec/unit/ogr/geometries/multi_polygon_25d_spec.rb +23 -0
  221. data/spec/unit/ogr/geometries/point_25d_spec.rb +23 -0
  222. data/spec/unit/ogr/geometries/point_spec.rb +14 -24
  223. data/spec/unit/ogr/geometries/polygon_25d_spec.rb +23 -0
  224. data/spec/unit/ogr/geometries/polygon_spec.rb +1 -1
  225. data/spec/unit/ogr/geometry_field_definition_spec.rb +1 -1
  226. data/spec/unit/ogr/geometry_spec.rb +196 -30
  227. data/spec/unit/ogr/internal_helpers_spec.rb +20 -9
  228. data/spec/unit/ogr/layer_mixins/ogr_feature_methods_spec.rb +14 -6
  229. data/spec/unit/ogr/spatial_reference_mixins/exporters_spec.rb +9 -1
  230. data/spec/unit/ogr/spatial_reference_mixins/parameter_getter_setters_spec.rb +2 -1
  231. data/spec/unit/ogr/style_table_spec.rb +1 -1
  232. data/tmp/.keep +0 -0
  233. metadata +121 -19
  234. data/examples/points.txt +0 -127
  235. data/lib/gdal/grid_types.rb +0 -22
  236. data/lib/ogr/geometries/point_extensions.rb +0 -32
  237. data/lib/ogr/geometry_extensions.rb +0 -59
@@ -0,0 +1,294 @@
1
+ require 'narray'
2
+ require_relative '../gdal'
3
+ require_relative 'gridder_options'
4
+ require_relative 'gridder/point_extracting'
5
+ require_relative 'options'
6
+ require_relative '../ogr'
7
+
8
+ module GDAL
9
+ # Somewhat analogous to the gdal_grid utility.
10
+ class Gridder
11
+ include PointExtracting
12
+ include GDAL::Logger
13
+
14
+ DESIRED_BUFFER_SIZE = 16 * 1024 * 1024
15
+
16
+ # Object used by for doing the actual grid work.
17
+ #
18
+ # @!attribute [r] grid
19
+ # @return [GDAL::Grid]
20
+ attr_reader :grid
21
+
22
+ # @param source_layer [OGR::Layer] The layer from which to use points and
23
+ # spatial reference for interpolating. Alternatively, use +points+ to give
24
+ # specific point values for gridding.
25
+ # @param dest_file_name [String] The path to output the gridded raster to.
26
+ # @param gridder_options [GDAL::GridderOptions]
27
+ # @param points [Array<Array<Float>>] A 2D array of (x, y, z) points to use
28
+ # for gridding. Used for when you don't want to use all points from
29
+ # +source_layer+.
30
+ def initialize(source_layer, dest_file_name, gridder_options, points: nil)
31
+ @source_layer = source_layer
32
+ @dest_file_name = dest_file_name
33
+ @options = gridder_options
34
+
35
+ @points = points
36
+ @x_min = nil
37
+ @x_max = nil
38
+ @y_min = nil
39
+ @y_max = nil
40
+ end
41
+
42
+ # Does all of the things: gathers points from the associated Layer,
43
+ # processes the points according to associated GridderOptions, grids the
44
+ # points, and writes out the newly gridder raster.
45
+ def grid!
46
+ dataset = build_dataset(@options.output_driver,
47
+ @dest_file_name,
48
+ @options.output_data_type,
49
+ @options.output_creation_options)
50
+
51
+ grid_and_write(dataset.raster_band(1), dataset.geo_transform)
52
+
53
+ if @options.algorithm_options[:no_data_value]
54
+ dataset.raster_band(1).no_data_value = @options.algorithm_options[:no_data_value]
55
+ end
56
+
57
+ dataset.close
58
+ end
59
+
60
+ #--------------------------------------------------------------------------
61
+ # PRIVATES
62
+ #--------------------------------------------------------------------------
63
+
64
+ private
65
+
66
+ # Builds the Dataset to use for writing gridded raster data to.
67
+ #
68
+ # @param driver [GDAL::Driver]
69
+ # @param dest_file_name [String]
70
+ # @param data_type [FFI::GDAL::GDAL::DataType]
71
+ # @param creation_options [Hash]
72
+ # @return [GDAL::Dataset]
73
+ def build_dataset(driver, dest_file_name, data_type, creation_options = {})
74
+ dataset = driver.create_dataset(dest_file_name,
75
+ output_width,
76
+ output_height,
77
+ data_type: data_type,
78
+ **creation_options)
79
+
80
+ dataset.projection = build_output_spatial_reference
81
+ dataset.geo_transform = build_output_geo_transform
82
+
83
+ dataset
84
+ end
85
+
86
+ # Tries to get WKT of a spatial reference to use for the output dataset.
87
+ # First it tries to use {GDAL::GridderOptions#output_projection}, then tries
88
+ # the associated source layers {OGR::Layer#spatial_reference}.
89
+ #
90
+ # @return [String, nil] WKT of the spatial reference to use; +nil+ if none
91
+ # is found to use.
92
+ def build_output_spatial_reference
93
+ spatial_ref = @options.output_projection || @source_layer.spatial_reference
94
+
95
+ unless spatial_ref
96
+ log 'No spatial reference specified'
97
+ return
98
+ end
99
+
100
+ spatial_ref.to_wkt
101
+ end
102
+
103
+ # If @options.output_y_extent and/or @options.output_x_extent are set, it
104
+ # uses those and @options.output_size to build a {GDAL::GeoTransform} to be
105
+ # used with the output {GDAL::Dataset}.
106
+ #
107
+ # @return [GDAL::GeoTransform]
108
+ def build_output_geo_transform
109
+ envelope = OGR::Envelope.new
110
+
111
+ envelope.x_min = x_min
112
+ envelope.x_max = x_max
113
+ envelope.y_min = y_min
114
+ envelope.y_max = y_max
115
+
116
+ GDAL::GeoTransform.new_from_envelope(envelope, output_width, output_height)
117
+ end
118
+
119
+ # The x_min value to be used for the gridder and output raster.
120
+ #
121
+ # @return [Float]
122
+ def x_min
123
+ @x_min ||= @options.output_x_extent.fetch(:min) { @source_layer.extent.x_min }
124
+ end
125
+
126
+ # The x_max value to be used for the gridder and output raster.
127
+ #
128
+ # @return [Float]
129
+ def x_max
130
+ @x_max ||= @options.output_x_extent.fetch(:max) { @source_layer.extent.x_max }
131
+ end
132
+
133
+ # The y_min value to be used for the gridder and output raster.
134
+ #
135
+ # @return [Float]
136
+ def y_min
137
+ @y_min ||= @options.output_y_extent.fetch(:min) { @source_layer.extent.y_min }
138
+ end
139
+
140
+ # The y_max value to be used for the gridder and output raster.
141
+ #
142
+ # @return [Float]
143
+ def y_max
144
+ @y_max ||= @options.output_y_extent.fetch(:max) { @source_layer.extent.y_max }
145
+ end
146
+
147
+ # @return [Fixnum]
148
+ def output_width
149
+ @options.output_size[:width]
150
+ end
151
+
152
+ # @return [Fixnum]
153
+ def output_height
154
+ @options.output_size[:height]
155
+ end
156
+
157
+ # Figures out the proper block sizes to use for iterating over layer pixels,
158
+ # gridding them, and writing them to the raster file.
159
+ #
160
+ # @param raster_band_block_size [Fixnum]
161
+ def each_block(raster_band_block_size)
162
+ data_type_size = @options.output_data_type_size
163
+ block_size = build_block_sizes(raster_band_block_size, data_type_size)
164
+ log "Work buffer: #{block_size[:x]} * #{block_size[:y]}"
165
+
166
+ block_number = 0
167
+ block_count = build_block_count(block_size[:x], block_size[:y], output_width, output_height)
168
+ log "Block count: #{block_count}"
169
+
170
+ 0.step(output_height - 1, block_size[:y]).each do |y_offset|
171
+ 0.step(output_width - 1, block_size[:x]).each do |x_offset|
172
+ yield block_number, block_count, block_size, x_offset, y_offset
173
+ block_number += 1
174
+ end
175
+ end
176
+ end
177
+
178
+ # Iterates through each block of data, grids it, then writes it to the
179
+ # output raster.
180
+ #
181
+ # @param raster_band [GDAL::RasterBand]
182
+ # @param geo_transform [GDAL::GeoTransform]
183
+ def grid_and_write(raster_band, geo_transform)
184
+ data_ptr = GDAL._pointer_from_data_type(@options.output_data_type, output_width * output_height)
185
+ each_block(raster_band.block_size) do |block_number, block_count, block_size, x_offset, y_offset|
186
+ scaled_progress_ptr = nil
187
+ progress_arg = nil
188
+
189
+ if @options.progress_formatter
190
+ scaled_progress_ptr = build_scaled_progress_pointer(block_number, block_count)
191
+ progress_arg = FFI::CPL::Progress::ScaledProgress
192
+ end
193
+
194
+ x_request = build_data_request_size(block_size[:x], x_offset, output_width)
195
+ y_request = build_data_request_size(block_size[:y], y_offset, output_height)
196
+ output_size = { x: x_request, y: y_request }
197
+
198
+ grid_x_min, grid_x_max = build_grid_extents(x_min, geo_transform.pixel_width, x_offset, x_request)
199
+ grid_y_min, grid_y_max = build_grid_extents(y_min, geo_transform.pixel_height, y_offset, y_request)
200
+ extents = { x_min: grid_x_min, x_max: grid_x_max, y_min: grid_y_min, y_max: grid_y_max }
201
+
202
+ @options.grid.create(points, extents, data_ptr, output_size,
203
+ progress_arg, scaled_progress_ptr)
204
+
205
+ raster_band.raster_io('w', data_ptr, x_offset: x_offset, y_offset: y_offset,
206
+ x_size: x_request, y_size: y_request,
207
+ buffer_x_size: x_request, buffer_y_size: y_request)
208
+ end
209
+ end
210
+
211
+ # @param raster_band_block_size [Hash{x: Fixnum, y: Fixnum}]
212
+ # @param data_type_size [Fixnum]
213
+ # @return [Hash{x: Fixnum, y: Fixnum}]
214
+ def build_block_sizes(raster_band_block_size, data_type_size)
215
+ block_x_size = raster_band_block_size[:x]
216
+ block_y_size = raster_band_block_size[:y]
217
+
218
+ if block_x_size.to_i < output_width && block_y_size.to_i < output_height &&
219
+ block_x_size.to_i < DESIRED_BUFFER_SIZE / (block_y_size * data_type_size)
220
+ new_block_x_size = DESIRED_BUFFER_SIZE / (block_y_size * data_type_size)
221
+ block_x_size = (new_block_x_size / block_x_size) * block_x_size
222
+
223
+ block_x_size = output_width if block_x_size.to_i > output_width
224
+ elsif block_x_size.to_i == output_width && block_y_size.to_i < output_height &&
225
+ block_y_size.to_i < DESIRED_BUFFER_SIZE / (output_width * data_type_size)
226
+ new_block_y_size = DESIRED_BUFFER_SIZE / (output_width * data_type_size)
227
+ block_y_size = (new_block_y_size / block_y_size) * block_y_size
228
+
229
+ block_y_size = output_height if block_y_size.to_i > output_height
230
+ end
231
+
232
+ { x: block_x_size.freeze, y: block_y_size.freeze }
233
+ end
234
+
235
+ # Builds a pointer to a GDALScaledProgress function. This is used in
236
+ # conjunction with the @options.progress_function to be able to display
237
+ # progress across grid+rasterize iterations. Without this, the user would
238
+ # only get progress for each time through a block, not for all of the
239
+ # blocks.
240
+ #
241
+ # @see http://gdal.sourcearchive.com/documentation/1.7.2/gdal_8h_904fbbb050e16c9d0ac028dc5113ef27.html
242
+ # @param block_number [Fixnum]
243
+ # @param block_count [Fixnum]
244
+ # @return [FFI::Pointer]
245
+ def build_scaled_progress_pointer(block_number, block_count)
246
+ return unless @options.progress_formatter
247
+
248
+ FFI::CPL::Progress.GDALCreateScaledProgress(
249
+ block_number.to_f / block_count,
250
+ (block_number + 1).to_f / block_count,
251
+ @options.progress_formatter,
252
+ nil)
253
+ end
254
+
255
+ # Determines how large of a chunk of data to grid and rasterize.
256
+ #
257
+ # @param block_size [Fixnum]
258
+ # @param raster_border [Fixnum]
259
+ # @return [Fixnum]
260
+ def build_data_request_size(block_size, offset, raster_border)
261
+ request = block_size
262
+
263
+ if offset + request > raster_border
264
+ raster_border - offset
265
+ else
266
+ request
267
+ end
268
+ end
269
+
270
+ # @param min [Float]
271
+ # @param pixel_size [Float]
272
+ # @param offset [Float]
273
+ # @param request_size [Fixnum]
274
+ # @return [Array<Fixnum, Fixnum>] The min and max values based on the given
275
+ # parameters.
276
+ def build_grid_extents(min, pixel_size, offset, request_size)
277
+ grid_min = min + pixel_size * offset
278
+ grid_max = min + pixel_size * (offset + request_size)
279
+
280
+ [grid_min, grid_max]
281
+ end
282
+
283
+ # @param block_x_size [Fixnum]
284
+ # @param block_y_size [Fixnum]
285
+ # @param raster_width [Fixnum]
286
+ # @param raster_height [Fixnum]
287
+ # @return [Fixnum] The total number of blocks that should be iterated
288
+ # through during the grid+rasterize process.
289
+ def build_block_count(block_x_size, block_y_size, raster_width, raster_height)
290
+ ((raster_width + block_x_size - 1) / block_x_size) *
291
+ ((raster_height + block_y_size - 1) / block_y_size)
292
+ end
293
+ end
294
+ end
@@ -0,0 +1,273 @@
1
+ require_relative 'grid'
2
+ require_relative 'options'
3
+ require_relative '../ogr'
4
+
5
+ module GDAL
6
+ # Object to be used with a {GDAL::Gridder}.
7
+ class GridderOptions
8
+ extend Forwardable
9
+
10
+ # Name of field attribute to extract from each feature to use for Z values.
11
+ #
12
+ # @!attribute [rw] input_field_name
13
+ # @return [String]
14
+ attr_accessor :input_field_name
15
+
16
+ # Custom progress output Proc, passed on to {GDAL::Grid#create}. This must
17
+ # follow semantics imposed by +FFI::GDAL::GDAL.ProgressFunc+.
18
+ #
19
+ # This option doesn't exist in gdal_grid; you only get their output format
20
+ # or no output at all (using +-q+).
21
+ #
22
+ # @!attribute [rw] progress_formatter
23
+ # @return [Proc]
24
+ attr_accessor :progress_formatter
25
+
26
+ # Use for filtering out input points; any input points from the layer that
27
+ # fall within this boundary will be excluded from the gridding process.
28
+ # Note that this does not clip the output raster.
29
+ #
30
+ # Replaces gdal_grid's +-clipsrc+ option.
31
+ #
32
+ # @!attribute [rw] input_clipping_geometry
33
+ # @return [OGR::Geometry]
34
+ attr_reader :input_clipping_geometry
35
+
36
+ # Driver-specific options to pass to the {GDAL::Driver} when creating the
37
+ # raster. Check out GDAL documentation for the driver you're specifying
38
+ # (specified here through {#output_format}) to see what options you have
39
+ # available.
40
+ #
41
+ # Correlates to gdal_grid option +-co+.
42
+ #
43
+ # @!attribute output_creation_options
44
+ # @return [Hash]
45
+ attr_reader :output_creation_options
46
+
47
+ # The {GDAL::Driver} name to use for creating the output raster.
48
+ #
49
+ # Correlates to gdal_grid option +-of+.
50
+ #
51
+ # @!attribute [rw] output_format
52
+ # @return [String]
53
+ attr_reader :output_format
54
+
55
+ # The minimum and maximum X coordinates for the output raster.
56
+ #
57
+ # Correlates to gdal_grid option +-txe+.
58
+ #
59
+ # @!attribute [rw] output_x_extent
60
+ # @return [Hash{min: Number, max: Number}]
61
+ attr_reader :output_x_extent
62
+
63
+ # The minimum and maximum Y coordinates for the output raster.
64
+ #
65
+ # Correlates to gdal_grid option +-tye+.
66
+ #
67
+ # @!attribute [rw] output_y_extent
68
+ # @return [Hash{min: Number, max: Number}]
69
+ attr_reader :output_y_extent
70
+
71
+ # The SpatialReference to use for the output raster's {GDAL::Dataset#projection}.
72
+ # If one isn't given, the {GDAL::Gridder} will try to use the one from the
73
+ # source layer.
74
+ #
75
+ # Correlates to gdal_grid option +-a_srs+.
76
+ #
77
+ # @!attribute [rw] output_projection
78
+ # @return [OGR::SpatialReference]
79
+ attr_reader :output_projection
80
+
81
+ # Dimensions to output the raster in.
82
+ #
83
+ # Correlates to gdal_grid option +-outsize+.
84
+ #
85
+ # @overload output_size
86
+ # @overload output_size=(width_height_array)
87
+ # Sets the output Hash using a 2-element Array.
88
+ # @param width_height_array [Array<Float>]
89
+ # A 2-element Array specifying the width and height of the output raster.
90
+ # @overload output_size=(width_height_hash)
91
+ # Sets the output Hash using a similar input Hash.
92
+ # @param width_height_hash [Hash{width => Number, height => Number}]
93
+ # A Hash with :width and :height keys, specifying the width and height
94
+ # of the output raster.
95
+ # @return [Hash{width: Number, height: Number}]
96
+ attr_reader :output_size
97
+
98
+ # Data type of the output raster values.
99
+ #
100
+ # Correlates to gdal_grid option +-ot+.
101
+ #
102
+ # @!attribute [rw] output_data_type
103
+ # @return [FFI::GDAL::GDAL::DataType]
104
+ attr_reader :output_data_type
105
+
106
+ # Object used by the {GDAL::Gridder} for doing the actual grid work.
107
+ #
108
+ # @!attribute [r] grid
109
+ # @return [GDAL::Grid]
110
+ attr_reader :grid
111
+
112
+ # Object used by the {GDAL::Gridder} for doing the actual grid work.
113
+ #
114
+ # @!method algorithm_options
115
+ # @return [FFI::Struct] One of the FFI::GDAL grid algorithm Options objects.
116
+ def_delegator :@grid, :algorithm_options, :algorithm_options
117
+
118
+ # @param algorithm_type [Symbol] One of {FFI::GDAL::Alg::GridAlgorithm}.
119
+ def initialize(algorithm_type)
120
+ # Options with defaults
121
+ @output_data_type = :GDT_Float64
122
+ @output_format = 'GTiff'
123
+ @output_size = { width: 256, height: 256 }
124
+
125
+ # Options without defaults
126
+ @input_clipping_geometry = nil
127
+ @output_x_extent = {}
128
+ @output_y_extent = {}
129
+ @output_projection = nil
130
+ @output_creation_options = {}
131
+ @progress_formatter = nil
132
+
133
+ @grid = GDAL::Grid.new(algorithm_type, data_type: @output_data_type)
134
+ end
135
+
136
+ # @param geometry [OGR::Geometry]
137
+ # @return [OGR::Geometry]
138
+ def input_clipping_geometry=(geometry)
139
+ unless geometry.is_a?(OGR::Geometry)
140
+ raise OGR::InvalidGeometry,
141
+ "Clipping geometry must be a OGR::Geometry type, but was a #{geometry.class}"
142
+ end
143
+
144
+ @input_clipping_geometry = geometry
145
+ end
146
+
147
+ # @param type [Symbol] Must be one of FFI::GDAL::GDAL::DataType.
148
+ def output_data_type=(type)
149
+ data_types = FFI::GDAL::GDAL::DataType.symbols
150
+
151
+ unless data_types.include?(type)
152
+ raise GDAL::InvalidDataType,
153
+ "output_data_type must be one of #{data_types} but was #{type}"
154
+ end
155
+
156
+ @grid.data_type = @output_data_type = type
157
+ end
158
+
159
+ # @return [Fixnum]
160
+ def output_data_type_size
161
+ GDAL::DataType.size(@output_data_type) / 8
162
+ end
163
+
164
+ # @param format [String] Must be one of GDAL::Driver.short_names.
165
+ def output_format=(format)
166
+ driver_names = GDAL::Driver.short_names
167
+
168
+ unless driver_names.include?(format)
169
+ raise GDAL::InvalidDriverName,
170
+ "output_form must be one of #{driver_names} but was #{format}"
171
+ end
172
+
173
+ @output_format = format
174
+ end
175
+
176
+ # The {GDAL::Driver}, based on {#output_format} to use for creating the
177
+ # output raster.
178
+ #
179
+ # @return [GDAL::Driver]
180
+ def output_driver
181
+ @output_driver ||= GDAL::Driver.by_name(@output_format)
182
+ end
183
+
184
+ # @param min_max [Array<Fixnum>, Hash{min => Number, max => Number}]
185
+ def output_x_extent=(min_max)
186
+ min, max = extract_min_max(min_max, :min, :max)
187
+
188
+ @output_x_extent = { min: min, max: max }
189
+ end
190
+
191
+ # @param min_max [Array<Fixnum>, Hash{min => Number, max => Number}]
192
+ def output_y_extent=(min_max)
193
+ min, max = extract_min_max(min_max, :min, :max)
194
+
195
+ @output_y_extent = { min: min, max: max }
196
+ end
197
+
198
+ # @param width_height [Array<Float>, Hash{width => Number, height => Number}]
199
+ # Either a 2-element Array or a Hash with :width and :height keys,
200
+ # specifying the width and height of the output raster.
201
+ def output_size=(width_height)
202
+ width, height = extract_min_max(width_height, :width, :height)
203
+
204
+ @output_size = { width: width, height: height }
205
+ end
206
+
207
+ # Set to use a different SRID for the output raster. Defaults to use the
208
+ # same as the source Layer.
209
+ #
210
+ # @param spatial_reference [OGR::SpatialReference]
211
+ def output_projection=(spatial_reference)
212
+ unless spatial_reference.is_a?(OGR::SpatialReference)
213
+ raise OGR::InvalidSpatialReference,
214
+ "output_projection must be an OGR::SpatialReference but was a #{spatial_reference.class}"
215
+ end
216
+
217
+ @output_projection = spatial_reference
218
+ end
219
+
220
+ # @param options_hash [Hash]
221
+ def output_creation_options=(**options_hash)
222
+ return if options_hash.empty?
223
+
224
+ @output_creation_options = options_hash
225
+ end
226
+
227
+ private
228
+
229
+ # Extracts a min and max value from either a 2-element Array or a Hash with
230
+ # :min and :max keys.
231
+ #
232
+ # @param content [Array, Hash]
233
+ # @param min_name [Symbol]
234
+ # @param max_name [Symbol]
235
+ # @return [Array<Number>]
236
+ def extract_min_max(content, min_name, max_name)
237
+ case content
238
+ when Array
239
+ extract_min_max_from_array(content, min_name, max_name)
240
+ when Hash
241
+ extract_min_max_from_hash(content, min_name, max_name)
242
+ end
243
+ end
244
+
245
+ # @param content [Array, Hash]
246
+ # @param min_name [Symbol]
247
+ # @param max_name [Symbol]
248
+ # @return [Array<Number>]
249
+ def extract_min_max_from_array(content, min_name, max_name)
250
+ unless content.length == 2
251
+ raise ArgumentError,
252
+ "Please supply only 2 elements, one for #{min_name}, one for #{max_name}"
253
+ end
254
+
255
+ [content[0], content[1]]
256
+ end
257
+
258
+ # @param content [Array, Hash]
259
+ # @param min_name [Symbol]
260
+ # @param max_name [Symbol]
261
+ # @return [Array<Number>]
262
+ def extract_min_max_from_hash(content, min_name, max_name)
263
+ valid_keys = [min_name, max_name]
264
+ actual_keys = content.keys
265
+
266
+ unless actual_keys.length == 2 && valid_keys & actual_keys == valid_keys
267
+ raise ArgumentError, "Please supply only key/value pairs for #{min_name} and #{max_name}"
268
+ end
269
+
270
+ [content[min_name], content[max_name]]
271
+ end
272
+ end
273
+ end