Handling custom defined tags in QuakeML and the ObsPy Catalog/Event framework (在QuakeML和ObsPy的目录/事件框架中处理自定义标记)
除了QuakeML标准定义的“usual”信息之外,QuakeML还允许使用自定义元素。它允许:
- a自定义命名空间属性到QuakeML命名空间标记
- b自定义命名空间子标签到QuakeML命名空间元素
ObsPy可以在输入/输出QuakeML的过程中处理事件类型对象中的基本自定义标记和自定义属性。
以下基本示例说明如何使用自定义xml标记/属性输出有效的QuakeML文件:
from obspy import Catalog, UTCDateTime
extra = {'my_tag': {'value': True,
'namespace': 'http://some-page.de/xmlns/1.0',
'attrib': {'{http://some-page.de/xmlns/1.0}my_attrib1': '123.4',
'{http://some-page.de/xmlns/1.0}my_attrib2': '567'}},
'my_tag_2': {'value': u'True',
'namespace': 'http://some-page.de/xmlns/1.0'},
'my_tag_3': {'value': 1,
'namespace': 'http://some-page.de/xmlns/1.0'},
'my_tag_4': {'value': UTCDateTime('2013-01-02T13:12:14.600000Z'),
'namespace': 'http://test.org/xmlns/0.1'},
'my_attribute': {'value': 'my_attribute_value',
'type': 'attribute',
'namespace': 'http://test.org/xmlns/0.1'}}
cat = Catalog()
cat.extra = extra
cat.write('my_catalog.xml', format='QUAKEML',
nsmap={'my_ns': 'http://test.org/xmlns/0.1'})
所有储存在自定义QuakeML中的信息必须存为带有自定义信息(如Catalog, Event, Pick)的extra属性的dict 或AttribDict对象。关键字用作xml标记的名称,xml标记的内容在一个字典中定义:'value'定义标记的内容(对象的字符串表示形式存储在文本xml输出中)。 'namespace'必须为标记指定自定义命名空间。 'type'可用于指定额外信息是应存储为子元素('element',default)还是存储为属性('attribute')。 自定义子元素的属性可以以字典的形式提供为'attrib'。如果需要更好的可读性,输出xml中的命名空间缩写可以在输出期间指定为QuakeML,方法是将命名空间缩写映射字典作为nsmap参数提供给Catalog.write()。上例的xml输出如下所示:
<?xml version='1.0' encoding='utf-8'?>
<q:quakeml xmlns:q='http://quakeml.org/xmlns/quakeml/1.2'
xmlns:ns0='http://some-page.de/xmlns/1.0'
xmlns:my_ns='http://test.org/xmlns/0.1'
xmlns='http://quakeml.org/xmlns/bed/1.2'>
<eventParameters publicID='smi:local/b425518c-9445-40c7-8284-d1f299ed2eac'
my_ns:my_attribute='my_attribute_value'>
<ns0:my_tag ns0:my_attrib1='123.4' ns0:my_attrib2='567'>true</ns0:my_tag>
<my_ns:my_tag_4>2013-01-02T13:12:14.600000Z</my_ns:my_tag_4>
<ns0:my_tag_2>True</ns0:my_tag_2>
<ns0:my_tag_3>1</ns0:my_tag_3>
</eventParameters>
</q:quakeml>
再次使用read_events()读取上述xml时,自定义标记被解析并附加到相应的事件类型对象(在此示例中为Catalog对象)为“.extra”。 请注意,所有值都作为文本字符串读取:
from obspy import read_events
cat = read_events('my_catalog.xml')
print(cat.extra)
AttribDict({u'my_tag': {u'attrib': {'{http://some-page.de/xmlns/1.0}my_attrib2': '567',
'{http://some-page.de/xmlns/1.0}my_attrib1': '123.4'},
u'namespace': u'http://some-page.de/xmlns/1.0',
u'value': 'true'},
u'my_tag_4': {u'namespace': u'http://test.org/xmlns/0.1',
u'value': '2013-01-02T13:12:14.600000Z'},
u'my_attribute': {u'type': u'attribute',
u'namespace': u'http://test.org/xmlns/0.1',
u'value': 'my_attribute_value'},
u'my_tag_2': {u'namespace': u'http://some-page.de/xmlns/1.0',
u'value': 'True'},
u'my_tag_3': {u'namespace': u'http://some-page.de/xmlns/1.0',
u'value': '1'}})
自定义标签可以嵌套:
from obspy import Catalog
from obspy.core import AttribDict
ns = 'http://some-page.de/xmlns/1.0'
my_tag = AttribDict()
my_tag.namespace = ns
my_tag.value = AttribDict()
my_tag.value.my_nested_tag1 = AttribDict()
my_tag.value.my_nested_tag1.namespace = ns
my_tag.value.my_nested_tag1.value = 1.23E+10
my_tag.value.my_nested_tag2 = AttribDict()
my_tag.value.my_nested_tag2.namespace = ns
my_tag.value.my_nested_tag2.value = True
cat = Catalog()
cat.extra = AttribDict()
cat.extra.my_tag = my_tag
cat.write('my_catalog.xml', 'QUAKEML')
这将产生类似于以下内容的xml输出:
<?xml version='1.0' encoding='utf-8'?>
<q:quakeml xmlns:q='http://quakeml.org/xmlns/quakeml/1.2'
xmlns:ns0='http://some-page.de/xmlns/1.0'
xmlns='http://quakeml.org/xmlns/bed/1.2'>
<eventParameters publicID='smi:local/97d2b338-0701-41a4-9b6b-5903048bc341'>
<ns0:my_tag>
<ns0:my_nested_tag1>12300000000.0</ns0:my_nested_tag1>
<ns0:my_nested_tag2>true</ns0:my_nested_tag2>
</ns0:my_tag>
</eventParameters>
</q:quakeml>
输出的XML可以再次使用read_events()读取,嵌套标签可使用下面的方法检索:
from obspy import read_events
cat = read_events('my_catalog.xml')
print(cat.extra.my_tag.value.my_nested_tag1.value)
print(cat.extra.my_tag.value.my_nested_tag2.value)
结果:
12300000000.0
true
可以使用OrderedDict为额外属性控制额外标签的顺序:
from collections import OrderedDict
from obspy.core.event import Catalog, Event
ns = 'http://some-page.de/xmlns/1.0'
my_tag1 = {'namespace': ns, 'value': 'some value 1'}
my_tag2 = {'namespace': ns, 'value': 'some value 2'}
event = Event()
cat = Catalog(events=[event])
event.extra = OrderedDict()
event.extra['myFirstExtraTag'] = my_tag2
event.extra['mySecondExtraTag'] = my_tag1
cat.write('my_catalog.xml', 'QUAKEML')
Handling custom defined tags in StationXML with the Obspy Inventory(使用Obspy Inventory处理StationXML中的自定义标签)
以下基本示例说明如何输出包含其他xml标记/属性的StationXML文件:
from obspy import Inventory, UTCDateTime
from obspy.core.inventory import Network
from obspy.core.util import AttribDict
extra = AttribDict({
'my_tag': {
'value': True,
'namespace': 'http://some-page.de/xmlns/1.0',
'attrib': {
'{http://some-page.de/xmlns/1.0}my_attrib1': '123.4',
'{http://some-page.de/xmlns/1.0}my_attrib2': '567'
}
},
'my_tag_2': {
'value': u'True',
'namespace': 'http://some-page.de/xmlns/1.0'
},
'my_tag_3': {
'value': 1,
'namespace': 'http://some-page.de/xmlns/1.0'
},
'my_tag_4': {
'value': UTCDateTime('2013-01-02T13:12:14.600000Z'),
'namespace': 'http://test.org/xmlns/0.1'
},
'my_attribute': {
'value': 'my_attribute_value',
'type': 'attribute',
'namespace': 'http://test.org/xmlns/0.1'
}
})
inv = Inventory([Network('XX')], 'XX')
inv[0].extra = extra
inv.write('my_inventory.xml', format='STATIONXML',
nsmap={'my_ns': 'http://test.org/xmlns/0.1',
'somepage_ns': 'http://some-page.de/xmlns/1.0'})
要存储在定制的StationXML中的所有自定义信息必须以dict 或AttribDict对象的形式存储,作为应携带附加自定义信息的对象的.extra属性(例如,Network, Station, Channel)。关键字作xml标记的名称,xml标记的内容在简单字典中定义:'value'定义标记的内容(对象的字符串表示形式存储在文本xml输出中)。 'namespace'必须为标记指定自定义命名空间。 'type'可用于指定额外信息是应存储为子元素('element',default)还是存储为属性('attribute')。 自定义子元素的属性可以以字典的形式提供为“attrib”。
如果需要更好可读性,输出xml中的命名空间缩写可以在输出期间指定为StationXML,方法是将名称空间缩写映射字典作为nsmap参数提供给Inventory.write()。xml输出如下所示:
<?xml version='1.0' encoding='UTF-8'?>
<FDSNStationXML xmlns:my_ns="http://test.org/xmlns/0.1" xmlns:somepage_ns="http://some-page.de/xmlns/1.0" xmlns="http://www.fdsn.org/xml/station/1" schemaVersion="1.0">
<Source>XX</Source>
<Module>ObsPy 1.0.2</Module>
<ModuleURI>https://www.obspy.org</ModuleURI>
<Created>2016-10-17T18:32:28.696287+00:00</Created>
<Network code="XX">
<somepage_ns:my_tag somepage_ns:my_attrib1="123.4" somepage_ns:my_attrib2="567">True</somepage_ns:my_tag>
<my_ns:my_tag_4>2013-01-02T13:12:14.600000Z</my_ns:my_tag_4>
<my_ns:my_attribute>my_attribute_value</my_ns:my_attribute>
<somepage_ns:my_tag_2>True</somepage_ns:my_tag_2>
<somepage_ns:my_tag_3>1</somepage_ns:my_tag_3>
</Network>
</FDSNStationXML>
再次读取上面的XML时,使用read_inventory(),自定义标记被解析并作为为'.extra'附加到相应的网络类型对象(在此示例中为Inventory对象)。请注意,所有值都作为文本字符串读取:
from obspy import read_inventory
inv = read_inventory('my_inventory.xml')
print(inv[0].extra)
AttribDict({
u'my_tag': AttribDict({
'attrib': {
'{http://some-page.de/xmlns/1.0}my_attrib2': '567',
'{http://some-page.de/xmlns/1.0}my_attrib1': '123.4'
},
'namespace': 'http://some-page.de/xmlns/1.0',
'value': 'True'
}),
u'my_tag_4': AttribDict({
'namespace': 'http://test.org/xmlns/0.1',
'value': '2013-01-02T13:12:14.600000Z'
}),
u'my_attribute': AttribDict({
'namespace': 'http://test.org/xmlns/0.1',
'value': 'my_attribute_value'
}),
u'my_tag_2': AttribDict({
'namespace': 'http://some-page.de/xmlns/1.0',
'value': 'True'
}),
u'my_tag_3': AttribDict({
'namespace': 'http://some-page.de/xmlns/1.0',
'value': '1'
})
})
自定义标签可以嵌套:
from obspy import Inventory
from obspy.core.inventory import Network
from obspy.core.util import AttribDict
ns = 'http://some-page.de/xmlns/1.0'
my_tag = AttribDict()
my_tag.namespace = ns
my_tag.value = AttribDict()
my_tag.value.my_nested_tag1 = AttribDict()
my_tag.value.my_nested_tag1.namespace = ns
my_tag.value.my_nested_tag1.value = 1.23E+10
my_tag.value.my_nested_tag2 = AttribDict()
my_tag.value.my_nested_tag2.namespace = ns
my_tag.value.my_nested_tag2.value = True
inv = Inventory([Network('XX')], 'XX')
inv[0].extra = AttribDict()
inv[0].extra.my_tag = my_tag
inv.write('my_inventory.xml', format='STATIONXML',
nsmap={'somepage_ns': 'http://some-page.de/xmlns/1.0'})
这将产生类似于以下内容的xml输出:
<?xml version='1.0' encoding='UTF-8'?>
<FDSNStationXML xmlns:somepage_ns="http://some-page.de/xmlns/1.0" xmlns="http://www.fdsn.org/xml/station/1" schemaVersion="1.0">
<Source>XX</Source>
<Module>ObsPy 1.0.2</Module>
<ModuleURI>https://www.obspy.org</ModuleURI>
<Created>2016-10-17T18:45:14.302265+00:00</Created>
<Network code="XX">
<somepage_ns:my_tag>
<somepage_ns:my_nested_tag1>12300000000.0</somepage_ns:my_nested_tag1>
<somepage_ns:my_nested_tag2>True</somepage_ns:my_nested_tag2>
</somepage_ns:my_tag>
</Network>
</FDSNStationXML>
输出XML可以使用read_inventory()再次读取,嵌套标签使用下面的方法检索:
from obspy import read_inventory
inv = read_inventory('my_inventory.xml')
print(inv[0].extra.my_tag.value.my_nested_tag1.value)
print(inv[0].extra.my_tag.value.my_nested_tag2.value)
结果:
12300000000.0
True
Creating a StationXML file from Scratch(从Scratch创建StationXML文件)
在地震学中有时需要创建自定义StationXML文件。本节演示如何使用ObsPy完成这样的任务请注意,这不一定比直接编辑XML文件更容易或更容易观察,但它确实提供了一种与ObsPy的其余部分更紧密结合的方法,并且可以保证最终结果有效。
这里假定您对FDSN StationXML standard.有一定的了解。我们将创建一个相当简单的StationXML文件,并且许多参数是可选的。 ObsPy将在写入时根据其模式验证生成的StationXML文件,以确保最终文件对StationXML模式有效。下图显示了ObsPy内部表示的基本结构。
每个大框都是一个对象,所有对象都必须分层链接以形成一个Inventory对象。Inventory可以包含任意数量的网络对象,这些对象又可以包含任意数量的工作站对象,这些工具又可以包含任意数量的信道对象。对于每个信道,仪器响应可以存储为response属性。
使用ObsPy的NRL客户端,可以从IRIS DMC Library of Nominal Responses(NRL)中查找仪器响应并将其附加到每个信道。
import obspy
from obspy.core.inventory import Inventory, Network, Station, Channel, Site
from obspy.clients.nrl import NRL
# We'll first create all the various objects. These strongly follow the
# hierarchy of StationXML files.
inv = Inventory(
# We'll add networks later.
networks=[],
# The source should be the id whoever create the file.
source="ObsPy-Tutorial")
net = Network(
# This is the network code according to the SEED standard.
code="XX",
# A list of stations. We'll add one later.
stations=[],
description="A test stations.",
# Start-and end dates are optional.
start_date=obspy.UTCDateTime(2016, 1, 2))
sta = Station(
# This is the station code according to the SEED standard.
code="ABC",
latitude=1.0,
longitude=2.0,
elevation=345.0,
creation_date=obspy.UTCDateTime(2016, 1, 2),
site=Site(name="First station"))
cha = Channel(
# This is the channel code according to the SEED standard.
code="HHZ",
# This is the location code according to the SEED standard.
location_code="",
# Note that these coordinates can differ from the station coordinates.
latitude=1.0,
longitude=2.0,
elevation=345.0,
depth=10.0,
azimuth=0.0,
dip=-90.0,
sample_rate=200)
# By default this accesses the NRL online. Offline copies of the NRL can
# also be used instead
nrl = NRL()
# The contents of the NRL can be explored interactively in a Python prompt,
# see API documentation of NRL submodule:
# http://docs.obspy.org/packages/obspy.clients.nrl.html
# Here we assume that the end point of data logger and sensor are already
# known:
response = nrl.get_response( # doctest: +SKIP
sensor_keys=['Streckeisen', 'STS-1', '360 seconds'],
datalogger_keys=['REF TEK', 'RT 130 & 130-SMA', '1', '200'])
# Now tie it all together.
cha.response = response
sta.channels.append(cha)
net.stations.append(sta)
inv.networks.append(net)
# And finally write it to a StationXML file. We also force a validation against
# the StationXML schema to ensure it produces a valid StationXML file.
#
# Note that it is also possible to serialize to any of the other inventory
# output formats ObsPy supports.
inv.write("station.xml", format="stationxml", validate=True)
Connecting to a SeedLink Server(连接到SeedLink服务器)
obspy.clients.seedlink模块提供了Python实现的SeedLink客户端协议。obspy.clients.seedlink.easyseedlink子模块包含SeedLink实现的高级接口,有助于创建SeedLink客户端。
使用create_client()函数创建一个新的EasySeedLinkClient类是连接到SeedLink服务器的最简单方法。它接受一个从SeedLink服务器接收的新数据的函数作为参数,例如:
def handle_data(trace):
print('Received the following trace:')
print(trace)
print()
此函数随后可以与SeedLink服务器URL一同传递给create_client()创建一个客户端:
client = create_client('geofon.gfz-potsdam.de', on_data=handle_data)
客户端在创建时立即连接到服务器。
发送INFO请求到服务器
客户端可以发送INFO请求到服务器:
# Send the INFO:ID request
client.get_info('ID')
# Returns:
# <?xml version="1.0"?>\n<seedlink software="SeedLink v3.2 (2014.071)" organization="GEOFON" started="2014/09/01 14:08:37.4192"/>\n
INFO请求的响应是XML格式的。客户端提供了检索和解析服务器功能的快捷方式(经由一个INFO:CAPABILITIES请求。)
>>> client.capabilities
['dialup', 'multistation', 'window-extraction', 'info:id', 'info:capabilities', 'info:stations', 'info:streams']
首次访问属性时会提取和解析这些功能,并在此之后进行缓存。
为了开始接受波形数据,需要先通过select_stream()函数选择至少一个stream。
client.select_stream('BW', 'MANZ', 'EHZ')
多个stream也可以被选择,也支持SeedLink通配符:
client.select_stream('BW', 'ROTZ', 'EH?')
选择stream后,客户端就准备好进入streaming模式了:
client.run()
这将从服务器开始流式传输数据。 在从服务器接收的每个完整跟踪时,使用跟踪对象调用上面定义的函数:
Received new data:
BW.MANZ..EHZ | 2014-09-04T19:47:25.625000Z - 2014-09-04T19:47:26.770000Z | 200.0 Hz, 230 samples
Received new data:
BW.ROTZ..EHZ | 2014-09-04T19:47:22.685000Z - 2014-09-04T19:47:24.740000Z | 200.0 Hz, 412 samples
Received new data:
BW.ROTZ..EHZ | 2014-09-04T19:47:24.745000Z - 2014-09-04T19:47:26.800000Z | 200.0 Hz, 412 samples
Received new data:
BW.ROTZ..EHN | 2014-09-04T19:47:20.870000Z - 2014-09-04T19:47:22.925000Z | 200.0 Hz, 412 samples
Received new data:
BW.ROTZ..EHN | 2014-09-04T19:47:22.930000Z - 2014-09-04T19:47:24.985000Z | 200.0 Hz, 412 samples
对于高级用法,继承EasySeedLinkClient类可以更好地控制。下列代码实现与上面相同的客户端:
class DemoClient(EasySeedLinkClient):
"""
A custom SeedLink client
"""
def on_data(self, trace):
"""
Override the on_data callback
"""
print('Received trace:')
print(trace)
print()