KML in the Java world

Abstract

The main goal of the Java API for KML (JAK) is to provide automatically generated full reference implementation of the KML object model defined by OGC's KML standard and Google's GX extensions. It is an object orientated API that enables the convenient and easy use of KML in existing Java environments.

This section provides a brief introduction to the most important classes and concepts of the Java API for KML. To understand how the API works, examples of the naming conventions used by this API are given. These naming conventions are applied throughout the whole API without any exceptions. They tell 99% of the API users everything that needs to be known. Use-cases serve as high-level documentation. Hence, a dry, full API specification is not required. Those interested in recieving more information on this topic can find the full reference of all exposed API elements in the documentation the Java API for KML delivered with [JAK09A].

Java API for KML should not be confused with Google's Earth API. The Earth API is actually a browser plugin that allows users to embed Google Earth into a web page and control it with JavaScript. The API implements only a subset of the features of Google Earth and has no ability to create KML.

JAK was developed with a use-case driven design approach. As a result, the code is fully tested (229 unit-tests with 2592 assertions so far). In addition, the development of JAK was influenced by the real world application.

Purpose

The purpose of this API encompasses:

  • Creating KML documents.
  • Saving KML documents (marshal).
  • Loading existing KML documents (unmarshal).
  • Editing KML documents.
  • Validating KML documents.

JAXB + XSD = JAK

JAK profits from JAXB's schema compiler and its binding framework. With its plugin mechanism, the schema compiler creates and annotates the Java classes of the Java API for KML from OGC's KML specification. As a result, the error prone adaption process of hand coding every element defined in the KML standard as its Java counterpart is no longer necessary. The plugins are used to tweak the generated code and to build the semantic model of the KML API (see Implementation). The classes that are created are then automatically annotated with JAXB's binding customizations. They implicitly reflect the underlying KML schema. Thereby JAK is able to use JAXB's marshalling and unmarshalling capabilities. Hence, there is no need to develop a custom KML parser or to serialize a graph of Java KML objects into a text file. JAXB's unmarshaller is capable of transforming even incomplete or faulty KML documents into a graph of Java objects in a fault-tolerant and flexible way. It offers the ability to convert XML data into JAXB annotated objects. In addition, JAK is able to use JAXB's validation mechanism.

Version numbering

The OGC's KML standard specifies a double numbering system (majorVersion.minorVersion) for versioning. The Java API for KML uses a triple numbering system (majorVersion.minorVersion.JavaAPIforKMLVersion) for versioning. This versioning system is reflected in the package structure of the classes. The first two of them match the official KML version number exactly. The third number reflects the revision number of Java API for KML counterpart. It only increases if the changes made to the Java API for KML are not source compatible to its previous version. Since version 2.2 of the KML specifications was passed by the OGC, the language itself is now considered stable. In turn, JAK should also be.

Structure

The Java API for KML is a direct representation of the KML specification. Everything that can be expressed in KML can be expressed with the Java API for KML, too. It is automatically generated by JAXB from OGC's KML schema specification ogckml22.xsd [KML22] and Google's GX extensions kml22gx.xsd[KML22GX]. This generation process is explained in detail in Here. It is not essential to understanding the purpose and the usage of JAK.

OGC's KML schema document has two internal references to two other XML schema documents: xAL.xsd, and atom-author-link.xsd. JAXB maps the complex elements defined in these schema files to classes as well. This is necessary because these elements might be used and referenced in KML documents. Applying the Common Closure and the Common Reuse principle, the elements of each schema file are mapped to another Java package. This results in a clean and tidy API with the classes belonging together packed together:

Each complex KML element maps to a Java class whose name is the same as the element's name. Complex elements can contain simple elements. Each simple element is a field of a Java class. Each box in Figure 1 (below) represents a complex KML element defined in OGC's KML and Google's GX schema. The name behind the -icon represents the tag name of the complex KML element, and the name behind one of the Java class symbols ( , , , and ) is its Java class name. Some KML element identifiers are not allowed in Java and had to be renamed to be valid as a Java class name, e.g. Object was renamed to AbstractObject.

Figure 1: The KML to Java mapping. Each complex KML element is mapped to a Java class.

 Only classes defined in ogckml22.xsd and kml22gx.xsd are shown. Dashed boxes are used for abstract elements.

Google Earth uses the <Placemark> element to mark a position on the earth's surface. It uses a yellow pushpin ( ) as its icon. The KML reference gives the following explanation [KML09]:

"A Placemark is a Feature with associated Geometry. In Google Earth, a Placemark appears as a list item in the Places panel. A Placemark with a Point has an icon associated with it that marks a point on the Earth in the 3D viewer." KML REFERENCE

Figure 2: An example of a Placemark element.

Figure 2 was created with this KML document:

Listing 1: A KML document that shows the usage of the Placemark element with a Point. The result is shown in Figure 2 (above).

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<kml xmlns="http://www.opengis.net/kml/2.2" >
    <Placemark>
        <name>Java User Group Hessen - JUGH!</name>
        <visibility>true</visibility>
        <open>false</open>
        <description>die Java User Group Hessen [...]</description>
        <styleUrl>styles.kml#jugh_style</styleUrl>
        <Point>
            <extrude>false</extrude>
            <altitudeMode>clampToGround</altitudeMode>
            <coordinates>9.444652669565212,51.30473589438118</coordinates>
        </Point>
    </Placemark>
</kml>

The structure of Listing 1 breaks down as follows:

  • The XML header. This is line 1 in every KML file.
  • The KML namespace declaration. This is line 2 in every KML file.
  • The Placemark object contains the following elements:
    • The name of the Placemark element (line 04). It appears in the balloon as the header.
    • visibility specifies that this Feature can be drawn in the 3D viewer when initially loaded.
    • open specifies whether this Feature appears open or closed in the side panel (line 06).
    • The description of the Placemark element. It appears in the balloon as continuous text.
    • The style assigned to this Placemark (line 08).
    • The Point marks the position on the earth's surface and contains the following elements:
      • extrude specifies whether this Point is connected to the ground with a line (line 10).
      • The altitudeMode defines how altitude components are interpreted.clampToGround indicates that an altitude specification (line 11) should be ignored.
      • The coordinates are defined in the WGS84 CRS and consist of longitude, latitude, and an optional altitude (line 12).

Further explanations of all KML elements are provided in the KML reference [KML09].

In an object-oriented way, a Placemark element is a special kind of abstract element Feature, which on the other hand is a special kind of abstract element AbstractObject (Figure 3). For instance, thegeometry content is specific to the Placemark element, while the name element is derived from the abstract element Feature and is present in all classes inheriting from this class, e.g. PhotoOverlay,PlacemarkNetworkLink, ... (Figure 1).
Figure 3 shows how the inheritance hierarchy is reflected in KML documents and as a Java class hierarchy. The background color in this figure matches the origin of an element, e.g. fields that are defined in Feature have the same background color in the class and in the KML element. It illustrates the close relationship between the KML specification and the Java API for KML.

Figure 3: The Placemark element as KML and its Java class representation. Dashed boxes are used for abstract elements.

Java API for KML offers several ways to create new instances of a complex element defined in the KML standard. Objects of complex elements can be created classically (e.g., new Placemark()). In addition, it offers a static factory, called KmlFactory. The factory defines a static method for the creation of every complex element defined in the KML standard. Each method has the prefix 'create', followed by the name of the complex element (e.g., createPlacemark()). Elements that are placed in packages other than the default KML package (gxxal, or atom) have their package name put between 'create' an its name (e.g., createGxPlaylist()):

Figure 4: An extract of the class KmlFactory that is able to return a reference to every complex element defined in the KML standard. It defines a total of 98 static create-methods.

Some of the create-methods defined in the KmlFactory need a parameter, as their signature shows (e.g., createData(String)). This is the case for every complex element with mandatory fields. Some elements are valid and well formed only if all required fields are set. To reflect this in JAK, Java's default no-arg constructor is made private for every complex element that contains mandatory fields and an overloaded constructor with the mandatory parameter(s) is offered (Listing 2) instead. The element instance can only be created if all required parameters are set.

Listing 2: The Java representation of the element. The value field is required. An instance of this class can only be created if the required field is set.

public class Data extends AbstractObject {
  @XmlElement(required = true)
  protected String value;

  /**
   * Value constructor with only mandatory fields
   * @param value required parameter
   */
  public Data(final String value) {
    this.value = value;
  }

  /**
   * Default no-arg constructor is private.
   * Use overloaded constructor instead!
   */
  @Deprecated
  private Data() {}
  ...
}

This step prevents misuse of the API. An API client is not able to create invalid KML elements. Hence, all marshalled KML documents are always correct and well formed.

Each class of JAK provides the usual Java functions setter and getter to access its private fields. For example, any Feature (such as Placemark) has a <name> that can be manipulated using these methods:

Listing 3: Get and set-methods exemplary for the Java field name (line 01-02) and the same analogous for boolean field open (line 04-05)

public String getName();
public void setName(String value);

public boolean isOpen();
public void setOpen(boolean open);

The next listing shows the basic usage of the Java API for KML. It is the Java counterpart to Listing 1:

Listing 4: The creation of a placemark element with a point programmatically with JAK.

// The all encapsulating kml element.
Kml kml = KmlFactory.createKml();
// Create <Placemark> and set values.
Placemark placemark = KmlFactory.createPlacemark();
placemark.setName("Java User Group Hessen - JUGH!");
placemark.setVisibility(true);
placemark.setOpen(false);
placemark.setDescription("die Java User Group Hessen");
placemark.setStyleUrl("styles.kml#jugh_style");

// Create <Point> and set values.
Point point = KmlFactory.createPoint();
point.setExtrude(false);
point.setAltitudeMode(AltitudeMode.CLAMP_TO_GROUND);
// Add <coordinates>9.444652669565212,51.30473589438118,0<coordinates>.
point.getCoordinates().add(new Coordinate("9.444652669565212,51.30473589438118,0"));

placemark.setGeometry(point);      // <-- point is registered at placemark ownership.
kml.setFeature(placemark);         // <-- placemark is registered at kml ownership.
kml.marshal(System.out);           // <-- Print the KML structure to the console.

The code of Listing 4 should be self-explanatory, as its structure is similar to Listing 1. As before, it demonstrates the usage of a Placemark element containing a Point element. All elements are created with their corresponding factory method: createKML() (line 02), createPlacemark() (line 04), andcreatePoint() (line 12). The returned objects are configured to the same values as shown in Listing 4.1 1, e.g. the name of the Placemark object is set with setName()-method (line 05). The only difference is found in how elements are bound to their parent element. In KML, respectively XML, child elements are nested inside their parent element, e.g. <parent><child/></parent>. In Java, a child element needs to be registered at its parent element, with the parent's set-method. For example, the instance of placemark has to be set to the kml object. This is done with the setFeature() method in line 19 ( Placemark derives from Feature as seen in Figure 1 and Figure 3). Whenever an element defined in OGC's KML specification is able to contain zero or more elements of the same type, it is represented as a list. While a <Point> technically contains only one coordinate, a <Polygon> element is able to contain more than one coordinate. A reference to the list is returned by its get-method, and objects are registered to it with its add-method as seen in line 16. This may appear unusual to plain KML authors, but it is common practice in the Java world.

In line 14, the enum type AltitudeMode.CLAMP_TO_GROUND is set to the point. The AltitudeModespecifies how altitude components in a <coordinates> element are interpreted. In the example the default value clampToGround was chosen, which indicates that any altitude specifications should be ignored. Every enumerated value defined in OGC's KML schema specification is mapped to a Java enum whose name is the same as in the specification defined. Because the fields of an enum type consist of a fixed set of constants, the names of the fields are all in uppercase letters, e.g. CLAMP_TO_GROUND,RELATIVE_TO_GROUNDABSOLUTECLAMP_TO_SEA_FLOOR, and RELATIVE_TO_SEA_FLOOR all defined in the enum type AltitudeMode. A list of all enums defined in the Java API for KML can be seen in Figure 1.

OGC's KML schema specification defines coordinates as a list of strings. Each coordinates entry is a triple consisting of values for longitude, latitude, and altitude. The values of longitude and latitude are in degrees and the value of altitude is in meters above sea level (Listing 1 line 16). In KML files, spaces separate two coordinates. Hence, no spaces are allowed between the three values that describe a coordinate:

Listing 5: Extract of a KML document that shows a list of four coordinate triples.

<coordinates>
  -122.377830,37.830445,0
  -122.377576,37.830631,0
  -122.377840,37.830642,0
  -122.377830,37.830445,0
</coordinates>

The Java API for KML treats coordinates differently. It defines coordinates as a list of
Coordinate classes (List<Coordinate> coordinates) instead as a list of strings as the specification does. The class Coordinate acts as a container for the triple consisting of values for longitude, latitude, and altitude. This is done slightly differently - the three values are stored as double values:

Figure 5: The class diagram of the Coordinate class (first used in Listing 1 line 16).

An advantage to using a double instead of a plain string representation is that the double type better reflects the real intent of these values. This makes arithmetic operation with these values easier, as the string to double conversion is done by JAK.
The Kml class offers convenience methods for marshalling and unmarshalling (Figure 6). The implementation details can be found at the Implementation site.

Figure 6: An extract of the class Kml. It offers a total of ten convenience methods: five for marshalling and five for unmarshalling.

The kml.marshal(System.out) method in Listing 4 at line 20 is one of them. It serializes the KML object graph as a KML document to the console. To be exact, the KML document shown in Listing 1 at the beginning of this chapter is created by line 20 in Listing 4.

The Kml class offers five different methods to marshal:

  • marshal()
    This is a convenience method for kml.marshal(System.out). It marshals the object graph to the console.
  • marshal(File)
    The object graph is marshalled to a File object. The object graph is not saved as a zipped .kmzfile.
  • marshal(OutputStream)
    Similar to the method above but with the exception that the object graph is marshalled to anOutputStream object instead. The object graph is not saved as a zipped .kmz file.
  • marshal(Writer)
    Similar to the method above but with the exception that the object graph is marshalled to aWriter object instead. The object graph is not saved as a zipped .kmz file.
  • marshalAsKmz(String, Kml...)
    The object graph is zipped as a .kmz file. A filename for the .kmz file is mandatory. Additional Kml objects can be placed inside the .kmz file. They are saved under their name defined inkmzFile.getFeature().getName() followed by a .kml suffix. IfkmzFile.getFeature().getName() is null an automatic incrementing default name is choosen "noFeatureNameSet"+ (missingNameCounter++) +".kml").

Additionally, the Kml class offers five unmarshal methods. They transform a given KML as a file object or string into a graph of Java objects. Since each unmarshal method returns a new Kml object, they are all static. The unmarshal(File, boolean) is able to validate the given input file automatically during unmarshalling and check if the object graph meets all constraints defined in OGC's KML schema specification.

  • unmarshal(File, boolean)
    KML given as a file object is transformed into a graph of Java objects. The boolean value indicates, whether the File object should be validated automatically during unmarshalling and be checked if the object graph meets all constraints defined in OGC's KML schema specification.
  • unmarshal(File)
    Similar to the method above but with the exception that the File object is not validated (boolean is false).
  • unmarshal(String)
    Similar to the method above but with the exception that it transforms a String into a graph of Java objects. The String is not validated (boolean is false)
  • unmarshal(InputStream)
    Similar to the method above but with the exception that it transforms a InputStream into a graph of Java objects. The String is not validated (boolean is false)
  • unmarshalFromKmz(File) : Kml[]
    Similar to the other unmarshal methods with the exception that it transforms a KMZ-file into a graph of Java objects. Since each .kmz file could contain more than one .kml file, the method returns an array of Kml objects.

These methods save a lot of boilerplate code to clients of the API and should satisfy most user requirements. As result, JAXB is hidden from the user, the API seems clean, and the entry level for new users is lowered.
It is still possible to use JAXB's own methods to marshal and unmarshal if needed (or forced):

Listing 6: Example of how to use JAXB's own marshal and unmarshal methods with the Java API for KML.

JAXBContext jc = JAXBContext.newInstance(Kml.class);

Unmarshaller u = jc.createUnmarshaller();
Kml kml = (Kml) u.unmarshal(new File("HelloKML.xml"));

Marshaller m = jc.createMarshaller();
Kml kml2 = m.marshal(kml);

Finally, each class in the Java API for KML overrides and implements the hashCode() and equals()method pair as described by Bloch [BLO08]. These are both essential methods if two objects need to be compared. The implementation details can be found Here.

Bibliography

[BLO08] JOSHUA BLOCH; Effective Java: A Programming Language Guide; Addison-Wesley (2008)
[KML09] GOOGLE CODE; KML Reference; http://code.google.com/apis/kml/documentation/kmlreference.html
[KML22] OGC; OGC KML standard; http://schemas.opengis.net/kml/2.2.0/ogckml22.xsd
[KML22GX] GOOGLE CODE; Google's Gx extension; http://code.google.com/intl/de/apis/kml/schema/kml22gx.xsd