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 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