1. Introduction

This document will give a couple of guidelines as to how a client (say, an ECDIS, a website or an app) might render Navigational Warnings (NW) and Notices to Mariners (NM) for the end-user.

The messages are assumed to be in the Niord Message Model format as returned by the Niord Public API. Please acquaint yourself with these documents first.

An example client website rendering messages fetched via the Niord Public API can be found at nautiskinformation.soefartsstyrelsen.dk

2. Presentation

The Niord Message Model is fairly rich, and, depending on the situation, only parts of the model should be presented to the end user.

Message.areas is an example of a message attribute that will typically not be rendered directly, but may be used to group or filter messages.

Message.categories is an example of a message attribute that will typically not be rendered directly, but may be used to drive portrayal (e.g. display a special icon for firing exercises).

2.1. Translations

As described in the Niord Message Model document, all localizable components of the message has an associated list of description entities with all the localizable attributes.

For instance, a Message, has an associated MessageDesc list, as defined in JSON:

{
  "descs": [
    {
      "lang": "en",
      "title": "Denmark. The Baltic Sea. Gedser S. Rødsand Rende. Buoyage missing.",
      ...
    },
    {
      "lang": "da",
      "title": "Danmark. Østersøen. Gedser S. Rødsand Rende. Afmærkning forsvundet.",
      ...
    },
  ],
  ...
}

If no language parameter was specified, when querying the public REST API, all translations will be returned (see example above). When the client renders the message, it must iterate the "descs" list to find the preferred language variant.

If, however, a language parameter was specified, say "da", only that description entity is returned. Thus, when the client renders the message, it can pick the first (and only) description entity (descs[0]).
If a message does not have the requested translation, it will return another variant (typically "en"), and the client may choose to signal this to the end user (e.g. by displaying a flag).

2.2. Message Lists

When lists of messages are displayed in the client, typically it is sufficient to display the short ID and the title of a message.

In simplified pseudo-code, and assuming the client is a web application, this would amount to:

for msg in message-list
  emit-title-line(msg)
end-for

procedure emit-title-line(msg)
  emit "<div>"

  /** Emit the short ID **/
  if msg.shortId defined
    emit "<span>" + msg.shortId + "</span>"
  end-if

  /** For NM T&P messages, emit (T) or (P) **/
  if msg.type == 'PRELIMINARY_NOTICE'
    emit " (P)"
  else if msg.type == 'TEMPORARY_NOTICE'
    emit " (T)"
  end-if

  /** Emit the message title **/
  emit " <span>" + msg.descs[0].title + "</span>"

  emit "</div>"
end-procedure

The resulting message list may look something along the lines of:

Message List

2.3. Message Details

When the details of a message, msg, is displayed in the client, several more message fields are of interest.

In simplified pseudo-code, and assuming the client is a web application, this would amount to:

/** Emit asterisk "*" when the message is based on original information **/
if msg.originalInformation == true
  emit "<div>*</div>"
end-if

/** Emit the title line as described in the "Message Lists" section **/
emit-title-line(msg)

/** Emit references **/
for ref in msg.references
  emit "<div>Reference: " + ref.messageId + " (" + ref.type + ") "
       + ref.descs[0].description + "</div>"
end-for

/** Emit the subject and details of the message parts **/
for part in msg.parts
  /**
   * The part.type field contains one of the values: "DETAILS", "TIME",
   * "POSITIONS", "NOTE", "PROHIBITION" or "SIGNALS", and may be used
   * to give the message part a header.
   */
  emit "<div>Details: <strong>" + part.descs[0].subject + "</strong></div>"
  emit "<div class='message-description'>" + part.descs[0].details + "</div>"
end-for

/** Emit attachments **/
for att in msg.attachments
  emit "<div>Attachment: <a href='" + att.path + "'>" + att.fileName + "</a> "
       + att.descs[0].caption + "</div>"
end-for

/** Emit charts **/
for chart in msg.charts
  emit "<div>Charts: " + chart.chartNumber
  if chart.internationalNumber
    emit " (INT " + chart.internationalNumber + ")"
  end-if
  emit "</div>"
end-for

/** Emit publication **/
emit "<div>Publication: " + msg.descs[0].publication + "</div>"

/** Emit source **/
emit "<div style='text-align:right'>(" + msg.descs[0].source + ")</div>"

It is important that the client is able to display the HTML of the part.descs[0].details and msg.descs[0].publication fields. If the client does not have the capacity to do this, it should convert HTML to plain text first.

The message part details (HTML) may contains stylesheet classes. To improve the rendering of the message details, please adhere to the following CSS classes of the message.css stylesheet: message-description, table.no-border, table.condensed, tr.underline, table.positions, ul.positions, table.position-table.

The resulting message details may look something along the lines of:

Message Details

2.4. Message Geometry

The client may want to display the messages on a map, either as an overview map for a list of messages or when presenting the details of a single message.

Since Niord relies on GeoJSON for message geometries, you are likely to be able to visualize the message with very little work in your mapping library of choice, since most of these libraries supports GeoJSON out of the box.

As described in the Niord Message Model document, Niord actually extends the model, by specifying how e.g. localized feature and coordinate labels may be encoded in the GeoJSON Feature properties.

Assuming that a message, msg, is to be rendered in OpenLayers, the simplified pseudo-code may look something like:

geoJsonFormat = new ol.format.GeoJSON()
olLayer = ...

/** Generate features for all geometry fields of all message parts **/
for part in msg.parts
  /** The part.geometry is always of GeoJSON type: FeatureCollection **/
  if part.geometry && part.geometry.type == 'FeatureCollection'
      for feature in part.geometry.features
        olFeature = geoJsonFormat.readFeature(feature, {
                    dataProjection: 'EPSG:4326',
                    featureProjection: 'EPSG:3857'
                })
        olFeature.set('message', message);
        olLayer.getSource().addFeature(olFeature)
      end-for
  end-if
end-for

Naturally, the client should also be able to handle messages with no associated geometry.

The resulting message details including geometry may look something along the lines of:

Message Details