seis_ruby 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +23 -4
- data/Rakefile +10 -0
- data/bin/seis_ruby +1 -1
- data/lib/seis_ruby/application.rb +76 -0
- data/lib/seis_ruby/command.rb +15 -0
- data/lib/seis_ruby/core_ext/file.rb +7 -0
- data/lib/seis_ruby/core_ext.rb +5 -0
- data/lib/seis_ruby/data/cmtsolution.rb +84 -64
- data/lib/seis_ruby/data/sac/ascii/head.rb +49 -0
- data/lib/seis_ruby/data/sac/ascii.rb +61 -0
- data/lib/seis_ruby/data/sac/binary/head.rb +5 -0
- data/lib/seis_ruby/data/sac/binary.rb +26 -0
- data/lib/seis_ruby/data/sac/body.rb +62 -0
- data/lib/seis_ruby/data/sac/head.rb +162 -0
- data/lib/seis_ruby/data/sac.rb +99 -3
- data/lib/seis_ruby/data.rb +6 -5
- data/lib/seis_ruby/database/global_cmt_catalog_search/cmtsolution_format.rb +30 -0
- data/lib/seis_ruby/database/global_cmt_catalog_search.rb +44 -0
- data/lib/seis_ruby/database.rb +3 -0
- data/lib/seis_ruby/version.rb +1 -1
- data/lib/seis_ruby.rb +12 -3
- data/seis_ruby.gemspec +8 -14
- data/test/data/000000001.SAC +0 -0
- data/test/data/000000001.SAC_ascii +32 -0
- data/test/seis_ruby/command_test.rb +19 -0
- data/test/seis_ruby/core_ext/file_test.rb +7 -0
- data/test/seis_ruby/data/cmtsolution_test.rb +63 -0
- data/test/seis_ruby/data/sac/ascii_test.rb +15 -0
- data/test/seis_ruby/data/sac/binary_test.rb +15 -0
- data/test/seis_ruby/data/sac/body_test.rb +27 -0
- data/test/seis_ruby/data/sac/head_test.rb +78 -0
- data/test/seis_ruby/data/sac_test.rb +52 -0
- data/test/seis_ruby/database/global_cmt_catalog_search/cmtsolution_format_test.rb +142 -0
- data/test/seis_ruby/database/global_cmt_catalog_search_test.rb +10 -0
- data/test/seis_ruby_test.rb +0 -0
- data/test/test_helper.rb +7 -0
- data/test.watchr +13 -0
- metadata +79 -54
- data/lib/seis_ruby/bin/seis_ruby_runner.rb +0 -58
- data/lib/seis_ruby/bin.rb +0 -6
- data/lib/seis_ruby/io/gcmt_catalog/custom_html_parser.rb +0 -16
- data/lib/seis_ruby/io/gcmt_catalog.rb +0 -50
- data/lib/seis_ruby/io.rb +0 -6
- data/rakefile +0 -8
- data/spec/seis_ruby/data/cmtsolution_spec.rb +0 -83
- data/spec/seis_ruby/io/gcmt_catalog_spec.rb +0 -19
- data/spec/seis_ruby/io/scrape.yaml +0 -3972
- data/spec/spec_helper.rb +0 -18
data/README.rdoc
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
= SeisRuby
|
2
|
-
==
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
== What is this?
|
3
|
+
Ruby library and command line tools for earthquake science.
|
4
|
+
|
5
|
+
== Command line tool
|
6
|
+
seis_ruby help
|
7
|
+
|
8
|
+
== Library
|
9
|
+
Please check {SeisRuby::Command} which provides handy methods via {::SeisRuby}.
|
10
|
+
|
11
|
+
== Quick start
|
12
|
+
require 'seis_ruby'
|
13
|
+
sac = SeisRuby.load_file('test_read.SAC') # Read a SAC file
|
14
|
+
sac.head[:a] += 3.0 # Shift Tp 3.0 seconds
|
15
|
+
sac.dump_file('test_dump_shifted.SAC') # Output a SAC file
|
16
|
+
|
17
|
+
== License
|
18
|
+
{http://www.gnu.org/licenses/gpl.html GPL v3}
|
19
|
+
|
20
|
+
== Author
|
21
|
+
Kasahara Amato
|
22
|
+
|
23
|
+
== Semantic Versioning
|
24
|
+
This software basically follow the {http://semver.org/ Semantic Versioning}.
|
data/Rakefile
ADDED
data/bin/seis_ruby
CHANGED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
class ::SeisRuby::Application < Thor
|
4
|
+
require 'yaml'
|
5
|
+
require 'pry'
|
6
|
+
require 'fileutils'
|
7
|
+
require 'ruby_patch'
|
8
|
+
|
9
|
+
COMMAND = 'seis_ruby'
|
10
|
+
COMPLETION_FILE = File.join(ENV['HOME'], ".config", COMMAND, "completion.bash")
|
11
|
+
|
12
|
+
desc "repl", "Start interactive mode."
|
13
|
+
def repl
|
14
|
+
Pry.start(
|
15
|
+
::TOPLEVEL_BINDING,
|
16
|
+
prompt: [
|
17
|
+
lambda{|obj, nest_level, pry| "[#{pry.input_array.size}] SeisRuby(#{obj}):#{nest_level}> "},
|
18
|
+
lambda{|obj, nest_level, pry| "[#{pry.input_array.size}] SeisRuby(#{obj}):#{nest_level}* "}])
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "yamlize URI", "Read data from URI, parse it and output to STDOUT in YAML format."
|
22
|
+
def yamlize(uri)
|
23
|
+
puts YAML.dump(::SeisRuby.load_file(uri))
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "generate_completion", "Generate bash completion of seis_ruby's commands."
|
27
|
+
def generate_completion
|
28
|
+
FileUtils.mkdir_p(File.dirname(COMPLETION_FILE))
|
29
|
+
FileUtils.mv(COMPLETION_FILE, "#{COMPLETION_FILE}.#{Time.now.ymdhms}.bak") if File.exist?(COMPLETION_FILE)
|
30
|
+
File.write(COMPLETION_FILE, completion_function_str)
|
31
|
+
|
32
|
+
puts <<-EOS
|
33
|
+
Please add following code to your ~/.bashrc if necessary.
|
34
|
+
|
35
|
+
# enable seis_ruby completion.
|
36
|
+
if [ -f #{COMPLETION_FILE} ]; then
|
37
|
+
source #{COMPLETION_FILE}
|
38
|
+
fi
|
39
|
+
EOS
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'version', 'Print seis_ruby version'
|
43
|
+
def version
|
44
|
+
puts ::SeisRuby::VERSION
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def completion_function_str
|
50
|
+
<<-EOS
|
51
|
+
_#{COMMAND}()
|
52
|
+
{
|
53
|
+
local sub_commands current previous
|
54
|
+
sub_commands="#{sub_commands_str()}"
|
55
|
+
current="${COMP_WORDS[COMP_CWORD]}"
|
56
|
+
previous="${COMP_WORDS[COMP_CWORD-1]}"
|
57
|
+
|
58
|
+
case "${previous}" in
|
59
|
+
#{COMMAND}|help)
|
60
|
+
COMPREPLY=( $(compgen -W "${sub_commands}" ${current}) );;
|
61
|
+
* )
|
62
|
+
COMPREPLY=( $(compgen -f ${current}) );;
|
63
|
+
esac
|
64
|
+
}
|
65
|
+
complete -F _#{COMMAND} #{COMMAND}
|
66
|
+
EOS
|
67
|
+
end
|
68
|
+
|
69
|
+
def sub_commands_str
|
70
|
+
`#{COMMAND} --help`\
|
71
|
+
.split("\n")\
|
72
|
+
.select{|l| l =~ / *#{COMMAND} /}\
|
73
|
+
.map{|l| l.split[1]}\
|
74
|
+
.join(' ')
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ::SeisRuby::Command
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
# @param [String] uri URI of file or database query.
|
5
|
+
# @return [Array, Hash, ::SeisRuby::Data::*]
|
6
|
+
def load_file(uri)
|
7
|
+
if (klass = ::SeisRuby::Data::Sac).uri_for_self?(uri)
|
8
|
+
klass.load_file(uri)
|
9
|
+
elsif (klass = ::SeisRuby::Database::GlobalCmtCatalogSearch).uri_for_self?(uri)
|
10
|
+
klass.load_file(uri)
|
11
|
+
else
|
12
|
+
raise ArgumentError, "File type undetectable: #{uri}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,77 +1,97 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Cmtsolution
|
4
|
-
require 'ruby_patch'
|
5
|
-
extend ::RubyPatch::AutoLoad
|
1
|
+
class ::SeisRuby::Data::Cmtsolution < ::SeisRuby::Data
|
2
|
+
require 'ostruct'
|
6
3
|
|
7
|
-
|
4
|
+
def self.load_file(file)
|
5
|
+
load(File.read_uri(file), file: file)
|
6
|
+
end
|
8
7
|
|
9
|
-
|
8
|
+
def self.load(raw_data, meta_data)
|
9
|
+
new(raw_data, meta_data)
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
def initialize(raw_data, meta_data)
|
13
|
+
@raw_data = raw_data
|
14
|
+
@meta_data = meta_data
|
15
|
+
parse!
|
16
|
+
end
|
17
|
+
attr_accessor :hypocenter
|
18
|
+
attr_accessor :centroid
|
17
19
|
|
18
|
-
|
19
|
-
h = {hypocenter: {}, centroid: {}}
|
20
|
+
private
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
h[:hypocenter][k] = line0_rest.shift.to_f
|
31
|
-
}
|
32
|
-
h[:hypocenter][:region_name] = line0_rest.join(' ')
|
33
|
-
h[:event_name] = lines[1].split(':')[1].strip
|
34
|
-
values = lines[2..-1].map{|line| line.split(':')[1].to_f}
|
35
|
-
[
|
36
|
-
:time_shift, :half_duration,
|
37
|
-
:latitude, :longitude, :depth,
|
38
|
-
:mrr, :mtt, :mpp, :mrt, :mrp, :mtp,
|
39
|
-
].zip(values).each{|k, v| h[:centroid][k] = v}
|
22
|
+
def parse!
|
23
|
+
lines\
|
24
|
+
= @raw_data\
|
25
|
+
.split("\n")\
|
26
|
+
.map(&:strip)\
|
27
|
+
.delete_if(&:empty?)
|
28
|
+
@hypocenter = Hypocenter.new(lines[0])
|
29
|
+
@centroid = Centroid.new(lines[1..-1])
|
30
|
+
end
|
40
31
|
|
32
|
+
class Hypocenter < OpenStruct
|
33
|
+
FIELDS = [
|
34
|
+
# name, column, converter
|
35
|
+
[:data_source, ( 0..3 ), :strip],
|
36
|
+
[:year, ( 4..8 ), :to_i ],
|
37
|
+
[:month, ( 9..11), :to_i ],
|
38
|
+
[:day, (12..14), :to_i ],
|
39
|
+
[:hour, (15..17), :to_i ],
|
40
|
+
[:minute, (18..20), :to_i ],
|
41
|
+
[:second, (21..26), :to_f ],
|
42
|
+
[:latitude, (27..35), :to_f ],
|
43
|
+
[:longitude, (36..45), :to_f ],
|
44
|
+
[:depth, (46..51), :to_f ],
|
45
|
+
[:mb, (52..55), :to_f ],
|
46
|
+
[:ms, (56..59), :to_f ],
|
47
|
+
[:region_name, (60..-1), :strip],
|
48
|
+
]
|
41
49
|
|
42
|
-
|
43
|
-
|
50
|
+
def initialize(str)
|
51
|
+
h = {}
|
52
|
+
FIELDS.each{|name, column, converter|
|
53
|
+
h[name] = str[column].__send__(converter)
|
54
|
+
}
|
55
|
+
super(h)
|
56
|
+
end
|
57
|
+
attr_accessor :table
|
44
58
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
59
|
+
# Origin time
|
60
|
+
def origin_time
|
61
|
+
sec = self.second
|
62
|
+
int_sec = sec.to_i
|
63
|
+
decimal_sec = sec - int_sec
|
64
|
+
micro_sec = (decimal_sec*(10**6)).round
|
65
|
+
Time.gm(
|
66
|
+
self.year, self.month, self.day,
|
67
|
+
self.hour, self.minute, int_sec, micro_sec)
|
68
|
+
end
|
69
|
+
end
|
53
70
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
EOS
|
71
|
-
end
|
71
|
+
class Centroid < OpenStruct
|
72
|
+
FIELDS = [
|
73
|
+
# name, converter
|
74
|
+
[:event_name, :strip],
|
75
|
+
[:time_shift, :to_f ],
|
76
|
+
[:half_duration, :to_f ],
|
77
|
+
[:latitude, :to_f ],
|
78
|
+
[:longitude, :to_f ],
|
79
|
+
[:depth, :to_f ],
|
80
|
+
[:mrr, :to_f ],
|
81
|
+
[:mtt, :to_f ],
|
82
|
+
[:mpp, :to_f ],
|
83
|
+
[:mrt, :to_f ],
|
84
|
+
[:mrp, :to_f ],
|
85
|
+
[:mtp, :to_f ],
|
86
|
+
]
|
72
87
|
|
73
|
-
|
74
|
-
|
88
|
+
def initialize(lines)
|
89
|
+
h = {}
|
90
|
+
FIELDS.zip(lines){|(name, converter), str|
|
91
|
+
h[name] = str.split(':', 2).last.__send__(converter)
|
92
|
+
}
|
93
|
+
super(h)
|
75
94
|
end
|
95
|
+
attr_accessor :table
|
76
96
|
end
|
77
97
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module ::SeisRuby::Data::Sac::Ascii::Head
|
2
|
+
N_LINES = 30
|
3
|
+
|
4
|
+
RANGES_15_BY_5 = [[(0..14), (15..29), (30..44), (45..59), (60..-1)]]
|
5
|
+
RANGE_10_BY_5 = [[(0..9), (10..19), (20..29), (30..39), (40..-1)]]
|
6
|
+
RANGES_8_BY_3 = [[(0..7), (8..15), (16..-1)]]
|
7
|
+
RANGES = [
|
8
|
+
*(RANGES_15_BY_5*14),
|
9
|
+
*(RANGE_10_BY_5*8),
|
10
|
+
[(0..7), (8..-1)],
|
11
|
+
*(RANGES_8_BY_3*7),
|
12
|
+
]
|
13
|
+
|
14
|
+
FORMATS_15_7G_BY_5 = ["%#15.7g", "%#15.7g", "%#15.7g", "%#15.7g", "%#15.7g\n"]
|
15
|
+
FORMATS_10I_BY_5 = ["%10i", "%10i", "%10i", "%10i", "%10i\n"]
|
16
|
+
FORMATS_8S_BY_3 = ["%-8s", "%-8s", "%-8s\n"]
|
17
|
+
FORMATS = [
|
18
|
+
*(FORMATS_15_7G_BY_5*14),
|
19
|
+
*(FORMATS_10I_BY_5*8),
|
20
|
+
"%-8s", "%-16s\n",
|
21
|
+
*(FORMATS_8S_BY_3*7),
|
22
|
+
]
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def convert_from_head_str(head_str)
|
26
|
+
::SeisRuby::Data::Sac::Head\
|
27
|
+
.convert_from_head(split_head_str(head_str))
|
28
|
+
end
|
29
|
+
|
30
|
+
def convert_to_head_str(head)
|
31
|
+
array = ::SeisRuby::Data::Sac::Head\
|
32
|
+
.convert_to_head(head)
|
33
|
+
sprintf(FORMATS.join, *array)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def split_head_str(head_str)
|
39
|
+
head_lines = head_str.split("\n")
|
40
|
+
raise ArgumentError unless head_lines.size == N_LINES
|
41
|
+
|
42
|
+
head_lines\
|
43
|
+
.zip(RANGES)\
|
44
|
+
.map{|line, ranges| ranges\
|
45
|
+
.map{|range| line[range]}}\
|
46
|
+
.flatten
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class ::SeisRuby::Data::Sac::Ascii < ::SeisRuby::Data::Sac
|
2
|
+
require 'seis_ruby/data/sac/ascii/head'
|
3
|
+
|
4
|
+
EXT = '.sac_ascii'
|
5
|
+
|
6
|
+
DATA_COLUMN_SIZE = 5
|
7
|
+
DATA_FORMAT_STRING = '%#15.7g'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def parse(str)
|
11
|
+
head_str, body_str = separate_head_body(str)
|
12
|
+
|
13
|
+
head = ::SeisRuby::Data::Sac::Ascii::Head\
|
14
|
+
.convert_from_head_str(head_str)
|
15
|
+
body = ::SeisRuby::Data::Sac::Body\
|
16
|
+
.shape_body(body_str.split.map(&:to_f), head)
|
17
|
+
|
18
|
+
[head, body]
|
19
|
+
end
|
20
|
+
|
21
|
+
def dump(head, body)
|
22
|
+
::SeisRuby::Data::Sac::Ascii::Head.convert_to_head_str(head)\
|
23
|
+
+ format_body(::SeisRuby::Data::Sac::Body.body_for_dump(body, head))
|
24
|
+
end
|
25
|
+
|
26
|
+
def uri_for_self?(uri)
|
27
|
+
uri =~ /#{Regexp.escape(EXT)}\z/i
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def format_body(body)
|
33
|
+
body\
|
34
|
+
.each_slice(DATA_COLUMN_SIZE)\
|
35
|
+
.map{|line| line\
|
36
|
+
.map{|v| sprintf(DATA_FORMAT_STRING, v)}\
|
37
|
+
.join << "\n"}\
|
38
|
+
.join
|
39
|
+
end
|
40
|
+
|
41
|
+
def separate_head_body(str)
|
42
|
+
# Use <tt>nth_index</tt> to deal with bloken SAC_ASCII file produced by SAC's <tt>write alpha</tt> command.
|
43
|
+
last_of_head = nth_index(str, "\n", ::SeisRuby::Data::Sac::Ascii::Head::N_LINES)
|
44
|
+
head_str = str[0..last_of_head]
|
45
|
+
body_str = str[(last_of_head + 1)..-1]
|
46
|
+
|
47
|
+
[head_str, body_str]
|
48
|
+
end
|
49
|
+
|
50
|
+
def nth_index(str, pattern, nth = 1, pos = 0)
|
51
|
+
raise ArgumentError, "nth should >= 1: #{nth}" if nth < 1
|
52
|
+
|
53
|
+
index = str.index(pattern, pos)
|
54
|
+
if index.nil? || nth == 1
|
55
|
+
index
|
56
|
+
else
|
57
|
+
nth_index(str, pattern, nth - 1, index + 1)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ::SeisRuby::Data::Sac::Binary
|
2
|
+
require 'seis_ruby/data/sac/binary/head'
|
3
|
+
|
4
|
+
EXT = '.sac'
|
5
|
+
|
6
|
+
def self.parse(raw_data)
|
7
|
+
raw_data_array = raw_data.unpack(::SeisRuby::Data::Sac::Binary::Head::FORMAT_STRING)
|
8
|
+
head = ::SeisRuby::Data::Sac::Head\
|
9
|
+
.convert_from_head(raw_data_array[0...::SeisRuby::Data::Sac::Head::NAMES.size])
|
10
|
+
body = ::SeisRuby::Data::Sac::Body\
|
11
|
+
.shape_body(raw_data_array[head.size..-1], head)
|
12
|
+
|
13
|
+
[head, body]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.dump(head, body)
|
17
|
+
(
|
18
|
+
::SeisRuby::Data::Sac::Head.convert_to_head(head)\
|
19
|
+
+ ::SeisRuby::Data::Sac::Body.body_for_dump(body, head))\
|
20
|
+
.pack(::SeisRuby::Data::Sac::Binary::Head::FORMAT_STRING)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.uri_for_self?(uri)
|
24
|
+
uri =~ /#{Regexp.escape(EXT)}\z/i
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module ::SeisRuby::Data::Sac::Body
|
2
|
+
module_function
|
3
|
+
|
4
|
+
def shape_body(body, head)
|
5
|
+
case head[:iftype]
|
6
|
+
when :itime
|
7
|
+
# [y0, y1, ...]
|
8
|
+
body
|
9
|
+
when :ixy, :iamph
|
10
|
+
# ixy: [[x0, y0], [x1, y1], ...]
|
11
|
+
# iamph: [[theta0, amp0], [theta1, amp1], ...]
|
12
|
+
body\
|
13
|
+
.each_slice(body.size/2)\
|
14
|
+
.to_a\
|
15
|
+
.reverse\
|
16
|
+
.transpose
|
17
|
+
when :irlim
|
18
|
+
# [(real0, imaginary0), (real1, imaginary1), ...]
|
19
|
+
body\
|
20
|
+
.each_slice(body.size/2)\
|
21
|
+
.to_a\
|
22
|
+
.transpose\
|
23
|
+
.map{|real, imaginary| Complex(real, imaginary)}
|
24
|
+
when :ixyz
|
25
|
+
# z[ix][iy]
|
26
|
+
body\
|
27
|
+
.each_slice(head[:nxsize])\
|
28
|
+
.to_a\
|
29
|
+
.transpose
|
30
|
+
else
|
31
|
+
raise ArgumentError, "Unknown iftype: #{head[:iftype].inspect}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def body_for_dump(body, head)
|
36
|
+
case head[:iftype]
|
37
|
+
when :itime
|
38
|
+
# [y0, y1, ...]
|
39
|
+
body
|
40
|
+
when :ixy, :iamph
|
41
|
+
# ixy: [[x0, y0], [x1, y1], ...]
|
42
|
+
# iamph: [[theta0, amp0], [theta1, amp1], ...]
|
43
|
+
body\
|
44
|
+
.transpose\
|
45
|
+
.reverse\
|
46
|
+
.flatten
|
47
|
+
when :irlim
|
48
|
+
# [(real0, imaginary0), (real1, imaginary1), ...]
|
49
|
+
body\
|
50
|
+
.map{|c| [c.real, c.imaginary]}\
|
51
|
+
.transpose\
|
52
|
+
.flatten
|
53
|
+
when :ixyz
|
54
|
+
# z[ix][iy]
|
55
|
+
body\
|
56
|
+
.transpose\
|
57
|
+
.flatten
|
58
|
+
else
|
59
|
+
raise ArgumentError, "Unknown iftype: #{head[:iftype].inspect}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
module ::SeisRuby::Data::Sac::Head
|
2
|
+
|
3
|
+
# SAC header field names.
|
4
|
+
NAMES = [
|
5
|
+
:delta, :depmin, :depmax, :scale, :odelta,
|
6
|
+
:b, :e, :o, :a, :internal1,
|
7
|
+
:t0, :t1, :t2, :t3, :t4,
|
8
|
+
:t5, :t6, :t7, :t8, :t9,
|
9
|
+
:f, :resp0, :resp1, :resp2, :resp3,
|
10
|
+
:resp4, :resp5, :resp6, :resp7, :resp8,
|
11
|
+
:resp9, :stla, :stlo, :stel, :stdp,
|
12
|
+
:evla, :evlo, :evel, :evdp, :mag,
|
13
|
+
:user0, :user1, :user2, :user3, :user4,
|
14
|
+
:user5, :user6, :user7, :user8, :user9,
|
15
|
+
:dist, :az, :baz, :gcarc, :internal2,
|
16
|
+
:internal3, :depmen, :cmpaz, :cmpinc, :xminimum,
|
17
|
+
:xmaximum, :yminimum, :ymaximum, :adjtm, :unused1,
|
18
|
+
:unused2, :unused3, :unused4, :unused5, :unused6,
|
19
|
+
:nzyear, :nzjday, :nzhour, :nzmin, :nzsec,
|
20
|
+
:nzmsec, :nvhdr, :norid, :nevid, :npts,
|
21
|
+
:nsnpts, :nwfid, :nxsize, :nysize, :unused7,
|
22
|
+
:iftype, :idep, :iztype, :unused8, :iinst,
|
23
|
+
:istreg, :ievreg, :ievtyp, :iqual, :isynth,
|
24
|
+
:imagtyp, :imagsrc, :unused9, :unused10, :unused11,
|
25
|
+
:unused12, :unused13, :unused14, :unused15, :unused16,
|
26
|
+
:leven, :lpspol, :lovrok, :lcalda, :unused17,
|
27
|
+
:kstnm, :kevnm,
|
28
|
+
:khole, :ko, :ka,
|
29
|
+
:kt0, :kt1, :kt2,
|
30
|
+
:kt3, :kt4, :kt5,
|
31
|
+
:kt6, :kt7, :kt8,
|
32
|
+
:kt9, :kf, :kuser0,
|
33
|
+
:kuser1, :kuser2, :kcmpnm,
|
34
|
+
:knetwk, :kdatrd, :kinst,
|
35
|
+
]
|
36
|
+
|
37
|
+
# Enumerated values and their meanings.
|
38
|
+
ENUMERATED_VALUES = [
|
39
|
+
:itime, :irlim, :iamph, :ixy,
|
40
|
+
:iunkn, :idisp, :ivel, :iacc,
|
41
|
+
:ib, :iday, :io, :ia,
|
42
|
+
:it0, :it1, :it2, :it3,
|
43
|
+
:it4, :it5, :it6, :it7,
|
44
|
+
:it8, :it9, :iradnv, :itannv,
|
45
|
+
:iradev, :itanev, :inorth, :ieast,
|
46
|
+
:ihorza, :idown, :iup, :illlbb,
|
47
|
+
:iwwsn1, :iwwsn2, :ihglp, :isro,
|
48
|
+
:inucl, :ipren, :ipostn, :iquake,
|
49
|
+
:ipreq, :ipostq, :ichem, :iother,
|
50
|
+
:igood, :iglch, :idrop, :ilowsn,
|
51
|
+
:irldta, :ivolts, :ixyz, :imb,
|
52
|
+
:ims, :iml, :imw, :imd,
|
53
|
+
:imx, :ineic, :ipde, :iisc,
|
54
|
+
:ireb, :iusgs, :ibrk, :icaltech,
|
55
|
+
:illnl, :ievloc, :ijsop, :iuser,
|
56
|
+
:iunknown, :iqb, :iqb1, :iqb2,
|
57
|
+
:iqbx, :iqmt, :ieq, :ieq1,
|
58
|
+
:ieq2, :ime, :iex, :inu,
|
59
|
+
:inc, :io, :il, :ir,
|
60
|
+
:it, :iu,
|
61
|
+
].map.with_index.with_object({}){|(v, i), h| h[i + 1] = v}
|
62
|
+
|
63
|
+
# Float
|
64
|
+
CONVERTER_FROM_HEAD_FLOAT = lambda{|x|
|
65
|
+
x_f = x.to_f
|
66
|
+
return nil if x_f == -12345.0
|
67
|
+
x_f
|
68
|
+
}
|
69
|
+
CONVERTER_TO_HEAD_FLOAT = lambda{|x|
|
70
|
+
return -12345.0 if x.nil?
|
71
|
+
x.to_f
|
72
|
+
}
|
73
|
+
|
74
|
+
# Integer
|
75
|
+
CONVERTER_FROM_HEAD_INTEGER = lambda{|n|
|
76
|
+
n_i = n.to_f.round
|
77
|
+
return nil if n_i == -12345
|
78
|
+
n_i
|
79
|
+
}
|
80
|
+
CONVERTER_TO_HEAD_INTEGER = lambda{|n|
|
81
|
+
return -12345 if n.nil?
|
82
|
+
n.to_f.round
|
83
|
+
}
|
84
|
+
|
85
|
+
# Enumerated value
|
86
|
+
CONVERTER_FROM_HEAD_ENUMERATED_VALUE = lambda{|n|
|
87
|
+
n_i = n.to_f.round
|
88
|
+
return nil if n_i == -12345
|
89
|
+
raise ArgumentError, "Enumerated value out of range: #{n.inspect}.to_f.round" unless ENUMERATED_VALUES.has_key?(n_i)
|
90
|
+
ENUMERATED_VALUES.fetch(n_i)
|
91
|
+
}
|
92
|
+
CONVERTER_TO_HEAD_ENUMERATED_VALUE = lambda{|sym|
|
93
|
+
return -12345 if sym.nil?
|
94
|
+
sym_sym = sym.to_sym
|
95
|
+
raise ArgumentError, "Unknown enumerated value: #{sym.inspect}.to_sym" unless ENUMERATED_VALUES.has_value?(sym_sym)
|
96
|
+
ENUMERATED_VALUES.key(sym_sym)
|
97
|
+
}
|
98
|
+
|
99
|
+
# Logical
|
100
|
+
CONVERTER_FROM_HEAD_LOGICAL = lambda{|n| n.to_f.round == 1}
|
101
|
+
CONVERTER_TO_HEAD_LOGICAL = lambda{|v|
|
102
|
+
if v
|
103
|
+
1
|
104
|
+
else
|
105
|
+
0
|
106
|
+
end
|
107
|
+
}
|
108
|
+
|
109
|
+
# String
|
110
|
+
CONVERTER_FROM_HEAD_STRING = lambda{|str|
|
111
|
+
stripped_str = str.to_s.strip
|
112
|
+
return nil if stripped_str == "-12345"
|
113
|
+
stripped_str
|
114
|
+
}
|
115
|
+
CONVERTER_TO_HEAD_STRING = lambda{|str|
|
116
|
+
return "-12345" if str.nil?
|
117
|
+
str.to_s.strip
|
118
|
+
}
|
119
|
+
|
120
|
+
CONVERTERS_FROM_HEAD\
|
121
|
+
= [CONVERTER_FROM_HEAD_FLOAT]*70\
|
122
|
+
+ [CONVERTER_FROM_HEAD_INTEGER]*15\
|
123
|
+
+ [CONVERTER_FROM_HEAD_ENUMERATED_VALUE]*20\
|
124
|
+
+ [CONVERTER_FROM_HEAD_LOGICAL]*5\
|
125
|
+
+ [CONVERTER_FROM_HEAD_STRING]*23
|
126
|
+
|
127
|
+
CONVERTERS_TO_HEAD\
|
128
|
+
= [CONVERTER_TO_HEAD_FLOAT]*70\
|
129
|
+
+ [CONVERTER_TO_HEAD_INTEGER]*15\
|
130
|
+
+ [CONVERTER_TO_HEAD_ENUMERATED_VALUE]*20\
|
131
|
+
+ [CONVERTER_TO_HEAD_LOGICAL]*5\
|
132
|
+
+ [CONVERTER_TO_HEAD_STRING]*23
|
133
|
+
|
134
|
+
Field = Struct.new(:name, :converter_from_head, :converter_to_head)
|
135
|
+
FIELDS = NAMES\
|
136
|
+
.zip(CONVERTERS_FROM_HEAD, CONVERTERS_TO_HEAD)\
|
137
|
+
.map{|name, from, to| Field.new(name, from, to)}
|
138
|
+
|
139
|
+
class << self
|
140
|
+
# @param [Array] array
|
141
|
+
# @return [Hash]
|
142
|
+
def convert_from_head(array)
|
143
|
+
name_val = FIELDS\
|
144
|
+
.zip(array)\
|
145
|
+
.map{|field, val|
|
146
|
+
begin
|
147
|
+
[field.name, field.converter_from_head.call(val)]
|
148
|
+
rescue ArgumentError => e
|
149
|
+
$stderr.puts "#{field.name.inspect}"
|
150
|
+
raise e
|
151
|
+
end
|
152
|
+
}
|
153
|
+
Hash[name_val]
|
154
|
+
end
|
155
|
+
|
156
|
+
# @param [Hash] hash
|
157
|
+
# @return [Array]
|
158
|
+
def convert_to_head(hash)
|
159
|
+
FIELDS.map{|field| field.converter_to_head.call(hash[field.name])}
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|