ffi-gdal 1.0.0.beta4 → 1.0.0.beta5
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/History.md +80 -54
- data/README.md +1 -1
- data/lib/ffi/gdal/grid_inverse_distance_to_a_power_options.rb +2 -2
- data/lib/ffi/gdal/version.rb +1 -1
- data/lib/gdal/dataset.rb +4 -18
- data/lib/gdal/dataset_mixins/extensions.rb +75 -168
- data/lib/gdal/exceptions.rb +3 -0
- data/lib/gdal/grid_types/data_metrics_base.rb +1 -1
- data/lib/gdal/grid_types/inverse_distance_to_a_power.rb +1 -1
- data/lib/gdal/grid_types/moving_average.rb +1 -1
- data/lib/gdal/grid_types/nearest_neighbor.rb +1 -1
- data/lib/gdal/internal_helpers.rb +12 -0
- data/lib/gdal/raster_band.rb +90 -33
- data/lib/gdal/raster_band_classifier.rb +5 -2
- data/lib/gdal/raster_band_mixins/extensions.rb +12 -13
- data/lib/ogr/geometry.rb +1 -1
- data/lib/ogr/internal_helpers.rb +1 -0
- data/lib/ogr/layer_mixins/ogr_field_methods.rb +1 -0
- data/lib/ogr/spatial_reference.rb +1 -0
- data/spec/unit/gdal/raster_band_classifier_spec.rb +5 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5e2273d2b75bb5a7af15693af6a84cb32c74852
|
4
|
+
data.tar.gz: f986fe1b75a472ce0a0d663d13ceed15714e0ef0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 584e34e7de26e51fff046707de49750f8071d937dac8259ad594387f1ff60c7bca2fa72c22fad48525fdd3cf42a6d734a564d4d054aac675ba48c7a55ed4e3e8
|
7
|
+
data.tar.gz: c8eca7619a1eeaf878582c8efead2bde968a52149e92cd64ad37fbb3475bcc2f25ba2182da3cfa200e9ce8925ca90c60a55be5b59b43998d1d2a867843f35e7a
|
data/History.md
CHANGED
@@ -1,83 +1,109 @@
|
|
1
|
-
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
Format for this file derived from [http://keepachangelog.com](http://keepachangelog.com).
|
4
|
+
|
5
|
+
## 1.0.0.beta5 / 2015-06-16
|
6
|
+
|
7
|
+
* Improvements
|
8
|
+
* `GDAL::RasterBandClassifier#equal_count_ranges` now returns `nil` if there
|
9
|
+
aren't enough points per group/class to return the requested number of
|
10
|
+
breaks.
|
11
|
+
* Simplified NDVI (and related) methods in
|
12
|
+
`GDAL::DatasetMixins::Extensions`.
|
13
|
+
* NDVI and related methods in `GDAL::DatasetMixins::Extensions` now close
|
14
|
+
the newly created dataset instead of leaving it open. It's been far too
|
15
|
+
easy to forget to close the dataset after creation, leaving seemingly
|
16
|
+
incorrect resulting datasets (since GDAL doesn't flush writes until the
|
17
|
+
dataset is closed).
|
18
|
+
* NDVI methods in `GDAL::DatasetMixins::Extensions` no longer check for
|
19
|
+
NaNs after doing the NDVI calculations, thus speeding up the algorithm.
|
20
|
+
* `GDAL::DatasetMixins::Extensions#remove_negatives_from` now uses an NArray
|
21
|
+
mask to remove the negative values instead of looping through each value.
|
22
|
+
* Added `GDAL::RasterBand#raster_io` and refactored
|
23
|
+
`GDAL::RasterBand#write_array` to use it.
|
24
|
+
* `GDAL::DatasetMixins::Extensions` NDVI methods now default to NODATA of -9999.0.
|
25
|
+
|
26
|
+
## 1.0.0.beta4 / 2015-04-22
|
2
27
|
|
3
28
|
Whoa there's lots of changes here... Many are outlined below, but there's really
|
4
29
|
a ton more.
|
5
30
|
|
6
31
|
* Improvements
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
32
|
+
* Full refactor of FFI/C function wrapper. Closer mapping of Ruby functions
|
33
|
+
within modules to the C-header files where the functions actually reside.
|
34
|
+
* Full redesign of the error handling mechanism; ffi-gdal now hooks in to
|
35
|
+
GDAL's error handling, thus errors raised from GDAL automagically get handed
|
36
|
+
over as Ruby exceptions. (GDAL only; OGR doesn't provide this.) This also
|
37
|
+
entailed adding a bunch of new exceptions and renaming some old ones.
|
38
|
+
* Better library finding on Linux.
|
39
|
+
* Wrapped most of GDAL's Grid API.
|
40
|
+
* Wrapped some of GDAL's Warp API.
|
41
|
+
* GDAL::Dataset can now open PostGISRaster datasets.
|
42
|
+
* Lots more OGR love. Much of this API has now been vetted.
|
43
|
+
* Added `GDAL::RasterClassifier` for classifying raster bands.
|
44
|
+
* Added some wrapper methods for classes that support capability testing.
|
20
45
|
* Bug fixes
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
46
|
+
* Fixed some GDAL::Dataset extension methods (`extract_ndvi` and friends)
|
47
|
+
that weren't properly handling various data types.
|
48
|
+
* Better handling of large files.
|
49
|
+
* Fixed regular crashes when dealing with OGR Geometries and
|
50
|
+
SpatialReferences.
|
26
51
|
|
27
|
-
|
52
|
+
## 1.0.0.beta3 / 2014-11-11
|
28
53
|
|
29
54
|
* Bug fixes
|
30
|
-
|
31
|
-
|
55
|
+
* `ogr/exceptions` wasn't being required for `ext/error_symbols.rb`, thus
|
56
|
+
any use of an OGR exception was causing a `NameError`.
|
32
57
|
|
33
|
-
|
58
|
+
## 1.0.0.beta2 / 2014-10-23
|
34
59
|
|
35
60
|
* Improvements
|
36
|
-
|
37
|
-
|
38
|
-
|
61
|
+
* Added more documentation
|
62
|
+
* Uncommented `attach_function` calls that had been commented out due to
|
63
|
+
lack of support in versions I'd tested on. These get handled now on load.
|
64
|
+
|
65
|
+
## 1.0.0.beta1 / 2014-10-23
|
39
66
|
|
40
|
-
|
67
|
+
Lots of changes, so just the highlights here...
|
41
68
|
|
42
|
-
* Lots of changes, so just the highlights here...
|
43
69
|
* API Improvements
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
70
|
+
* Added C and Ruby wrapper for most of OGR.
|
71
|
+
* Better handling of CPLErr return values.
|
72
|
+
* Allow loading, even when C functions aren't defined in the version of
|
73
|
+
GDAL that you're using.
|
74
|
+
* Split out additions to GDAL/OGR in `*_extensions.rb` modules. Methods
|
75
|
+
contained in `Extentions` modules don't directly wrap GDAL/OGR functions,
|
76
|
+
but either provide new functionality or attempt to make library usage more
|
77
|
+
Rubyesque.
|
78
|
+
* Added `#as_json`, `#to_json` to many classes.
|
53
79
|
* Internal Improvements
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
80
|
+
* Lots of cleanup of class internals.
|
81
|
+
* `autoload` child GDAL and OGR Ruby classes.
|
82
|
+
* Renamed files under ffi/ that were derived from GDAL/OGR header files to
|
83
|
+
include `_h` in the name.
|
58
84
|
|
59
|
-
|
85
|
+
## 0.0.4 / 2014-09-27
|
60
86
|
|
61
87
|
* Bug fixes
|
62
|
-
|
88
|
+
* Fixed failure to load on Ubuntu 12.04 (GDAL v1.7.3).
|
63
89
|
|
64
|
-
|
90
|
+
## 0.0.3 / 2014-09-26
|
65
91
|
|
66
92
|
* Improvements
|
67
|
-
|
68
|
-
|
93
|
+
* The `approx_ok` param for `RasterBand#histogram` should default to
|
94
|
+
`false` (preferring data exactness over performance).
|
69
95
|
* Bug fixes
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
96
|
+
* Fixed URL silliness introduced in 0.0.2.
|
97
|
+
* `Dataset#*_band` methods should return `nil` if the band with that color
|
98
|
+
isn't found.
|
99
|
+
* `RasterBand#default_histogram` died if the band didn't have any values.
|
100
|
+
* `RasterBand#histogram` wasn't returning totals.
|
75
101
|
|
76
|
-
|
102
|
+
## 0.0.2 / 2014-09-26
|
77
103
|
|
78
104
|
* New things
|
79
|
-
|
105
|
+
* Added ability to pass a URL into `GDAL::Dataset`.
|
80
106
|
|
81
|
-
|
107
|
+
## 0.0.1 / 2014-09-26
|
82
108
|
|
83
109
|
* Happy Birthday!
|
data/README.md
CHANGED
@@ -52,7 +52,7 @@ Testing
|
|
52
52
|
|
53
53
|
You'll need some images to run the integration specs against, and instead of
|
54
54
|
keeping those as part of this repo, there's a Rake task that will pull OSGeo's
|
55
|
-
set of sample geotiffs down via FTP. Running `rake
|
55
|
+
set of sample geotiffs down via FTP. Running `rake get_tiffs` will pull
|
56
56
|
everything down from ftp://downloads.osgeo.org/geotiff/samples and put the
|
57
57
|
files under spec/support/images/osgeo/geotiff.
|
58
58
|
|
@@ -10,8 +10,8 @@ module FFI
|
|
10
10
|
:radius1, :double,
|
11
11
|
:radius2, :double,
|
12
12
|
:angle, :double,
|
13
|
-
:max_points, :GUInt32,
|
14
|
-
:min_points, :GUInt32,
|
13
|
+
:max_points, CPL::Port.find_type(:GUInt32),
|
14
|
+
:min_points, CPL::Port.find_type(:GUInt32),
|
15
15
|
:no_data_value, :double
|
16
16
|
end
|
17
17
|
end
|
data/lib/ffi/gdal/version.rb
CHANGED
data/lib/gdal/dataset.rb
CHANGED
@@ -66,6 +66,7 @@ module GDAL
|
|
66
66
|
ObjectSpace.define_finalizer self, -> { close }
|
67
67
|
|
68
68
|
@geo_transform = nil
|
69
|
+
@raster_bands = Array.new(raster_count)
|
69
70
|
end
|
70
71
|
|
71
72
|
# Close the dataset.
|
@@ -137,18 +138,9 @@ module GDAL
|
|
137
138
|
fail GDAL::InvalidRasterBand, "Invalid raster band number '#{raster_index}'. Must be <= #{raster_count}"
|
138
139
|
end
|
139
140
|
|
140
|
-
@raster_bands ||= Array.new(raster_count)
|
141
|
-
zero_index = raster_index - 1
|
142
|
-
|
143
|
-
if @raster_bands[zero_index] && !@raster_bands[zero_index].null?
|
144
|
-
return @raster_bands[zero_index]
|
145
|
-
end
|
146
|
-
|
147
141
|
raster_band_ptr = FFI::GDAL.GDALGetRasterBand(@c_pointer, raster_index)
|
148
|
-
@raster_bands[zero_index] = GDAL::RasterBand.new(raster_band_ptr)
|
149
|
-
@raster_bands.compact!
|
150
142
|
|
151
|
-
|
143
|
+
GDAL::RasterBand.new(raster_band_ptr)
|
152
144
|
end
|
153
145
|
|
154
146
|
# @param type [FFI::GDAL::DataType]
|
@@ -177,7 +169,7 @@ module GDAL
|
|
177
169
|
# @param new_projection [String]
|
178
170
|
# @return [Boolean]
|
179
171
|
def projection=(new_projection)
|
180
|
-
|
172
|
+
FFI::GDAL.GDALSetProjection(@c_pointer, new_projection.to_s)
|
181
173
|
end
|
182
174
|
|
183
175
|
# @return [GDAL::GeoTransform]
|
@@ -299,13 +291,7 @@ module GDAL
|
|
299
291
|
x_size ||= raster_x_size
|
300
292
|
y_size ||= raster_y_size
|
301
293
|
|
302
|
-
gdal_access_flag =
|
303
|
-
case access_flag
|
304
|
-
when 'r' then :GF_Read
|
305
|
-
when 'w' then :GF_Write
|
306
|
-
else fail "Invalid access flag: #{access_flag}"
|
307
|
-
end
|
308
|
-
|
294
|
+
gdal_access_flag = GDAL._gdal_access_flag(access_flag)
|
309
295
|
x_buffer_size = x_size
|
310
296
|
y_buffer_size = y_size
|
311
297
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'narray'
|
3
|
+
require_relative '../../ffi-gdal'
|
3
4
|
require_relative '../raster_band'
|
4
5
|
require_relative '../warp_operation'
|
5
6
|
require_relative '../../ogr/driver'
|
@@ -11,119 +12,53 @@ module GDAL
|
|
11
12
|
module DatasetMixins
|
12
13
|
# Methods not originally supplied with GDAL, but enhance it.
|
13
14
|
module Extensions
|
14
|
-
# Computes NDVI from the red and near-infrared bands in the dataset.
|
15
|
-
# a GDAL::RequiredBandNotFound if one of those band types isn't
|
15
|
+
# Computes NDVI from the red and near-infrared bands in the dataset.
|
16
|
+
# Raises a GDAL::RequiredBandNotFound if one of those band types isn't
|
17
|
+
# found. Also, it closes the dataset to ensure all data and metadata have
|
18
|
+
# been flushed to disk. To work with the file, you'll need to reopen it.
|
16
19
|
#
|
17
20
|
# @param destination [String] Path to output the new dataset to.
|
18
|
-
# @param
|
19
|
-
#
|
20
|
-
#
|
21
|
+
# @param red_band_number [Fixnum] Number of the band in the dataset that
|
22
|
+
# contains red data. Note that you can pass in band numbers of other
|
23
|
+
# types to perform NDVI using that type (ex. GNDVI).
|
24
|
+
# @param nir_band_number [Fixnum] Number of the band in the dataset that
|
25
|
+
# contains near-infrared data data.
|
26
|
+
# @param driver_name [String] The driver name to use for creating the
|
27
|
+
# dataset. Defaults to "GTiff".
|
21
28
|
# @param output_data_type [FFI::GDAL::DataType] Resulting dataset will be
|
22
|
-
# in this data type.
|
29
|
+
# in this data type. Defaults to use the current data type.
|
23
30
|
# @param remove_negatives [Boolean] Remove negative values after
|
24
|
-
# calculating NDVI.
|
25
|
-
# @param no_data_value [Float]
|
31
|
+
# calculating NDVI. Defaults to +false+.
|
32
|
+
# @param no_data_value [Float] Value to set the band's NODATA value to.
|
33
|
+
# Defaults to -9999.0.
|
26
34
|
# @param options [Hash] Options that get used for creating the new NDVI
|
27
35
|
# dataset. See docs for GDAL::Driver#create_dataset.
|
28
36
|
# @return [GDAL::Dataset] The new NDVI dataset. *Be sure to call #close on
|
29
37
|
# this object or the data may not persist!*
|
30
|
-
def extract_ndvi(destination, driver_name: 'GTiff',
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
red: red_band,
|
40
|
-
green: green_band,
|
41
|
-
blue: blue_band,
|
42
|
-
nir: undefined_band
|
43
|
-
}
|
44
|
-
end
|
45
|
-
|
46
|
-
red = original_bands[:red]
|
47
|
-
nir = original_bands[:nir]
|
48
|
-
|
49
|
-
if red.nil?
|
50
|
-
fail RequiredBandNotFound, 'Red band not found.'
|
51
|
-
elsif nir.nil?
|
52
|
-
fail RequiredBandNotFound, 'Near-infrared'
|
53
|
-
end
|
54
|
-
|
55
|
-
the_array = calculate_ndvi(red.to_na,
|
56
|
-
nir.to_na,
|
57
|
-
no_data_value,
|
58
|
-
remove_negatives,
|
59
|
-
output_data_type)
|
38
|
+
def extract_ndvi(destination, red_band_number, nir_band_number, driver_name: 'GTiff',
|
39
|
+
output_data_type: nil, remove_negatives: false, no_data_value: -9999.0, **options)
|
40
|
+
red = raster_band(red_band_number)
|
41
|
+
nir = raster_band(nir_band_number)
|
42
|
+
fail RequiredBandNotFound, 'Red band not found.' if red.nil?
|
43
|
+
fail RequiredBandNotFound, 'Near-infrared' if nir.nil?
|
44
|
+
|
45
|
+
output_data_type ||= red.data_type
|
46
|
+
the_array = calculate_ndvi(red.to_na, nir.to_na, no_data_value, remove_negatives, output_data_type)
|
60
47
|
driver = GDAL::Driver.by_name(driver_name)
|
61
48
|
|
62
|
-
driver.create_dataset(destination, raster_x_size, raster_y_size,
|
49
|
+
ndvi = driver.create_dataset(destination, raster_x_size, raster_y_size,
|
63
50
|
data_type: output_data_type, **options) do |ndvi_dataset|
|
64
51
|
ndvi_dataset.geo_transform = geo_transform
|
65
52
|
ndvi_dataset.projection = projection
|
66
53
|
|
67
54
|
ndvi_band = ndvi_dataset.raster_band(1)
|
68
|
-
ndvi_band.write_array(the_array
|
55
|
+
ndvi_band.write_array(the_array)
|
69
56
|
ndvi_band.no_data_value = no_data_value
|
70
57
|
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# Computes GNDVI from the green and near-infrared bands in the dataset.
|
74
|
-
# Raises a GDAL::RequiredBandNotFound if one of those band types isn't
|
75
|
-
# found.
|
76
|
-
#
|
77
|
-
# @param destination [String] Path to output the new dataset to.
|
78
|
-
# @param driver_name [String] The type of dataset to create.
|
79
|
-
# @param band_order [Array<String>] The list of band types, i.e. ['red',
|
80
|
-
# 'green', 'blue'].
|
81
|
-
# @param output_data_type [FFI::GDAL::DataType] Resulting dataset will be
|
82
|
-
# in this data type.
|
83
|
-
# @param remove_negatives [Boolean] Remove negative values after
|
84
|
-
# calculating NDVI.
|
85
|
-
# @param no_data_value [Float]
|
86
|
-
# @param options [Hash] Options that get used for creating the new NDVI
|
87
|
-
# dataset. See docs for GDAL::Driver#create_dataset.
|
88
|
-
# @return [GDAL::Dataset] The new NDVI dataset. *Be sure to call #close on
|
89
|
-
# this object or the data may not persist!*
|
90
|
-
def extract_gndvi(destination, driver_name: 'GTiff', band_order: nil,
|
91
|
-
output_data_type: :GDT_Byte, remove_negatives: false, no_data_value: -9999.0,
|
92
|
-
**options)
|
93
|
-
original_bands =
|
94
|
-
if band_order
|
95
|
-
bands_with_labels(band_order)
|
96
|
-
else
|
97
|
-
{
|
98
|
-
red: red_band,
|
99
|
-
green: green_band,
|
100
|
-
blue: blue_band,
|
101
|
-
nir: undefined_band
|
102
|
-
}
|
103
|
-
end
|
104
|
-
|
105
|
-
green = original_bands[:green]
|
106
|
-
nir = original_bands[:nir]
|
107
|
-
|
108
|
-
if green.nil?
|
109
|
-
fail RequiredBandNotFound, 'Green band not found.'
|
110
|
-
elsif nir.nil?
|
111
|
-
fail RequiredBandNotFound, 'Near-infrared'
|
112
|
-
end
|
113
|
-
|
114
|
-
the_array = calculate_ndvi(green.to_na, nir.to_na,
|
115
|
-
no_data_value, remove_negatives, output_data_type)
|
116
|
-
driver = GDAL::Driver.by_name(driver_name)
|
117
58
|
|
118
|
-
|
119
|
-
data_type: output_data_type, **options) do |gndvi_dataset|
|
120
|
-
gndvi_dataset.geo_transform = geo_transform
|
121
|
-
gndvi_dataset.projection = projection
|
59
|
+
ndvi.close
|
122
60
|
|
123
|
-
|
124
|
-
gndvi_band.write_array(the_array, data_type: output_data_type)
|
125
|
-
gndvi_band.no_data_value = no_data_value
|
126
|
-
end
|
61
|
+
ndvi
|
127
62
|
end
|
128
63
|
|
129
64
|
# Extracts the NIR band and writes to a new file. NOTE: be sure to close
|
@@ -136,20 +71,19 @@ module GDAL
|
|
136
71
|
# @param driver_name [String] the GDAL::Driver short name to use for the
|
137
72
|
# new dataset.
|
138
73
|
# @param output_data_type [FFI::GDAL::DataType] Resulting dataset will be
|
139
|
-
# in this data type.
|
74
|
+
# in this data type. Defaults to use the current data type.
|
140
75
|
# @param options [Hash] Options that get used for creating the new NDVI
|
141
76
|
# dataset. See docs for GDAL::Driver#create_dataset.
|
142
77
|
# @return [GDAL::Dataset] The new NIR dataset. *Be sure to call #close on
|
143
78
|
# this object or the data may not persist!*
|
144
|
-
def extract_nir(destination, band_number, driver_name: 'GTiff', output_data_type:
|
145
|
-
driver = GDAL::Driver.by_name(driver_name)
|
79
|
+
def extract_nir(destination, band_number, driver_name: 'GTiff', output_data_type: nil, **options)
|
146
80
|
original_nir_band = raster_band(band_number)
|
81
|
+
fail InvalidBandNumber, "Band #{band_number} found but was nil." if original_nir_band.nil?
|
147
82
|
|
148
|
-
|
149
|
-
|
150
|
-
end
|
83
|
+
output_data_type ||= original_nir_band.data_type
|
84
|
+
driver = GDAL::Driver.by_name(driver_name)
|
151
85
|
|
152
|
-
driver.create_dataset(destination, raster_x_size, raster_y_size,
|
86
|
+
nir = driver.create_dataset(destination, raster_x_size, raster_y_size,
|
153
87
|
data_type: output_data_type, **options) do |nir_dataset|
|
154
88
|
nir_dataset.geo_transform = geo_transform
|
155
89
|
nir_dataset.projection = projection
|
@@ -157,37 +91,39 @@ module GDAL
|
|
157
91
|
nir_band = nir_dataset.raster_band(1)
|
158
92
|
original_nir_band.copy_whole_raster(nir_band)
|
159
93
|
end
|
94
|
+
|
95
|
+
nir.close
|
96
|
+
|
97
|
+
nir
|
160
98
|
end
|
161
99
|
|
162
|
-
# Extracts the RGB bands and writes to a new file. NOTE:
|
163
|
-
#
|
164
|
-
#
|
100
|
+
# Extracts the RGB bands and writes to a new file. NOTE: this closes the
|
101
|
+
# dataset to ensure all data and metadata have been flushed to disk. To
|
102
|
+
# work with the file, you'll need to reopen it.
|
165
103
|
#
|
166
104
|
# @param destination [String] The destination file path.
|
105
|
+
# @param red_band_number [Fixnum]
|
106
|
+
# @param green_band_number [Fixnum]
|
107
|
+
# @param blue_band_number [Fixnum]
|
167
108
|
# @param driver_name [String] the GDAL::Driver short name to use for the
|
168
109
|
# new dataset.
|
169
|
-
# @param
|
170
|
-
#
|
110
|
+
# @param output_data_type [FFI::GDAL::DataType] Resulting dataset will be
|
111
|
+
# in this data type. Defaults to use the current data type.
|
171
112
|
# @param options [Hash] Options that get used for creating the new NDVI
|
172
113
|
# dataset. See docs for GDAL::Driver#create_dataset.
|
173
114
|
# @return [GDAL::Dataset]
|
174
|
-
def extract_natural_color(destination,
|
175
|
-
|
176
|
-
|
177
|
-
|
115
|
+
def extract_natural_color(destination, red_band_number, green_band_number, blue_band_number,
|
116
|
+
driver_name: 'GTiff', output_data_type: nil, **options)
|
117
|
+
original_bands = {
|
118
|
+
red: raster_band(red_band_number),
|
119
|
+
green: raster_band(green_band_number),
|
120
|
+
blue: raster_band(blue_band_number)
|
121
|
+
}
|
122
|
+
|
123
|
+
output_data_type ||= raster_band(1).data_type
|
178
124
|
driver = GDAL::Driver.by_name(driver_name)
|
179
125
|
|
180
|
-
|
181
|
-
bands_with_labels(band_order)
|
182
|
-
else
|
183
|
-
{
|
184
|
-
red: red_band,
|
185
|
-
green: green_band,
|
186
|
-
blue: blue_band
|
187
|
-
}
|
188
|
-
end
|
189
|
-
|
190
|
-
driver.create_dataset(destination, columns, rows,
|
126
|
+
natural_color = driver.create_dataset(destination, raster_x_size, raster_y_size,
|
191
127
|
band_count: 3, data_type: output_data_type, **options) do |new_dataset|
|
192
128
|
new_dataset.geo_transform = geo_transform
|
193
129
|
new_dataset.projection = projection
|
@@ -201,11 +137,15 @@ module GDAL
|
|
201
137
|
new_blue_band = new_dataset.raster_band(3)
|
202
138
|
original_bands[:blue].copy_whole_raster(new_blue_band)
|
203
139
|
end
|
140
|
+
|
141
|
+
natural_color.close
|
142
|
+
|
143
|
+
natural_color
|
204
144
|
end
|
205
145
|
|
206
146
|
# @param red_band_array [NArray]
|
207
147
|
# @param nir_band_array [NArray]
|
208
|
-
# @param
|
148
|
+
# @param no_data_value [Number] Value to represent NODATA.
|
209
149
|
# @return [NArray]
|
210
150
|
def calculate_ndvi(red_band_array, nir_band_array, no_data_value,
|
211
151
|
remove_negatives = false, output_data_type = nil)
|
@@ -215,53 +155,19 @@ module GDAL
|
|
215
155
|
red_band_array = red_band_array.to_type(NArray::DFLOAT)
|
216
156
|
|
217
157
|
numerator = nir_band_array - red_band_array
|
218
|
-
denominator =
|
158
|
+
denominator = nir_band_array + red_band_array
|
219
159
|
ndvi = numerator / denominator
|
220
|
-
|
221
|
-
|
222
|
-
0.upto(ndvi.size - 1) do |i|
|
223
|
-
ndvi[i] = no_data_value if ndvi[i].is_a?(Float) && ndvi[i].nan?
|
224
|
-
end
|
160
|
+
mask = nir_band_array.and(red_band_array).not
|
161
|
+
ndvi[mask] = no_data_value
|
225
162
|
|
226
163
|
# Convert to output data type
|
227
164
|
final_array = case output_data_type
|
228
|
-
when :GDT_Byte
|
229
|
-
|
230
|
-
|
231
|
-
calculate_ndvi_uint16(ndvi)
|
232
|
-
else
|
233
|
-
ndvi
|
165
|
+
when :GDT_Byte then calculate_ndvi_byte(ndvi)
|
166
|
+
when :GDT_UInt16 then calculate_ndvi_uint16(ndvi)
|
167
|
+
else ndvi # Already in Float32
|
234
168
|
end
|
235
169
|
|
236
|
-
remove_negatives ? remove_negatives_from(final_array) : final_array
|
237
|
-
end
|
238
|
-
|
239
|
-
# Map raster bands to a label, as a hash. Useful for when bands don't match
|
240
|
-
# the color_interpretation that's returned from GDAL. This simply maps the
|
241
|
-
# list of labels you pass in to the raster bands.
|
242
|
-
#
|
243
|
-
# Valid labels:
|
244
|
-
# * Near-infrared: 'N', :nir
|
245
|
-
# * Red: 'R', :red
|
246
|
-
# * Green: 'G', :green
|
247
|
-
# * Blue: 'B', :blue
|
248
|
-
# * Alpha: 'A', :alpha
|
249
|
-
#
|
250
|
-
# @param order [Array<Object>]
|
251
|
-
# @return [Hash{<Object> => GDAL::RasterBand}]
|
252
|
-
def bands_with_labels(order)
|
253
|
-
order.each_with_object({}).each_with_index do |(band_label, obj), i|
|
254
|
-
label = case band_label.to_s
|
255
|
-
when 'N', 'nir' then :nir
|
256
|
-
when 'R', 'red' then :red
|
257
|
-
when 'G', 'green' then :green
|
258
|
-
when 'B', 'blue' then :blue
|
259
|
-
else
|
260
|
-
band_label
|
261
|
-
end
|
262
|
-
|
263
|
-
obj[label] = raster_band(i + 1)
|
264
|
-
end
|
170
|
+
remove_negatives ? remove_negatives_from(final_array, no_data_value) : final_array
|
265
171
|
end
|
266
172
|
|
267
173
|
# @return [Array<GDAL::RasterBand>]
|
@@ -490,6 +396,7 @@ module GDAL
|
|
490
396
|
NArray[*na.transpose]
|
491
397
|
end
|
492
398
|
|
399
|
+
# @return [Hash]
|
493
400
|
def as_json(options = nil)
|
494
401
|
{
|
495
402
|
dataset: {
|
@@ -526,14 +433,14 @@ module GDAL
|
|
526
433
|
((ndvi + 1) * (65_535.0 / 2)).to_type(NArray::INT)
|
527
434
|
end
|
528
435
|
|
529
|
-
# Sets any negative values in the NArray to
|
436
|
+
# Sets any negative values in the NArray to +replace_with+.
|
530
437
|
#
|
531
438
|
# @param narray [NArray]
|
439
|
+
# @param replace_with [Number] Replace negative values with this. Useful
|
440
|
+
# for setting to a NODATA value.
|
532
441
|
# @return [NArray]
|
533
|
-
def remove_negatives_from(narray, replace_with
|
534
|
-
|
535
|
-
narray[i] = replace_with if narray[i] < 0
|
536
|
-
end
|
442
|
+
def remove_negatives_from(narray, replace_with)
|
443
|
+
narray[narray.lt(0)] = replace_with
|
537
444
|
|
538
445
|
narray
|
539
446
|
end
|
data/lib/gdal/exceptions.rb
CHANGED
@@ -5,6 +5,7 @@ module GDAL
|
|
5
5
|
include LogSwitch
|
6
6
|
end
|
7
7
|
|
8
|
+
# @private
|
8
9
|
module InternalHelpers
|
9
10
|
def self.included(base)
|
10
11
|
base.extend(ClassMethods)
|
@@ -89,6 +90,17 @@ module GDAL
|
|
89
90
|
!FFI::GDAL.unsupported_gdal_functions.include?(function_name) &&
|
90
91
|
FFI::GDAL.respond_to?(function_name)
|
91
92
|
end
|
93
|
+
|
94
|
+
# @param char [String] 'r' or 'w'
|
95
|
+
# @return [Symbol] :GF_Read if 'r' or :GF_Write if 'w'.
|
96
|
+
# @raise [GDAL::InvalidAccessFlag] If +char+ is not 'r' or 'w'.
|
97
|
+
def _gdal_access_flag(char)
|
98
|
+
case char
|
99
|
+
when 'r' then :GF_Read
|
100
|
+
when 'w' then :GF_Write
|
101
|
+
else fail GDAL::InvalidAccessFlag, "Invalid access flag: #{char}"
|
102
|
+
end
|
103
|
+
end
|
92
104
|
end
|
93
105
|
end
|
94
106
|
end
|
data/lib/gdal/raster_band.rb
CHANGED
@@ -36,8 +36,6 @@ module GDAL
|
|
36
36
|
#
|
37
37
|
# @return [Fixnum]
|
38
38
|
def x_size
|
39
|
-
return nil if null?
|
40
|
-
|
41
39
|
FFI::GDAL.GDALGetRasterBandXSize(@c_pointer)
|
42
40
|
end
|
43
41
|
|
@@ -45,8 +43,6 @@ module GDAL
|
|
45
43
|
#
|
46
44
|
# @return [Fixnum]
|
47
45
|
def y_size
|
48
|
-
return nil if null?
|
49
|
-
|
50
46
|
FFI::GDAL.GDALGetRasterBandYSize(@c_pointer)
|
51
47
|
end
|
52
48
|
|
@@ -54,8 +50,6 @@ module GDAL
|
|
54
50
|
#
|
55
51
|
# @return [Symbol] Either :GA_Update or :GA_ReadOnly.
|
56
52
|
def access_flag
|
57
|
-
return nil if null?
|
58
|
-
|
59
53
|
FFI::GDAL.GDALGetRasterAccess(@c_pointer)
|
60
54
|
end
|
61
55
|
|
@@ -64,8 +58,6 @@ module GDAL
|
|
64
58
|
#
|
65
59
|
# @return [Fixnum]
|
66
60
|
def number
|
67
|
-
return nil if null?
|
68
|
-
|
69
61
|
FFI::GDAL.GDALGetBandNumber(@c_pointer)
|
70
62
|
end
|
71
63
|
|
@@ -583,49 +575,114 @@ module GDAL
|
|
583
575
|
NArray.to_na(the_array)
|
584
576
|
end
|
585
577
|
|
586
|
-
#
|
578
|
+
# Writes an NArray of pixels to the raster band using {#raster_io}. It
|
579
|
+
# determines +x_size+ and +y_size+ for the {#raster_io} call using the
|
580
|
+
# dimensions of the array.
|
581
|
+
#
|
582
|
+
# @param pixel_array [NArray] The 2d list of pixels.
|
587
583
|
# @param x_offset [Fixnum] The left-most pixel to start writing.
|
588
|
-
# @param y_offset [Fixnum] The top-most
|
589
|
-
# @param
|
584
|
+
# @param y_offset [Fixnum] The top-most line to start writing.
|
585
|
+
# @param buffer_data_type [FFI::GDAL::DataType] The type of pixel contained in
|
590
586
|
# the +pixel_array+.
|
591
587
|
# @param line_space [Fixnum]
|
592
588
|
# @param pixel_space [Fixnum]
|
593
589
|
# TODO: Write using #buffer_size to write most efficiently.
|
594
|
-
def write_array(pixel_array, x_offset: 0, y_offset: 0,
|
590
|
+
def write_array(pixel_array, x_offset: 0, y_offset: 0, x_size: nil, y_size: nil,
|
591
|
+
buffer_x_size: nil, buffer_y_size: nil, buffer_data_type: data_type,
|
595
592
|
line_space: 0, pixel_space: 0)
|
596
|
-
|
597
|
-
|
598
|
-
y_size = pixel_array.sizes.last
|
593
|
+
x_size ||= pixel_array.sizes.first
|
594
|
+
y_size ||= pixel_array.sizes.last
|
599
595
|
|
600
596
|
columns_to_write = x_size - x_offset
|
601
597
|
lines_to_write = y_size - y_offset
|
602
|
-
scan_line = GDAL._pointer_from_data_type(
|
598
|
+
scan_line = GDAL._pointer_from_data_type(buffer_data_type, columns_to_write * lines_to_write)
|
603
599
|
|
604
|
-
(y_offset).upto(lines_to_write - 1) do |
|
605
|
-
pixels = pixel_array[true,
|
606
|
-
ffi_type = GDAL._gdal_data_type_to_ffi(
|
600
|
+
(y_offset).upto(lines_to_write - 1) do |line_number|
|
601
|
+
pixels = pixel_array[true, line_number]
|
602
|
+
ffi_type = GDAL._gdal_data_type_to_ffi(buffer_data_type)
|
607
603
|
meth = "write_array_of_#{ffi_type}".to_sym
|
608
604
|
scan_line.send(meth, pixels.to_a)
|
609
605
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
x_offset, # nXOff
|
614
|
-
y,
|
615
|
-
x_size, # nXSize
|
616
|
-
line_size, # nYSize
|
617
|
-
scan_line, # pData
|
618
|
-
x_size, # nBufXSize
|
619
|
-
line_size, # nBufYSize
|
620
|
-
data_type, # eBufType
|
621
|
-
pixel_space, # nPixelSpace
|
622
|
-
line_space # nLineSpace
|
623
|
-
)
|
606
|
+
raster_io('w', scan_line, x_size: x_size, y_size: 1, x_offset: x_offset, y_offset: line_number,
|
607
|
+
buffer_x_size: x_size, buffer_y_size: line_number, buffer_data_type: buffer_data_type,
|
608
|
+
pixel_space: pixel_space, line_space: line_space)
|
624
609
|
end
|
625
610
|
|
626
611
|
flush_cache
|
627
612
|
end
|
628
613
|
|
614
|
+
# IO access for raster data in this band. Default values are set up to
|
615
|
+
# operate on one line at a time, keeping the same aspect ratio.
|
616
|
+
#
|
617
|
+
# On buffers... You can use different size buffers from the original x and
|
618
|
+
# y size to allow for resampling. Using larger buffers will upsample the
|
619
|
+
# raster data; smaller buffers will downsample it.
|
620
|
+
#
|
621
|
+
# On +pixel_space+ and +line_space+.... These values control how data is
|
622
|
+
# organized in the buffer.
|
623
|
+
#
|
624
|
+
# @param [Symbol] access_flag Must be 'r' or 'w'.
|
625
|
+
# @param [FFI::MemoryPointer] buffer Allows for passing in your own buffer,
|
626
|
+
# which is really only useful when writing.
|
627
|
+
# @param [Fixnum] x_size The number of pixels per line to operate on.
|
628
|
+
# Defaults to the value of {#x_size} - +x_offset+.
|
629
|
+
# @param [Fixnum] y_size The number of lines to operate on. Defaults to 1.
|
630
|
+
# @param [Fixnum] x_offset The pixel number in the line to start operating
|
631
|
+
# on. Note that when using this, {#x_size} - +x_offset+ should be >= 0,
|
632
|
+
# otherwise this means you're telling the method to read past the end of
|
633
|
+
# the line. Defaults to 0.
|
634
|
+
# @param [Fixnum] y_offset The line number to start operating on. Note that
|
635
|
+
# when using this, {#y_size} - +y_offset+ should be >= 0, otherwise this
|
636
|
+
# means you're telling the method to read more lines than the raster has.
|
637
|
+
# Defaults to 0.
|
638
|
+
# @param [Fixnum] buffer_x_size The width of the buffer image in which to
|
639
|
+
# read/write the raster data into/from. Typically this should be the same
|
640
|
+
# size as +x_size+; if it's different, GDAL will resample accordingly.
|
641
|
+
# @param [Fixnum] buffer_y_size The height of the buffer image in which to
|
642
|
+
# read/write the raster data into/from. Typically this should be the same
|
643
|
+
# size as +y_size+; if it's different, GDAL will resample accordingly.
|
644
|
+
# @param [FFI::GDAL::DataType] buffer_data_type Can be used to convert the
|
645
|
+
# data to a different type. You must account for this when reading/writing
|
646
|
+
# to/from your buffer--your buffer size must be +buffer_x_size+ *
|
647
|
+
# +buffer_y_size+. Defaults to {#data_type}.
|
648
|
+
# @param [Fixnum] pixel_space The byte offset from the start of one pixel
|
649
|
+
# value in the buffer to the start of the next pixel value within a line.
|
650
|
+
# If defaulted (0), the size of +buffer_data_type+ is used.
|
651
|
+
# @param [Fixnum] line_space The byte offset from the start of one line in
|
652
|
+
# the buffer to the start of the next. If defaulted (0), the size of
|
653
|
+
# +buffer_data_type+ * +buffer_x_size* is used.
|
654
|
+
# @return [FFI::MemoryPointer] Pointer to the data that was read/written.
|
655
|
+
def raster_io(access_flag, buffer = nil,
|
656
|
+
x_size: nil, y_size: nil, x_offset: 0, y_offset: 0,
|
657
|
+
buffer_x_size: nil, buffer_y_size: nil, buffer_data_type: data_type,
|
658
|
+
pixel_space: 0, line_space: 0)
|
659
|
+
x_size ||= self.x_size
|
660
|
+
y_size ||= self.y_size
|
661
|
+
pixels_to_io = x_size - x_offset
|
662
|
+
lines_to_io = y_size - y_offset
|
663
|
+
|
664
|
+
buffer_x_size ||= pixels_to_io
|
665
|
+
buffer_y_size ||= lines_to_io
|
666
|
+
buffer ||= GDAL._pointer_from_data_type(buffer_data_type, buffer_x_size * buffer_y_size)
|
667
|
+
|
668
|
+
FFI::GDAL.GDALRasterIO(
|
669
|
+
@c_pointer,
|
670
|
+
GDAL._gdal_access_flag(access_flag),
|
671
|
+
x_offset,
|
672
|
+
y_offset,
|
673
|
+
x_size,
|
674
|
+
y_size,
|
675
|
+
buffer,
|
676
|
+
buffer_x_size,
|
677
|
+
buffer_y_size,
|
678
|
+
buffer_data_type,
|
679
|
+
pixel_space,
|
680
|
+
line_space
|
681
|
+
)
|
682
|
+
|
683
|
+
buffer
|
684
|
+
end
|
685
|
+
|
629
686
|
# Read a block of image data, more efficiently than #read. Doesn't
|
630
687
|
# resample or do data type conversion.
|
631
688
|
#
|
@@ -45,17 +45,20 @@ module GDAL
|
|
45
45
|
# max end of the values, those get lumped in with the last range.
|
46
46
|
#
|
47
47
|
# @param range_count [Fixnum] The number of ranges to create.
|
48
|
+
# @return [Array<Hash>, nil]
|
48
49
|
def equal_count_ranges(range_count)
|
49
50
|
sorted_pixels = @raster_band.to_na.sort
|
50
51
|
sorted_and_masked_pixels = sorted_pixels[sorted_pixels.ne(@raster_band.no_data_value[:value])]
|
51
52
|
range_size = (sorted_and_masked_pixels.size / range_count).to_i
|
53
|
+
|
52
54
|
log "Pixel count: #{sorted_and_masked_pixels.size}"
|
53
55
|
log "Min pixel value: #{sorted_and_masked_pixels.min}"
|
54
56
|
log "Max pixel value: #{sorted_and_masked_pixels.max}"
|
55
57
|
log "Range size: #{range_size}"
|
56
|
-
|
58
|
+
|
57
59
|
break_values = range_count.times.map { |i| sorted_and_masked_pixels[range_size * i] }
|
58
60
|
log "Break values: #{break_values}"
|
61
|
+
return if break_values.uniq.size != range_count
|
59
62
|
|
60
63
|
breakpoint_calculator = lambda do |range_number|
|
61
64
|
min = break_values[range_number]
|
@@ -95,7 +98,7 @@ module GDAL
|
|
95
98
|
end
|
96
99
|
end
|
97
100
|
|
98
|
-
@raster_band.write_array(narray,
|
101
|
+
@raster_band.write_array(narray, buffer_data_type: @raster_band.data_type)
|
99
102
|
end
|
100
103
|
|
101
104
|
private
|
@@ -121,25 +121,24 @@ module GDAL
|
|
121
121
|
def colorize!(*colors)
|
122
122
|
return if colors.empty?
|
123
123
|
|
124
|
+
self.color_interpretation ||= :GCI_PaletteIndex
|
124
125
|
table = GDAL::ColorTable.new(:GPI_RGB)
|
126
|
+
table.add_color_entry(0, 0, 0, 0, 255)
|
125
127
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
else
|
132
|
-
fail "Can't colorize a #{data_type} band--must be :GDT_Byte or :GDT_UInt16"
|
128
|
+
# Start at 1 instead of 0 because we manually set the first color entry
|
129
|
+
# to white.
|
130
|
+
color_entry_index_range =
|
131
|
+
if data_type == :GDT_Byte then 1..255
|
132
|
+
elsif data_type == :GDT_UInt16 then 1..65_535
|
133
|
+
else fail "Can't colorize a #{data_type} band--must be :GDT_Byte or :GDT_UInt16"
|
133
134
|
end
|
134
135
|
|
135
|
-
|
136
|
-
table.add_color_entry(0, 0, 0, 0, 255)
|
137
|
-
bin_count = color_entry_index_count / colors.size.to_f
|
138
|
-
|
139
|
-
1.upto(color_entry_index_count - 1) do |color_entry_index|
|
140
|
-
color_number = (color_entry_index / bin_count).to_i
|
136
|
+
bin_count = (color_entry_index_range.last + 1) / colors.size.to_f
|
141
137
|
|
138
|
+
color_entry_index_range.step do |color_entry_index|
|
139
|
+
color_number = (color_entry_index / bin_count).floor.to_i
|
142
140
|
color = colors[color_number]
|
141
|
+
|
143
142
|
# TODO: Fix possible uninitialized rgb_array
|
144
143
|
rgb_array = hex_to_rgb(color) unless color.is_a?(Array)
|
145
144
|
table.add_color_entry(color_entry_index,
|
data/lib/ogr/geometry.rb
CHANGED
@@ -29,7 +29,6 @@ module OGR
|
|
29
29
|
end
|
30
30
|
|
31
31
|
new_pointer = geometry.c_pointer
|
32
|
-
# geometry.c_pointer.autorelease = true
|
33
32
|
|
34
33
|
case geometry.type
|
35
34
|
when :wkbPoint, :wkbPoint25D then OGR::Point.new(new_pointer)
|
@@ -122,6 +121,7 @@ module OGR
|
|
122
121
|
def self.included(base)
|
123
122
|
base.send(:include, GDAL::Logger)
|
124
123
|
base.send(:include, GeometryExtensions)
|
124
|
+
base.send(:extend, ClassMethods)
|
125
125
|
end
|
126
126
|
|
127
127
|
#--------------------------------------------------------------------------
|
data/lib/ogr/internal_helpers.rb
CHANGED
@@ -90,6 +90,11 @@ RSpec.describe GDAL::RasterBandClassifier do
|
|
90
90
|
expect(map_to_values).to contain_exactly(1.0, 2.0, 3.0, 4.0)
|
91
91
|
end
|
92
92
|
end
|
93
|
+
|
94
|
+
context 'not enough values between breaks to generate unique break values' do
|
95
|
+
subject { classifier.equal_count_ranges(1_000) }
|
96
|
+
it { is_expected.to be_nil }
|
97
|
+
end
|
93
98
|
end
|
94
99
|
|
95
100
|
describe '#classify!' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffi-gdal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.beta5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Loveless
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|