agwx_grids 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7e7a63b606ce4a0f651f292a6ece2615f818b86a
4
+ data.tar.gz: e59ec22dc5d2a84139f60d9f0759ea0a7bc582cf
5
+ SHA512:
6
+ metadata.gz: 1fa6e4b7ac7fde9dc827e0b405c3d430a10a5fe3e3e3b42e853d0a4db3ad99cfe7f642bed77777b91498049a51077b2e1f95bc02fe4c8ffe4aa9f7bf634ca419
7
+ data.tar.gz: b570b8d0e52a6572b0801e1a6d877dab9a6001460184ecc04f46a21e104de5cf3715f4f61e01f1d17b860ac54307e74ac1c7c6eda1af5f9515379589292f95a5
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in agwx_grids.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 RickWayne
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # AgwxGrids
2
+
3
+ This gem implements the simple three-dimensional grid datatype beloved at Soil Science since the Larry Murdoch days of 1997.
4
+
5
+ Example of a grid file (also on Andi). This one defines a 7 x 8 cell grid, currently containing 245 layers starting at 121 and ending at 365. Note that the Z layers are not necessarily contiguous; there can be layers missing from the file (e.g. if layer 125 were missing, the first few lines here would be the same except the number of layers would be 244 instead of 245). The file format is happy with that, and implementations should be too (that's why each layer of grid values has an index). The stuff in square brackets are illustrative, they would not appear in actual files (there's no provision for embedded comments).
6
+
7
+ Also note that the rows of the raster are stored in reverse order from what you'd see on a map! So the row beginning "8 10 7..." below
8
+ corresponds to the SMALLEST latitude (43.330002)!
9
+
10
+ (From Larry's original dox) The following is a typical top part of a Grid file. Note the space at the start of each line. This allows you to examine the Z index list with something like: egrep '^ [0-9]+$' grid_file_name
11
+
12
+ 7 8 245 [XNo YNo ZNo]
13
+ -93.000000 -87.000000 [XStart XEnd]
14
+ 42.330002 47.000000 [YStart YEnd]
15
+ 121 365 1 [ZStart ZEnd ZInc]
16
+ -99.000000 0 [BadVal #Decimal_Places]
17
+ 121 [ZIndex]
18
+ 8 10 7 4 4 4 4 [Grid Values]
19
+ 7 6 6 4 5 4 4
20
+ 6 8 8 4 4 4 3
21
+ 7 8 9 6 4 3 3
22
+ 8 8 8 6 4 3 2
23
+ 8 8 6 4 4 2 1
24
+ 6 6 4 2 2 2 2
25
+ 4 4 2 2 0 2 2
26
+ 122
27
+ 19 21 18 14 14 14 13
28
+
29
+ Not much to say outside of the rdoc for the API of the gem.
30
+
31
+ ## Installation
32
+
33
+ Add this line to your application's Gemfile:
34
+
35
+ gem 'agwx_grids'
36
+
37
+ And then execute:
38
+
39
+ $ bundle
40
+
41
+ Or install it yourself as:
42
+
43
+ $ gem install agwx_grids
44
+
45
+ ## Usage
46
+
47
+ TODO: Write usage instructions here
48
+
49
+ ## Contributing
50
+
51
+ 1. Fork it
52
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
53
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
54
+ 4. Push to the branch (`git push origin my-new-feature`)
55
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.test_files = FileList['test/*.rb']
7
+ end
8
+
9
+ desc "Run tests"
10
+ task default: :test
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'agwx_grids/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "agwx_grids"
8
+ spec.version = AgwxGrids::VERSION
9
+ spec.authors = ["RickWayne"]
10
+ spec.email = ["fewayne@wisc.edu"]
11
+ spec.description = %q{UW Soils Ag Weather grid data format (X by Y by DOY) }
12
+ spec.summary = %q{UW Soils Ag Weather grid data format (X by Y by DOY) }
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,231 @@
1
+ # The Grid ADT maniputates a very simple ASCII format called a Grid File. Grid files
2
+ # are designed for data which is easily maintained as a 3D matrix. The following is
3
+ # a typical top part of a Grid file:
4
+ #
5
+ # 7 8 245 [XNo YNo ZNo]
6
+ # -93.000000 -87.000000 [XStart XEnd]
7
+ # 42.330002 47.000000 [YStart YEnd]
8
+ # 121 365 1 [ZStart ZEnd ZInc]
9
+ # -99.000000 0 [BadVal #Decimal_Places]
10
+ # 121 [ZIndex]
11
+ # 8 10 7 4 4 4 4 [Grid Values]
12
+ # 7 6 6 4 5 4 4
13
+ # 6 8 8 4 4 4 3
14
+ # 7 8 9 6 4 3 3
15
+ # 8 8 8 6 4 3 2
16
+ # 8 8 6 4 4 2 1
17
+ # 6 6 4 2 2 2 2
18
+ # 4 4 2 2 0 2 2
19
+ # 122
20
+ # 19 21 18 14 14 14 13
21
+ module AgwxGrids
22
+ class GridMetaData
23
+ attr_writer :zDim
24
+ attr_reader :xDim, :yDim, :zDim
25
+ attr_reader :xStart, :xEnd, :xIncr
26
+ attr_reader :yStart, :yEnd, :yIncr
27
+ attr_writer :zStart, :zEnd, :zIncr
28
+ attr_reader :zStart, :zEnd, :zIncr
29
+ attr_writer :badVal
30
+ attr_reader :badVal
31
+
32
+ def xDim=(newXDim)
33
+ @xDim = newXDim
34
+ calcXIncr
35
+ end
36
+ def xStart=(newXStart)
37
+ @xStart = newXStart
38
+ calcXIncr
39
+ end
40
+ def xEnd=(newXEnd)
41
+ @xEnd = newXEnd
42
+ calcXIncr
43
+ end
44
+
45
+ def calcXIncr
46
+ if @xStart != nil && @xEnd != nil && @xDim != nil then
47
+ @xIncr = (@xEnd - @xStart) / (@xDim - 1)
48
+ end
49
+ end
50
+
51
+ def yDim=(newYDim)
52
+ @yDim = newYDim
53
+ calcYIncr
54
+ end
55
+
56
+ def yStart=(newYStart)
57
+ @yStart = newYStart
58
+ calcYIncr
59
+ end
60
+
61
+ def yEnd=(newYEnd)
62
+ @yEnd = newYEnd
63
+ calcYIncr
64
+ end
65
+
66
+ def calcYIncr
67
+ if @yStart != nil && @yEnd != nil && @yDim != nil then
68
+ @yIncr = (@yEnd - @yStart) / @yDim
69
+ end
70
+ end
71
+
72
+ def to_s
73
+ x = "xDim="+@xDim.to_s+",xStart="+@xStart.to_s+",xEnd="+@xEnd.to_s+",xIncr="+xIncr.to_s
74
+ y = "yDim="+@yDim.to_s+",yStart="+@yStart.to_s+",yEnd="+@yEnd.to_s+",yIncr="+yIncr.to_s
75
+ z = "zDim="+@zDim.to_s+",zStart="+@zStart.to_s+",zEnd="+@zEnd.to_s+",zIncr="+zIncr.to_s
76
+ badVal = "badVal="+@badVal.to_s
77
+ x+"\n"+y+"\n"+z+"\n"+badVal
78
+ end
79
+
80
+ def initialize(gridFile)
81
+ readMeta(gridFile)
82
+ end
83
+
84
+ def readMeta(gridFile)
85
+ @xDim,@yDim,@zDim = gridFile.gets.scan(/\d+/).collect { |s| s.to_f }
86
+ @xStart,@xEnd = gridFile.gets.scan(/-*\d+.\d+/).collect {|s| s.to_f }
87
+ calcXIncr
88
+ @yStart,@yEnd = gridFile.gets.scan(/-*\d+.\d+/).collect {|s| s.to_f }
89
+ calcYIncr
90
+ @zStart,@zEnd,@zIncr = gridFile.gets.scan(/\d+/).collect {|s| s.to_i }
91
+ @badVal = gridFile.gets.scan(/-*\d+.\d+/).collect {|s| s.to_f }
92
+ end
93
+ end
94
+
95
+ class GridLayer
96
+ attr_writer :zIndex
97
+ attr_reader :zIndex
98
+ attr_reader :rows
99
+ def initialize(gridFile,metaData)
100
+ @zIndex = gridFile.gets.scan(/\d+/)[0].to_i
101
+ @rows = Array.new
102
+ for row in 0...metaData.yDim
103
+ @rows[row] = gridFile.gets.scan(/-*\d+.\d+/).collect {|s| s.to_f }
104
+ end
105
+ end
106
+ def to_s
107
+ row0 = @rows[0]
108
+ row0Length = row0.length
109
+ "zIndex: #{@zIndex} num rows: #{@rows.length} num cols: #{row0Length}\n row 0: #{@rows[0]}"
110
+ end
111
+ # return value for x-y posn (x and y in tuple space, not "real" space)
112
+ def get(x,y)
113
+ row = @rows[y]
114
+ if row == nil
115
+ nil
116
+ else
117
+ # puts "GLayer.get got a row: #{row.inspect} and the value is #{row[x]}"
118
+ row[x]
119
+ end
120
+ end
121
+ # compare two layers (based on zIndex)
122
+ def <=>(aLayer)
123
+ if @zIndex < aLayer.zIndex then
124
+ return -1
125
+ elsif @zIndex == aLayer.zIndex then
126
+ return 0
127
+ else
128
+ return 1
129
+ end
130
+ end
131
+
132
+ def row(y)
133
+ @rows[y]
134
+ end
135
+
136
+ end
137
+
138
+
139
+ class Grid
140
+ include Enumerable
141
+ HOURLY=0
142
+ DAILY=1
143
+ attr_reader :period, :xDim, :yDim, :mD
144
+
145
+ def initialize(path,period)
146
+ @layers = {}
147
+ File.open(path) do |gridFile|
148
+ @mD = GridMetaData.new(gridFile)
149
+ if @mD == nil
150
+ raise "nil metadata"
151
+ end
152
+ @xDim = @mD.xDim
153
+ @yDim = @mD.yDim
154
+ @mD.zStart.step(@mD.zEnd,@mD.zIncr) do |layer_index|
155
+ if (!(gridFile.eof) && (layer = GridLayer.new(gridFile,@mD)))
156
+ @layers[layer.zIndex] = layer
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ def realToIndex(x,y,z)
163
+ @myX = ((x-@mD.xStart)/@mD.xIncr).to_i
164
+ @myY = ((y-@mD.yStart)/@mD.yIncr).to_i
165
+ @myZ = z # ((z-@mD.zStart)/@mD.zIncr).to_i
166
+ # puts "realToIndex: x #{x}, xStart #{@mD.xStart}, xIncr #{@mD.xIncr} myX #{@myX}; y #{y}, myY #{@myY} z #{z}, myZ #{@myZ}"
167
+ [@myX,@myY,@myZ]
168
+ end
169
+
170
+ def get(x,y,z)
171
+ # puts "get #{x},#{y},#{z}"
172
+ # puts "xStart=#{@mD.xStart}, yStart=#{@mD.yStart}, zStart=#{@mD.zStart}"
173
+ # note that this just truncates; it should round to center of cell!
174
+ realToIndex(x,y,z)
175
+ # puts "@myX=#{@myX}, @myY=#{@myY}, @myZ=#{@myZ}"
176
+ # puts "#{@layers[@myZ]}"
177
+ if @layers[@myZ] == nil
178
+ nil
179
+ else
180
+ val = @layers[@myZ].get(@myX,@myY)
181
+ val == mD.badVal ? nil : val
182
+ end
183
+ end
184
+
185
+ # Grids are stored from low to high latitude, so as you look at the text file, it's mirrored vertically.
186
+ # The first line in a given grid layer is the LOWEST latitude, the last is the highest.
187
+ def get_by_index(longitude_index,latitude_index,doy)
188
+ if @layers[doy]
189
+ val = @layers[doy].get(longitude_index,latitude_index)
190
+ val == mD.badVal ? nil : val
191
+ else
192
+ nil
193
+ end
194
+ end
195
+
196
+ def each_value(lat,long)
197
+ # note switch here -- grids do longitude as X
198
+ realToIndex(long,lat,0)
199
+ @layers.keys.sort.each do |doy|
200
+ layer = @layers[doy]
201
+ if layer == nil
202
+ yield nil
203
+ else
204
+ val = layer.get(@myX,@myY)
205
+ val = nil if val == mD.badVal
206
+ yield val
207
+ end
208
+ end
209
+ end
210
+
211
+ def each
212
+ @layers.each { |layer| yield layer }
213
+ end
214
+
215
+ def layer_list
216
+ @layers.keys
217
+ end
218
+
219
+ def layer(doy)
220
+ @layers[doy]
221
+ end
222
+
223
+ def latitude_for(index)
224
+ mD.yStart + (index * mD.yIncr)
225
+ end
226
+
227
+ def longitude_for(index)
228
+ mD.xStart + (index * mD.xIncr)
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,3 @@
1
+ module AgwxGrids
2
+ VERSION = "0.0.1"
3
+ end
data/lib/agwx_grids.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "agwx_grids/version"
2
+ require 'agwx_grids/grid'
3
+
4
+ module AgwxGrids
5
+ # Your code goes here...
6
+ end
data/test/grid_test.rb ADDED
@@ -0,0 +1,60 @@
1
+ require "test/unit"
2
+ require "agwx_grids"
3
+
4
+ include AgwxGrids
5
+
6
+ DOY = 7
7
+ ROW = 1
8
+ COL = 5
9
+ VAL = -14.35 # The value for get_by_index(col,row,doy)
10
+
11
+ class TestAgwxGrid < Test::Unit::TestCase
12
+ def setup
13
+ @test_data_path = File.join(File.dirname(__FILE__),'grids')
14
+ @grid = Grid.new(File.join(@test_data_path,"WIMNTMin2002"),Grid::DAILY)
15
+ end
16
+
17
+ def test_can_load
18
+ assert(@grid)
19
+ end
20
+
21
+ def test_each_value
22
+ ii=0
23
+ @grid.each_value(44.0,-89.0) {|val| assert_equal(Float, val.class); ii+=1}
24
+ assert_equal(364, ii)
25
+ end
26
+
27
+ def test_get_by_index
28
+ assert_equal(VAL, @grid.get_by_index(COL,ROW,DOY))
29
+ end
30
+
31
+ def test_layer
32
+ assert_equal(GridLayer, @grid.layer(DOY).class)
33
+ end
34
+
35
+ def test_row
36
+ assert_equal(Array, @grid.layer(DOY).row(1).class)
37
+ assert_equal(VAL, @grid.layer(DOY).row(1)[5])
38
+ end
39
+
40
+ def test_y_latitude
41
+ lat_start = @grid.mD.yStart
42
+ lat_incr = @grid.mD.yIncr
43
+ assert_in_delta(lat_start + 3 * lat_incr, @grid.latitude_for(3), 2 ** -20)
44
+ end
45
+
46
+ def test_x_longitude
47
+ long_start = @grid.mD.xStart
48
+ long_incr = @grid.mD.xIncr
49
+ assert_in_delta(long_start + 3 * long_incr, @grid.longitude_for(3), 2 ** -20)
50
+ end
51
+ end
52
+ # begin # test the Grid class
53
+ # puts "====== initializing a grid =========="
54
+ # grid = Grid.new("grids/WIMNTMin2002",Grid::DAILY)
55
+ # # puts "====== dumping each_value =========="
56
+ # # grid.each_value(44.0,-89.0) {|vapr| puts vapr}
57
+ # for y_index in (0..grid.yDim)
58
+ # puts grid.get_by_index(0,y_index,22)
59
+ # end
60
+ # end