HyperWrite - Consultancy and Training 

Help Systems, Structured Authoring, DITA, Hypertext, Documentation Technologies

HyperWrite Logo
Skip Navigation LinksHome > Articles > This Article
Skip Navigation Links

Dynamically converted measure units in DITA through XSL-T

When technical content includes measurements that can be expressed using different units, it is often difficult for the author to know which unit to use. Different readers may have requirements for seeing different units. Working within DITA, and using the data element for measurements, it is possible to produce dynamically-converted output using units that suit the individual reader's needs.

By Tony Self

Oils ain't oils, and standards ain't standard

"A stone's throw" is not a very accurate unit of measure. And while other units of measure are more accurate, they can be incomprehensible to those unfamiliar, and especially those inculcated with an alternative unit. A person may know how long it takes to walk a mile, but has no idea whether a fathom is shorter or longer, or even a measure of distance. Whether it's distance, weight, volume, currency, pressure, temperature, or scores of other units, a "standard" is rarely universal.

Even very common standard units have variations... or perhaps homonyms (words that share spelling and punctuation, but not meaning). A "gallon" to someone from the Canada is 20% more than a "gallon" to someone from the United States. And a "dry gallon", also used in the United States, is 3% less than a "standard" "US gallon".

What a unit means not only changes across borders; they also change over time. In fact, over time, a mile has varied in length from about 1 kilometre to about 10 kilometres. A Roman mile (remembering that "mille" in Latin means "one thousand") was 1,000 paces. And a pace meant two steps, in case you thought "pace" and "step" were the same unit! There were also different Arab miles, Danish miles, Scots miles, Irish miles, Hungarian miles, Portuguese miles, and so on.

Miles are not the same as miles

Even today, there are two commonly used standard miles: one of 5,280 feet, and another of 6,076 feet. The former is used for distance on land, and is qualified as a "land mile" or a "statute mile". The latter measure is used for distance across the sea or the through the air, and is equivalent to one degree of latitude; it is differentiated from a land mile through the term "nautical mile".

In the United States, aircraft speeds are sometimes measured in (statute) miles per hour (mph), but in other countries, nautical miles per hour, or "knots", is the more common unit.

Miles are not the same as miles

Air Speed Indicator with knots

Air Speed Indicator with mph

The problem in practice

An example of the problem for technical communicators in practice is summarised in the question, "what unit do I use for aircraft flight manual?". The choices are to use knots, to use mph, or to use both. Remembering that "the reader is king", the correct choice is influenced by the audience. For a German pilot, the flight manual should use knots. For a US pilot, the flight manual should use mph. Single-sourcing techniques make it possible to deliver different variants for different audiences. But what about the flight manual for a German pilot flying a US aircraft? Or a US pilot flying an aircraft fitted with an ASI that uses knots? Would including both measurements be a wiser choice?

The problem with including both units, particularly when the speeds are so important as they are in aviation, is that there may be confusion. For example, the emergency procedures for an 8KCAB aircraft (as listed in the Operating Manual) include a forced landing minimum approach speed of 70 mph. Approaching at 70 knots is too fast, and it's easy to see how a quick glance at the "70" figure with an assumption about the unit could be problematic. Ideally, the solution would be for the manual to show the units that match the aircraft instrument units. If the aircraft displays two units, then the manual should use the same units that the pilot "thinks in".

To make this possible, the author would have to nominate a default unit for the publication, with an override based on the type of instrument fitted to a particular aircraft (so that the manual is specific to the individual aircraft), and another override that the pilot can choose. Of course, this is not possible for a paper manual, or a PDF, but it is possible for an electronic flight manual.

Dynamic units with DITA

In a typical scenario, some parts of an aircraft manual might be maintained by the manufacturer, other parts by the aircraft national distributor, and other parts by the aircraft owner or operator. Let's assume that the "base manual" is provided by the manufacturer in DITA format, using mph as the default unit. The distributor will then add or replace some content based on local certification requirements, in DITA format, using knots as the default unit. The operator then makes some further changes, also in DITA, to reflect the instruments fitted to the individual aircraft. Finally, let's make an assumption that the pilot needs to work in kilometres per hour for a particular operational requirement. It is quite easy to cater for this scenario in DITA.

Firstly, the semantics for marking up speeds needs to be agreed by all contributors. The suggested markup rules are shown in the example:

<ph><data type="speed" datatype="mph" value="70" translate="yes"></data>70 mph</ph>
where the type attribute indicates the type of measurement unit, the datatype attribute indicates the unit itself, the value attribute contains the number, and the translate attribute whether the unit should be converted or not. (In some cases, it may be necessary to quote a speed with a fixed unit.)

A better alternative would be to create a specialisation of ph, perhaps named measure, which would be the "container" for the data element. Another alternative would be to create a specialisation of foreign, and incorporate all the metadata attributes within that element. But let's assume we want to stay with base DITA.

Although its name suggests otherwise, the data element in DITA is intended for metadata, which would not normally be displayed in the output. That's why the ph container is used to store the default text. If the content is published or otherwise processed by a tool that is not aware of the markup rules, then the content of the ph element will be displayed normally. However, if a processor was aware of the data markup, it could replace the content of the containing ph element with a calculated value based on datatype and value attributes of the data element, and a unit preference.

The calculation would be made within the processing using XSL-T. An example of such conversion code is:

<!-- Set the default unit to be used to display speeds -->
<!-- and create a parameter to be used for the value to be converted -->
  <xsl:param name="unit_speed" select="'kts'"/>
  <xsl:param name="value_speed"></xsl:param>

<!-- Look for data elements within ph elements -->
<!-- If found, call the 'convert' template -->
  <xsl:template match="*[local-name() = &apos;ph&apos;][child::*[local-name() = &apos;data&apos;]]" priority = "1" >
    <xsl:call-template name="convert" />
  </xsl:template>

<!-- The 'convert' template -->
<!-- If the data elements are OK for conversion, run the standardise_to_kph template -->
  <xsl:template name="convert">
    <xsl:choose>
      <xsl:when test="@translate='yes' and $unit_speed!='source'">
        <span class="data">
          <xsl:call-template name="standardise_to_kph" />
          </xsl:call-template>
        </span>
      </xsl:when>
      <xsl:otherwise>
        <span class="data">
          <xsl:value-of select="."/>
        </span>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

<!-- standardise_to_kph template -->
<!-- Convert from the original speed using the value and datatype attributes -->
<!-- Then using the converted speed (in kph), call the speed template -->
  <xsl:template name="standardise_to_kph">
    <xsl:param name="unit_speed" select="$unit_speed"/>
    <xsl:param name="value_speed" select="$value_speed"/>
    <xsl:choose>
      <xsl:when test="@datatype='kph'">
        <xsl:call-template name="speed">
          <xsl:with-param name="unit_speed" select="$unit_speed" />
          <xsl:with-param name="value_speed" select="$value_speed div 1" />
        </xsl:call-template>
      </xsl:when>
      <xsl:when test="@datatype='mph'">
        <xsl:call-template name="speed">
          <xsl:with-param name="unit_speed" select="$unit_speed" />
          <xsl:with-param name="value_speed" select="$value_speed div 0.621371" />
        </xsl:call-template>
      </xsl:when>
      <xsl:when test="@datatype='kts'">
        <xsl:call-template name="speed">
          <xsl:with-param name="unit_speed" select="$unit_speed" />
          <xsl:with-param name="value_speed" select="$value_speed div 0.539957" />
        </xsl:call-template>
      </xsl:when>
    </xsl:choose>
  </xsl:template>


<!-- speed template -->
<!-- Using the passed speed (in kph) and value to be converted to, -->
<!-- write the speed, then a space (&#160;), then the unit -->
  <xsl:template name="speed">
    <xsl:param name="unit_speed"/>
    <xsl:param name="value_speed"/>
    <xsl:choose>
      <xsl:when test="$unit_speed='kph'">
        <xsl:value-of select="format-number($value_speed, '#.0')"/>
      </xsl:when>
      <xsl:when test="$unit_speed='mph'">
        <xsl:value-of select="format-number($value_speed *  0.621371, '#.0')"/>
      </xsl:when>
      <xsl:when test="$unit_speed='kts'">
        <xsl:value-of select="format-number($value_speed * 0.539957, '#.0')"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="format-number($value_speed, '#.0')"/>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:text>&#160;</xsl:text>
    <xsl:value-of select="$unit_speed"/>
  </xsl:template>

The final requirement is to allow the pilot to override the display unit, and this can be done by setting the unit_speed parameter when the XSL-T is called. (This would typically be implemented in the user interface as a preference dropdown.) A code sample for such a server-side processing request (in ASP.Net VB) is:

        Dim XslArg As New System.Xml.Xsl.XsltArgumentList
        Dim speedunits As String = Request.QueryString("unit_speed")
        If Not (speedunits = "mph" Or speedunits = "kts" Or speedunits = "source") Then speedunits = "kph"
        If Not speedunits = "" Then XslArg.AddParam("unit_speed", "", speedunits)

Example of default, knots and mph dynamically-converted output

Result

The resultant dynamically-converted units can be displayed in knots, mph or kph, regardless of what unit has been used when the speed is mentioned. As with all document engineering processes, the system is reliant upon accurate metadata. If the attributes are not completed accurately, the result will not be accurate. Once the processing routines for DITA content using the speed markup have been written, there is no additional cost to converting all of a document's speeds, or for those of a suite of documents, or for the system to be introduced globally. DITA is a standard, and if you work within that standard, many benefits flow.

Oils Ain't Oils

To an Australian audience of a certain age, the "oils ain't oils" in the title makes some sense. It is a catchphrase from a famous Castrol advertising campaign, built on the premise that not all motor oils are the same.

Acknowledgement

Thanks to Eliot Kimber for his generous advice.

  • Speeds displaying only data element content (that is, not converted)
  • Speeds converted to mph
  • Speeds converted to knots

  • Bookmark and Share

    Created with DITA


    Valid XHTML 1.0 Transitional Valid CSS!

    See it in action...

    You can see the techniques explained in this article in use in the Airspeeds demo page.