Visualize KML placemarks

A KML placemark is a feature with associated geometry. LuciadRIA represents it as KMLPlacemarkFeature. Visualizing KML data with KML placemarks on a map requires two steps:

  1. Create a KMLModel by loading a KML file.

  2. Use a KMLLayer to visualize the model.

//First decode the data into a model
const model = new KMLModel(url);

//Create a layer for the model
//The layer visualizes the KML data using the
//styling information provided in the data
const layer = new KMLLayer(model);

//Add the layer to the map
map.layerTree.addChild(layer);

You can also use a KMLCodec with any kind of Store. For example, you can use a WFSFeatureStore to connect to a WFS service returning KML data, or you can implement a custom Store that decodes KML data from a string.

The example shows how to visualize KML data using a custom Store that reads KML data directly from a string.
//StringStore is a read only store that can handle KML data from a string
class StringStore implements Store {
  private readonly content: string;
  private readonly codec: Codec;

  constructor(kmlString: string) {
    this.content = kmlString;
    this.codec = new KMLCodec();
  }

  query(_query?: any, _options?: any): Cursor | Promise<Cursor> {
    return this.codec.decode({content: this.content});
  }
}

//Instantiate the StringStore and configure a FeatureModel with it.
//Where the kmlString contains a text with KML data
const store = new StringStore(kmlString);
const model = new FeatureModel(store);

//Create a layer for the model
const layer = new KMLLayer(model as KMLModel);

//Add the layer to the map
map.layerTree.addChild(layer);

A KML network link is a feature that references a KML or KMZ file on a local or remote network. LuciadRIA represents a KML network link as KMLNetworkLinkFeature. When the KMLCodec encounters a KML network link while it’s decoding KML data, it emits a "KMLNetworkLink" event with the KMLNetworkLinkFeature instance. For your convenience, the KMLModel propagates this event from the KMLCodec. You can register a handler function that consumes this event to create a new KMLLayer for the emitted KMLNetworkLinkFeature.

You don’t get instances of KMLNetworkLinkFeature when you iterate over a Cursor returned by KMLCodec.decode().

Handle KML ground overlays

A KML ground overlay represents an image overlay draped onto a specified DrapeTarget, or floating at a certain altitude. Both the KMLCodec and the KMLModel emit the "KMLGroundOverlay" event with a KMLGroundOverlayFeature. You can register a handler function that consumes this event. In the handler, create a layer to visualize the KMLGroundOverlayFeature with createGroundOverlayLayer, which is exported from the @luciad/ria/util/KMLUtil module.

You do get instances of KMLGroundOverlayFeature when you iterate over a Cursor returned by KMLCodec.decode().

See the Draping on 3D Tiles how to page’s KML section to learn about draping possibilities.

Handle KML screen overlays

A KML screen overlay represents an image overlay that you need to add to the view instead of to the map, such as a legend or a logo. Both the KMLCodec and the KMLModel emit the "KMLScreenOverlay" event with a KMLScreenOverlayFeature. You can register a handler function that consumes this event. In the handler, create a HTMLElement to add the image to.

You don’t get instances of KMLScreenOverlayFeature when you iterate over a Cursor returned by KMLCodec.decode().

See the KML sample and Visualizing KML screen overlays to find out how to create an HTML element to add the image of the screen overlay to your view.

Example of how to handle "KMLNetworkLink", "KMLGroundOverlay", and "KMLScreenOverlay" events

The example shows how to handle the "KMLNetworkLink", "KMLGroundOverlay", and "KMLScreenOverlay" events.
//Register handlers for KML events
addListeners(parentLayer);

function addListeners(layer: KMLLayer) {
  (layer.model as KMLModel)?.on("KMLNetworkLink",
      (networkLink: KMLNetworkLinkFeature) => onKMLNetworkLinkEvent(networkLink, layer));
  (layer.model as KMLModel)?.on("KMLGroundOverlay",
      (groundOverlay: KMLGroundOverlayFeature) => onKMLGroundOverlayEvent(groundOverlay, layer));
  (layer.model as KMLModel)?.on("KMLScreenOverlay",
      (screenOverlay: KMLScreenOverlayFeature) => onKMLScreenOverlayEvent(screenOverlay));
}

//Handler to consume KMLNetworkLink event
function onKMLNetworkLinkEvent(networkLink: KMLNetworkLinkFeature, parentLayer: KMLLayer) {
  const {link, name, id} = networkLink.properties;
  if (link.href) {
    //Create a new KML layer that points to the network link's KML data
    const kmlLayerForNetworkLink = new KMLLayer(new KMLModel(link.href), {
      id: `${id}`,
      label: `${name}`
    });
    //Register handlers for newly created KML layer
    addListeners(kmlLayerForNetworkLink);
    //Add the layer to the map
    map.layerTree.addChild(kmlLayerForNetworkLink, "above", parentLayer);
  }
}

//Handler to consume KMLGroundOverlay event
function onKMLGroundOverlayEvent(groundOverlay: KMLGroundOverlayFeature, parentLayer: KMLLayer) {
  const {id, properties, shape} = groundOverlay;
  const {icon, name} = properties;

  if (shape && shape.bounds && icon && icon.href) {
    //Create a new layer with the referenced image
    createGroundOverlayLayer(map, groundOverlay, {
      layerOptions: {
        id: `${id}`,
        label: `${name}`
      }
    }).then(layer => {
      //Add the layer to the map
      map.layerTree.addChild(layer, "below", parentLayer);
    });
  }
}

//Handler to consume KMLScreenOverlay event
function onKMLScreenOverlayEvent(screenOverlay: KMLScreenOverlayFeature) {
  //For an example implementation see ScreenOverlay.tsx in the KML sample.
  createOverlay(screenOverlay);
}

Handle the KML data structure

KML data holds features nested in KML containers: KML document and KML folder. LuciadRIA represents those as KMLDocumentContainer and KMLFolderContainer. Both KMLDocumentContainer and KMLFolderContainer expose the children property with a value that’s an ordered array of all discovered KML features of the types KMLFolderContainer, KMLPlacemarkFeature, but also the types KMLNetworkLinkFeature, KMLGroundOverlayFeature and KMLScreenOverlayFeature.

When the KMLCodec has fully decoded the KML data, it emits a single "KMLTree" event with the top-most KML container element. Usually, that’s a KMLDocumentContainer. You can register a handler for the "KMLTree" event on the KMLCodec or KMLModel to build a user interface that reflects the hierarchical data structure.

Each KMLFeature has common properties that you can use for setting the initial states of UI components, for example:

  • open - specifies whether to show an expanded or collapsed Document or Folder.

  • visibility - specifies whether to make a feature visible or not when it’s first loaded.

  • snippet - a short description of the feature.

The example shows how to handle the "KMLTree" event to count specific features.
// Count all features in folders that contain the "Buildings" text in the "name" property
(kmlLayer.model as KMLModel)?.on("KMLTree", (treeData: KMLFeature[]) => {
  const count = treeData.reduce((result: number, feature: KMLFeature) => {
    return (feature instanceof KMLFolderContainer &&
            feature.properties.name &&
            feature.properties.name.indexOf("Buildings") > -1) ?
           result + feature.children.length :
           result
  }, 0);
  console.log("Number of features in 'Buildings' folders", count);
});

See the KML sample to find out how to create a UI component which displays the data structure of loaded KML.

Handle KML parsing errors

While decoding KML data, the KMLCodec might encounter a problem with the input data. This can be a fatal error from which the KMLCodec cannot recover, or an error for which a best effort is done by providing fallback behavior.

When a fatal error occurs, the KMLCodec will emit the "KMLFatalError" event. You can register a handler function that consumes this event on the KMLCodec or the KMLModel. Non-fatal errors and fallback behavior that occurred while decoding a KMLFeature are reported in the errorMessages property.