You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

684 lines
30 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!-- **********************************************************************
* Copyright (C) 2017 by gempa GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, version 3.
*
* This program 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
* Lesser General Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* SC3ML 0.5 to QuakeML 1.2 stylesheet converter
* Author : Stephan Herrnkind
* Email : stephan.herrnkind@gempa.de
* Version : 2017.342.01
*
* ================
* Usage
* ================
*
* This stylesheet converts a SC3ML to a QuakeML document. It may be invoked
* e.g. using xalan or xsltproc:
*
* xalan -in sc3ml.xml -xsl sc3ml_0.5__quakeml_1.2.xsl -out quakeml.xml
* xsltproc -o quakeml.xml sc3ml_0.5__quakeml_1.2.xsl sc3ml.xml
*
* You can also modify the default ID prefix with the reverse DNS name of your
* institute by setting the ID_PREFIX param:
*
* xalan -param ID_PREFIX "'smi:org.gfz-potsdam.de/geofon/'" -in sc3ml.xml -xsl sc3ml_0.5__quakeml_1.2.xsl -out quakeml.xml
* xsltproc -stringparam ID_PREFIX smi:org.gfz-potsdam.de/geofon/ -o quakeml.xml sc3ml_0.5__quakeml_1.2.xsl sc3ml.xml
*
* ================
* Transformation
* ================
*
* QuakeML and SC3ML are quite similar schemas. Nevertheless some differences
* exist:
*
* - IDs : SC3ML does not enforce any particular ID restriction. An ID in
* SC3ML has no semantic, it simply must be unique. Hence QuakeML uses ID
* restrictions, a conversion of a SC3ML to a QuakeML ID must be performed:
* 'sc3id' -> 'smi:org.gfz-potsdam.de/geofon/'. If no SC3ML ID is available
* but QuakeML enforces one, a static ID value of 'NA' is used.
* If the ID starts with `smi:` or `quakeml:`, the ID is considered valid
* and let untouched. This can lead to an invalid generated file but avoid
* to always modify IDs, especially when converting several times.
* - Repositioning of nodes: In QuakeML all information is grouped under the
* event element. As a consequence every node not referenced by an event
* will be lost during the conversion.
*
* <EventParameters> <eventParameters>
* <event>
* <pick/> <pick/>
* <amplitude/> <amplitude/>
* <reading/>
* <origin> <origin/>
* <stationMagnitude/> <stationMagnitude/>
* <magnitude/> <magnitude/>
* </origin>
* <focalMechanism/> <focalMechanism/>
* <event/> </event>
* </EventParameters> </eventParameters>
*
* - Renaming of nodes: The following table lists the mapping of names between
* both schema:
*
* Parent (SC3) SC3 name QuakeML name
* """""""""""""""""""""""""""""""""""""""""""""""""""""""
* seiscomp EventParameters eventParameters
* arrival weight [copied to following fields if true]
* timeUsed timeWeight
* horizontalSlownessUsed horizontalSlownessWeight
* backazimuthUsed backazimuthWeight
* takeOffAngle takeoffAngle
* magnitude magnitude mag
* stationMagnitude magnitude mag
* amplitude amplitude genericAmplitude
* origin uncertainty originUncertainty
* momentTensor method category
* waveformID resourceURI CDATA
* comment id id (attribute)
*
* - Enumerations: Both schema use enumerations. Numerous mappings are applied.
*
* - Unit conversion: SC3ML uses kilometer for origin depth, origin
* uncertainty and confidence ellipsoid, QuakeML uses meter
*
* - Unmapped nodes: The following nodes can not be mapped to the QuakeML
* schema, thus their data is lost:
*
* Parent Element lost
* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""
* creationInfo modificationTime
* momentTensor method
* stationMomentTensorContribution
* status
* cmtName
* cmtVersion
* phaseSetting
* eventParameters reading
* comment start
* comment end
* RealQuantity pdf
* TimeQuality pdf
*
* - Mandatory nodes: The following nodes is mandatory in QuakeML but not in
* SC3ML:
*
* Parent Mandatory element
* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""
* Amplitude genericAmplitude
* StationMagnitude originID
*
* - Restriction of data size: QuakeML restricts string length of some
* elements. This length restriction is _NOT_ enforced by this
* stylesheet to prevent data loss. As a consequence QuakeML files
* generated by this XSLT may not validate because of these
* restrictions.
*
* ================
* Change log
* ===============
*
* * 08.09.2014: Fixed typo in event type conversion (meteo[r] impact)
*
* * 25.08.2014: Applied part of the patch proposed by Philipp Kaestli on
* seiscomp-l@gfz-potsdam.de
* - use public id of parent origin if origin id propertery of magnitude
* and station magnitude elements is unset
* - fixed takeOffAngle conversion vom real (SC3ML) to RealQuantity
* (QuakeML)
*
* * 04.07.2016: Version bump. No modification here, SC3 datamodel was updated
* on the inventory side.
*
* * 28.11.2016: Version bump. No modification here, SC3 datamodel was updated
* on the inventory side.
*
* * 28.06.2017: Changed license from GPL to LGPL
*
* * 08.08.2017: Added some fixes to use this XSLT in ObsPy
* - Change ID_PREFIX variable to a param
* - Do not copy Amplitude if amplitude/amplitude doesn't existing
* - focalMechanism/evaluationMode and focalMechanism/evaluationStatus were
* not copied but can actually be mapped to the QuakeML schema
* - Some event/type enumeration was mapped to `other` instead of
* `other event`
* - Fix origin uncertainty and confidence ellispoid units
* - Rename momentTensor/method to momentTensor/category
* - Fix amplitude/unit (enumeration in QuakeML, not in SC3ML)
* - Don't modify id if it starts with 'smi:' or 'quakeml:'
* - Fix Arrival publicID generation
*
* * 27.09.2017:
* - Use '_' instead of '#' in arrival publicID generation
* - Map SC3 arrival weight to timeWeight, horizontalSlownessWeight and
* backazimuthWeight depending on timeUsed, horizontalUsed and
* backzimuthUsed values
*
* * 08.12.2017:
* - Remove unmapped nodes
* - Fix arrival weight mapping
*
* * 07.12.2018: Copy picks referenced by amplitudes
*
* * 10.12.2018: Put the non-QuakeML nodes in a custom namespace
*
* * 31.10.2022: Improve performance when processing origins with many arrivals.
*
********************************************************************** -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:scs="http://geofon.gfz-potsdam.de/ns/seiscomp3-schema/0.5"
xmlns:qml="http://quakeml.org/xmlns/quakeml/1.0"
xmlns="http://quakeml.org/xmlns/bed/1.2"
xmlns:q="http://quakeml.org/xmlns/quakeml/1.2"
exclude-result-prefixes="scs qml xsl">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Define parameters-->
<xsl:param name="ID_PREFIX" select="'smi:org.gfz-potsdam.de/geofon/'"/>
<!-- Define global variables -->
<xsl:variable name="PID" select="'publicID'"/>
<!-- Starting point: Match the root node and select the one and only
EventParameters node -->
<xsl:template match="/">
<xsl:variable name="scsRoot" select="."/>
<q:quakeml>
<xsl:for-each select="$scsRoot/scs:seiscomp/scs:EventParameters">
<eventParameters>
<!-- Mandatory publicID attribute -->
<xsl:attribute name="{$PID}">
<xsl:call-template name="convertOptionalID">
<xsl:with-param name="id" select="@publicID"/>
</xsl:call-template>
</xsl:attribute>
<!-- Put the QuakeML nodes at the beginning -->
<xsl:apply-templates select="*[not(self::scs:reading)]" />
<!-- Put the non-QuakeML nodes at the end -->
<xsl:apply-templates select="scs:reading" mode="scs-only" />
</eventParameters>
</xsl:for-each>
</q:quakeml>
</xsl:template>
<!-- event -->
<xsl:template match="scs:event">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@*"/>
<!-- search origins referenced by this event -->
<xsl:for-each select="scs:originReference">
<xsl:for-each select="../../scs:origin[@publicID=current()]">
<xsl:variable name="origin" select="current()" />
<!-- stationMagnitudes and referenced amplitudes -->
<xsl:for-each select="scs:stationMagnitude">
<xsl:for-each select="../../scs:amplitude[@publicID=current()/scs:amplitudeID]">
<!-- amplitude/genericAmplitude is mandatory in QuakeML -->
<xsl:if test="scs:amplitude">
<!-- copy picks referenced in amplitudes -->
<xsl:for-each select="../scs:pick[@publicID=current()/scs:pickID]">
<xsl:call-template name="genericNode" />
</xsl:for-each>
<xsl:call-template name="genericNode"/>
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="." mode="originMagnitude">
<xsl:with-param name="oID" select="../@publicID"/>
</xsl:apply-templates>
</xsl:for-each>
<!-- magnitudes -->
<xsl:for-each select="scs:magnitude">
<xsl:apply-templates select="." mode="originMagnitude">
<xsl:with-param name="oID" select="../@publicID"/>
</xsl:apply-templates>
</xsl:for-each>
<!-- picks, referenced by arrivals -->
<!-- we exclude picks already referenced in amplitudes: -->
<xsl:variable name="amplitudes" select="../scs:amplitude[@publicID=$origin/scs:stationMagnitude/scs:amplitudeID]" />
<xsl:for-each select="scs:arrival[not(./scs:pickID=$amplitudes/scs:pickID)]">
<xsl:for-each select="../../scs:pick[@publicID=current()/scs:pickID]">
<xsl:call-template name="genericNode"/>
</xsl:for-each>
</xsl:for-each>
<!-- origin -->
<xsl:call-template name="genericNode"/>
</xsl:for-each>
</xsl:for-each>
<!-- search focalMechanisms referenced by this event -->
<xsl:for-each select="scs:focalMechanismReference">
<xsl:for-each select="../../scs:focalMechanism[@publicID=current()]">
<xsl:call-template name="genericNode"/>
</xsl:for-each>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- Default match: Map node 1:1 -->
<xsl:template match="*">
<xsl:call-template name="genericNode"/>
</xsl:template>
<!-- Delete elements -->
<xsl:template match="scs:EventParameters/scs:pick"/>
<xsl:template match="scs:EventParameters/scs:amplitude"/>
<xsl:template match="scs:EventParameters/scs:origin"/>
<xsl:template match="scs:EventParameters/scs:focalMechanism"/>
<xsl:template match="scs:event/scs:originReference"/>
<xsl:template match="scs:event/scs:focalMechanismReference"/>
<xsl:template match="scs:comment/scs:id"/>
<xsl:template match="scs:arrival/scs:weight"/>
<xsl:template match="scs:arrival/scs:timeUsed"/>
<xsl:template match="scs:arrival/scs:horizontalSlownessUsed"/>
<xsl:template match="scs:arrival/scs:backazimuthUsed"/>
<xsl:template match="scs:origin/scs:stationMagnitude"/>
<xsl:template match="scs:origin/scs:magnitude"/>
<xsl:template match="scs:momentTensor/scs:method"/>
<!-- Converts a scs magnitude/stationMagnitude to a qml
magnitude/stationMagnitude -->
<xsl:template match="*" mode="originMagnitude">
<xsl:param name="oID"/>
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@*"/>
<!-- if no originID element is available, create one with
the value of the publicID attribute of parent origin -->
<xsl:if test="not(scs:originID)">
<originID>
<xsl:call-template name="convertID">
<xsl:with-param name="id" select="$oID"/>
</xsl:call-template>
</originID>
</xsl:if>
<!-- Put the QuakeML nodes at the beginning -->
<xsl:apply-templates select="*[not(self::scs:passedQC)]" />
<!-- Put the non-QuakeML nodes at the end -->
<xsl:apply-templates select="scs:passedQC" mode="scs-only" />
</xsl:element>
</xsl:template>
<!-- event type, enumeration differs slightly -->
<xsl:template match="scs:event/scs:type">
<xsl:element name="{local-name()}">
<xsl:variable name="v" select="current()"/>
<xsl:choose>
<xsl:when test="$v='induced earthquake'">induced or triggered event</xsl:when>
<xsl:when test="$v='meteor impact'">meteorite</xsl:when>
<xsl:when test="$v='not locatable'">other event</xsl:when>
<xsl:when test="$v='outside of network interest'">other event</xsl:when>
<xsl:when test="$v='duplicate'">other event</xsl:when>
<xsl:when test="$v='other'">other event</xsl:when>
<xsl:otherwise><xsl:value-of select="$v"/></xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:template>
<!-- origin depth, SC3ML uses kilometer, QML meter -->
<xsl:template match="scs:origin/scs:depth/scs:value
| scs:origin/scs:depth/scs:uncertainty
| scs:origin/scs:depth/scs:lowerUncertainty
| scs:origin/scs:depth/scs:upperUncertainty
| scs:origin/scs:uncertainty/scs:horizontalUncertainty
| scs:origin/scs:uncertainty/scs:minHorizontalUncertainty
| scs:origin/scs:uncertainty/scs:maxHorizontalUncertainty
| scs:confidenceEllipsoid/scs:semiMajorAxisLength
| scs:confidenceEllipsoid/scs:semiMinorAxisLength
| scs:confidenceEllipsoid/scs:semiIntermediateAxisLength">
<xsl:element name="{local-name()}">
<xsl:value-of select="current() * 1000"/>
</xsl:element>
</xsl:template>
<!-- evaluation status, enumeration of QML does not include 'reported' -->
<xsl:template match="scs:evaluationStatus">
<xsl:variable name="v" select="current()"/>
<xsl:if test="$v!='reported'">
<xsl:element name="{local-name()}">
<xsl:value-of select="$v"/>
</xsl:element>
</xsl:if>
</xsl:template>
<!-- data used wave type, enumeration differs slightly -->
<xsl:template match="scs:dataUsed/scs:waveType">
<xsl:element name="{local-name()}">
<xsl:variable name="v" select="current()"/>
<xsl:choose>
<xsl:when test="$v='P body waves'">P waves</xsl:when>
<xsl:when test="$v='long-period body waves'">body waves</xsl:when>
<xsl:when test="$v='intermediate-period surface waves'">surface waves</xsl:when>
<xsl:when test="$v='long-period mantle waves'">mantle waves</xsl:when>
<xsl:otherwise><xsl:value-of select="$v"/></xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:template>
<!-- origin uncertainty description, enumeration of QML does not include 'probability density function' -->
<xsl:template match="scs:origin/scs:uncertainty/scs:preferredDescription">
<xsl:variable name="v" select="current()"/>
<xsl:if test="$v!='probability density function'">
<xsl:element name="{local-name()}">
<xsl:value-of select="$v"/>
</xsl:element>
</xsl:if>
</xsl:template>
<!-- momentTensor/method -> momentTensor/category -->
<xsl:template match="scs:momentTensor/scs:method">
<xsl:variable name="v" select="current()"/>
<xsl:if test="$v='teleseismic' or $v='regional'">
<xsl:element name="category">
<xsl:value-of select="$v"/>
</xsl:element>
</xsl:if>
</xsl:template>
<!-- amplitude/unit is an enumeration in QuakeML, not in SC3ML -->
<xsl:template match="scs:amplitude/scs:unit">
<xsl:variable name="v" select="current()"/>
<xsl:element name="{local-name()}">
<xsl:choose>
<xsl:when test="$v='m'
or $v='s'
or $v='m/s'
or $v='m/(s*s)'
or $v='m*s'
or $v='dimensionless'">
<xsl:value-of select="$v"/>
</xsl:when>
<xsl:otherwise>other</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:template>
<!-- origin arrival -->
<xsl:template match="scs:arrival">
<xsl:element name="{local-name()}">
<!-- since SC3ML does not include a publicID it is generated from pick and origin id -->
<xsl:attribute name="{$PID}">
<xsl:call-template name="convertID">
<xsl:with-param name="id" select="concat(scs:pickID, '_', translate(../@publicID, ' :', '__'))"/>
</xsl:call-template>
</xsl:attribute>
<!-- mapping of weight to timeWeight, horizontalSlownessWeight and backazimuthWeight
depending on timeUsed, horizontalSlownessUsed and backazimuthUsed values -->
<xsl:choose>
<xsl:when test="scs:weight">
<xsl:if test="((scs:timeUsed='true') or (scs:timeUsed='1'))
or (not(scs:timeUsed|scs:horizontalSlownessUsed|scs:backazimuthUsed))">
<xsl:element name="timeWeight">
<xsl:value-of select="scs:weight"/>
</xsl:element>
</xsl:if>
<xsl:if test="((scs:horizontalSlownessUsed='true') or (scs:horizontalSlownessUsed='1'))">
<xsl:element name="horizontalSlownessWeight">
<xsl:value-of select="scs:weight"/>
</xsl:element>
</xsl:if>
<xsl:if test="((scs:backazimuthUsed='true') or (scs:backazimuthUsed='1'))">
<xsl:element name="backazimuthWeight">
<xsl:value-of select="scs:weight"/>
</xsl:element>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:if test="((scs:timeUsed='true') or (scs:timeUsed='1'))">
<xsl:element name="timeWeight">
<xsl:value-of select="'1'"/>
</xsl:element>
</xsl:if>
<xsl:if test="((scs:horizontalSlownessUsed='true') or (scs:horizontalSlownessUsed='1'))">
<xsl:element name="horizontalSlownessWeight">
<xsl:value-of select="'1'"/>
</xsl:element>
</xsl:if>
<xsl:if test="((scs:backazimuthUsed='true') or (scs:backazimuthUsed='1'))">
<xsl:element name="backazimuthWeight">
<xsl:value-of select="'1'"/>
</xsl:element>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- Value of ID nodes must be converted to a qml identifier -->
<xsl:template match="scs:agencyURI|scs:authorURI|scs:pickID|scs:methodID|scs:earthModelID|scs:amplitudeID|scs:originID|scs:stationMagnitudeID|scs:preferredOriginID|scs:preferredMagnitudeID|scs:originReference|scs:filterID|scs:slownessMethodID|scs:pickReference|scs:amplitudeReference|scs:referenceSystemID|scs:triggeringOriginID|scs:derivedOriginID|momentMagnitudeID|scs:preferredFocalMechanismID|scs:focalMechanismReference|scs:momentMagnitudeID|scs:greensFunctionID">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@*"/>
<xsl:call-template name="valueOfIDNode"/>
</xsl:element>
</xsl:template>
<!-- arrival/takeOffAngle -> arrival/takeoffAngle -->
<xsl:template match="scs:arrival/scs:takeOffAngle">
<xsl:element name="takeoffAngle">
<xsl:element name="value">
<xsl:value-of select="."/>
</xsl:element>
</xsl:element>
</xsl:template>
<!-- stationMagnitude/magnitude -> stationMagnitude/mag -->
<xsl:template match="scs:stationMagnitude/scs:magnitude|scs:magnitude/scs:magnitude">
<xsl:call-template name="genericNode">
<xsl:with-param name="name" select="'mag'"/>
</xsl:call-template>
</xsl:template>
<!-- amplitude/amplitude -> amplitude/genericAmplitude -->
<xsl:template match="scs:amplitude/scs:amplitude">
<xsl:call-template name="genericNode">
<xsl:with-param name="name" select="'genericAmplitude'"/>
</xsl:call-template>
</xsl:template>
<!-- origin/uncertainty -> origin/originUncertainty -->
<xsl:template match="scs:origin/scs:uncertainty">
<xsl:call-template name="genericNode">
<xsl:with-param name="name" select="'originUncertainty'"/>
</xsl:call-template>
</xsl:template>
<!-- waveformID: SCS uses a child element 'resourceURI', QML
inserts the URI directly as value -->
<xsl:template match="scs:waveformID">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@*"/>
<xsl:if test="scs:resourceURI">
<xsl:call-template name="convertID">
<xsl:with-param name="id" select="scs:resourceURI"/>
</xsl:call-template>
</xsl:if>
</xsl:element>
</xsl:template>
<!-- comment: SCS uses a child element 'id', QML an attribute 'id' -->
<xsl:template match="scs:comment">
<xsl:element name="{local-name()}">
<xsl:if test="scs:id">
<xsl:attribute name="id">
<xsl:call-template name="convertID">
<xsl:with-param name="id" select="scs:id"/>
</xsl:call-template>
</xsl:attribute>
</xsl:if>
<!-- Put the QuakeML nodes at the beginning -->
<xsl:apply-templates select="*[not(self::scs:start|self::scs:end)]" />
<!-- Put the non-QuakeML nodes at the end -->
<xsl:apply-templates select="scs:start|scs:end" mode="scs-only" />
</xsl:element>
</xsl:template>
<!-- Generic transformation of all attributes of an element. If the
attribute name is 'eventID' it is transfered to a QML id -->
<xsl:template match="@*">
<xsl:variable name="attName" select="local-name()"/>
<xsl:attribute name="{$attName}">
<xsl:choose>
<xsl:when test="$attName=$PID">
<xsl:call-template name="convertID">
<xsl:with-param name="id" select="string(.)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="string(.)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:template>
<!--
************************************************************************
Unmapped nodes
************************************************************************
-->
<xsl:template match="scs:creationInfo">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@*"/>
<!-- Put the QuakeML nodes at the beginning -->
<xsl:apply-templates select="*[not(self::scs:modificationTime)]" />
<!-- Put the non-QuakeML nodes at the end -->
<xsl:apply-templates select="scs:modificationTime" mode="scs-only" />
</xsl:element>
</xsl:template>
<xsl:template match="scs:momentTensor">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="@*"/>
<!-- Put the QuakeML nodes at the beginning -->
<xsl:apply-templates select="*[not(self::scs:stationMomentTensorContribution
| self::scs:status
| self::scs:cmtName
| self::scs:cmtVersion
| self::scs:phaseSetting)]" />
<!-- Put the non-QuakeML nodes at the end -->
<xsl:apply-templates select="scs:stationMomentTensorContribution
| scs:status
| scs:cmtName
| scs:cmtVersion
| scs:phaseSetting" mode="scs-only" />
</xsl:element>
</xsl:template>
<xsl:template match="scs:pdf">
<xsl:apply-templates select="." mode="scs-only" />
</xsl:template>
<xsl:template match="node()|@*" mode="scs-only">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- Keep seiscomp namespace for unmapped node -->
<xsl:template match="scs:*" mode="scs-only">
<xsl:element name="scs:{local-name()}">
<xsl:apply-templates select="@*|node()" mode="scs-only" />
</xsl:element>
</xsl:template>
<!--
************************************************************************
Named Templates
************************************************************************
-->
<!-- Generic and recursively transformation of elements and their
attributes -->
<xsl:template name="genericNode">
<xsl:param name="name"/>
<xsl:param name="reqPID"/>
<xsl:variable name="nodeName">
<xsl:choose>
<xsl:when test="$name">
<xsl:value-of select="$name"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="local-name()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:element name="{$nodeName}">
<xsl:apply-templates select="@*"/>
<xsl:if test="$reqPID">
<xsl:attribute name="{$PID}">
<xsl:call-template name="convertOptionalID">
<xsl:with-param name="id" select="@publicID"/>
</xsl:call-template>
</xsl:attribute>
</xsl:if>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- Converts and returns value of an id node -->
<xsl:template name="valueOfIDNode">
<xsl:call-template name="convertOptionalID">
<xsl:with-param name="id" select="string(.)"/>
</xsl:call-template>
</xsl:template>
<!-- Converts a scs id to a quakeml id. If the scs id is not set
the constant 'NA' is used -->
<xsl:template name="convertOptionalID">
<xsl:param name="id"/>
<xsl:choose>
<xsl:when test="$id">
<xsl:call-template name="convertID">
<xsl:with-param name="id" select="$id"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="convertID">
<!--xsl:with-param name="id" select="concat('NA-', generate-id())"/-->
<xsl:with-param name="id" select="'NA'"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Converts a scs id to a quakeml id -->
<xsl:template name="convertID">
<xsl:param name="id"/>
<!-- If the id starts with 'smi:' or 'quakeml:', consider that the id
is already well formated -->
<xsl:choose>
<xsl:when test="starts-with($id, 'smi:')
or starts-with($id, 'quakeml:')">
<xsl:value-of select="$id"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($ID_PREFIX, translate($id, ' :', '__'))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>