ffi-gdal 1.0.0.beta4 → 1.0.0.beta5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|