ffi-gdal 0.0.4 → 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -1
  3. data/Rakefile +12 -1
  4. data/TODO.md +11 -0
  5. data/ffi-gdal.gemspec +2 -2
  6. data/lib/ext/error_symbols.rb +59 -0
  7. data/lib/ext/float_ext.rb +15 -0
  8. data/lib/ext/narray_ext.rb +16 -0
  9. data/lib/ext/to_bool.rb +2 -0
  10. data/lib/ffi-gdal.rb +139 -4
  11. data/lib/ffi/{gdal/cpl_conv.rb → cpl/conv_h.rb} +2 -3
  12. data/lib/ffi/{gdal/cpl_error.rb → cpl/error_h.rb} +1 -25
  13. data/lib/ffi/cpl/minixml_h.rb +14 -0
  14. data/lib/ffi/{gdal/cpl_string.rb → cpl/string_h.rb} +0 -25
  15. data/lib/ffi/{gdal/cpl_vsi.rb → cpl/vsi_h.rb} +0 -0
  16. data/lib/ffi/cpl/xml_node.rb +13 -0
  17. data/lib/ffi/gdal.rb +57 -593
  18. data/lib/ffi/gdal/alg_h.rb +127 -0
  19. data/lib/ffi/gdal/gdal_grid_data_metrics_options.rb +14 -0
  20. data/lib/ffi/gdal/gdal_grid_inverse_distance_to_a_power_options.rb +19 -0
  21. data/lib/ffi/gdal/gdal_grid_moving_average_options.rb +14 -0
  22. data/lib/ffi/gdal/gdal_grid_nearest_neighbor_options.rb +13 -0
  23. data/lib/ffi/gdal/gdal_h.rb +683 -0
  24. data/lib/ffi/gdal/gdal_rpc_info.rb +27 -0
  25. data/lib/ffi/gdal/gdal_transformer_info.rb +14 -0
  26. data/lib/ffi/gdal/gdal_warp_options.rb +43 -0
  27. data/lib/ffi/gdal/grid_h.rb +51 -0
  28. data/lib/ffi/gdal/version.rb +1 -1
  29. data/lib/ffi/gdal/warper_h.rb +48 -0
  30. data/lib/ffi/ogr.rb +12 -0
  31. data/lib/ffi/ogr/api_h.rb +553 -0
  32. data/lib/ffi/ogr/core_h.rb +148 -0
  33. data/lib/ffi/ogr/featurestyle_h.rb +22 -0
  34. data/lib/ffi/ogr/geocoding_h.rb +21 -0
  35. data/lib/ffi/ogr/ogr_contour_writer_info.rb +14 -0
  36. data/lib/ffi/ogr/ogr_envelope.rb +12 -0
  37. data/lib/ffi/ogr/ogr_envelope_3d.rb +14 -0
  38. data/lib/ffi/ogr/ogr_field.rb +50 -0
  39. data/lib/ffi/ogr/ogr_style_param.rb +12 -0
  40. data/lib/ffi/ogr/ogr_style_value.rb +13 -0
  41. data/lib/ffi/ogr/srs_api_h.rb +325 -0
  42. data/lib/gdal/color_entry.rb +47 -0
  43. data/lib/gdal/color_entry_extensions.rb +30 -0
  44. data/lib/gdal/color_interpretation.rb +15 -0
  45. data/lib/gdal/color_table.rb +146 -0
  46. data/lib/gdal/color_table_extensions.rb +47 -0
  47. data/lib/gdal/color_table_types/cmyk.rb +25 -0
  48. data/lib/gdal/color_table_types/gray.rb +9 -0
  49. data/lib/gdal/color_table_types/hls.rb +21 -0
  50. data/lib/gdal/color_table_types/rgb.rb +25 -0
  51. data/lib/gdal/data_type.rb +38 -0
  52. data/lib/gdal/dataset.rb +437 -0
  53. data/lib/gdal/dataset_extensions.rb +496 -0
  54. data/lib/gdal/driver.rb +244 -0
  55. data/lib/gdal/driver_extensions.rb +56 -0
  56. data/lib/gdal/environment_methods.rb +43 -0
  57. data/lib/{ffi-gdal → gdal}/exceptions.rb +4 -1
  58. data/lib/gdal/geo_transform.rb +188 -0
  59. data/lib/gdal/geo_transform_extensions.rb +90 -0
  60. data/lib/gdal/logger.rb +7 -0
  61. data/lib/{ffi-gdal → gdal}/major_object.rb +15 -14
  62. data/lib/gdal/options.rb +49 -0
  63. data/lib/gdal/raster_attribute_table.rb +185 -0
  64. data/lib/gdal/raster_attribute_table_extensions.rb +40 -0
  65. data/lib/{ffi-gdal → gdal}/raster_band.rb +227 -99
  66. data/lib/gdal/raster_band_extensions.rb +198 -0
  67. data/lib/{ffi-gdal → gdal}/version_info.rb +8 -0
  68. data/lib/gdal/warp_operation.rb +96 -0
  69. data/lib/ogr/coordinate_transformation.rb +108 -0
  70. data/lib/ogr/data_source.rb +172 -0
  71. data/lib/ogr/data_source_extensions.rb +32 -0
  72. data/lib/ogr/driver.rb +119 -0
  73. data/lib/ogr/envelope.rb +80 -0
  74. data/lib/ogr/envelope_extensions.rb +92 -0
  75. data/lib/ogr/exceptions.rb +35 -0
  76. data/lib/ogr/feature.rb +212 -0
  77. data/lib/ogr/feature_definition.rb +120 -0
  78. data/lib/ogr/feature_definition_extensions.rb +36 -0
  79. data/lib/ogr/feature_extensions.rb +31 -0
  80. data/lib/ogr/field.rb +91 -0
  81. data/lib/ogr/field_extensions.rb +23 -0
  82. data/lib/ogr/geocoding_session.rb +84 -0
  83. data/lib/ogr/geometry.rb +617 -0
  84. data/lib/ogr/geometry_extensions.rb +60 -0
  85. data/lib/ogr/geometry_types/collection.rb +45 -0
  86. data/lib/ogr/geometry_types/curve.rb +120 -0
  87. data/lib/ogr/geometry_types/surface.rb +20 -0
  88. data/lib/ogr/layer.rb +226 -0
  89. data/lib/ogr/layer_extensions.rb +55 -0
  90. data/lib/ogr/line_string.rb +7 -0
  91. data/lib/ogr/linear_ring.rb +6 -0
  92. data/lib/ogr/multi_line_string.rb +9 -0
  93. data/lib/ogr/multi_point.rb +7 -0
  94. data/lib/ogr/multi_polygon.rb +14 -0
  95. data/lib/ogr/point.rb +89 -0
  96. data/lib/ogr/polygon.rb +9 -0
  97. data/lib/ogr/spatial_reference.rb +723 -0
  98. data/lib/ogr/spatial_reference_extensions.rb +32 -0
  99. data/lib/ogr/style_table.rb +17 -0
  100. data/lib/ogr/style_table_extensions.rb +16 -0
  101. data/spec/{ffi-gdal/integration → integration}/color_table_info_spec.rb +1 -1
  102. data/spec/{ffi-gdal/integration → integration}/dataset_info_spec.rb +0 -0
  103. data/spec/{ffi-gdal/integration → integration}/driver_info_spec.rb +1 -1
  104. data/spec/{ffi-gdal/integration → integration}/geo_transform_info_spec.rb +0 -0
  105. data/spec/{ffi-gdal/integration → integration}/raster_attribute_table_info_spec.rb +1 -1
  106. data/spec/{ffi-gdal/integration → integration}/raster_band_info_spec.rb +5 -5
  107. data/spec/spec_helper.rb +4 -1
  108. data/spec/support/shapefiles/states_21basic/states.prj +1 -0
  109. data/spec/support/shapefiles/states_21basic/states.sbn +0 -0
  110. data/spec/support/shapefiles/states_21basic/states.sbx +0 -0
  111. data/spec/support/shapefiles/states_21basic/states.shp +0 -0
  112. data/spec/support/worldfiles/SR_50M/SR_50M.VERSION.txt +1 -0
  113. data/spec/support/worldfiles/SR_50M/SR_50M.prj +1 -0
  114. data/spec/support/worldfiles/SR_50M/SR_50M.tfw +6 -0
  115. data/spec/{ext/cpl_error_symbols_spec.rb → unit/ext/error_symbols_spec.rb} +1 -1
  116. data/spec/unit/gdal/color_table_spec.rb +146 -0
  117. data/spec/unit/ogr/layer_spec.rb +97 -0
  118. data/spec/unit/ogr/linear_ring_spec.rb +111 -0
  119. data/spec/unit/ogr/point_spec.rb +321 -0
  120. data/spec/{ffi-gdal/unit → unit}/version_info_spec.rb +1 -1
  121. data/testing_gdal.rb +168 -0
  122. data/testing_gdalwarp.rb +91 -0
  123. data/testing_layer_to_layer.rb +35 -0
  124. data/testing_ndvi.rb +76 -0
  125. data/testing_nir.rb +77 -0
  126. data/testing_ogr.rb +63 -0
  127. metadata +167 -59
  128. data/lib/ext/cpl_error_symbols.rb +0 -37
  129. data/lib/ffi-gdal/color_table.rb +0 -59
  130. data/lib/ffi-gdal/dataset.rb +0 -359
  131. data/lib/ffi-gdal/driver.rb +0 -151
  132. data/lib/ffi-gdal/geo_transform.rb +0 -137
  133. data/lib/ffi-gdal/raster_attribute_table.rb +0 -78
  134. data/lib/ffi/gdal/ogr_api.rb +0 -21
  135. data/lib/ffi/gdal/ogr_core.rb +0 -195
  136. data/lib/ffi/gdal/ogr_srs_api.rb +0 -44
  137. data/meow.rb +0 -144
  138. data/rubby.rb +0 -224
@@ -0,0 +1,47 @@
1
+ require_relative 'color_entry_extensions'
2
+
3
+ module GDAL
4
+ class ColorEntry
5
+ include ColorEntryExtensions
6
+
7
+ def initialize(color_entry=nil)
8
+ @color_entry_struct = color_entry || FFI::GDAL::GDALColorEntry.new
9
+ end
10
+
11
+ def c_pointer
12
+ @color_entry_struct
13
+ end
14
+
15
+ def color1
16
+ @color_entry_struct[:c1]
17
+ end
18
+
19
+ def color1=(new_color)
20
+ @color_entry_struct[:c1] = new_color
21
+ end
22
+
23
+ def color2
24
+ @color_entry_struct[:c2]
25
+ end
26
+
27
+ def color2=(new_color)
28
+ @color_entry_struct[:c2] = new_color
29
+ end
30
+
31
+ def color3
32
+ @color_entry_struct[:c3]
33
+ end
34
+
35
+ def color3=(new_color)
36
+ @color_entry_struct[:c3] = new_color
37
+ end
38
+
39
+ def color4
40
+ @color_entry_struct[:c4]
41
+ end
42
+
43
+ def color4=(new_color)
44
+ @color_entry_struct[:c4] = new_color
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ require 'json'
2
+
3
+ module GDAL
4
+ module ColorEntryExtensions
5
+
6
+ # @param include_fourth [Boolean] Turn off in case you don't want the fourth
7
+ # color in the array.
8
+ # @return [Array]
9
+ def to_a(include_fourth=true)
10
+ if include_fourth
11
+ [color1, color2, color3, color4]
12
+ else
13
+ [color1, color2, color3]
14
+ end
15
+ end
16
+
17
+ def as_json
18
+ {
19
+ color1: color1,
20
+ color2: color2,
21
+ color3: color3,
22
+ color4: color4
23
+ }
24
+ end
25
+
26
+ def to_json
27
+ as_json.to_json
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ module GDAL
2
+ class ColorInterpretation
3
+ # @param gdal_color_interp [FFI::GDAL::GDALColorInterp]
4
+ # @return [String]
5
+ def self.name(gdal_color_interp)
6
+ FFI::GDAL.GDALGetColorInterpretationName(gdal_color_interp)
7
+ end
8
+
9
+ # @param name [String]
10
+ # @return [FFI::GDAL::GDALColorInterp]
11
+ def self.by_name(name)
12
+ FFI::GDAL.GDALGetColorInterpretationByName(name)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,146 @@
1
+ require_relative '../ffi/gdal'
2
+ require_relative 'color_table_extensions'
3
+ require_relative 'color_entry'
4
+
5
+
6
+ module GDAL
7
+ module ColorTableTypes
8
+ autoload :CMYK,
9
+ File.expand_path('color_table_types/cmyk', __dir__)
10
+ autoload :Gray,
11
+ File.expand_path('color_table_types/gray', __dir__)
12
+ autoload :HLS,
13
+ File.expand_path('color_table_types/hls', __dir__)
14
+ autoload :RGB,
15
+ File.expand_path('color_table_types/rgb', __dir__)
16
+ end
17
+
18
+ class ColorTable
19
+ include ColorTableExtensions
20
+
21
+ # @param palette_interpretation [FFI::GDAL::GDALPaletteInterp]
22
+ # @return [GDAL::ColorTable]
23
+ def self.create(palette_interpretation)
24
+ color_table_pointer = FFI::GDAL::GDALCreateColorTable(palette_interpretation)
25
+ return nil if color_table_pointer.null?
26
+
27
+ new(color_table_pointer)
28
+ end
29
+
30
+ # @param color_table
31
+ def initialize(color_table)
32
+ @color_table_pointer = GDAL._pointer(self.class, color_table)
33
+ @color_entries = []
34
+
35
+ case palette_interpretation
36
+ when :GPI_Gray then extend GDAL::ColorTableTypes::Gray
37
+ when :GPI_RGB then extend GDAL::ColorTableTypes::RGB
38
+ when :GPI_CMYK then extend GDAL::ColorTableTypes::CMYK
39
+ when :GPI_HLS then extend GDAL::ColorTableTypes::HLS
40
+ else
41
+ raise "Unknown PaletteInterpretation: #{palette_interpretation}"
42
+ end
43
+ end
44
+
45
+ def c_pointer
46
+ @color_table_pointer
47
+ end
48
+
49
+ def destroy!
50
+ FFI::GDAL.GDALDestroyColorTable(@color_table_pointer)
51
+ end
52
+
53
+ # Clones the ColorTable using the C API.
54
+ #
55
+ # @return [GDAL::ColorTable]
56
+ def clone
57
+ ct_ptr = FFI::GDAL.GDALCloneColorTable(@color_table_pointer)
58
+ return nil if ct_ptr.null?
59
+
60
+ GDAL::ColorTable.new(ct_ptr)
61
+ end
62
+
63
+ # Usually :GPI_RGB.
64
+ #
65
+ # @return [Symbol] One of FFI::GDAL::GDALPaletteInterp.
66
+ def palette_interpretation
67
+ @palette_interpretation ||= FFI::GDAL.GDALGetPaletteInterpretation(@color_table_pointer)
68
+ end
69
+
70
+ # @return [Fixnum]
71
+ def color_entry_count
72
+ FFI::GDAL.GDALGetColorEntryCount(@color_table_pointer)
73
+ end
74
+
75
+ # @param index [Fixnum]
76
+ # @return [GDAL::ColorEntry]
77
+ def color_entry(index)
78
+ @color_entries.fetch(index) do
79
+ color_entry = FFI::GDAL.GDALGetColorEntry(@color_table_pointer, index)
80
+ return nil if color_entry.null?
81
+
82
+ GDAL::ColorEntry.new(color_entry)
83
+ end
84
+ end
85
+
86
+ # @param index [Fixnum]
87
+ # @return [GDAL::ColorEntry]
88
+ def color_entry_as_rgb(index)
89
+ entry = color_entry(index)
90
+ return unless entry
91
+
92
+ FFI::GDAL.GDALGetColorEntryAsRGB(@color_table_pointer, index, entry.c_pointer)
93
+ return nil if entry.c_pointer.null?
94
+
95
+ entry
96
+ end
97
+
98
+ # Add a new ColorEntry to the ColorTable. Valid values depend on the image
99
+ # type you're working with (i.e. for Tiff, values can be between 0 and
100
+ # 65535). Values must also be relevant to the PaletteInterp type you're
101
+ # working with.
102
+ #
103
+ # @param index [Fixnum] The index of the color table's color entry to set.
104
+ # Must be between 0 and color_entry_count - 1.
105
+ # @param one [Fixnum] The `c1` value of the GDAL::ColorEntry struct
106
+ # to set.
107
+ # @param two [Fixnum] The `c2` value of the GDAL::ColorEntry struct
108
+ # to set.
109
+ # @param three [Fixnum] The `c3` value of the GDAL::ColorEntry
110
+ # struct to set.
111
+ # @param four [Fixnum] The `c4` value of the GDAL::ColorEntry
112
+ # struct to set.
113
+ # @return [GDAL::ColorEntry]
114
+ def add_color_entry(index, one=nil, two=nil, three=nil, four=nil)
115
+ entry = GDAL::ColorEntry.new
116
+ entry.color1 = one if one
117
+ entry.color2 = two if two
118
+ entry.color3 = three if three
119
+ entry.color4 = four if four
120
+
121
+ FFI::GDAL.GDALSetColorEntry(@color_table_pointer, index, entry.c_pointer)
122
+ @color_entries.insert(index, entry)
123
+
124
+ entry
125
+ end
126
+
127
+ # Automatically creates a color ramp from one color entry to another. It
128
+ # can be called several times to create multiple ramps in the same color
129
+ # table.
130
+ #
131
+ # @param start_index [Fixnum] Index to start the ramp on (0..255)
132
+ # @param start_color [GDAL::ColorEntry] Value to start the ramp.
133
+ # @param end_index [Fixnum] Index to end the ramp on (0..255)
134
+ # @param end_color [GDAL::ColorEntry] Value to end the ramp.
135
+ # @return [Fixnum] The total number of entries. nil or -1 on error.
136
+ def create_color_ramp!(start_index, start_color, end_index, end_color)
137
+ start_color_ptr = GDAL._pointer(GDAL::ColorEntry, start_color)
138
+ end_color_ptr = GDAL._pointer(GDAL::ColorEntry, end_color)
139
+
140
+ FFI::GDAL.GDALCreateColorRamp(@color_table_pointer, start_index,
141
+ start_color_ptr,
142
+ end_index,
143
+ end_color_ptr)
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,47 @@
1
+ require 'json'
2
+
3
+ module GDAL
4
+ module ColorTableExtensions
5
+
6
+ def color_entries_for(color_number)
7
+ unless (1..4).to_a.include? color_number
8
+ raise "Invalid ColorEntry number 'color#{color_number}'"
9
+ end
10
+
11
+ 0.upto(color_entry_count - 1).map do |i|
12
+ color_entry(i).send("color#{color_number}".to_sym)
13
+ end
14
+ end
15
+
16
+ # @return [Array<GDAL::ColorEntry>]
17
+ def color_entries
18
+ 0.upto(color_entry_count - 1).map do |i|
19
+ color_entry(i)
20
+ end
21
+ end
22
+
23
+ # Does the same as #color_entries, but calls #color_entry_as_rgb() instead
24
+ # of #color_entry().
25
+ #
26
+ # @return [Array<GDAL::ColorEntry>]
27
+ def color_entries_as_rgb
28
+ 0.upto(color_entry_count - 1).map do |i|
29
+ color_entry_as_rgb(i)
30
+ end
31
+ end
32
+
33
+ # @return [Hash]
34
+ def as_json
35
+ {
36
+ color_entry_count: color_entry_count,
37
+ color_entries: color_entries.map(&:as_json),
38
+ palette_interpretation: palette_interpretation,
39
+ }
40
+ end
41
+
42
+ # @return [String]
43
+ def to_json
44
+ as_json.to_json
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,25 @@
1
+ module GDAL
2
+ module ColorTableTypes
3
+ module CMYK
4
+ def cyans
5
+ color_entries_for(1)
6
+ end
7
+
8
+ def magentas
9
+ color_entries_for(2)
10
+ end
11
+
12
+ def yellows
13
+ color_entries_for(3)
14
+ end
15
+
16
+ def blacks
17
+ color_entries_for(4)
18
+ end
19
+
20
+ def to_a
21
+ NMatrix[cyans, magentas, yellows, blacks].transpose.to_a
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ module GDAL
2
+ module ColorTableTypes
3
+ module Gray
4
+ def grays
5
+ color_entries_for(1)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ module GDAL
2
+ module ColorTableTypes
3
+ module HLS
4
+ def hues
5
+ color_entries_for(1)
6
+ end
7
+
8
+ def lightnesses
9
+ color_entries_for(2)
10
+ end
11
+
12
+ def saturations
13
+ color_entries_for(3)
14
+ end
15
+
16
+ def to_a
17
+ NMatrix[hues, lightnesses, saturations].transpose.to_a
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ module GDAL
2
+ module ColorTableTypes
3
+ module RGB
4
+ def reds
5
+ color_entries_for(1)
6
+ end
7
+
8
+ def greens
9
+ color_entries_for(2)
10
+ end
11
+
12
+ def blues
13
+ color_entries_for(3)
14
+ end
15
+
16
+ def alphas
17
+ color_entries_for(4)
18
+ end
19
+
20
+ def to_a
21
+ NMatrix[reds, greens, blues, alphas].transpose.to_a
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ module GDAL
2
+ class DataType
3
+ # The size in bits.
4
+ #
5
+ # @param gdal_data_type [FFI::GDAL::GDALDataType]
6
+ # @return [Fixnum]
7
+ def self.size(gdal_data_type)
8
+ FFI::GDAL.GDALGetDataTypeSize(gdal_data_type)
9
+ end
10
+
11
+ # @param gdal_data_type [FFI::GDAL::GDALDataType]
12
+ # @return [Fixnum]
13
+ def self.complex?(gdal_data_type)
14
+ FFI::GDAL.GDALDataTypeIsComplex(gdal_data_type)
15
+ end
16
+
17
+ # @param gdal_data_type [FFI::GDAL::GDALDataType]
18
+ # @return [String]
19
+ def self.name(gdal_data_type)
20
+ FFI::GDAL.GDALGetDataTypeName(gdal_data_type)
21
+ end
22
+
23
+ # The data type's symbolic name.
24
+ #
25
+ # @param name [String]
26
+ # @return [FFI::GDAL::GDALDataType]
27
+ def self.by_name(name)
28
+ FFI::GDAL.GDALGetDataTypeByName(name)
29
+ end
30
+
31
+ # @param gdal_data_type1 [FFI::GDAL::GDALDataType]
32
+ # @param gdal_data_type2 [FFI::GDAL::GDALDataType]
33
+ # @return [FFI::GDAL::GDALDataType]
34
+ def self.union(gdal_data_type1, gdal_data_type2)
35
+ FFI::GDAL.GDALDataTypeUnion(gdal_data_type1, gdal_data_type2)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,437 @@
1
+ require 'uri'
2
+ require_relative '../ffi/gdal'
3
+ require_relative 'driver'
4
+ require_relative 'geo_transform'
5
+ require_relative 'raster_band'
6
+ require_relative 'exceptions'
7
+ require_relative 'major_object'
8
+ require_relative 'dataset_extensions'
9
+ require_relative '../ogr/spatial_reference'
10
+
11
+
12
+ module GDAL
13
+
14
+ # A set of associated raster bands and info common to them all. It's also
15
+ # responsible for the georeferencing transform and coordinate system
16
+ # definition of all bands.
17
+ class Dataset
18
+ include MajorObject
19
+ include DatasetExtensions
20
+ include GDAL::Logger
21
+
22
+ ACCESS_FLAGS = {
23
+ 'r' => :GA_ReadOnly,
24
+ 'w' => :GA_Update
25
+ }
26
+
27
+ # @param path [String] Path to the file that contains the dataset. Can be
28
+ # a local file or a URL.
29
+ # @param access_flag [String] 'r' or 'w'.
30
+ def self.open(path, access_flag)
31
+ uri = URI.parse(path)
32
+ file_path = uri.scheme.nil? ? ::File.expand_path(path) : path
33
+
34
+ pointer = FFI::GDAL.GDALOpen(file_path, ACCESS_FLAGS[access_flag])
35
+ raise OpenFailure.new(file_path) if pointer.null?
36
+
37
+ new(pointer)
38
+ end
39
+
40
+ #---------------------------------------------------------------------------
41
+ # Instance methods
42
+ #---------------------------------------------------------------------------
43
+
44
+ # @param dataset_pointer [FFI::Pointer] Pointer to the dataset in memory.
45
+ def initialize(dataset_pointer)
46
+ @dataset_pointer = dataset_pointer
47
+ @last_known_file_list = []
48
+ @open = true
49
+ close_me = -> { self.close }
50
+ ObjectSpace.define_finalizer self, close_me
51
+ end
52
+
53
+ # @return [FFI::Pointer] Pointer to the GDALDatasetH that's represented by
54
+ # this Ruby object.
55
+ def c_pointer
56
+ @dataset_pointer
57
+ end
58
+
59
+ # Close the dataset.
60
+ def close
61
+ if @dataset_pointer.null?
62
+ false
63
+ else
64
+ FFI::GDAL.GDALClose(@dataset_pointer)
65
+ end
66
+ end
67
+
68
+ # @return [Symbol]
69
+ def access_flag
70
+ return nil if null?
71
+
72
+ flag = FFI::GDAL.GDALGetAccess(@dataset_pointer)
73
+
74
+ FFI::GDAL.GDALAccess[flag]
75
+ end
76
+
77
+ # @return [GDAL::Driver] The driver to be used for working with this
78
+ # dataset.
79
+ def driver
80
+ return @driver if @driver
81
+ driver_ptr = FFI::GDAL.GDALGetDatasetDriver(@dataset_pointer)
82
+
83
+ @driver = Driver.new(driver_ptr)
84
+ end
85
+
86
+ # Fetches all files that form the dataset.
87
+ # @return [Array<String>]
88
+ def file_list
89
+ list_pointer = FFI::GDAL.GDALGetFileList(@dataset_pointer)
90
+ return [] if list_pointer.null?
91
+ file_list = list_pointer.get_array_of_string(0)
92
+ FFI::GDAL.CSLDestroy(list_pointer)
93
+
94
+ file_list
95
+ end
96
+
97
+ # Flushes all write-cached data to disk.
98
+ def flush_cache
99
+ FFI::GDAL.GDALFlushCache(@dataset_pointer)
100
+ end
101
+
102
+ # @return [Fixnum]
103
+ def raster_x_size
104
+ return nil if null?
105
+
106
+ FFI::GDAL.GDALGetRasterXSize(@dataset_pointer)
107
+ end
108
+
109
+ # @return [Fixnum]
110
+ def raster_y_size
111
+ return nil if null?
112
+
113
+ FFI::GDAL.GDALGetRasterYSize(@dataset_pointer)
114
+ end
115
+
116
+ # @return [Fixnum]
117
+ def raster_count
118
+ return 0 if null?
119
+
120
+ FFI::GDAL.GDALGetRasterCount(@dataset_pointer)
121
+ end
122
+
123
+ # @param raster_index [Fixnum]
124
+ # @return [GDAL::RasterBand]
125
+ def raster_band(raster_index)
126
+ @raster_bands ||= Array.new(raster_count)
127
+ zero_index = raster_index - 1
128
+
129
+ if @raster_bands[zero_index] && !@raster_bands[zero_index].null?
130
+ return @raster_bands[zero_index]
131
+ end
132
+
133
+ raster_band_ptr = FFI::GDAL.GDALGetRasterBand(@dataset_pointer, raster_index)
134
+ @raster_bands[zero_index] = GDAL::RasterBand.new(raster_band_ptr)
135
+ @raster_bands.compact!
136
+
137
+ @raster_bands[zero_index]
138
+ end
139
+
140
+ # @param type [FFI::GDAL::GDALDataType]
141
+ # @param options [Hash]
142
+ # @return [GDAL::RasterBand, nil]
143
+ def add_band(type, **options)
144
+ options_ptr = GDAL::Options.pointer(options)
145
+ cpl_err = FFI::GDAL.GDALAddBand(@dataset_pointer, type, options_ptr)
146
+ cpl_err.to_bool
147
+
148
+ raster_band(raster_count)
149
+ end
150
+
151
+ # Adds a mask band to the dataset
152
+ #
153
+ # @return [Boolean]
154
+ def create_mask_band
155
+ cpl_err = FFI::GDAL.GDALCreateDatasetMaskBand(@dataset_pointer, 0)
156
+
157
+ cpl_err.to_bool
158
+ end
159
+
160
+ # @return [String]
161
+ def projection
162
+ FFI::GDAL.GDALGetProjectionRef(@dataset_pointer)
163
+ end
164
+
165
+ # @param new_projection [String]
166
+ # @return [Boolean]
167
+ def projection=(new_projection)
168
+ cpl_err = FFI::GDAL.GDALSetProjection(@dataset_pointer, new_projection)
169
+
170
+ cpl_err.to_bool
171
+ end
172
+
173
+ # @return [GDAL::GeoTransform]
174
+ def geo_transform
175
+ return @geo_transform if @geo_transform
176
+
177
+ geo_transform_pointer = FFI::MemoryPointer.new(:double, 6)
178
+ cpl_err = FFI::GDAL.GDALGetGeoTransform(@dataset_pointer, geo_transform_pointer)
179
+ cpl_err.to_ruby
180
+
181
+ @geo_transform = GeoTransform.new(geo_transform_pointer)
182
+ end
183
+
184
+ # @param new_transform [GDAL::GeoTransform]
185
+ # @return [GDAL::GeoTransform]
186
+ def geo_transform=(new_transform)
187
+ new_pointer = FFI::Pointer.new(new_transform.c_pointer)
188
+ cpl_err = FFI::GDAL.GDALSetGeoTransform(@dataset_pointer, new_pointer)
189
+ cpl_err.to_bool
190
+
191
+ @geo_transform = GeoTransform.new(new_pointer)
192
+ end
193
+
194
+ # @return [Fixnum]
195
+ def gcp_count
196
+ return 0 if null?
197
+
198
+ FFI::GDAL.GDALGetGCPCount(@dataset_pointer)
199
+ end
200
+
201
+ # @return [String]
202
+ def gcp_projection
203
+ return '' if null?
204
+
205
+ FFI::GDAL.GDALGetGCPProjection(@dataset_pointer)
206
+ end
207
+
208
+ # @return [FFI::GDAL::GDALGCP]
209
+ def gcps
210
+ return FFI::GDAL.GDALGCP.new if null?
211
+
212
+ gcp_array_pointer = FFI::GDAL.GDALGetGCPs(@dataset_pointer)
213
+
214
+ if gcp_array_pointer.null?
215
+ FFI::GDAL.GDALGCP.new
216
+ else
217
+ FFI::GDAL.GDALGCP.new(gcp_array_pointer)
218
+ end
219
+ end
220
+
221
+ # @return [Fixnum]
222
+ def layer_count
223
+ if GDAL._supported?(:GDALDatasetGetLayerCount)
224
+ FFI::GDAL.GDALDatasetGetLayerCount(@dataset_pointer)
225
+ end
226
+ end
227
+
228
+ # @param resampling [String, Symbol] One of:
229
+ # * :nearest
230
+ # * :gauss
231
+ # * :cubic
232
+ # * :average
233
+ # * :mode
234
+ # * :average_magphase
235
+ # * :none
236
+ # @param overview_levels [Array<Fixnum>] The list of overview decimation
237
+ # factors to build.
238
+ # @param band_numbers [Array<Fixnum>] The numbers of the bands to build
239
+ # overviews from.
240
+ def build_overviews(resampling, overview_levels, band_numbers=nil, &progress)
241
+ resampling_string = if resampling.is_a? String
242
+ resampling.upcase
243
+ elsif resampling.is_a? Symbol
244
+ resampling.to_s.upcase
245
+ end
246
+
247
+ overview_levels_ptr = FFI::MemoryPointer.new(:int, overview_levels.size)
248
+ overview_levels_ptr.write_array_of_int(overview_levels)
249
+
250
+ if band_numbers
251
+ band_count = band_numbers.size
252
+ band_numbers_ptr = FFI::MemoryPointer.new(:int, band_count)
253
+ band_numbers_ptr.write_array_of_int(band_numbers)
254
+ else
255
+ band_numbers_ptr = nil
256
+ band_count = nil
257
+ end
258
+
259
+ cpl_err = FFI::GDAL.GDALBuildOverviews(@dataset_pointer,
260
+ resampling_string,
261
+ overview_levels.size,
262
+ overview_levels_ptr,
263
+ band_count,
264
+ band_numbers_ptr,
265
+ progress,
266
+ nil
267
+ )
268
+
269
+ cpl_err.to_bool
270
+ end
271
+
272
+ # Rasterizes the geometric objects +geometries+ into this raster dataset.
273
+ # +transformer+ can be nil as long as the +geometries+ are within the
274
+ # georeferenced coordinates of this raster's dataset.
275
+ #
276
+ # @param band_numbers [Array<Fixnum>, Fixnum]
277
+ # @param geometries [Array<OGR::Geometry>, OGR::Geometry]
278
+ # @param burn_values [Array<Float>, Float]
279
+ # @param transformer [Proc]
280
+ # @param options [Hash]
281
+ # @option options all_touched [Boolean] If +true+, sets all pixels touched
282
+ # by the line or polygons, not just those whose center is within the
283
+ # polygon or that are selected by Brezenham's line algorithm. Defaults to
284
+ # +false+.
285
+ # @option options burn_value_from ["Z"] Use the Z values of the geometries.
286
+ # @option @options merge_alg [String] "REPLACE" or "ADD". REPLACE results
287
+ # in overwriting of value, while ADD adds the new value to the existing
288
+ # raster, suitable for heatmaps for instance.
289
+ def rasterize_geometries!(band_numbers, geometries, burn_values,
290
+ transformer: nil, transform_arg: nil, **options, &progress_block)
291
+
292
+ if geo_transform.nil? && gcp_count.zero?
293
+ raise "Can't rasterize geometries--no geo_transform or GCPs have been defined on the dataset."
294
+ end
295
+
296
+ gdal_options = GDAL::Options.pointer(options)
297
+ band_numbers = band_numbers.is_a?(Array) ? band_numbers : [band_numbers]
298
+ geometries = geometries.is_a?(Array) ? geometries : [geometries]
299
+ burn_values = burn_values.is_a?(Array) ? burn_values : [burn_values]
300
+
301
+ band_numbers_ptr = FFI::MemoryPointer.new(:pointer, band_numbers.size)
302
+ band_numbers_ptr.write_array_of_int(band_numbers)
303
+
304
+ geometries_ptr = FFI::MemoryPointer.new(:pointer, geometries.size)
305
+ geometries_ptr.write_array_of_pointer(geometries.map(&:c_pointer))
306
+
307
+ burn_values_ptr = FFI::MemoryPointer.new(:pointer, burn_values.size)
308
+ burn_values_ptr.write_array_of_double(burn_values)
309
+
310
+ # not allowing for now
311
+ progress_callback_data = nil
312
+
313
+ cpl_err = FFI::GDAL.GDALRasterizeGeometries(@dataset_pointer,
314
+ band_numbers.size,
315
+ band_numbers_ptr,
316
+ geometries.size,
317
+ geometries_ptr,
318
+ transformer,
319
+ transform_arg,
320
+ burn_values_ptr,
321
+ gdal_options,
322
+ progress_block,
323
+ progress_callback_data)
324
+
325
+ cpl_err.to_bool
326
+ end
327
+
328
+ # @param band_numbers [Array<Fixnum>, Fixnum]
329
+ # @param layers [Array<OGR::Layer>, OGR::Layer]
330
+ # @param burn_values [Array<Float>, Float]
331
+ # @param transformer [Proc]
332
+ # @param options [Hash]
333
+ # @option options attribute [String] An attribute field on features to be
334
+ # used for a burn-in value, which will be burned into all output bands.
335
+ # @option options chunkysize [Fixnum] The height in lines of the chunk to
336
+ # operate on.
337
+ # @option options all_touched [Boolean] If +true+, sets all pixels touched
338
+ # by the line or polygons, not just those whose center is within the
339
+ # polygon or that are selected by Brezenham's line algorithm. Defaults to
340
+ # +false+.
341
+ # @option options burn_value_from ["Z"] Use the Z values of the geometries.
342
+ # @option @options merge_alg [String] "REPLACE" or "ADD". REPLACE results
343
+ # in overwriting of value, while ADD adds the new value to the existing
344
+ # raster, suitable for heatmaps for instance.
345
+ def rasterize_layers!(band_numbers, layers, burn_values,
346
+ transformer: nil, transform_arg: nil, **options, &progress_block)
347
+ gdal_options = GDAL::Options.pointer(options)
348
+ band_numbers = band_numbers.is_a?(Array) ? band_numbers : [band_numbers]
349
+ log "band numbers: #{band_numbers}"
350
+
351
+ layers = layers.is_a?(Array) ? layers : [layers]
352
+ log "layers: #{layers}"
353
+
354
+ burn_values = burn_values.is_a?(Array) ? burn_values : [burn_values]
355
+ log "burn values: #{burn_values}"
356
+
357
+ band_numbers_ptr = FFI::MemoryPointer.new(:pointer, band_numbers.size)
358
+ band_numbers_ptr.write_array_of_int(band_numbers)
359
+ log "band numbers ptr null? #{band_numbers_ptr.null?}"
360
+
361
+ layers_ptr = FFI::MemoryPointer.new(:pointer, layers.size)
362
+ layers_ptr.write_array_of_pointer(layers.map(&:c_pointer))
363
+ log "layers ptr null? #{layers_ptr.null?}"
364
+
365
+ burn_values_ptr = FFI::MemoryPointer.new(:pointer, burn_values.size)
366
+ burn_values_ptr.write_array_of_double(burn_values)
367
+ log "burn value ptr null? #{burn_values_ptr.null?}"
368
+
369
+ cpl_err = FFI::GDAL.GDALRasterizeLayers(@dataset_pointer, # hDS
370
+ band_numbers.size, # nBandCount
371
+ band_numbers_ptr, # panBandList
372
+ layers.size, # nLayerCount
373
+ layers_ptr, # pahLayers
374
+ transformer, # pfnTransformer
375
+ transform_arg, # pTransformerArg
376
+ burn_values_ptr, # padfLayerBurnValues
377
+ gdal_options, # papszOptions
378
+ progress_block, # pfnProgress
379
+ nil) # pProgressArg
380
+
381
+ cpl_err.to_bool
382
+ end
383
+
384
+ # @todo Implement
385
+ def simple_image_warp(destination_file, driver, band_numbers,
386
+ transformer, transformer_arg, **options)
387
+ raise NotImplementedError, '#simple_image_warp not yet implemented.'
388
+
389
+ options_ptr = GDAL::Options.pointer(options)
390
+ driver = GDAL::Driver.by_name(driver)
391
+ destination_dataset_ptr = driver.open(destination_file, 'w')
392
+
393
+ band_numbers = band_numbers.is_a?(Array) ? band_numbers : [band_numbers]
394
+ log "band numbers: #{band_numbers}"
395
+
396
+ bands_ptr = FFI::MemoryPointer.new(:pointer, band_numbers.size)
397
+ bands_ptr.write_array_of_int(band_numbers)
398
+ log "band numbers ptr null? #{bands_ptr.null?}"
399
+
400
+ log "transformer: #{transformer}"
401
+
402
+ success = FFI::GDAL.GDALSimpleImageWarp(@dataset_pointer,
403
+ destination_dataset_ptr,
404
+ band_numbers.size,
405
+ bands_ptr,
406
+ transformer,
407
+ nil,
408
+ nil,
409
+ nil,
410
+ options_ptr)
411
+
412
+ raise 'Image warp failed!' unless success
413
+
414
+ GDAL::Dataset.new(destination_dataset_ptr)
415
+ end
416
+
417
+ # @return [FFI::Pointer] Pointer to be used for transformations.
418
+ def create_general_image_projection_transformer(destination_dataset,
419
+ source_projection: nil, destination_projection: nil, use_gcps: false)
420
+ log "destination dataset: #{destination_dataset}"
421
+
422
+ error_threshold = 0.0
423
+ order = 0
424
+
425
+ transformer_ptr = FFI::GDAL.GDALCreateGenImgProjTransformer(@dataset_pointer,
426
+ source_projection,
427
+ destination_dataset.c_pointer,
428
+ destination_projection,
429
+ use_gcps,
430
+ error_threshold,
431
+ order)
432
+
433
+ log "transformer pointer: #{transformer_ptr}"
434
+ transformer_ptr
435
+ end
436
+ end
437
+ end