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
@@ -1,3 +1,5 @@
1
+ require_relative '../gdal'
2
+
1
3
  module GDAL
2
4
  # Takes a list of Ranges of color values and remaps them. Note that these
3
5
  # values are directly written to the raster band, overwriting all existing
@@ -28,7 +30,7 @@ module GDAL
28
30
  # @param range [Range] The range of values to map to a new value.
29
31
  # @param map_to_value [Number]
30
32
  def add_range(range, map_to_value)
31
- fail 'range must be a Ruby Range' unless range.is_a? Range
33
+ raise 'range must be a Ruby Range' unless range.is_a? Range
32
34
 
33
35
  @ranges << { range: range, map_to: map_to_value }
34
36
  end
@@ -49,14 +51,16 @@ module GDAL
49
51
  def equal_count_ranges(range_count)
50
52
  sorted_pixels = @raster_band.to_na.sort
51
53
  sorted_and_masked_pixels = sorted_pixels[sorted_pixels.ne(@raster_band.no_data_value[:value])]
54
+ return [] if sorted_and_masked_pixels.empty?
55
+
52
56
  range_size = (sorted_and_masked_pixels.size / range_count).to_i
53
57
 
54
- log "Pixel count: #{sorted_and_masked_pixels.size}"
58
+ log "Masked pixel count/total pixel count: #{sorted_and_masked_pixels.size}/#{sorted_pixels.size}"
55
59
  log "Min pixel value: #{sorted_and_masked_pixels.min}"
56
60
  log "Max pixel value: #{sorted_and_masked_pixels.max}"
57
61
  log "Range size: #{range_size}"
58
62
 
59
- break_values = range_count.times.map { |i| sorted_and_masked_pixels[range_size * i] }
63
+ break_values = Array.new(range_count) { |i| sorted_and_masked_pixels[range_size * i] }.uniq
60
64
  log "Break values: #{break_values}"
61
65
  return if break_values.uniq.size != range_count
62
66
 
@@ -82,23 +86,20 @@ module GDAL
82
86
  # values, so if you don't want to overwrite the Dataset you're working with,
83
87
  # you should copy it first.
84
88
  def classify!
85
- narray = @raster_band.to_na.dup
86
-
87
- 0.upto(narray.size - 1) do |pixel_number|
88
- next if narray[pixel_number] == @raster_band.no_data_value[:value]
89
-
90
- range = @ranges.find do |r|
91
- r[:range].member? narray[pixel_number]
92
- end
93
-
94
- if range
95
- narray[pixel_number] = range[:map_to]
96
- else
97
- log "pixel #{pixel_number} (value: #{narray[pixel_number]}) not in any given range"
98
- end
89
+ nodata_value = @raster_band.no_data_value[:value]
90
+ band_pixels = @raster_band.to_na
91
+ new_band_pixels = GDAL._narray_from_data_type(@raster_band.data_type, @raster_band.x_size, @raster_band.y_size)
92
+ new_band_pixels[band_pixels.eq(nodata_value)] = nodata_value
93
+
94
+ @ranges.each do |r|
95
+ new_band_pixels[
96
+ band_pixels.ne(nodata_value).
97
+ and(band_pixels.le(r[:range].max)).
98
+ and(band_pixels.ge(r[:range].min))
99
+ ] = r[:map_to]
99
100
  end
100
101
 
101
- @raster_band.write_array(narray, buffer_data_type: @raster_band.data_type)
102
+ @raster_band.write_xy_narray(new_band_pixels)
102
103
  end
103
104
 
104
105
  private
@@ -0,0 +1,107 @@
1
+ require_relative '../../ogr'
2
+
3
+ module GDAL
4
+ module RasterBandMixins
5
+ # RasterBand methods for doing warp-like things, but not using GDAL's Warp
6
+ # API.
7
+ module AlgorithmExtensions
8
+ # A method that doesn't use GDAL's Warp API for erasing pixels (setting to
9
+ # NODATA) in the method-owning RasterBand. It simply iterates over all of
10
+ # the valued pixels (ones that aren't NODATA pixels) and yields them to a
11
+ # block. The return value of the block tells the method whether to keep
12
+ # the pixel or not: a truthy return value keeps the pixel; a false value
13
+ # sets the pixel value to the band's NODATA value. If there's no NODATA
14
+ # value set, it will raise an exception.
15
+ #
16
+ # A typical clip operation would be from checking to see if the
17
+ # RasterBand's pixel is within some Polygon. Doing so would look like:
18
+ #
19
+ # @example Clipping by polygon
20
+ #
21
+ # contrived_wkt = 'POLYGON ((0 0, 0 20, 20 20, 20 0, 0 0))'
22
+ # polygon = OGR::Geometry.create_from_wkt(contrived_wkt)
23
+ #
24
+ # # Note the 'w' to open for writing!
25
+ # dataset = GDAL::Dataset.open('my.tif', 'w')
26
+ # geo_transform = dataset.geo_transform
27
+ # test_point = OGR::Point.new
28
+ #
29
+ # dataset.raster_band(1).simple_erase! do |pixel_num, line_num|
30
+ # # Project the pixel using the dataset's GeoTransform
31
+ # projected_point_values = geo_transform.apply_geo_transform(pixel_num, line_num)
32
+ # test_point.set_point(projected_point_values[:x_geo], projected_point_values[:y_geo])
33
+ #
34
+ # test_point.within? polygon
35
+ # end
36
+ #
37
+ # # Make sure to close the dataset so the changes get flushed to disk.
38
+ # dataset.close
39
+ #
40
+ # You could, however use any other criteria in the block for erasing
41
+ # points away. The method also the pixel value in case you can use it for
42
+ # erasing. Perhaps, for example, you want to remove all pixels in the
43
+ # upper left quadrant of your raster that have a value less than 0. That
44
+ # would look like:
45
+ #
46
+ # @example Erasing using pixel location and values
47
+ #
48
+ # dataset = GDAL::Dataset.open('my.tif', 'w')
49
+ # raster_band = dataset.raster_band(1)
50
+ #
51
+ # raster_band.simple_erase! do |x, y, value|
52
+ # if x < (raster_band.x_size / 2) && y < (raster_band.y_size / 2) && value < 0
53
+ # false
54
+ # else
55
+ # true
56
+ # end
57
+ # end
58
+ #
59
+ # @return [Fixnum] The number of pixels that were erased.
60
+ def simple_erase!
61
+ erase_value = no_data_value[:value]
62
+ write_buffer = GDAL._buffer_from_data_type(data_type, x_size)
63
+ erased_pixel_count = 0
64
+
65
+ unless erase_value
66
+ raise GDAL::NoRasterEraseValue,
67
+ 'Cannot erase values, RasterBand does not have a NODATA value set'
68
+ end
69
+
70
+ y_size.times do |line_num|
71
+ pixel_row = raster_io('r', x_size: x_size, y_size: 1, x_offset: 0, y_offset: line_num)
72
+ pixel_values = GDAL._read_pointer(pixel_row, data_type, x_size)
73
+ row_changed = false
74
+ pixel_num = 0
75
+
76
+ while pixel_num < pixel_values.length
77
+ pixel_value = pixel_values[pixel_num]
78
+ next if pixel_value == no_data_value[:value]
79
+
80
+ keep_in_raster = yield(pixel_num, line_num, pixel_value)
81
+ next if keep_in_raster
82
+
83
+ erased_pixel_count += 1
84
+ pixel_values[pixel_num] = erase_value
85
+ row_changed = true
86
+ pixel_num += 1
87
+ end
88
+
89
+ rewrite_pixel_row(write_buffer, pixel_values, line_num) if row_changed
90
+ end
91
+
92
+ erased_pixel_count
93
+ end
94
+
95
+ private
96
+
97
+ # @param write_buffer [FFI::Buffer]
98
+ # @param pixel_values [Array<Number>]
99
+ # @param line_number [Fixnum]
100
+ def rewrite_pixel_row(write_buffer, pixel_values, line_number)
101
+ GDAL._write_pointer(write_buffer, data_type, pixel_values)
102
+ raster_io('w', write_buffer, x_size: x_size, y_size: 1, y_offset: line_number)
103
+ write_buffer.clear
104
+ end
105
+ end
106
+ end
107
+ end
@@ -1,5 +1,3 @@
1
- require_relative '../../ffi/gdal/alg'
2
-
3
1
  module GDAL
4
2
  module RasterBandMixins
5
3
  module AlgorithmMethods
@@ -18,22 +16,25 @@ module GDAL
18
16
  # @param green_band [GDAL::RasterBand, FFI::Pointer]
19
17
  # @param blue_band [GDAL::RasterBand, FFI::Pointer]
20
18
  # @param colors [Fixnum] Number of colors to return; 2-256.
21
- # @param color_interpretation [FFI::GDAL::PaletteInterp] The type
19
+ # @param color_interpretation [FFI::GDAL::GDAL::PaletteInterp] The type
22
20
  # of ColorTable to return.
21
+ # @param progress_function [Proc, FFI:GDAL::GDAL.ProgressFunc]
22
+ # @param progress_arg [FFI::Pointer] Usually used when when using a
23
+ # +FFI::CPL::Progress.GDALCreateScaledProgress+.
23
24
  # @return [GDAL::ColorTable]
24
25
  def compute_median_cut_pct(red_band, green_band, blue_band,
25
- colors, color_interpretation, &progress)
26
+ colors, color_interpretation, progress_function: nil, progress_arg: nil)
26
27
  color_table = GDAL::ColorTable.new(color_interpretation)
27
28
 
28
29
  FFI::GDAL::Alg.GDALComputeMedianCutPCT(
29
30
  red_band,
30
31
  green_band,
31
32
  blue_band,
32
- nil, # This isn't yet supported in GDAL.
33
+ nil, # This isn't yet supported in GDAL.
33
34
  colors,
34
35
  color_table.c_pointer,
35
- progress,
36
- nil)
36
+ progress_function,
37
+ progress_arg)
37
38
 
38
39
  color_table
39
40
  end
@@ -54,10 +55,13 @@ module GDAL
54
55
  # @param blue_band [GDAL::RasterBand, FFI::Pointer]
55
56
  # @param output_band [GDAL::RasterBand, FFI::Pointer]
56
57
  # @param color_table [GDAL::ColorTable, FFI::Pointer]
58
+ # @param progress_function [Proc, FFI:GDAL::GDAL.ProgressFunc]
59
+ # @param progress_arg [FFI::Pointer] Usually used when when using a
60
+ # +FFI::CPL::Progress.GDALCreateScaledProgress+.
57
61
  # @return [GDAL::RasterBand] +output_band+ with the dithering algorithm
58
62
  # applied.
59
63
  def dither_rgb_to_pct(red_band, green_band, blue_band, output_band,
60
- color_table, &progress)
64
+ color_table, progress_function: nil, progress_arg: nil)
61
65
  red_ptr = GDAL._pointer(GDAL::RasterBand, red_band)
62
66
  green_ptr = GDAL._pointer(GDAL::RasterBand, green_band)
63
67
  blue_ptr = GDAL._pointer(GDAL::RasterBand, blue_band)
@@ -70,8 +74,8 @@ module GDAL
70
74
  blue_ptr,
71
75
  output_ptr,
72
76
  color_table_ptr,
73
- progress,
74
- nil)
77
+ progress_function,
78
+ progress_arg)
75
79
 
76
80
  output_band
77
81
  end
@@ -105,6 +109,9 @@ module GDAL
105
109
  # values.
106
110
  #
107
111
  # @param proximity_band [GDAL::RasterBand, FFI::Pointer]
112
+ # @param progress_function [Proc, FFI:GDAL::GDAL.ProgressFunc]
113
+ # @param progress_arg [FFI::Pointer] Usually used when when using a
114
+ # +FFI::CPL::Progress.GDALCreateScaledProgress+.
108
115
  # @param options [Hash]
109
116
  # @option options [String] values A list of target pixel values to measure
110
117
  # the distance from. If this isn't provided, proximity will be computed
@@ -118,7 +125,7 @@ module GDAL
118
125
  # @option options [Fixnum] fixed_buf_val If set, all pixels within the
119
126
  # +maxdist+ threshold are set to this fixed value instead of to a
120
127
  # proximity distance.
121
- def compute_proximity!(proximity_band, **options, &progress)
128
+ def compute_proximity!(proximity_band, progress_function: nil, progress_arg: nil, **options)
122
129
  proximity_band_ptr = GDAL._pointer(GDAL::RasterBand, proximity_band)
123
130
  options_ptr = GDAL::Options.pointer(options)
124
131
 
@@ -126,8 +133,8 @@ module GDAL
126
133
  @c_pointer,
127
134
  proximity_band_ptr,
128
135
  options_ptr,
129
- progress,
130
- nil)
136
+ progress_function,
137
+ progress_arg)
131
138
  end
132
139
 
133
140
  # Fill selected raster regions by interpolation from the edges. It
@@ -153,20 +160,24 @@ module GDAL
153
160
  # directions to find values to interpolate from.
154
161
  # @param smoothing_iterations [Fixnum] The number of 3x3 smoothing filter
155
162
  # passes to run. Can be 0.
163
+ # @param progress_function [Proc, FFI:GDAL::GDAL.ProgressFunc]
164
+ # @param progress_arg [FFI::Pointer] Usually used when when using a
165
+ # +FFI::CPL::Progress.GDALCreateScaledProgress+.
156
166
  # @param options [Hash]
157
167
  # TODO: document what valid options are.
158
- def fill_nodata!(mask_band, max_search_distance, smoothing_iterations, **options, &progress)
168
+ def fill_nodata!(mask_band, max_search_distance, smoothing_iterations, progress_function: nil, progress_arg: nil,
169
+ **options)
159
170
  mask_band_ptr = GDAL._pointer(GDAL::RasterBand, mask_band)
160
171
  options_ptr = GDAL::Options.pointer(options)
161
172
 
162
- !!FFI::GDAL.GDALFillNodata(@c_pointer,
173
+ !!FFI::GDAL::Alg.GDALFillNodata(@c_pointer,
163
174
  mask_band_ptr,
164
175
  max_search_distance,
165
- 0, # deprecated option in GDAL
176
+ 0, # deprecated option in GDAL
166
177
  smoothing_iterations,
167
178
  options_ptr,
168
- progress,
169
- nil)
179
+ progress_function,
180
+ progress_arg)
170
181
  end
171
182
 
172
183
  # Creates vector polygons for all connected regions of pixels in the raster
@@ -196,16 +207,23 @@ module GDAL
196
207
  # suitable for collection as polygons.
197
208
  # @param pixel_value_field [Fixnum] Index of the feature attribute into
198
209
  # which the pixel value of the polygon should be written.
210
+ # @param use_integer_function [Boolean] Indicates using GDAL's
211
+ # GDALPolygonize() instead of GDALFPolygonize(); the former uses a
212
+ # 32-bit integer buffer for reading pixel band values, the latter uses a
213
+ # 32-bit float buffer. The integer based function is faster but less
214
+ # precise.
199
215
  # @param options [Hash]
200
216
  # @option options [Fixnum] '8CONNECTED' (4) Set to 8 to use 8
201
217
  # connectedness.
202
- # @param progress [Proc]
218
+ # @param progress_function [Proc, FFI:GDAL::GDAL.ProgressFunc]
219
+ # @param progress_arg [FFI::Pointer] Usually used when when using a
220
+ # +FFI::CPL::Progress.GDALCreateScaledProgress+.
203
221
  # @return [OGR::Layer]
204
- def polygonize(layer, mask_band: nil, pixel_value_field: -1, use_integer_function: false,
205
- **options, &progress)
222
+ def polygonize(layer, mask_band: nil, pixel_value_field: -1, use_integer_function: false, progress_function: nil,
223
+ progress_arg: nil, **options)
206
224
  mask_band_ptr = GDAL._pointer(GDAL::RasterBand, mask_band, false)
207
225
  layer_ptr = GDAL._pointer(OGR::Layer, layer)
208
- fail OGR::InvalidLayer, "Invalid layer: #{layer.inspect}" if layer_ptr.null?
226
+ raise OGR::InvalidLayer, "Invalid layer: #{layer.inspect}" if layer_ptr.null?
209
227
  log "Pixel value field: #{pixel_value_field}"
210
228
 
211
229
  options_ptr = GDAL::Options.pointer(options)
@@ -219,8 +237,8 @@ module GDAL
219
237
  layer_ptr, # hOutLayer
220
238
  pixel_value_field, # iPixValField
221
239
  options_ptr, # papszOptions
222
- progress, # pfnProgress
223
- nil # pProgressArg
240
+ progress_function, # pfnProgress
241
+ progress_arg # pProgressArg
224
242
  )
225
243
 
226
244
  layer_ptr.instance_of?(OGR::Layer) ? layer_ptr : OGR::Layer.new(layer_ptr)
@@ -242,10 +260,16 @@ module GDAL
242
260
  # @param mask_band [GDAL::RasterBand] [description] All pixels in this
243
261
  # band with a value other than 0 will be considered suitable for
244
262
  # inclusion in polygons.
263
+ # @param progress_function [Proc, FFI:GDAL::GDAL.ProgressFunc]
264
+ # @param progress_arg [FFI::Pointer] Usually used when when using a
265
+ # +FFI::CPL::Progress.GDALCreateScaledProgress+.
245
266
  # @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)
267
+ def sieve_filter!(size_threshold, connectedness, mask_band: nil, progress_function: nil, progress_arg: nil,
268
+ **options)
269
+ _sieve_filter(size_threshold, connectedness, self, mask_band: mask_band,
270
+ progress_function: progress_function,
271
+ progress_arg: progress_arg,
272
+ **options)
249
273
  end
250
274
 
251
275
  # The same as +sieve_filter!+, but returns a new GDAL::RasterBand as the
@@ -253,15 +277,12 @@ module GDAL
253
277
  #
254
278
  # @see +sieve_filter!
255
279
  # @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)
280
+ def sieve_filter(size_threshold, connectedness, destination_band, mask_band: nil, progress_function: nil,
281
+ progress_arg: nil, **options)
282
+ _sieve_filter(size_threshold, connectedness, destination_band, mask_band: mask_band,
283
+ progress_function: progress_function,
284
+ progress_arg: progress_arg,
285
+ **options)
265
286
 
266
287
  if destination_band.is_a? GDAL::RasterBand
267
288
  destination_band
@@ -272,9 +293,27 @@ module GDAL
272
293
 
273
294
  private
274
295
 
275
- def _sieve_filter(size_threshold, connectedness, destination_band_ptr = nil,
276
- mask_band: nil, **options, &progress)
296
+ # @param size_threshold [Fixnum] Polygons found in the raster with sizes
297
+ # smaller than this will be merged into their largest neighbor.
298
+ # @param connectedness [Fixnum] 4 or 8. 4 indicates that diagonal pixels
299
+ # are not considered directly adjacent for polygon membership purposes;
300
+ # 8 indicates they are.
301
+ # @param mask_band [GDAL::RasterBand] [description] All pixels in this
302
+ # band with a value other than 0 will be considered suitable for
303
+ # inclusion in polygons.
304
+ # @param progress_function [Proc, FFI:GDAL::GDAL.ProgressFunc]
305
+ # @param progress_arg [FFI::Pointer] Usually used when when using a
306
+ # +FFI::CPL::Progress.GDALCreateScaledProgress+.
307
+ # @param options [Hash] None supported in GDAL as of this writing.
308
+ def _sieve_filter(size_threshold, connectedness, destination_band, mask_band: nil, progress_function: nil,
309
+ progress_arg: nil, **options)
277
310
  mask_band_ptr = GDAL._pointer(GDAL::RasterBand, mask_band, false)
311
+ destination_band_ptr = GDAL._pointer(GDAL::RasterBand, destination_band)
312
+
313
+ if destination_band.nil? || destination_band.null?
314
+ raise GDAL::InvalidRasterBand, "destination_band isn't a valid GDAL::RasterBand: #{destination_band}"
315
+ end
316
+
278
317
  options_ptr = GDAL::Options.pointer(options)
279
318
 
280
319
  FFI::GDAL::Alg.GDALSieveFilter(
@@ -284,8 +323,8 @@ module GDAL
284
323
  size_threshold,
285
324
  connectedness,
286
325
  options_ptr,
287
- progress,
288
- nil)
326
+ progress_function,
327
+ progress_arg)
289
328
  end
290
329
  end
291
330
  end
@@ -0,0 +1,84 @@
1
+ module GDAL
2
+ module RasterBandMixins
3
+ # RasterBand methods added for dealing with colorizing.
4
+ module ColoringExtensions
5
+ # Sets the band to be a Palette band, then applies an RGB color table using
6
+ # the given colors. Colors are distributed evenly across the table based
7
+ # on the number of colors given. Note that this will overwrite any existing
8
+ # color table that may be set on this band.
9
+ #
10
+ # @param colors [Array<Fixnum, String>, Fixnum, String] Can be a single or
11
+ # many colors, given as either R, G, and B integers (0-255) or as strings
12
+ # of Hex.
13
+ #
14
+ # @example Colors as RGB values
15
+ # # This will make the first 128 values black, and the last 128, red.
16
+ # my_band.colorize!([[0, 0, 0], [255, 0, 0]])
17
+ #
18
+ # @example Colors as Hex values
19
+ # # Same as above...
20
+ # my_band.colorize!(%w[#000000 #FF0000])
21
+ def colorize!(*colors)
22
+ return if colors.empty?
23
+
24
+ self.color_interpretation ||= :GCI_PaletteIndex
25
+ table = GDAL::ColorTable.new(:GPI_RGB)
26
+ table.add_color_entry(0, 0, 0, 0, 255)
27
+
28
+ # Start at 1 instead of 0 because we manually set the first color entry
29
+ # to white.
30
+ color_entry_index_range =
31
+ case data_type
32
+ when :GDT_Byte then 1..255
33
+ when :GDT_UInt16 then 1..65_535
34
+ else raise "Can't colorize a #{data_type} band--must be :GDT_Byte or :GDT_UInt16"
35
+ end
36
+
37
+ bin_count = (color_entry_index_range.last + 1) / colors.size.to_f
38
+
39
+ color_entry_index_range.step do |color_entry_index|
40
+ color_number = (color_entry_index / bin_count).floor.to_i
41
+ color = colors[color_number]
42
+
43
+ # TODO: Fix possible uninitialized rgb_array
44
+ rgb_array = hex_to_rgb(color) unless color.is_a?(Array)
45
+ table.add_color_entry(color_entry_index,
46
+ rgb_array[0], rgb_array[1], rgb_array[2], 255)
47
+ end
48
+
49
+ self.color_table = table
50
+ end
51
+
52
+ # Gets the colors from the associated ColorTable and returns an Array of
53
+ # those, where each ColorEntry is [R, G, B, A].
54
+ #
55
+ # @return [Array<Array<Fixnum>>]
56
+ def colors_as_rgb
57
+ return [] unless color_table
58
+
59
+ color_table.color_entries_as_rgb.map(&:to_a)
60
+ end
61
+
62
+ # Gets the colors from the associated ColorTable and returns an Array of
63
+ # Strings, where the RGB color for each ColorEntry has been converted to
64
+ # Hex.
65
+ #
66
+ # @return [Array<String>]
67
+ def colors_as_hex
68
+ colors_as_rgb.map do |rgba|
69
+ rgb = rgba.to_a[0..2]
70
+
71
+ "##{rgb[0].to_s(16)}#{rgb[1].to_s(16)}#{rgb[2].to_s(16)}"
72
+ end
73
+ end
74
+
75
+ # @param hex [String]
76
+ def hex_to_rgb(hex)
77
+ hex.sub!(/^#/, '')
78
+ matches = hex.match(/(?<red>[a-zA-Z0-9]{2})(?<green>[a-zA-Z0-9]{2})(?<blue>[a-zA-Z0-9]{2})/)
79
+
80
+ [matches[:red].to_i(16), matches[:green].to_i(16), matches[:blue].to_i(16)]
81
+ end
82
+ end
83
+ end
84
+ end