# -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (C) 2014-2025 GEM Foundation and G. Weatherill
#
# OpenQuake is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenQuake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake. If not, see <http://www.gnu.org/licenses/>.
"""
Parser for a flatfile downloaded from the esm custom url database.
--> (https://esm-db.eu/esmws/flatfile/1/)
This parser assumes you have selected all available headers in your URL search
when downloading the flatfile.
Last updated 6th May 2026 (some changes were needed for column reformatting
and how they now report the nodal plane solutions).
"""
import os
import tempfile
import csv
import pickle
import numpy as np
import pandas as pd
from linecache import getline
from openquake.smt.residuals.sm_database import (GroundMotionDatabase,
GroundMotionRecord)
from openquake.smt.residuals.parsers.esm_flatfile_parser import (parse_event_data,
parse_distances,
parse_site_data,
parse_waveform_data,
parse_ground_motion)
from openquake.smt.residuals.sm_database import (GroundMotionDatabase,
GroundMotionRecord,
Rupture,
FocalMechanism,
MECHANISM_TYPE,
DIP_TYPE,
GCMTNodalPlanes)
from openquake.smt.residuals.parsers.base_database_parser import SMDatabaseReader
from openquake.smt.utils import vfloat
BASE = os.path.abspath("")
HEADERS = ["event_id",
"event_time",
"ISC_ev_id",
"USGS_ev_id",
"INGV_ev_id",
"EMSC_ev_id",
"ev_nation_code",
"ev_latitude",
"ev_longitude",
"ev_depth_km",
"ev_hyp_ref",
"fm_type_code",
"ML",
"ML_ref",
"Mw",
"Mw_ref",
"Ms",
"Ms_ref",
"EMEC_Mw",
"EMEC_Mw_type",
"EMEC_Mw_ref",
"es_strike",
"es_dip",
"es_rake",
"es_strike_dip_rake_ref",
"es_z_top",
"es_z_top_ref",
"es_length",
"es_width",
"es_geometry_ref",
"network_code",
"station_code",
"location_code",
"instrument_code",
"sensor_depth_m",
"proximity_code",
"housing_code",
"installation_code",
"st_nation_code",
"st_latitude",
"st_longitude",
"st_elevation",
"ec8_code",
"ec8_code_method",
"ec8_code_ref",
"vs30_m_sec",
"vs30_ref",
"vs30_calc_method",
"vs30_meas_type",
"slope_deg",
"vs30_m_sec_WA",
"epi_dist",
"epi_az",
"JB_dist",
"rup_dist",
"Rx_dist",
"Ry0_dist",
"instrument_type_code",
"late_triggered_flag_01",
"U_channel_code",
"U_azimuth_deg",
"V_channel_code",
"V_azimuth_deg",
"W_channel_code",
"U_hp",
"V_hp",
"W_hp",
"U_lp",
"V_lp",
"W_lp"]
[docs]
def convert_bracketed_value(val):
"""
Clean up the nodal plane solutions and if multiple are
provided take the first one.
"""
# Might be empty
if pd.isna(val):
return np.nan
# Get a string
val_str = str(val).strip()
# If null or list of null...
if 'NULL' in val_str:
return np.nan
# Remove brackets
val_str = val_str.strip('[]')
# If comma separated, take first
if ',' in val_str:
val_str = val_str.split(',')[0].strip()
# Remove quotes if any
val_str = val_str.strip("'\"")
return float(val_str)
[docs]
def parse_rupture_mechanism(metadata, eq_id, eq_name, mag, depth):
"""
Parse rupture mechanism.
NOTE: The esm URL format flatfile does not contain necessarily provide
an ``event_source_id`` value for each record providing finite rupture
information, and therefore this function differs from the same one
within the esm18 parser (within which if there is an ``event_source_id``
there is also complete finite rupture information e.g. length, width).
"""
# Get the SoF
sof = metadata["fm_type_code"]
# Initial rupture
rupture = Rupture(eq_id, eq_name, mag, None, None, depth)
# Mechanism
mechanism = FocalMechanism(
eq_id,
eq_name,
GCMTNodalPlanes(),
None,
mechanism_type=sof)
# See if focal mechanism exists and get it if so
fm_set = []
for key in ["es_strike", "es_dip", "es_rake"]:
# Nodal plane values already cleaned using convert_bracketed_value
if key in metadata:
fm_param = vfloat(metadata[key], key)
if fm_param is not None:
fm_set.append(fm_param)
if len(fm_set) == 3:
# Has a valid focal mechanism
mechanism.nodal_planes.nodal_plane_1 = {
"strike": fm_set[0], "dip": fm_set[1], "rake": fm_set[2]}
if not mechanism.nodal_planes.nodal_plane_1:
# No information - base on style-of-faulting code
mechanism.nodal_planes.nodal_plane_1 = {
"strike": 0.0, "dip": DIP_TYPE[sof], "rake": MECHANISM_TYPE[sof]
}
return rupture, mechanism
[docs]
def parse_esm_url(esm):
"""
Convert from esm URL format flatfile to esm18 format flatfile.
"""
# Arias intensity columns are diff in older versions so handle
ai_cols = {}
suffix = "_ai" if 'u_ai' in esm.columns else "_ia"
for col in ["u", 'v', 'w', 'rotd50', 'rotd100', 'rotd00']:
ai_cols[col] = f"{col}{suffix}"
# Construct dataframe with original esm format
esm_original_headers = pd.DataFrame(
{
# Non-GMIM headers
"event_id":esm.esm_event_id,
"event_time":esm.event_time.str.replace('T',' '),
"ISC_ev_id":esm.isc_event_id,
"USGS_ev_id":esm.usgs_event_id,
"INGV_ev_id":esm.ingv_event_id,
"EMSC_ev_id":esm.emsc_event_id,
"ev_nation_code":esm.ev_nation_code,
"ev_latitude":esm.ev_latitude,
"ev_longitude":esm.ev_longitude,
"ev_depth_km":esm.ev_depth_km,
"ev_hyp_ref":None,
"fm_type_code":esm.fm_type_code,
"ML":esm.ml,
"ML_ref":esm.ml_ref,
"Mw":esm.mw,
"Mw_ref":esm.mw_ref,
"Ms":esm.ms,
"Ms_ref":esm.ms_ref,
"EMEC_Mw":esm.emec_mw,
"EMEC_Mw_type":esm.emec_mw_type,
"EMEC_Mw_ref":esm.emec_mw_ref,
"es_strike":esm.es_strike,
"es_dip":esm.es_dip,
"es_rake":esm.es_rake,
"es_strike_dip_rake_ref":None,
"es_z_top":esm.es_z_top,
"es_z_top_ref":esm.es_z_top_ref,
"es_length":esm.es_length,
"es_width":esm.es_width,
"es_geometry_ref":esm.es_geometry_ref,
"network_code":esm.network_code,
"station_code":esm.station_code,
"location_code":esm.location_code,
"instrument_code":esm.instrument_type_code,
"sensor_depth_m":esm.sensor_depth_m,
"proximity_code":esm.proximity,
"housing_code":esm.hounsing, # Currently typo in their database header
"installation_code":esm.installation,
"st_nation_code":esm.st_nation_code,
"st_latitude":esm.st_latitude,
"st_longitude":esm.st_longitude,
"st_elevation":esm.st_elevation,
"ec8_code":esm.ec8_code,
"ec8_code_method":None,
"ec8_code_ref":None,
"vs30_m_sec":esm.vs30_m_s,
"vs30_ref":None,
"vs30_calc_method":None,
"vs30_meas_type":esm.vs30_meas_type,
"slope_deg":esm.slope_deg,
"vs30_m_sec_WA":esm.vs30_m_s_wa,
"epi_dist":esm.epi_dist,
"epi_az":esm.epi_az,
"JB_dist":esm.jb_dist,
"rup_dist":esm.rup_dist,
"Rx_dist":esm.rx_dist,
"Ry0_dist":esm.ry0_dist,
"instrument_type_code":esm.instrument_type_code,
"late_triggered_flag_01":esm.late_triggered_event_01,
"U_channel_code":esm.u_channel_code,
"U_azimuth_deg":esm.u_azimuth_deg,
"V_channel_code":esm.v_channel_code,
"V_azimuth_deg":esm.v_azimuth_deg,
"W_channel_code":esm.w_channel_code,
"U_hp":esm.u_hp,
"V_hp":esm.v_hp,
"W_hp":esm.w_hp,
"U_lp":esm.u_lp,
"V_lp":esm.v_lp,
"W_lp":esm.w_lp,
"U_pga":esm.u_pga,
"V_pga":esm.v_pga,
"W_pga":esm.w_pga,
"rotD50_pga":esm.rotd50_pga,
"rotD100_pga":esm.rotd100_pga,
"rotD00_pga":esm.rotd00_pga,
"U_pgv":esm.u_pgv,
"V_pgv":esm.v_pgv,
"W_pgv":esm.w_pgv,
"rotD50_pgv":esm.rotd50_pgv,
"rotD100_pgv":esm.rotd100_pgv,
"rotD00_pgv":esm.rotd00_pgv,
"U_pgd":esm.u_pgd,
"V_pgd":esm.v_pgd,
"W_pgd":esm.w_pgd,
"rotD50_pgd":esm.rotd50_pgd,
"rotD100_pgd":esm.rotd100_pgd,
"rotD00_pgd":esm.rotd00_pgv,
"U_T90":esm.u_t90,
"V_T90":esm.v_t90,
"W_T90":esm.w_t90,
"rotD50_T90":esm.rotd50_t90,
"rotD100_T90":esm.rotd100_t90,
"rotD00_T90":esm.rot_d00_t90, # This header has typo in current db version
"U_housner":esm.u_housner,
"V_housner":esm.v_housner,
"W_housner":esm.w_housner,
"rotD50_housner":esm.rotd50_housner,
"rotD100_housner":esm.rotd100_housner,
"rotD00_housner":esm.rotd00_housner,
"U_CAV":esm.u_cav,
"V_CAV":esm.v_cav,
"W_CAV":esm.w_cav,
"rotD50_CAV":esm.rotd50_cav,
"rotD100_CAV":esm.rotd100_cav,
"rotD00_CAV":esm.rotd00_cav,
"U_ia":esm[ai_cols['u']],
"V_ia":esm[ai_cols['v']],
"W_ia":esm[ai_cols['w']],
"rotD50_ia":esm[ai_cols['rotd50']],
"rotD100_ia":esm[ai_cols['rotd100']],
"rotD00_ia":esm[ai_cols['rotd00']],
"U_T0_010":esm.u_t0_010,
"U_T0_025":esm.u_t0_025,
"U_T0_040":esm.u_t0_040,
"U_T0_050":esm.u_t0_050,
"U_T0_070":esm.u_t0_070,
"U_T0_100":esm.u_t0_100,
"U_T0_150":esm.u_t0_150,
"U_T0_200":esm.u_t0_200,
"U_T0_250":esm.u_t0_250,
"U_T0_300":esm.u_t0_300,
"U_T0_350":esm.u_t0_350,
"U_T0_400":esm.u_t0_400,
"U_T0_450":esm.u_t0_450,
"U_T0_500":esm.u_t0_500,
"U_T0_600":esm.u_t0_600,
"U_T0_700":esm.u_t0_700,
"U_T0_750":esm.u_t0_750,
"U_T0_800":esm.u_t0_800,
"U_T0_900":esm.u_t0_900,
"U_T1_000":esm.u_t1_000,
"U_T1_200":esm.u_t1_200,
"U_T1_400":esm.u_t1_400,
"U_T1_600":esm.u_t1_600,
"U_T1_800":esm.u_t1_800,
"U_T2_000":esm.u_t2_000,
"U_T2_500":esm.u_t2_500,
"U_T3_000":esm.u_t3_000,
"U_T3_500":esm.u_t3_500,
"U_T4_000":esm.u_t4_000,
"U_T4_500":esm.u_t4_500,
"U_T5_000":esm.u_t5_000,
"U_T6_000":esm.u_t6_000,
"U_T7_000":esm.u_t7_000,
"U_T8_000":esm.u_t8_000,
"U_T9_000":esm.u_t9_000,
"U_T10_000":esm.u_t10_000,
"V_T0_010":esm.v_t0_010,
"V_T0_025":esm.v_t0_025,
"V_T0_040":esm.v_t0_040,
"V_T0_050":esm.v_t0_050,
"V_T0_070":esm.v_t0_070,
"V_T0_100":esm.v_t0_100,
"V_T0_150":esm.v_t0_150,
"V_T0_200":esm.v_t0_200,
"V_T0_250":esm.v_t0_250,
"V_T0_300":esm.v_t0_300,
"V_T0_350":esm.v_t0_350,
"V_T0_400":esm.v_t0_400,
"V_T0_450":esm.v_t0_450,
"V_T0_500":esm.v_t0_500,
"V_T0_600":esm.v_t0_600,
"V_T0_700":esm.v_t0_700,
"V_T0_750":esm.v_t0_750,
"V_T0_800":esm.v_t0_800,
"V_T0_900":esm.v_t0_900,
"V_T1_000":esm.v_t1_000,
"V_T1_200":esm.v_t1_200,
"V_T1_400":esm.v_t1_400,
"V_T1_600":esm.v_t1_600,
"V_T1_800":esm.v_t1_800,
"V_T2_000":esm.v_t2_000,
"V_T2_500":esm.v_t2_500,
"V_T3_000":esm.v_t3_000,
"V_T3_500":esm.v_t3_500,
"V_T4_000":esm.v_t4_000,
"V_T4_500":esm.v_t4_500,
"V_T5_000":esm.v_t5_000,
"V_T6_000":esm.v_t6_000,
"V_T7_000":esm.v_t7_000,
"V_T8_000":esm.v_t8_000,
"V_T9_000":esm.v_t9_000,
"V_T10_000":esm.v_t10_000,
"W_T0_010":esm.w_t0_010,
"W_T0_025":esm.w_t0_025,
"W_T0_040":esm.w_t0_040,
"W_T0_050":esm.w_t0_050,
"W_T0_070":esm.w_t0_070,
"W_T0_100":esm.w_t0_100,
"W_T0_150":esm.w_t0_150,
"W_T0_200":esm.w_t0_200,
"W_T0_250":esm.w_t0_250,
"W_T0_300":esm.w_t0_300,
"W_T0_350":esm.w_t0_350,
"W_T0_400":esm.w_t0_400,
"W_T0_450":esm.w_t0_450,
"W_T0_500":esm.w_t0_500,
"W_T0_600":esm.w_t0_600,
"W_T0_700":esm.w_t0_700,
"W_T0_750":esm.w_t0_750,
"W_T0_800":esm.w_t0_800,
"W_T0_900":esm.w_t0_900,
"W_T1_000":esm.w_t1_000,
"W_T1_200":esm.w_t1_200,
"W_T1_400":esm.w_t1_400,
"W_T1_600":esm.w_t1_600,
"W_T1_800":esm.w_t1_800,
"W_T2_000":esm.w_t2_000,
"W_T2_500":esm.w_t2_500,
"W_T3_000":esm.w_t3_000,
"W_T3_500":esm.w_t3_500,
"W_T4_000":esm.w_t4_000,
"W_T4_500":esm.w_t4_500,
"W_T5_000":esm.w_t5_000,
"W_T6_000":esm.w_t6_000,
"W_T7_000":esm.w_t7_000,
"W_T8_000":esm.w_t8_000,
"W_T9_000":esm.w_t9_000,
"W_T10_000":esm.w_t10_000,
"rotD50_T0_010":esm.rotd50_t0_010,
"rotD50_T0_025":esm.rotd50_t0_025,
"rotD50_T0_040":esm.rotd50_t0_040,
"rotD50_T0_050":esm.rotd50_t0_050,
"rotD50_T0_070":esm.rotd50_t0_070,
"rotD50_T0_100":esm.rotd50_t0_100,
"rotD50_T0_150":esm.rotd50_t0_150,
"rotD50_T0_200":esm.rotd50_t0_200,
"rotD50_T0_250":esm.rotd50_t0_250,
"rotD50_T0_300":esm.rotd50_t0_300,
"rotD50_T0_350":esm.rotd50_t0_350,
"rotD50_T0_400":esm.rotd50_t0_400,
"rotD50_T0_450":esm.rotd50_t0_450,
"rotD50_T0_500":esm.rotd50_t0_500,
"rotD50_T0_600":esm.rotd50_t0_600,
"rotD50_T0_700":esm.rotd50_t0_700,
"rotD50_T0_750":esm.rotd50_t0_750,
"rotD50_T0_800":esm.rotd50_t0_800,
"rotD50_T0_900":esm.rotd50_t0_900,
"rotD50_T1_000":esm.rotd50_t1_000,
"rotD50_T1_200":esm.rotd50_t1_200,
"rotD50_T1_400":esm.rotd50_t1_400,
"rotD50_T1_600":esm.rotd50_t1_600,
"rotD50_T1_800":esm.rotd50_t1_800,
"rotD50_T2_000":esm.rotd50_t2_000,
"rotD50_T2_500":esm.rotd50_t2_500,
"rotD50_T3_000":esm.rotd50_t3_000,
"rotD50_T3_500":esm.rotd50_t3_500,
"rotD50_T4_000":esm.rotd50_t4_000,
"rotD50_T4_500":esm.rotd50_t4_500,
"rotD50_T5_000":esm.rotd50_t5_000,
"rotD50_T6_000":esm.rotd50_t6_000,
"rotD50_T7_000":esm.rotd50_t7_000,
"rotD50_T8_000":esm.rotd50_t8_000,
"rotD50_T9_000":esm.rotd50_t9_000,
"rotD50_T10_000":esm.rotd50_t10_000,
"rotD100_T0_010":esm.rotd100_t0_010,
"rotD100_T0_025":esm.rotd100_t0_025,
"rotD100_T0_040":esm.rotd100_t0_040,
"rotD100_T0_050":esm.rotd100_t0_050,
"rotD100_T0_070":esm.rotd100_t0_070,
"rotD100_T0_100":esm.rotd100_t0_100,
"rotD100_T0_150":esm.rotd100_t0_150,
"rotD100_T0_200":esm.rotd100_t0_200,
"rotD100_T0_250":esm.rotd100_t0_250,
"rotD100_T0_300":esm.rotd100_t0_300,
"rotD100_T0_350":esm.rotd100_t0_350,
"rotD100_T0_400":esm.rotd100_t0_400,
"rotD100_T0_450":esm.rotd100_t0_450,
"rotD100_T0_500":esm.rotd100_t0_500,
"rotD100_T0_600":esm.rotd100_t0_600,
"rotD100_T0_700":esm.rotd100_t0_700,
"rotD100_T0_750":esm.rotd100_t0_750,
"rotD100_T0_800":esm.rotd100_t0_800,
"rotD100_T0_900":esm.rotd100_t0_900,
"rotD100_T1_000":esm.rotd100_t1_000,
"rotD100_T1_200":esm.rotd100_t1_200,
"rotD100_T1_400":esm.rotd100_t1_400,
"rotD100_T1_600":esm.rotd100_t1_600,
"rotD100_T1_800":esm.rotd100_t1_800,
"rotD100_T2_000":esm.rotd100_t2_000,
"rotD100_T2_500":esm.rotd100_t2_500,
"rotD100_T3_000":esm.rotd100_t3_000,
"rotD100_T3_500":esm.rotd100_t3_500,
"rotD100_T4_000":esm.rotd100_t4_000,
"rotD100_T4_500":esm.rotd100_t4_500,
"rotD100_T5_000":esm.rotd100_t5_000,
"rotD100_T6_000":esm.rotd100_t6_000,
"rotD100_T7_000":esm.rotd100_t7_000,
"rotD100_T8_000":esm.rotd100_t8_000,
"rotD100_T9_000":esm.rotd100_t9_000,
"rotD100_T10_000":esm.rotd100_t10_000,
"rotD00_T0_010":esm.rotd00_t0_010,
"rotD00_T0_025":esm.rotd00_t0_025,
"rotD00_T0_040":esm.rotd00_t0_040,
"rotD00_T0_050":esm.rotd00_t0_050,
"rotD00_T0_070":esm.rotd00_t0_070,
"rotD00_T0_100":esm.rotd00_t0_100,
"rotD00_T0_150":esm.rotd00_t0_150,
"rotD00_T0_200":esm.rotd00_t0_200,
"rotD00_T0_250":esm.rotd00_t0_250,
"rotD00_T0_300":esm.rotd00_t0_300,
"rotD00_T0_350":esm.rotd00_t0_350,
"rotD00_T0_400":esm.rotd00_t0_400,
"rotD00_T0_450":esm.rotd00_t0_450,
"rotD00_T0_500":esm.rotd00_t0_500,
"rotD00_T0_600":esm.rotd00_t0_600,
"rotD00_T0_700":esm.rotd00_t0_700,
"rotD00_T0_750":esm.rotd00_t0_750,
"rotD00_T0_800":esm.rotd00_t0_800,
"rotD00_T0_900":esm.rotd00_t0_900,
"rotD00_T1_000":esm.rotd00_t1_000,
"rotD00_T1_200":esm.rotd00_t1_200,
"rotD00_T1_400":esm.rotd00_t1_400,
"rotD00_T1_600":esm.rotd00_t1_600,
"rotD00_T1_800":esm.rotd00_t1_800,
"rotD00_T2_000":esm.rotd00_t2_000,
"rotD00_T2_500":esm.rotd00_t2_500,
"rotD00_T3_000":esm.rotd00_t3_000,
"rotD00_T3_500":esm.rotd00_t3_500,
"rotD00_T4_000":esm.rotd00_t4_000,
"rotD00_T4_500":esm.rotd00_t4_500,
"rotD00_T5_000":esm.rotd00_t5_000,
"rotD00_T6_000":esm.rotd00_t6_000,
"rotD00_T7_000":esm.rotd00_t7_000,
"rotD00_T8_000":esm.rotd00_t8_000,
"rotD00_T9_000":esm.rotd00_t9_000,
"rotD00_T10_000":esm.rotd00_t10_000})
# Export to tmp
tmp = os.path.join(BASE, tempfile.mkdtemp(), 'tmp.csv')
esm_original_headers.to_csv(tmp, sep=';')
return tmp
[docs]
class ESMFlatfileParserURL(SMDatabaseReader):
"""
Parses the data from the flatfile to a set of metadata objects.
"""
[docs]
def parse(self, location='./'):
"""
Parse the flatfile.
"""
assert os.path.isfile(self.input_files)
headers = getline(self.input_files, 1).rstrip("\n").split(";")
for hdr in HEADERS:
if hdr not in headers:
raise ValueError(
"Required header %s is missing in file" % hdr)
# Read in csv
with open(self.input_files, "r", encoding="utf-8", newline='') as f:
reader = csv.DictReader(f, delimiter=";")
self.database = GroundMotionDatabase(self.id, self.name)
counter = 0
for row in reader:
# Build the metadata
record = self._parse_record(row)
if record:
# Parse the strong motion
record = parse_ground_motion(os.path.join(
location, "records"), row, record, headers)
self.database.records.append(record)
else:
print("Record with sequence number %s is null/invalid"
% "{:s}-{:s}".format(
row["event_id"], row["station_code"]))
if (counter % 100) == 0:
print(f"Processed record {counter} - {record.id}")
counter += 1
[docs]
@classmethod
def autobuild(cls, dbid, dbname, output_location, flatfile_location):
"""
Quick and dirty full database builder!
"""
# Import esm url format strong-motion flatfile
esm = pd.read_csv(flatfile_location)
# Clean up the nodal plane solutions and take the first one if multiple
for col in [
"es_strike", "es_dip", "es_rake", "es_length", "es_width", "es_z_top"]:
esm[col] = esm[col].apply(convert_bracketed_value)
# Handle empty fm type values
esm['fm_type_code'] = [fm if pd.notnull(fm) else "U" for fm in esm.fm_type_code]
# Get path to tmp csv containing reformatted dataframe
tmp = parse_esm_url(esm)
if os.path.exists(output_location):
raise IOError("Target database directory %s already exists!"
% output_location)
os.mkdir(output_location)
# Add on the records folder
os.mkdir(os.path.join(output_location, "records"))
# Create an instance of the parser class
database = cls(dbid, dbname, tmp)
# Parse the records
print("Parsing Records ...")
database.parse(location=output_location)
# Save itself to file
metadata_file = os.path.join(output_location, "metadatafile.pkl")
print("Storing metadata to file %s" % metadata_file)
with open(metadata_file, "wb+") as f:
pickle.dump(database.database, f)
return database
def _parse_record(self, metadata):
"""
Parse a record.
"""
# Waveform ID not provided in file so concatenate Event and Station ID
wfid = "_".join([metadata["event_id"],
metadata["network_code"],
metadata["station_code"],
metadata["location_code"]]
)
wfid = wfid.replace("-", "_").replace("__", "_").strip()
# Parse the event metadata
event = parse_event_data(metadata, parse_rupture_mechanism)
# Parse the distance metadata
distances = parse_distances(metadata, event.depth)
# Parse the station metadata
site = parse_site_data(metadata)
# Parse waveform data
xcomp, ycomp, vertical = parse_waveform_data(metadata, wfid)
return GroundMotionRecord(wfid,
[None, None, None], # No time-history files
event, distances, site,
xcomp, ycomp,
vertical=vertical)