From OGC specifications to Java interfaces

GeoAPI interfaces are sometimes generated from other files provided by OGC, like XSD files. But there is always a manual revision, and often modifications compared to automatically generated Java files. It would have been possible to automatically generate Java interfaces from OGC standards using existing tools. For example one of the most commonly-used approaches is to transform XSD schemas into Java interfaces using command line utility xjc. As this utility is included in most Java distributions (it is one of the JAXB tools), this approach is favoured by many projects found on the Internet. Other approaches use tools integrated into the Eclipse Development Environment, which is based on UML schemas rather than XSD ones. A similar approach was attempted in the early days of the GeoAPI project, but was quickly abandoned. We favor a manual approach for the following reasons:

Deviations from the standards are documented in each affected class and method. Each mention of a deviation is also collected on a single page in order to provide an overview. Since these deviations blur the relationships between the standards and certain Java interfaces, the correspondence between these languages is explained by @UML annotations and property files described in the following section.

Explicit mapping given by @UML annotations

For each class, method and constant defined by an OGC or ISO standard, GeoAPI indicates its provenance using annotations defined in the org.opengis.annotation package. In particular, the @UML annotations indicates the standard, the name of the element in that standard, and also its obligation. For example, in the following code snippet, the first @UML code indicates that the Java interface that follows (ProjectedCRS) is defined using the SC_ProjectedCRS type of ISO 19111 standard. The second @UML annotation, this time applied to the getCoordinateSystem() method, indicates that this method is defined using the coordinateSystem association of ISO 19111 standard, and that this association is mandatory — meaning, in Java, that the method is not allowed to return a null value.

package org.opengis.referencing.crs;

/**
 * A 2D coordinate reference system used to approximate the shape of the earth on a planar surface.
 */
@UML(specification=ISO_19111, identifier="SC_ProjectedCRS")
public interface ProjectedCRS extends GeneralDerivedCRS {
    /**
     * Returns the coordinate system, which must be Cartesian.
     */
    @UML(obligation=MANDATORY, specification=ISO_19111, identifier="coordinateSystem")
    CartesianCS getCoordinateSystem();
}

Java reflection methods allow access to this information during the execution of an application. This is useful for displaying UML identifiers for users familiar with OGC standards, or for writing elements in an XML document. Class org.apache.sis.util.iso.Types provides static convenience methods like getStandardName(Class) for such operations. For example the following code will display “Standard name of type org.opengis.referencing.crs.ProjectedCRS is SC_ProjectedCRS”:

Class<?> type = ProjectedCRS.class;
System.out.println("Standard name of type " + type.getName() + " is " + Types.getStandardName(type));

The Types.forStandardName(String) convenience method performs the reverse operation. Applications who want to perform those operations without SIS convenience methods can follow indications provided in a separated chapter.

Implicit mapping to standard JDK

Some classes and methods have neither an @UML annotation, nor an entry in the class-index.properties file. They are either extensions of GeoAPI, or else types defined in other libraries, such as standard JDK. In this last case, the mapping to ISO standards is implicit. The following table describes this mapping for ISO 19103 types. Java’s primitive types are preferred when applicable, but where necessary their wrappers are used in order to authorize null values.

Mapping between ISO 19103 and JDK types
ISO type JDK type Remarks
Numbers
Integer int Sometimes java.lang.Integer for optional attributes.
Integer (in some cases) long Sometimes java.lang.Long for optional attributes.
Real double Sometimes java.lang.Double for optional attributes.
Decimal java.math.BigDecimal
Number java.lang.Number
Texts
FreeText (no equivalent) See org.opengis.util.InternationalString below.
CharacterString java.lang.String Often org.opengis.util.InternationalString (see below).
LocalisedCharacterString java.lang.String
Sequence<Character> java.lang.CharSequence
Character char
Dates and hours
Date java.util.Date
Time java.util.Date
DateTime java.util.Date
Collections
Collection java.util.Collection
Bag java.util.Collection A Bag is similar to a Set without being restricted by uniqueness.
Set java.util.Set
Sequence java.util.List
Dictionary java.util.Map
KeyValuePair java.util.Map.Entry
Enumerations
Enumeration java.lang.Enum
CodeList (no equivalent) See org.opengis.util.CodeList below.
Various
Boolean boolean Sometimes java.lang.Boolean for optional attributes.
Any java.lang.Object

The nearest equivalent for CharacterString is the String class, but GeoAPI often uses the InternationalString interface, allowing the client to choose the language. For example, it is useful on a server that simultaneously provides pages in multiple languages. By returning translations when objects are used rather than at the time of their creation, we allow the SIS library to provide the same instances of Metadata or Coverage (for example) for the same data, regardless of the client’s language. Translations may be made on the fly with the help of the application’s ResourceBundle, or may be provided directly with the data (as in the case of Metadata).

An Enumeration corresponds to an Enum in Java. Both define all authorized values, without allowing the user to add any. A CodeList is similar to an enumeration, except that users may add their own items. Standard JDK does not offer this possibility. GeoAPI defines an abstract CodeList class that reproduces some of the functionality of Enum while being extensible. Extensions are made available by the valueOf(String) static method, which, in contrast to Enum, creates new instances if the name provided does not correspond to the name of an existing instance.

MediumName cdRom  = MediumName.CD_ROM;
MediumName usbKey = MediumName.valueOf("USB_KEY"); // There is no constraint on this value.
assert MediumName.valueOf("CD_ROM")  == cdRom  : "valueOf must return existing constants.";
assert MediumName.valueOf("USB_KEY") == usbKey : "valueOf must cache the previously requested values.";