Java API for KML - KML in the Java world
AbstractThe 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. PurposeThe purpose of this API encompasses:
JAXB + XSD = JAKJAK 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 numberingThe 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. StructureThe 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 These elements are arranged as a class tree and organize themselves in an object-oriented hierarchy. The further right an element is, the more specialized it is. Nearly all elements derive from AbstractObject. Abstract elements are represented as dashed boxes. They are not actually used in KML documents: 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 (
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:
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, the geometry 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, Placemark, NetworkLink, ... (Figure 1). 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 (gx, xal, 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 <Data> 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), and createPoint() (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 AltitudeMode specifies 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_GROUND, ABSOLUTE, CLAMP_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 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. 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:
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.
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. 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
|
Java API for KML | @Google |














is the OpenSource platform of 