How to use the TPEG2 module

The tpeg2 module defines a Java/Scala model for TPEG2 messages. There are
marshallers and unmarshallers to read and write the XML and binary formats
defined by the standard.

The binary format was designed for use cases with very limited bandwidth, so it
is very efficient, and intended for production use. The XML markup
representation is more verbose and generally used as a human readable format,
such as for testing and debugging.

The
main package
of the module provides access to the marshalling and unmarshalling
functionality.

There are sub-packages for each supported TPEG2 application/toolkit:

  • tpegdatatypes
    the basic datatypes shared between all applications and toolkits following ISO
    21219-2:2019.

  • sfw
    the Service Framework (SFW) types needed for framing application messages for
    reliable transmission following ISO 21219-5:2019.

  • mmc
    the types for the Message Management Container (MMC), which holds
    administrative information that allows a decoder to handle the message
    appropriately following ISO 21219-6:2019.

  • lrc
    the types to wrap location references in a generic way following ISO
    21219-7:2017.

  • sni
    the types used to express service and network information, including to
    describe the content of binary-encoded SFW frames following ISO 21219-9:2016.

  • tec
    the types used for the traffic event compact application following ISO
    21219-15:2016.

  • tfp
    the types used for the traffic flow and prediction application following ISO
    21219-18:2019.

  • etl
    the types used for extended TMC location references following TISA SP13003-20.

  • olr
    the types used for OpenLR location references following ISO 21219-22:2017.

  • tmc
    the types used for TPEG1 TMC Location Referencing following ISO 17572-2.

Working with TPEG2 data structures

Start working with the tpeg2 module by adding it to your project dependencies:

libraryDependencies ++= Seq("com.here.platform.location" %% "location-tpeg2" % "<version>")

<dependencies>
    <dependency>
        <groupId>com.here.platform.location</groupId>
        <artifactId>location-tpeg2_${scala.compat.version}</artifactId>
    </dependency>
</dependencies>
dependencies {
    compile group: 'com.here.platform.location', name: 'location-tpeg2_2.13', version:'<version>'
}

The entrypoint for the TPEG2 marshalling functionality are
BinaryMarshallers
and
XmlMarshallers
respectively.

TPEG2 provides a whole protocol for transmitting data, that specifies how to
transmit messages in frames. What is normally send over the wire or the air is
thus packaged into
sfw.TransportFrames
before being streamed.

While the TransportFrame is thus normally the starting point for marshalling
and unmarshalling, some other important types can be individually marshalled and
unmarshalled to TPEG-Binary or TPEG XML.

For example
OpenLRLocationReference
can be individually unmarshalled from TPEG-Binary like this:

import com.here.platform.location.tpeg2.BinaryMarshallers
import com.here.platform.location.tpeg2.olr.OpenLRLocationReference

import java.io.ByteArrayInputStream

// Given the TPEG-Binary representation of an OpenLR location reference,
val tpegBinaryOpenLRReference: Array[Byte] =
  Array(0x08, 0x28, 0x01, 0x10, 0x02, 0x24, 0x23, 0x09, 0x9B, 0x42, 0x25, 0x4F, 0xE2, 0x00,
        0x09, 0x05, 0x04, 0x05, 0x03, 0x27, 0x00, 0x0A, 0x04, 0x03, 0x05, 0x76, 0x00, 0x00,
        0xA0, 0x00, 0x29, 0x00, 0x09, 0x05, 0x04, 0x05, 0x03, 0xB3, 0x00, 0x01, 0x02, 0x00).map(
    _.toByte)

// We can use a BinaryMarshaller to unmarshall the reference.
val reference: OpenLRLocationReference =
  BinaryMarshallers.openLRLocationReference.unmarshall(
    new ByteArrayInputStream(tpegBinaryOpenLRReference))

// Now we can use the reference object. Here we just use its string representation.
System.out.println(reference)
import com.here.platform.location.tpeg2.javadsl.BinaryMarshallers;
import com.here.platform.location.tpeg2.olr.OpenLRLocationReference;

import java.io.ByteArrayInputStream;
// Given the TPEG-Binary representation of an OpenLR location reference,
final byte[] tpegBinaryOpenLRReference =
    new byte[] {
      (byte) 0x08, (byte) 0x28, (byte) 0x01, (byte) 0x10, (byte) 0x02, (byte) 0x24, (byte) 0x23,
      (byte) 0x09, (byte) 0x9B, (byte) 0x42, (byte) 0x25, (byte) 0x4F, (byte) 0xE2, (byte) 0x00,
      (byte) 0x09, (byte) 0x05, (byte) 0x04, (byte) 0x05, (byte) 0x03, (byte) 0x27, (byte) 0x00,
      (byte) 0x0A, (byte) 0x04, (byte) 0x03, (byte) 0x05, (byte) 0x76, (byte) 0x00, (byte) 0x00,
      (byte) 0xA0, (byte) 0x00, (byte) 0x29, (byte) 0x00, (byte) 0x09, (byte) 0x05, (byte) 0x04,
      (byte) 0x05, (byte) 0x03, (byte) 0xB3, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x00
    };

// We can use a BinaryMarshaller to unmarshall the reference.
final OpenLRLocationReference reference =
    BinaryMarshallers.openLRLocationReference()
        .unmarshall(new ByteArrayInputStream(tpegBinaryOpenLRReference));

// Now we can use the reference object. Here we just use its string representation.
System.out.println(reference);

For debugging purposes, you can also unmarshall them from TPEG-ML as follows:

import com.here.platform.location.tpeg2.{BinaryMarshallers, XmlMarshallers}

import java.io.{ByteArrayInputStream, ByteArrayOutputStream}
import java.nio.charset.StandardCharsets

// Given the TPEG-ML representation of an OpenLR location reference,
val tpegML =
  """|<?xml version="1.0" encoding="utf-8" standalone="yes"?>
     |<olr:OpenLRLocationReference xmlns:olr="http://www.tisa.org/TPEG/OLR_0_1">
     |    <olr:version>1.0</olr:version>
     |    <olr:locationReference>
     |        <olr:optionPointAlongLineLocationReference>
     |            <olr:pointAlongLine>
     |                <olr:first>
     |                    <olr:coordinate>
     |                        <olr:longitude>629570</olr:longitude>
     |                        <olr:latitude>2445282</olr:latitude>
     |                    </olr:coordinate>
     |                    <olr:lineProperties>
     |                        <olr:frc olr:table="olr001_FunctionalRoadClass" olr:code="5"/>
     |                        <olr:fow olr:table="olr002_FormOfWay" olr:code="3"/>
     |                        <olr:bearing>
     |                            <olr:value>39</olr:value>
     |                        </olr:bearing>
     |                    </olr:lineProperties>
     |                    <olr:pathProperties>
     |                        <olr:lfrcnp olr:table="olr001_FunctionalRoadClass" olr:code="5"/>
     |                        <olr:dnp>
     |                            <olr:value>118</olr:value>
     |                        </olr:dnp>
     |                        <olr:againstDrivingDirection>false</olr:againstDrivingDirection>
     |                    </olr:pathProperties>
     |                </olr:first>
     |                <olr:last>
     |                    <olr:coordinate>
     |                        <olr:longitude>160</olr:longitude>
     |                        <olr:latitude>41</olr:latitude>
     |                    </olr:coordinate>
     |                    <olr:lineProperties>
     |                        <olr:frc olr:table="olr001_FunctionalRoadClass" olr:code="5"/>
     |                        <olr:fow olr:table="olr002_FormOfWay" olr:code="3"/>
     |                        <olr:bearing>
     |                            <olr:value>179</olr:value>
     |                        </olr:bearing>
     |                    </olr:lineProperties>
     |                </olr:last>
     |                <olr:sideOfRoad olr:table="olr004_SideOfRoad" olr:code="1"/>
     |                <olr:orientation olr:table="olr003_Orientation" olr:code="2"/>
     |            </olr:pointAlongLine>
     |        </olr:optionPointAlongLineLocationReference>
     |    </olr:locationReference>
     |</olr:OpenLRLocationReference>
     |""".stripMargin

// We can use an XmlMarshaller to unmarshall the reference.
val ref = XmlMarshallers.openLRLocationReference.unmarshall(
  new ByteArrayInputStream(tpegML.getBytes(StandardCharsets.UTF_8)))

// Now we can use the reference object. Here we use a BinaryMarshaller to write it as TPEG-Binary.
val outputStream = new ByteArrayOutputStream
BinaryMarshallers.openLRLocationReference.marshall(ref, outputStream)
import com.here.platform.location.tpeg2.XmlMarshallers;
import com.here.platform.location.tpeg2.javadsl.BinaryMarshallers;
import com.here.platform.location.tpeg2.olr.OpenLRLocationReference;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
// Given the TPEG-ML representation of an OpenLR location reference,
final String tpegML =
    "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>"
        + "<olr:OpenLRLocationReference xmlns:olr=\"http://www.tisa.org/TPEG/OLR_0_1\">"
        + "    <olr:version>1.0</olr:version>"
        + "    <olr:locationReference>"
        + "        <olr:optionPointAlongLineLocationReference>"
        + "            <olr:pointAlongLine>"
        + "                <olr:first>"
        + "                    <olr:coordinate>"
        + "                        <olr:longitude>629570</olr:longitude>"
        + "                        <olr:latitude>2445282</olr:latitude>"
        + "                    </olr:coordinate>"
        + "                    <olr:lineProperties>"
        + "                        <olr:frc olr:table=\"olr001_FunctionalRoadClass\" olr:code=\"5\"/>"
        + "                        <olr:fow olr:table=\"olr002_FormOfWay\" olr:code=\"3\"/>"
        + "                        <olr:bearing>"
        + "                            <olr:value>39</olr:value>"
        + "                        </olr:bearing>"
        + "                    </olr:lineProperties>"
        + "                    <olr:pathProperties>"
        + "                        <olr:lfrcnp olr:table=\"olr001_FunctionalRoadClass\" olr:code=\"5\"/>"
        + "                        <olr:dnp>"
        + "                            <olr:value>118</olr:value>"
        + "                        </olr:dnp>"
        + "                        <olr:againstDrivingDirection>false</olr:againstDrivingDirection>"
        + "                    </olr:pathProperties>"
        + "                </olr:first>"
        + "                <olr:last>"
        + "                    <olr:coordinate>"
        + "                        <olr:longitude>160</olr:longitude>"
        + "                        <olr:latitude>41</olr:latitude>"
        + "                    </olr:coordinate>"
        + "                    <olr:lineProperties>"
        + "                        <olr:frc olr:table=\"olr001_FunctionalRoadClass\" olr:code=\"5\"/>"
        + "                        <olr:fow olr:table=\"olr002_FormOfWay\" olr:code=\"3\"/>"
        + "                        <olr:bearing>"
        + "                            <olr:value>179</olr:value>"
        + "                        </olr:bearing>"
        + "                    </olr:lineProperties>"
        + "                </olr:last>"
        + "                <olr:sideOfRoad olr:table=\"olr004_SideOfRoad\" olr:code=\"1\"/>"
        + "                <olr:orientation olr:table=\"olr003_Orientation\" olr:code=\"2\"/>"
        + "            </olr:pointAlongLine>"
        + "        </olr:optionPointAlongLineLocationReference>"
        + "    </olr:locationReference>"
        + "</olr:OpenLRLocationReference>";

// We can use an XmlMarshaller to unmarshall the reference.
final OpenLRLocationReference ref =
    XmlMarshallers.openLRLocationReference()
        .unmarshall(new ByteArrayInputStream(tpegML.getBytes(StandardCharsets.UTF_8)));

// Now we can use the reference object. Here we use a BinaryMarshaller to write it as
// TPEG-Binary.
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
BinaryMarshallers.openLRLocationReference().marshall(ref, outputStream);

For full SFW (Service Framework) streams, deserialize
TransportFrame
as follows:

import com.here.platform.location.tpeg2.sfw.TransportFrame
import com.here.platform.location.tpeg2.tec.TECMessage
import com.here.platform.location.tpeg2.{BinaryMarshallers, Tpeg2Messages}

// Given the TPEG-Binary representation of an OpenLR location reference,
val inputStream: InputStream = loadTpegBinaryTransportFrames()

// We can use a BinaryMarshaller to get an iterator that unmarshalls TransportFrames.
val transportFrames: Seq[TransportFrame] =
  BinaryMarshallers.transportFrames.unmarshall(inputStream).iterator.toSeq

// We can then use an extractor to get interesting information. E.g., here we get all TECMessages.
val tecMessages: Seq[TECMessage] =
  Tpeg2Messages(transportFrames).filterTecMessages.applicationMessages
import com.here.platform.location.tpeg2.Tpeg2Messages;
import com.here.platform.location.tpeg2.javadsl.BinaryMarshallers;
import com.here.platform.location.tpeg2.sfw.TransportFrame;
import com.here.platform.location.tpeg2.tec.TECMessage;

import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
// Given the TPEG-Binary representation of an OpenLR location reference,
final InputStream inputStream = loadTpegBinaryTransportFrames();

// We can use a BinaryMarshaller to get an iterator that unmarshalls TransportFrames.
final Iterator<TransportFrame> transportFrames =
    BinaryMarshallers.transportFrames().unmarshall(inputStream);

// We can then use an extractor to get interesting information. E.g., here we get all
// TECMessages.
final List<TECMessage> tecMessages =
    Tpeg2Messages.create(transportFrames).filterTecMessages().getApplicationMessages();