Keyhole Markup Language (KML) is an XML notation popularized by Google Earth and used to display geographic data for maps and scenes. KML is an extension of XML and uses a tag-based structure with nested elements and attributes. The KML file specifies a set of features (place marks, images, polygons, 3D models, textual descriptions, etc.) that can be displayed on maps in geospatial software implementing the KML encoding, include ArcGIS.
Along with geographic positioning information, KML files provide supporting content (including images and 3D models) and are often distributed in a zipped KMZ archive file. The contents of a KMZ file are a single root KML document (notionally "doc.kml") and optionally any overlays, images, icons, and COLLADA 3D models referenced in the KML, including network-linked KML files.
ArcGIS Runtime supports KML specification version 2.2 as defined by the Open GIS Consortium (OGC). Use the free Esri product ArcGIS Earth to read and write KML and KMZ files. See this Google KML Tutorial to learn more about the KML file structure, commonly used tags, and so on.
Display a KML/KMZ file
KML content is loaded using a
AGSKMLDataset
. The
AGSKMLDataset
constructor takes a URL, which can point to a local file or a network location. Loading the layer will cause the associated dataset to load. KML layers can also be loaded from portal items, which can specify a link to a file or the KML/KMZ file itself.
let url = URL(string: "https://www.arcgis.com/sharing/rest/content/items/f10b1d37fdd645c9bc9b189fb546307c/data")!
kmlDataset = AGSKMLDataset(url: url)
guard let kmlDataset = kmlDataset else { return }
kmlLayer = AGSKMLLayer(kmlDataset: kmlDataset)
Many KML files are wrappers that point to resources over the network. For example, a weather map might consist of a single network link that points to the latest forecast, to be retrieved every five minutes. As a result, loading the layer does not necessarily guarantee that the content has loaded linked content can fail to load without affecting the load status of the layer.
Explore the KML content tree
KML layers contain content in a hierarchy. You may need to programmatically explore this hierarchy to interact with KML content. For example, to turn off a screen overlay, you would need to first find it in the tree, then change its visibility. This code will follow a recursive pattern, using a function that calls itself for each node in the tree. The KML tree should be explored starting with
AGSKMLDataset
, which exposes the KML feature tree with the root
property. This provides a collection of
AGSKmlNode
objects.
The non-recursive part of turning off a screen overlay is toggling its visibility after it is found. Because you are working with a generic
AGSKmlNode
, you first need to determine whether each iterated node is a
AGSKmlScreenOverlay
. After the screen overlay is found, toggle its visibility.
func exploreKML(_ nodes: [AGSKMLNode]) {
for kmlNode in nodes {
if kmlNode is AGSKMLScreenOverlay {
kmlNode.isVisible = false
}
}
}
You should then look for screen overlay nodes that may exist deeper in the hierarchy, however. Recursively call the explore
function (shown next) on any nodes that have children.
func exploreKML(_ nodes: [AGSKMLNode]) {
for kmlNode in nodes {
if kmlNode is AGSKMLScreenOverlay {
kmlNode.isVisible = false
}
exploreKML(childNodes(of: kmlNode))
}
}
// Returns all the child nodes of this node
func childNodes(of kmlNode: AGSKMLNode) -> [AGSKMLNode] {
var children = [AGSKMLNode]()
if let container = kmlNode as? AGSKMLContainer {
children.append(contentsOf: container.childNodes)
}
if let networkLink = kmlNode as? AGSKMLNetworkLink {
children.append(contentsOf: networkLink.childNodes)
}
return children
}
Identify and popups in KML
In the ArcGIS information model, a popup is defined with a set of fields that describe an object, including how that information is formatted. Unlike ArcGIS feature services, KML files don't define a standard schema for object attributes. Instead, KML files may provide a rich HTML annotation for each object, which can be presented in place of a popup. You can access the HTML annotation via
AGSKmlNode
ballon
and then display that using a web view.
Viewpoints in KML
The OGC KML specification has two primary ways for defining viewpoints:
-
LookAt - defines a camera relative to the position of a KML feature
-
Camera - defines the position of the camera explicitly
The Runtime
AGSKmlViewpoint
class can represent both types of viewpoints. Custom code is required to convert from a KML viewpoint to an ArcGIS Runtime viewpoint, which can be used for navigating a scene. See Google's reference documentation for details, including diagrams. Earth browsing apps should respect LookAt viewpoints specified in KML content.
Play KML tours
ArcGIS Runtime supports playing tours contained in KML files. A KML tour defines a map exploration experience that can contain: narration and music, animated movement between viewpoints, and dynamic animation of KML feature properties.
To play a KML tour, create a
AGSKmlTourController
and set its tour
property to the tour you want to play. Then use the Play
, Pause
, and Restart
methods on
AGSKmlTourController
to work with the tour. If the tour contains audio, that audio will play automatically with the tour. To monitor the status of the tour, you can check to the tour
property on
AGSKmlTour
.
The following example shows a function that can search KML nodes for a tour and assign the first one it finds to a
AGSKmlTourController
.
See Touring in KML on the Google developers site to learn more about KML Tours.
// Initialize a KML tour controller to provide player controls for KML tours
kmlTourController = AGSKMLTourController()
guard let kmlTourController = kmlTourController else { return }
// Load the KML Dataset to obtain and play its first tour
kmlDataset.load {error in
if let error = kmlDataset.loadError {
print(error)
} else if let tour = kmlDataset.tours.first {
kmlTourController.tour = tour
kmlTourController.play()
}
}
The kml
property above is derived by creating extensions as shown in the code below.
//Note that AGSKMLDataset has been extended with 'tours' to obtain all the AGSKMLTours within the AGSKMLDataset, as follows:
private extension AGSKMLDataset {
// Returns all tours found in the dataset.
var tours: [AGSKMLTour] {
return rootNodes.reduce(into: []) { (tours, node) in
switch node {
case let tour as AGSKMLTour:
tours.append(tour)
case let container as AGSKMLContainer:
tours.append(contentsOf: container.tours)
default:
break
}
}
}
}
//AGSKMLContainer has been extended with 'tours' to obtain all the AGSKMLTours within the AGSKMLContainer, as follows:
private extension AGSKMLContainer {
// Returns all tours found in the container.
var tours: [AGSKMLTour] {
return childNodes.reduce(into: []) { (tours, node) in
switch node {
case let tour as AGSKMLTour:
tours.append(tour)
case let container as AGSKMLContainer:
tours.append(contentsOf: container.tours)
default:
break
}
}
}
}