def _read_channel(instrumentation_register, cha_element, _ns): """ reads channel element from sc3ml format :param instrumentation_register: register of instrumentation metadata :param cha_element: channel element :param _ns: namespace """ code = cha_element.get("code") # Information is also kept within the parent <sensorLocation> element sen_loc_element = cha_element.getparent() location_code = sen_loc_element.get("code") # get site info from the <sensorLocation> element longitude = _read_floattype(sen_loc_element, _ns("longitude"), Longitude, datum=True) latitude = _read_floattype(sen_loc_element, _ns("latitude"), Latitude, datum=True) elevation = _read_floattype(sen_loc_element, _ns("elevation"), Distance, unit=True) depth = _read_floattype(cha_element, _ns("depth"), Distance, unit=True) # Set values to 0 if they are is missing (see #1816) if longitude is None: msg = "Sensor is missing longitude information, using 0.0" warnings.warn(msg) longitude = 0 if latitude is None: msg = "Sensor is missing latitude information, using 0.0" warnings.warn(msg) latitude = 0 if elevation is None: msg = "Sensor is missing elevation information, using 0.0" warnings.warn(msg) elevation = 0 if depth is None: msg = "Channel is missing depth information, using 0.0" warnings.warn(msg) depth = 0 channel = obspy.core.inventory.Channel( code=code, location_code=location_code, latitude=latitude, longitude=longitude, elevation=elevation, depth=depth) # obtain the sensorID and link to particular publicID <sensor> element # in the inventory base node sensor_id = cha_element.get("sensor") sensor_element = instrumentation_register["sensors"].get(sensor_id) # obtain the poles and zeros responseID and link to particular # <responsePAZ> publicID element in the inventory base node if (sensor_element is not None and sensor_element.get("response") is not None): response_id = sensor_element.get("response") response_elements = [] for resp_element in instrumentation_register["responses"].values(): found_response = resp_element.get(response_id) if found_response is not None: response_elements.append(found_response) if len(response_elements) == 0: msg = ("Could not find response tag with public ID " "'{}'.".format(response_id)) raise obspy.ObsPyException(msg) elif len(response_elements) > 1: msg = ("Found multiple matching response tags with the same " "public ID '{}'.".format(response_id)) raise obspy.ObsPyException(msg) response_element = response_elements[0] else: response_element = None # obtain the dataloggerID and link to particular <responsePAZ> publicID # element in the inventory base node datalogger_id = cha_element.get("datalogger") data_log_element = \ instrumentation_register["dataloggers"].get(datalogger_id) channel.restricted_status = _get_restricted_status(cha_element, _ns) # There is no further information in the attributes of <stream> # Start and end date are included as tags instead channel.start_date = _tag2obj(cha_element, _ns("start"), obspy.UTCDateTime) channel.end_date = _tag2obj(cha_element, _ns("end"), obspy.UTCDateTime) # Determine sample rate (given is a numerator, denominator) # Assuming numerator is # samples and denominator is # seconds numerator = _tag2obj(cha_element, _ns("sampleRateNumerator"), int) denominator = _tag2obj(cha_element, _ns("sampleRateDenominator"), int) # If numerator is non-zero and denominator zero, will raise # ZeroDivisionError. rate = numerator / denominator if numerator != 0 else 0 channel.sample_rate_ratio_number_samples = numerator channel.sample_rate_ratio_number_seconds = denominator channel.sample_rate = _read_float_var(rate, SampleRate) if sensor_element is not None: channel.sensor = _read_sensor(sensor_element, _ns) if data_log_element is not None: channel.data_logger = _read_datalogger(data_log_element, _ns) temp = _read_floattype(data_log_element, _ns("maxClockDrift"), ClockDrift) if temp is not None: if channel.sample_rate != 0.0: channel.clock_drift_in_seconds_per_sample = \ _read_float_var(temp / channel.sample_rate, ClockDrift) else: msg = "Clock drift division by sample rate of 0: " \ "using sec/sample" warnings.warn(msg) channel.sample_rate = temp channel.azimuth = _read_floattype(cha_element, _ns("azimuth"), Azimuth) channel.dip = _read_floattype(cha_element, _ns("dip"), Dip) match = re.search(r'{([^}]*)}', cha_element.tag) if match: namespace = match.group(1) else: namespace = _get_schema_namespace('0.9') channel.extra = {'format': { 'value': _tag2obj(cha_element, _ns("format"), str), # storage format of channel not supported by StationXML1.1 anymore, # keep it as a foreign tag to be nice if anybody needs to access it 'namespace': namespace}} if channel.sample_rate == 0.0: msg = "Something went hopelessly wrong, found sampling-rate of 0!" warnings.warn(msg) # Begin to collect digital/analogue filter chains # This information is stored as an array in the datalogger element response_fir_id = [] response_paz_id = [] if data_log_element is not None: # Find the decimation element with a particular num/denom decim_element = data_log_element.find(_ns( "decimation[@sampleRateDenominator='" + str(int(denominator)) + "'][@sampleRateNumerator='" + str(int(numerator)) + "']")) analogue_filter_chain = _tag2obj(decim_element, _ns("analogueFilterChain"), str) if analogue_filter_chain is not None: response_paz_id = analogue_filter_chain.split(" ") digital_filter_chain = _tag2obj(decim_element, _ns("digitalFilterChain"), str) if digital_filter_chain is not None: response_fir_id = digital_filter_chain.split(" ") channel.response = _read_response(instrumentation_register['responses'], sensor_element, response_element, cha_element, data_log_element, _ns, channel.sample_rate, response_fir_id, response_paz_id) return channel
def _read_sc3ml(path_or_file_object): """ Function for reading a stationXML file. :param path_or_file_object: File name or file like object. """ root = etree.parse(path_or_file_object).getroot() # Code can be used for version 0.7, 0.8, and 0.9 for version in SCHEMA_VERSION: namespace = _get_schema_namespace(version) if root.find("{%s}%s" % (namespace, "Inventory")) is not None: break else: raise ValueError("Schema version not supported.") def _ns(tagname): return "{%s}%s" % (namespace, tagname) # This needs to be tested, did not find an inventory # with the journal entry. journal = root.find(_ns("Journaling")) if journal is not None: entry = journal.find(_ns("entry")) if entry is not None: created = _tag2obj(entry, _ns("created"), obspy.UTCDateTime) sender = _tag2obj(entry, _ns("sender"), str) else: created = None sender = "ObsPy Inventory" # Set source to this script source = "sc3ml import" module = None module_uri = None # Find the inventory root element. (Only finds the first. We expect only # one, so any more than that will be ignored.) inv_element = root.find(_ns("Inventory")) # Pre-generate a dictionary of the sensors, dataloggers and responses to # avoid costly linear search when parsing network nodes later. # Register sensors sensors = {} for sensor_element in inv_element.findall(_ns("sensor")): public_id = sensor_element.get("publicID") if public_id: if public_id in sensors: msg = ("Found multiple matching sensor tags with the same " "publicID '{}'.".format(public_id)) raise obspy.ObsPyException(msg) else: sensors[public_id] = sensor_element # Register dataloggers dataloggers = {} for datalogger_element in inv_element.findall(_ns("datalogger")): public_id = datalogger_element.get("publicID") if public_id: if public_id in dataloggers: msg = ("Found multiple matching datalogger tags with the same " "publicID '{}'.".format(public_id)) raise obspy.ObsPyException(msg) else: dataloggers[public_id] = datalogger_element # Register reponses responses = { "responsePAZ": {}, "responsePolynomial": {}, "responseFIR": {}, "responseIIR": {} } for response_type, all_elements in responses.items(): for response_element in inv_element.findall(_ns(response_type)): public_id = response_element.get("publicID") if public_id: if public_id in all_elements: msg = ("Found multiple matching {} tags with the same " "publicID '{}'.".format(response_type, public_id)) raise obspy.ObsPyException(msg) else: all_elements[public_id] = response_element # Organize all the collection instrument information into a unified # intrumentation register. instrumentation_register = { "sensors": sensors, "dataloggers": dataloggers, "responses": responses } # Collect all networks from the sc3ml inventory networks = [] for net_element in inv_element.findall(_ns("network")): networks.append(_read_network(instrumentation_register, net_element, _ns)) return obspy.core.inventory.Inventory(networks=networks, source=source, sender=sender, created=created, module=module, module_uri=module_uri)
def _read_channel(inventory_root, cha_element, _ns): """ reads channel element from sc3ml format :param sta_element: channel element :param _ns: namespace """ code = cha_element.get("code") # Information is also kept within the parent <sensorLocation> element sen_loc_element = cha_element.getparent() location_code = sen_loc_element.get("code") # get site info from the <sensorLocation> element longitude = _read_floattype(sen_loc_element, _ns("longitude"), Longitude, datum=True) latitude = _read_floattype(sen_loc_element, _ns("latitude"), Latitude, datum=True) elevation = _read_floattype(sen_loc_element, _ns("elevation"), Distance, unit=True) depth = _read_floattype(cha_element, _ns("depth"), Distance, unit=True) # Set values to 0 if they are is missing (see #1816) if longitude is None: msg = "Sensor is missing longitude information, using 0.0" warnings.warn(msg) longitude = 0 if latitude is None: msg = "Sensor is missing latitude information, using 0.0" warnings.warn(msg) latitude = 0 if elevation is None: msg = "Sensor is missing elevation information, using 0.0" warnings.warn(msg) elevation = 0 if depth is None: msg = "Channel is missing depth information, using 0.0" warnings.warn(msg) depth = 0 channel = obspy.core.inventory.Channel(code=code, location_code=location_code, latitude=latitude, longitude=longitude, elevation=elevation, depth=depth) # obtain the sensorID and link to particular publicID <sensor> element # in the inventory base node sensor_id = cha_element.get("sensor") sensor_element = inventory_root.find( _ns("sensor[@publicID='" + sensor_id + "']")) # obtain the poles and zeros responseID and link to particular # <responsePAZ> publicID element in the inventory base node if (sensor_element is not None and sensor_element.get("response") is not None): response_id = sensor_element.get("response") response_elements = [] for resp_type in ['responsePAZ', 'responsePolynomial']: search = "{}[@publicID='{}']".format(resp_type, response_id) response_elements += inventory_root.findall(_ns(search)) if len(response_elements) == 0: msg = ("Could not find response tag with public ID " "'{}'.".format(response_id)) raise obspy.ObsPyException(msg) elif len(response_elements) > 1: msg = ("Found multiple matching response tags with the same " "public ID '{}'.".format(response_id)) raise obspy.ObsPyException(msg) response_element = response_elements[0] else: response_element = None # obtain the dataloggerID and link to particular <responsePAZ> publicID # element in the inventory base node datalogger_id = cha_element.get("datalogger") search = "datalogger[@publicID='" + datalogger_id + "']" data_log_element = inventory_root.find(_ns(search)) channel.restricted_status = _get_restricted_status(cha_element, _ns) # There is no further information in the attributes of <stream> # Start and end date are included as tags instead channel.start_date = _tag2obj(cha_element, _ns("start"), obspy.UTCDateTime) channel.end_date = _tag2obj(cha_element, _ns("end"), obspy.UTCDateTime) # Determine sample rate (given is a numerator, denominator) # Assuming numerator is # samples and denominator is # seconds numerator = _tag2obj(cha_element, _ns("sampleRateNumerator"), int) denominator = _tag2obj(cha_element, _ns("sampleRateDenominator"), int) rate = numerator / denominator channel.sample_rate_ratio_number_samples = numerator channel.sample_rate_ratio_number_seconds = denominator channel.sample_rate = _read_float_var(rate, SampleRate) if sensor_element is not None: channel.sensor = _read_sensor(sensor_element, _ns) if data_log_element is not None: channel.data_logger = _read_datalogger(data_log_element, _ns) temp = _read_floattype(data_log_element, _ns("maxClockDrift"), ClockDrift) if temp is not None: if channel.sample_rate != 0.0: channel.clock_drift_in_seconds_per_sample = \ _read_float_var(temp / channel.sample_rate, ClockDrift) else: msg = "Clock drift division by sample rate of 0: " \ "using sec/sample" warnings.warn(msg) channel.sample_rate = temp channel.azimuth = _read_floattype(cha_element, _ns("azimuth"), Azimuth) channel.dip = _read_floattype(cha_element, _ns("dip"), Dip) channel.storage_format = _tag2obj(cha_element, _ns("format"), str) if channel.sample_rate == 0.0: msg = "Something went hopelessly wrong, found sampling-rate of 0!" warnings.warn(msg) # Begin to collect digital/analogue filter chains # This information is stored as an array in the datalogger element response_fir_id = [] response_paz_id = [] if data_log_element is not None: # Find the decimation element with a particular num/denom decim_element = data_log_element.find( _ns("decimation[@sampleRateDenominator='" + str(int(denominator)) + "'][@sampleRateNumerator='" + str(int(numerator)) + "']")) analogue_filter_chain = _tag2obj(decim_element, _ns("analogueFilterChain"), str) if analogue_filter_chain is not None: response_paz_id = analogue_filter_chain.split(" ") digital_filter_chain = _tag2obj(decim_element, _ns("digitalFilterChain"), str) if digital_filter_chain is not None: response_fir_id = digital_filter_chain.split(" ") channel.response = _read_response(inventory_root, sensor_element, response_element, cha_element, data_log_element, _ns, channel.sample_rate, response_fir_id, response_paz_id) return channel