-
Notifications
You must be signed in to change notification settings - Fork 8
/
sc3ml.go
271 lines (236 loc) · 8.41 KB
/
sc3ml.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/*
Package sc3ml is for parsing SeisComPML.
*/
package sc3ml
import (
"bytes"
"encoding/xml"
"errors"
"time"
)
const (
sc3ml06 = `http://geofon.gfz-potsdam.de/ns/seiscomp3-schema/0.6`
sc3ml07 = `http://geofon.gfz-potsdam.de/ns/seiscomp3-schema/0.7`
sc3ml08 = `http://geofon.gfz-potsdam.de/ns/seiscomp3-schema/0.8`
sc3ml09 = `http://geofon.gfz-potsdam.de/ns/seiscomp3-schema/0.9`
sc3ml10 = `http://geofon.gfz-potsdam.de/ns/seiscomp3-schema/0.10`
sc3ml11 = `http://geofon.gfz-potsdam.de/ns/seiscomp3-schema/0.11`
sc3ml12 = `http://geofon.gfz-potsdam.de/ns/seiscomp3-schema/0.12`
sc3ml13 = `http://geofon.gfz-potsdam.de/ns/seiscomp3-schema/0.13`
)
type Seiscomp struct {
XMLns string `xml:"xmlns,attr"`
EventParameters EventParameters `xml:"EventParameters"`
}
type EventParameters struct {
Events []Event `xml:"event"`
Picks []Pick `xml:"pick"`
Amplitudes []Amplitude `xml:"amplitude"`
Origins []Origin `xml:"origin"`
}
type Event struct {
PublicID string `xml:"publicID,attr"`
PreferredOriginID string `xml:"preferredOriginID"`
PreferredMagnitudeID string `xml:"preferredMagnitudeID"`
Type string `xml:"type"`
PreferredOrigin Origin
PreferredMagnitude Magnitude
ModificationTime time.Time `xml:"-"` // most recent modification time for all objects in the event. Not in the XML.
CreationInfo CreationInfo `xml:"creationInfo"`
}
type CreationInfo struct {
AgencyID string `xml:"agencyID"`
CreationTime time.Time `xml:"creationTime"`
ModificationTime time.Time `xml:"modificationTime"`
}
type Origin struct {
PublicID string `xml:"publicID,attr"`
Time TimeValue `xml:"time"`
Latitude RealQuantity `xml:"latitude"`
Longitude RealQuantity `xml:"longitude"`
Depth RealQuantity `xml:"depth"`
DepthType string `xml:"depthType"`
MethodID string `xml:"methodID"`
EarthModelID string `xml:"earthModelID"`
Quality Quality `xml:"quality"`
EvaluationMode string `xml:"evaluationMode"`
EvaluationStatus string `xml:"evaluationStatus"`
Arrivals []Arrival `xml:"arrival"`
StationMagnitudes []StationMagnitude `xml:"stationMagnitude"`
Magnitudes []Magnitude `xml:"magnitude"`
}
type Quality struct {
UsedPhaseCount int64 `xml:"usedPhaseCount"`
UsedStationCount int64 `xml:"usedStationCount"`
StandardError float64 `xml:"standardError"`
AzimuthalGap float64 `xml:"azimuthalGap"`
MinimumDistance float64 `xml:"minimumDistance"`
}
type Arrival struct {
PickID string `xml:"pickID"`
Phase string `xml:"phase"`
Azimuth float64 `xml:"azimuth"`
Distance float64 `xml:"distance"`
TimeResidual float64 `xml:"timeResidual"`
Weight float64 `xml:"weight"`
Pick Pick
}
type Pick struct {
PublicID string `xml:"publicID,attr"`
Time TimeValue `xml:"time"`
WaveformID WaveformID `xml:"waveformID"`
EvaluationMode string `xml:"evaluationMode"`
EvaluationStatus string `xml:"evaluationStatus"`
}
type WaveformID struct {
NetworkCode string `xml:"networkCode,attr"`
StationCode string `xml:"stationCode,attr"`
LocationCode string `xml:"locationCode,attr"`
ChannelCode string `xml:"channelCode,attr"`
}
type RealQuantity struct {
Value float64 `xml:"value"`
Uncertainty float64 `xml:"uncertainty"`
}
type TimeValue struct {
Value time.Time `xml:"value"`
}
type Magnitude struct {
PublicID string `xml:"publicID,attr"`
Magnitude RealQuantity `xml:"magnitude"`
Type string `xml:"type"`
MethodID string `xml:"methodID"`
StationCount int64 `xml:"stationCount"`
StationMagnitudeContributions []StationMagnitudeContribution `xml:"stationMagnitudeContribution"`
}
type StationMagnitudeContribution struct {
StationMagnitudeID string `xml:"stationMagnitudeID"`
Weight float64 `xml:"weight"`
Residual float64 `xml:"residual"`
StationMagnitude StationMagnitude
}
type StationMagnitude struct {
PublicID string `xml:"publicID,attr"`
Magnitude RealQuantity `xml:"magnitude"`
Type string `xml:"type"`
AmplitudeID string `xml:"amplitudeID"`
WaveformID WaveformID `xml:"waveformID"`
Amplitude Amplitude
}
type Amplitude struct {
PublicID string `xml:"publicID,attr"`
Amplitude RealQuantity `xml:"amplitude"`
PickID string `xml:"pickID"`
Azimuth float64 // not in the SC3ML - will be mapped from arrival using PickID
Distance float64 // not in the SC3ML - will be mapped from arrival using PickID
}
// Unmarshal unmarshals the SeisComPML in b and initialises all
// the objects referenced by ID in the SeisComPML e.g., PreferredOrigin,
// PreferredMagnitude etc.
//
// Supported SC3ML versions are 0.6, 0.7, 0.8, 0.9, 0.10, 0.11, 0.12, 0.13
// Any other versions will result in a error.
func Unmarshal(b []byte, s *Seiscomp) error {
if err := xml.Unmarshal(b, s); err != nil {
return err
}
switch s.XMLns {
case sc3ml06:
case sc3ml07:
case sc3ml08:
case sc3ml09:
case sc3ml10:
case sc3ml11:
case sc3ml12:
case sc3ml13:
default:
return errors.New("unsupported SC3ML version")
}
var picks = make(map[string]Pick)
for k, v := range s.EventParameters.Picks {
picks[v.PublicID] = s.EventParameters.Picks[k]
}
var arrivals = make(map[string]Arrival)
for i := range s.EventParameters.Origins {
for _, v := range s.EventParameters.Origins[i].Arrivals {
arrivals[v.PickID] = v
}
}
var amplitudes = make(map[string]Amplitude)
for k, v := range s.EventParameters.Amplitudes {
a := s.EventParameters.Amplitudes[k]
// add distance and azimuth from the arrival with the matching PickID.
pk := arrivals[v.PickID]
a.Distance = pk.Distance
a.Azimuth = pk.Azimuth
amplitudes[v.PublicID] = a
}
for i := range s.EventParameters.Origins {
for k, v := range s.EventParameters.Origins[i].Arrivals {
s.EventParameters.Origins[i].Arrivals[k].Pick = picks[v.PickID]
}
var stationMagnitudes = make(map[string]StationMagnitude)
for k, v := range s.EventParameters.Origins[i].StationMagnitudes {
s.EventParameters.Origins[i].StationMagnitudes[k].Amplitude = amplitudes[v.AmplitudeID]
stationMagnitudes[v.PublicID] = s.EventParameters.Origins[i].StationMagnitudes[k]
}
for j := range s.EventParameters.Origins[i].Magnitudes {
for k, v := range s.EventParameters.Origins[i].Magnitudes[j].StationMagnitudeContributions {
s.EventParameters.Origins[i].Magnitudes[j].StationMagnitudeContributions[k].StationMagnitude = stationMagnitudes[v.StationMagnitudeID]
}
}
}
// set the preferred origin.
// set the preferred mag which can come from any origin
for i := range s.EventParameters.Events {
for k, v := range s.EventParameters.Origins {
if v.PublicID == s.EventParameters.Events[i].PreferredOriginID {
s.EventParameters.Events[i].PreferredOrigin = s.EventParameters.Origins[k]
}
for _, mag := range v.Magnitudes {
if mag.PublicID == s.EventParameters.Events[i].PreferredMagnitudeID {
s.EventParameters.Events[i].PreferredMagnitude = mag
}
}
}
}
// set the most recent modified time as long as there is only one event.
// XML token parse the entire SC3ML, looking for creationInfo.
// assumes all objects in the SC3ML are associated, or have been associated,
// with the event somehow.
if len(s.EventParameters.Events) != 1 {
return nil
}
var by bytes.Buffer
by.Write(b)
d := xml.NewDecoder(&by)
var tk xml.Token
var err error
for {
// Read tokens from the XML document in a stream.
tk, err = d.Token()
if tk == nil {
break
}
if err != nil {
return err
}
switch se := tk.(type) {
case xml.StartElement:
if se.Name.Local == "creationInfo" {
var c CreationInfo
err = d.DecodeElement(&c, &se)
if err != nil {
return err
}
if c.ModificationTime.After(s.EventParameters.Events[0].ModificationTime) {
s.EventParameters.Events[0].ModificationTime = c.ModificationTime
}
if c.CreationTime.After(s.EventParameters.Events[0].ModificationTime) {
s.EventParameters.Events[0].ModificationTime = c.CreationTime
}
}
}
}
return nil
}