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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 034858e4f3db966a8a5d994ed558d88727b87a3b
4
- data.tar.gz: d4a624f17336db3eb515c2b3edcf2b5cba6d692a
3
+ metadata.gz: b5e2273d2b75bb5a7af15693af6a84cb32c74852
4
+ data.tar.gz: f986fe1b75a472ce0a0d663d13ceed15714e0ef0
5
5
  SHA512:
6
- metadata.gz: 5eb6d526bf0a2a0d2524426d63d619a5bc44a0ee8a97c2cc1c814dc180257e688fdd7f00b0089720ee9a943468ac6857c4ee546ba0969fc33a5b7a8ab8b4c48c
7
- data.tar.gz: 4bab75341f049696458a47293ebe6c36977b7d53920db502ee6d6e54a6c3f94415bcbebf53a636a1bef54cae5f57a0773aa69ecc909852319be831400d00cc3d
6
+ metadata.gz: 584e34e7de26e51fff046707de49750f8071d937dac8259ad594387f1ff60c7bca2fa72c22fad48525fdd3cf42a6d734a564d4d054aac675ba48c7a55ed4e3e8
7
+ data.tar.gz: c8eca7619a1eeaf878582c8efead2bde968a52149e92cd64ad37fbb3475bcc2f25ba2182da3cfa200e9ce8925ca90c60a55be5b59b43998d1d2a867843f35e7a
data/History.md CHANGED
@@ -1,83 +1,109 @@
1
- ### 1.0.0.beta4 / 2015-04-22
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
- * Full refactor of FFI/C function wrapper. Closer mapping of Ruby functions
8
- within modules to the C-header files where the functions actually reside.
9
- * Full redesign of the error handling mechanism; ffi-gdal now hooks in to
10
- GDAL's error handling, thus errors raised from GDAL automagically get handed
11
- over as Ruby exceptions. (GDAL only; OGR doesn't provide this.) This also
12
- entailed adding a bunch of new exceptions and renaming some old ones.
13
- * Better library finding on Linux.
14
- * Wrapped most of GDAL's Grid API.
15
- * Wrapped some of GDAL's Warp API.
16
- * GDAL::Dataset can now open PostGISRaster datasets.
17
- * Lots more OGR love. Much of this API has now been vetted.
18
- * Added `GDAL::RasterClassifier` for classifying raster bands.
19
- * Added some wrapper methods for classes that support capability testing.
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
- * Fixed some GDAL::Dataset extension methods (`extract_ndvi` and friends)
22
- that weren't properly handling various data types.
23
- * Better handling of large files.
24
- * Fixed regular crashes when dealing with OGR Geometries and
25
- SpatialReferences.
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
- ### 1.0.0.beta3 / 2014-11-11
52
+ ## 1.0.0.beta3 / 2014-11-11
28
53
 
29
54
  * Bug fixes
30
- * `ogr/exceptions` wasn't being required for `ext/error_symbols.rb`, thus
31
- any use of an OGR exception was causing a `NameError`.
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
- ### 1.0.0.beta2 / 2014-10-23
58
+ ## 1.0.0.beta2 / 2014-10-23
34
59
 
35
60
  * Improvements
36
- * Added more documentation
37
- * Uncommented `attach_function` calls that had been commented out due to
38
- lack of support in versions I'd tested on. These get handled now on load.
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
- ### 1.0.0.beta1 / 2014-10-23
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
- * Added C and Ruby wrapper for most of OGR.
45
- * Better handling of CPLErr return values.
46
- * Allow loading, even when C functions aren't defined in the version of
47
- GDAL that you're using.
48
- * Split out additions to GDAL/OGR in `*_extensions.rb` modules. Methods
49
- contained in `Extentions` modules don't directly wrap GDAL/OGR functions,
50
- but either provide new functionality or attempt to make library usage more
51
- Rubyesque.
52
- * Added `#as_json`, `#to_json` to many classes.
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
- * Lots of cleanup of class internals.
55
- * `autoload` child GDAL and OGR Ruby classes.
56
- * Renamed files under ffi/ that were derived from GDAL/OGR header files to
57
- include `_h` in the name.
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
- ### 0.0.4 / 2014-09-27
85
+ ## 0.0.4 / 2014-09-27
60
86
 
61
87
  * Bug fixes
62
- * Fixed failure to load on Ubuntu 12.04 (GDAL v1.7.3).
88
+ * Fixed failure to load on Ubuntu 12.04 (GDAL v1.7.3).
63
89
 
64
- ### 0.0.3 / 2014-09-26
90
+ ## 0.0.3 / 2014-09-26
65
91
 
66
92
  * Improvements
67
- * The `approx_ok` param for `RasterBand#histogram` should default to
68
- `false` (preferring data exactness over performance).
93
+ * The `approx_ok` param for `RasterBand#histogram` should default to
94
+ `false` (preferring data exactness over performance).
69
95
  * Bug fixes
70
- * Fixed URL silliness introduced in 0.0.2.
71
- * `Dataset#*_band` methods should return `nil` if the band with that color
72
- isn't found.
73
- * `RasterBand#default_histogram` died if the band didn't have any values.
74
- * `RasterBand#histogram` wasn't returning totals.
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
- ### 0.0.2 / 2014-09-26
102
+ ## 0.0.2 / 2014-09-26
77
103
 
78
104
  * New things
79
- * Added ability to pass a URL into `GDAL::Dataset`.
105
+ * Added ability to pass a URL into `GDAL::Dataset`.
80
106
 
81
- ### 0.0.1 / 2014-09-26
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 get_tifs` will pull
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
@@ -1,5 +1,5 @@
1
1
  module FFI
2
2
  module GDAL
3
- VERSION = '1.0.0.beta4'
3
+ VERSION = '1.0.0.beta5'
4
4
  end
5
5
  end
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
- @raster_bands[zero_index]
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
- !!FFI::GDAL.GDALSetProjection(@c_pointer, new_projection)
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. Raises
15
- # a GDAL::RequiredBandNotFound if one of those band types isn't found.
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 driver_name [String] The type of dataset to create.
19
- # @param band_order [Array<String>] The list of band types, i.e. ['red',
20
- # 'green', 'blue'].
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', band_order: nil,
31
- output_data_type: :GDT_Byte, remove_negatives: false,
32
- no_data_value: -9999.0,
33
- **options)
34
- original_bands =
35
- if band_order
36
- bands_with_labels(band_order)
37
- else
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, data_type: output_data_type)
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
- driver.create_dataset(destination, raster_x_size, raster_y_size,
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
- gndvi_band = gndvi_dataset.raster_band(1)
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: :GDT_Byte, **options)
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
- if original_nir_band.nil?
149
- fail InvalidBandNumber, "Band #{band_number} found but was nil."
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: be sure to close
163
- # the dataset object that gets returned or your data will not get written
164
- # to the file.
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 band_order [Array<String>] The list of band types, i.e. ['red',
170
- # 'green', 'blue'].
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, driver_name: 'GTiff',
175
- band_order: nil, output_data_type: :GDT_Byte, **options)
176
- rows = raster_y_size
177
- columns = raster_x_size
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
- original_bands = if band_order
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 remove_negatives [Fixnum] Value to replace negative values with.
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 = (nir_band_array + red_band_array)
158
+ denominator = nir_band_array + red_band_array
219
159
  ndvi = numerator / denominator
220
-
221
- # Remove NaNs
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
- calculate_ndvi_byte(ndvi)
230
- when :GDT_UInt16
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 0.
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 = 0)
534
- 0.upto(narray.size - 1) do |i|
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
@@ -30,6 +30,9 @@ module GDAL
30
30
  class NoWriteAccess < RuntimeError
31
31
  end
32
32
 
33
+ class InvalidAccessFlag < RuntimeError
34
+ end
35
+
33
36
  class NullObject < TypeError
34
37
  end
35
38
 
@@ -1,4 +1,4 @@
1
- require_relative '../../ffi/gdal/gdal_grid_data_metrics_options'
1
+ require_relative '../../ffi/gdal/grid_data_metrics_options'
2
2
 
3
3
  module GDAL
4
4
  module GridTypes
@@ -1,4 +1,4 @@
1
- require_relative '../../ffi/gdal/gdal_grid_inverse_distance_to_a_power_options'
1
+ require_relative '../../ffi/gdal/grid_inverse_distance_to_a_power_options'
2
2
 
3
3
  module GDAL
4
4
  module GridTypes
@@ -1,4 +1,4 @@
1
- require_relative '../../ffi/gdal/gdal_grid_moving_average_options'
1
+ require_relative '../../ffi/gdal/grid_moving_average_options'
2
2
 
3
3
  module GDAL
4
4
  module GridTypes
@@ -1,4 +1,4 @@
1
- require_relative '../../ffi/gdal/gdal_grid_nearest_neighbor_options'
1
+ require_relative '../../ffi/gdal/grid_nearest_neighbor_options'
2
2
 
3
3
  module GDAL
4
4
  module GridTypes
@@ -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
@@ -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
- # @param pixel_array [NArray] The list of pixels.
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 pixel to start writing.
589
- # @param data_type [FFI::GDAL::DataType] The type of pixel contained in
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, data_type: self.data_type,
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
- line_size = 1
597
- x_size = pixel_array.sizes.first
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(data_type, columns_to_write)
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 |y|
605
- pixels = pixel_array[true, y]
606
- ffi_type = GDAL._gdal_data_type_to_ffi(data_type)
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
- FFI::GDAL.GDALRasterIO(
611
- @c_pointer,
612
- :GF_Write,
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
- log 'Break offsets:'
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, data_type: @raster_band.data_type)
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
- color_entry_index_count =
127
- if data_type == :GDT_Byte
128
- 256
129
- elsif data_type == :GDT_UInt16
130
- 65_536
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
- self.color_interpretation ||= :GCI_PaletteIndex
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
  #--------------------------------------------------------------------------
@@ -6,6 +6,7 @@ module OGR
6
6
  base.extend(ClassMethods)
7
7
  end
8
8
 
9
+ # @private
9
10
  module ClassMethods
10
11
  # Makes the interface consistent with the access flags for GDAL.
11
12
  #
@@ -1,6 +1,7 @@
1
1
  require_relative '../field_definition'
2
2
  require_relative '../field'
3
3
  require_relative '../geometry_field_definition'
4
+ require_relative '../../ffi/ogr/api'
4
5
 
5
6
  module OGR
6
7
  module LayerMixins
@@ -1,4 +1,5 @@
1
1
  require_relative '../ffi/ogr'
2
+ require_relative '../gdal/logger'
2
3
  require_relative 'spatial_reference_extensions'
3
4
  require_relative 'spatial_reference_mixins/coordinate_system_getter_setters'
4
5
  require_relative 'spatial_reference_mixins/exporters'
@@ -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.beta4
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-04-22 00:00:00.000000000 Z
11
+ date: 2015-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi