ffi-gdal 0.0.4 → 1.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +7 -1
- data/Rakefile +12 -1
- data/TODO.md +11 -0
- data/ffi-gdal.gemspec +2 -2
- data/lib/ext/error_symbols.rb +59 -0
- data/lib/ext/float_ext.rb +15 -0
- data/lib/ext/narray_ext.rb +16 -0
- data/lib/ext/to_bool.rb +2 -0
- data/lib/ffi-gdal.rb +139 -4
- data/lib/ffi/{gdal/cpl_conv.rb → cpl/conv_h.rb} +2 -3
- data/lib/ffi/{gdal/cpl_error.rb → cpl/error_h.rb} +1 -25
- data/lib/ffi/cpl/minixml_h.rb +14 -0
- data/lib/ffi/{gdal/cpl_string.rb → cpl/string_h.rb} +0 -25
- data/lib/ffi/{gdal/cpl_vsi.rb → cpl/vsi_h.rb} +0 -0
- data/lib/ffi/cpl/xml_node.rb +13 -0
- data/lib/ffi/gdal.rb +57 -593
- data/lib/ffi/gdal/alg_h.rb +127 -0
- data/lib/ffi/gdal/gdal_grid_data_metrics_options.rb +14 -0
- data/lib/ffi/gdal/gdal_grid_inverse_distance_to_a_power_options.rb +19 -0
- data/lib/ffi/gdal/gdal_grid_moving_average_options.rb +14 -0
- data/lib/ffi/gdal/gdal_grid_nearest_neighbor_options.rb +13 -0
- data/lib/ffi/gdal/gdal_h.rb +683 -0
- data/lib/ffi/gdal/gdal_rpc_info.rb +27 -0
- data/lib/ffi/gdal/gdal_transformer_info.rb +14 -0
- data/lib/ffi/gdal/gdal_warp_options.rb +43 -0
- data/lib/ffi/gdal/grid_h.rb +51 -0
- data/lib/ffi/gdal/version.rb +1 -1
- data/lib/ffi/gdal/warper_h.rb +48 -0
- data/lib/ffi/ogr.rb +12 -0
- data/lib/ffi/ogr/api_h.rb +553 -0
- data/lib/ffi/ogr/core_h.rb +148 -0
- data/lib/ffi/ogr/featurestyle_h.rb +22 -0
- data/lib/ffi/ogr/geocoding_h.rb +21 -0
- data/lib/ffi/ogr/ogr_contour_writer_info.rb +14 -0
- data/lib/ffi/ogr/ogr_envelope.rb +12 -0
- data/lib/ffi/ogr/ogr_envelope_3d.rb +14 -0
- data/lib/ffi/ogr/ogr_field.rb +50 -0
- data/lib/ffi/ogr/ogr_style_param.rb +12 -0
- data/lib/ffi/ogr/ogr_style_value.rb +13 -0
- data/lib/ffi/ogr/srs_api_h.rb +325 -0
- data/lib/gdal/color_entry.rb +47 -0
- data/lib/gdal/color_entry_extensions.rb +30 -0
- data/lib/gdal/color_interpretation.rb +15 -0
- data/lib/gdal/color_table.rb +146 -0
- data/lib/gdal/color_table_extensions.rb +47 -0
- data/lib/gdal/color_table_types/cmyk.rb +25 -0
- data/lib/gdal/color_table_types/gray.rb +9 -0
- data/lib/gdal/color_table_types/hls.rb +21 -0
- data/lib/gdal/color_table_types/rgb.rb +25 -0
- data/lib/gdal/data_type.rb +38 -0
- data/lib/gdal/dataset.rb +437 -0
- data/lib/gdal/dataset_extensions.rb +496 -0
- data/lib/gdal/driver.rb +244 -0
- data/lib/gdal/driver_extensions.rb +56 -0
- data/lib/gdal/environment_methods.rb +43 -0
- data/lib/{ffi-gdal → gdal}/exceptions.rb +4 -1
- data/lib/gdal/geo_transform.rb +188 -0
- data/lib/gdal/geo_transform_extensions.rb +90 -0
- data/lib/gdal/logger.rb +7 -0
- data/lib/{ffi-gdal → gdal}/major_object.rb +15 -14
- data/lib/gdal/options.rb +49 -0
- data/lib/gdal/raster_attribute_table.rb +185 -0
- data/lib/gdal/raster_attribute_table_extensions.rb +40 -0
- data/lib/{ffi-gdal → gdal}/raster_band.rb +227 -99
- data/lib/gdal/raster_band_extensions.rb +198 -0
- data/lib/{ffi-gdal → gdal}/version_info.rb +8 -0
- data/lib/gdal/warp_operation.rb +96 -0
- data/lib/ogr/coordinate_transformation.rb +108 -0
- data/lib/ogr/data_source.rb +172 -0
- data/lib/ogr/data_source_extensions.rb +32 -0
- data/lib/ogr/driver.rb +119 -0
- data/lib/ogr/envelope.rb +80 -0
- data/lib/ogr/envelope_extensions.rb +92 -0
- data/lib/ogr/exceptions.rb +35 -0
- data/lib/ogr/feature.rb +212 -0
- data/lib/ogr/feature_definition.rb +120 -0
- data/lib/ogr/feature_definition_extensions.rb +36 -0
- data/lib/ogr/feature_extensions.rb +31 -0
- data/lib/ogr/field.rb +91 -0
- data/lib/ogr/field_extensions.rb +23 -0
- data/lib/ogr/geocoding_session.rb +84 -0
- data/lib/ogr/geometry.rb +617 -0
- data/lib/ogr/geometry_extensions.rb +60 -0
- data/lib/ogr/geometry_types/collection.rb +45 -0
- data/lib/ogr/geometry_types/curve.rb +120 -0
- data/lib/ogr/geometry_types/surface.rb +20 -0
- data/lib/ogr/layer.rb +226 -0
- data/lib/ogr/layer_extensions.rb +55 -0
- data/lib/ogr/line_string.rb +7 -0
- data/lib/ogr/linear_ring.rb +6 -0
- data/lib/ogr/multi_line_string.rb +9 -0
- data/lib/ogr/multi_point.rb +7 -0
- data/lib/ogr/multi_polygon.rb +14 -0
- data/lib/ogr/point.rb +89 -0
- data/lib/ogr/polygon.rb +9 -0
- data/lib/ogr/spatial_reference.rb +723 -0
- data/lib/ogr/spatial_reference_extensions.rb +32 -0
- data/lib/ogr/style_table.rb +17 -0
- data/lib/ogr/style_table_extensions.rb +16 -0
- data/spec/{ffi-gdal/integration → integration}/color_table_info_spec.rb +1 -1
- data/spec/{ffi-gdal/integration → integration}/dataset_info_spec.rb +0 -0
- data/spec/{ffi-gdal/integration → integration}/driver_info_spec.rb +1 -1
- data/spec/{ffi-gdal/integration → integration}/geo_transform_info_spec.rb +0 -0
- data/spec/{ffi-gdal/integration → integration}/raster_attribute_table_info_spec.rb +1 -1
- data/spec/{ffi-gdal/integration → integration}/raster_band_info_spec.rb +5 -5
- data/spec/spec_helper.rb +4 -1
- data/spec/support/shapefiles/states_21basic/states.prj +1 -0
- data/spec/support/shapefiles/states_21basic/states.sbn +0 -0
- data/spec/support/shapefiles/states_21basic/states.sbx +0 -0
- data/spec/support/shapefiles/states_21basic/states.shp +0 -0
- data/spec/support/worldfiles/SR_50M/SR_50M.VERSION.txt +1 -0
- data/spec/support/worldfiles/SR_50M/SR_50M.prj +1 -0
- data/spec/support/worldfiles/SR_50M/SR_50M.tfw +6 -0
- data/spec/{ext/cpl_error_symbols_spec.rb → unit/ext/error_symbols_spec.rb} +1 -1
- data/spec/unit/gdal/color_table_spec.rb +146 -0
- data/spec/unit/ogr/layer_spec.rb +97 -0
- data/spec/unit/ogr/linear_ring_spec.rb +111 -0
- data/spec/unit/ogr/point_spec.rb +321 -0
- data/spec/{ffi-gdal/unit → unit}/version_info_spec.rb +1 -1
- data/testing_gdal.rb +168 -0
- data/testing_gdalwarp.rb +91 -0
- data/testing_layer_to_layer.rb +35 -0
- data/testing_ndvi.rb +76 -0
- data/testing_nir.rb +77 -0
- data/testing_ogr.rb +63 -0
- metadata +167 -59
- data/lib/ext/cpl_error_symbols.rb +0 -37
- data/lib/ffi-gdal/color_table.rb +0 -59
- data/lib/ffi-gdal/dataset.rb +0 -359
- data/lib/ffi-gdal/driver.rb +0 -151
- data/lib/ffi-gdal/geo_transform.rb +0 -137
- data/lib/ffi-gdal/raster_attribute_table.rb +0 -78
- data/lib/ffi/gdal/ogr_api.rb +0 -21
- data/lib/ffi/gdal/ogr_core.rb +0 -195
- data/lib/ffi/gdal/ogr_srs_api.rb +0 -44
- data/meow.rb +0 -144
- 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,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
|
data/lib/gdal/dataset.rb
ADDED
@@ -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
|