Introduction to Apache SIS®

Table des matières

Foreward

A geospatial information community is a collection of systems or individuals capable of exchanging their geospatial data through the use of common standards, allowing them to communicate with one another. As there are many ways to represent geospatial information, each community tends to structure this information in light of its areas of interest. This diversity complicates the task of the users of (SIS) geographic information systems by confronting them with an apparently chaotic variety of data formats and structures. The characteristics of these structures vary according to the phenomenon observed and methods of measurement, as well as the habits of the organizations producing the data. Such a variety represents an obstacle in studies that require heterogeneous combinations of data, especially when they originate in communities that are traditionally distinct. For example, a researcher studying cholera might be interested in populations of shrimp as a propagation vector of the disease. But as doctors and oceanographers are not usually in the habit of sharing their work, the participants of such a study may be limited by the effort required to convert the data.

We cannot impose a uniform format on all the collections of data, as the diversity of formats is tied to factors such as the constraints imposed by the measuring apparatus, and the statistical distribution of values. A more flexible solution is to ensure the interoperability of data across a common programming interface (API). This API is not necessarily defined in a programming language; the actual tendency is rather to define conventions that use existing web protocols, which we can translate into various programming languages. But in order for this approach to be viable, the API must be generally accepted by independent developers. In other words, the API must come as near as possible to industrial standards.

For example, one task that would benefit from a successful standardization is the accessing of relational databases. The industry has established a common language - the SQL standard - that the creators of Java have embedded in standard JDBC programming interfaces. Today, these interfaces are implemented by many software programs, both free and commercial. Like databases, methods of accessing geographic information have been standardized. In this case, however, the efforts have been more recent, and their integration in software - especially in older programs - is incomplete and not always coherent. At the time of writing, no product to our knowledge has implemented all of the specifications in their entirety. However, there are many implementations that cover a fairly large spectrum. One of these is the Apache SIS® library that is described in this document.

Apache SIS is characterized by a sustained effort to comply with standards, going so far as to participate in certain OGC projects. In general, complying with standards demands a greater effort than would be required for an isolated development, but rewards us with a double advantage: not only does it improve the interoperability of our data with that of external projects, it also points towards a robust way of elaborating the conceptual model reflected in the API. In effect, the groups of experts who conceived of the standards anticipated difficulties that sometimes escape the engineer at the beginning of a project, but which risk catching up with them before the end.

Conventions Used in This Guide

The elements defined in an information language, such as classes and methods in Java or elements in an XML document, appear in monospaced font. In order to facilitate an understanding of the relationships between Apache SIS and the standards, these elements are also represented using the following colour codes:

Standards and Norms

Most standards used by Apache SIS have been devised by the Open Geospatial Consortium (OGC), sometimes in collaboration with the International Organization for Standardization (ISO). Some ISO standards themselves become European standards via the INSPIRE Directive, or French standards via AFNOR. These standards offer two key features:

These standards are made available to the international community for free, as specifications (PDF files) or as schemas (XSD files). Standardization organizations do not create software; to obtain an implementation of these specifications, users must choose one of the compliant products available on the market, or develop their own solutions. Such voluntary compliance with these specifications allow independent communities to more easily exchange geographic information.

Besides these formal standardization organizations, there are organizations that are not officially dedicated to the creation of standards, but whose work has largely been adopted as de facto standards. In particular, the EPSG database offers numeric codes which allow the easy identification of coordinates among several thousand. This database is offered by petroleum companies that have an interest in the results of their exploration in a particular place, knowing that they don't always control the production of the maps they use. Other examples of de facto standards include GeoTIFF for data distributed on a grid (such as images), and Shapefile for vector data (such as geometric shapes).

OGC Standardization Process

The work of the OGC is done by email, teleconferences, and at in-person meetings. The OGC organizes four meetings per year, each lasting five days, and hosted by member organizations that sponsor the event (companies, universities, research centres, etc). The host continent alternates between Europe and North America, with a growing presence in Asia since 2011. These meetings are usually attended by between 50 and 100 participants from among the hundreds of members of the OGC. Some participants are present at almost all the meetings, forming the pillars of the organization. The meetings of the OGC offer opportunities for exchange among members from diverse backgrounds.

The creation of a OGC standard begins with a gathering of organizations or individuals with a common interest in an issue. A working group is proposed as a Discussion Working Group (DWG) if the work is still in the exploratory stages, or as a Standard Working Group (SWG) if the work of standardization is ready to proceed. DWGs are open to all members of the OGC, while SWGs require that their participants enter into an agreement not to hinder the distribution of the standard through intellectual property claims.

Standard Working Group (SWG) Procedures

In order to be accepted, a standardization project must be supported by a minimum number of members belonging to distinct organizations. These founding members draft a charter defining the objectives of the SWG, which must be approved by the Technical Committee of the OGC. Each founding member is endowed with the right to vote, with a limit of one voting member per organization. Each new member that wishes to join the SWG after its creation is granted the role of observer, and receives on request the right to vote after several months of observation.

A SWG may contain several dozen members, but the volunteers performing the bulk of the work are usually fewer. Their proposals are submitted to the entire membership of the group, who may accept them by unanimous consent. Any objections must be debated, and an alternative proposed. SWGs usually try to debate an issue until a consensus emerges rather than move ahead despite negative votes, even if those opposed are in a minority. The decisions of the group are then integrated into the specifications by a member who assumes the role of editor.

As far as possible, the working group must structure the specifications as a nucleus around which various extensions might be built. A series of tests must accompany the standard, allowing implementations to be classified by the level of test passed. There must be at least one reference implementation that passes all the tests in order to demonstrate that the standard is usable.

When the standard is judged ready, the SWG votes on a motion proposing its submission to a vote of the higher authorities of the OGC. This process takes several months. There is a faster process for approving de facto standards, but it is applied sparingly.

The Architecture Board (OAB) and the Technical Committee (TC)

All proposals for standards are first examined by the OGC Architecture Board (OAB). This board ensures that the standard conforms to the requirements of the OGC in form, modularization, and in terms of integration with other standards. If the OAB approves it, the standard is next submitted to a vote of the members of the Technical Committee (TC). This committee consists of the principal members of the OGC, and only they are capable of granting final approval. If approved, the standard is made publicly available for comments during a period of several months. At the end of this period, the SWG must examine and respond to each comment. The eventual modifications of the standard are submitted to the OAB, then the standard is published in its final form. This distribution is announced in a press release by the OGC.

Certain members of the OGC and the TC also act as liaisons with the International Organization for Standardization (ISO). Cooperation between the two organizations goes two ways: the OGC adopts the ISO standards as a foundation on which to develop new standards, and certain new OGC standards become ISO standards.

Procedure for the Submission of Proposals for Modification

All users, whether or not they are members of the Open Geospatial Consortium, may propose modifications to OGC standards. A list of current proposals for changes, along with a form for submitting new proposals, is available online. Each proposal is reviewed by the SWG.

Some working groups use other parallel systems for submissions. In particular, the GeoAPI project continues to use a JIRA task system hosted outside of the structures of the OGC. This set-up exists in part for historical reasons, and in part because developments on the GeoAPI project are done more at the level of source code rather than at the level of documents or class diagrams.

Different Types of Specifications

OGC standards are specified in several dozen documents. Each document outlines a service - for example, the transformation of coordinates. The function of each service is described by a collection of object classes and their interactions. These elements are illustrated by UML (Unified Modeling Language) diagrams in specifications called "abstracts."

Abstract specifications do not refer to any specific information language. Their concepts may be applied more or less directly to a programming language, a database or an XML schema. There is always an element of arbitrariness in the method of applying an abstract specification, given that adjustments are often necessary to take into account the constraints or conventions of the target language. For example:

At the turn of the millennium, the abstract specifications were explicitly concretized in implementation specifications. The term "implementation" is used here in the sense of all types of interfaces (Java or others) derived from UML diagrams, and not implementations in the Java sense. Such specifications exist for SQL, CORBA, COM, and Java languages. As these languages are capable of executing procedures, the specifications of this period define not only data structures, but also operations that apply to these structures.

Thereafter, enthusiasm for "Web 2.0" increased interest XML over other languages. Older implementation specifications became fell into disuse, and XSD schemas became the principle concretization of abstract specifications. Even the way abstract specifications are designed has evolved: they are less likely to define operations, and so what remains is closer to descriptions of database schemas. Some operations that were defined in older standards now appear, in another form, in web service specifications. Finally, the term "implementation specification" has been abandoned, to be subsumed under the term "OGC standard." But despite their disuse, old implementation specifications remain useful to programs in Java, because:

The Apache SIS project is based on the most recent specifications, drawing from the archives of the OGC to complete certain abstract standards or make them more usable. Some old definitions are preserved as "methods of convenience," not always bringing new functionality, but facilitating the practical use of a library.

The following table lists the principal norms used by the project. Many norms are published both as ISO standards and as OGC standards, and their corresponding names are listed next to one another in the first two columns. Standards that are depreciated but still used appear struck through. Finally, GeoAPI packets will be introduced in upcoming chapters.

Principal Standards Used in the Apache SIS project
ISO Norm OGC Norm Titre GeoAPI Packet
Abstract Specifications
ISO 19103 Conceptual schema language org.opengis.util
ISO 19115 Topic 11 Metadata org.opengis.metadata
ISO 19111 Topic 2 Spatial referencing by coordinates org.opengis.referencing
ISO 19108 Temporal Schema org.opengis.temporal
ISO 19107 Topic 1 Feature geometry org.opengis.geometry
ISO 19101 Topic 5 Features org.opengis.feature
ISO 19123 Topic 6 Schema for coverage geometry and functions org.opengis.coverage
ISO 19156 Topic 20 Observations and measurements org.opengis.observation
Implementation Specifications
ISO 19139 Metadata XML schema implementation
ISO 13249 SQL spatial
OGC 01-009 Coordinate Transformation Services org.opengis.referencing
OGC 01-004 Grid Coverage org.opengis.coverage
SLD Styled Layer Descriptor org.opengis.style
Web Services
ISO 19128 WMS Web Map Service
WMTS Web Map Tile Service
ISO 19142 WFS Web Feature Service
WCS Web Coverage Service
WPS Web Processing Service
OpenLS Location Services
SWE Sensor Web Enablement
SOS Sensor Observation Service

Definition of Terms

Standards sometimes favour the application of certain generic terms to particular contexts, which may differ from the context in which other communities use these terms. For example, the terms domain and range may apply to arbitrary functions in order to designate a set of possible values of inputs and outputs respectively. But the functions to which they are applied by certain ISO standards are not the same as the functions to which they are applied by other libraries. For example, ISO 19123 applies these terms to CV_Coverage objects, seen as functions in which the domain is the set of spatio-temporal coordinates encompassed by the data, and the range is the set of values encompassed. But UCAR's NetCDF library applies these terms instead to the function of converting pixel indices (its domain) to spatial-temporal coordinates (its range). Thus the UCAR library's range may be the domain of ISO 19123.

The Apache SIS library prefers as much as possible to use terms in the sense of OGC and ISO norms. Particular care must be taken, however, with the interfaces between SIS and certain other external libraries, in order to reduce the risk of confusion.

GeoAPI

The GeoAPI project offers a set of Java interfaces for geospatial applications. In a series of org.opengis.* packets, GeoAPI defines structures representing metadata, reference systems of coordinates and operations that perform cartographic projections. In one part that is not yet standardized - called pending - GeoAPI defines structures that represent geo-referenced images, geometries, filters that can be applied to queries, and other features. These interfaces closely follow the specifications of the OGC, while interpreting and adapting them to meet the needs of Java developers - for example, conforming with naming conventions. These interfaces benefit both client applications and libraries:

Interface Specifications

Since OGC standards are defined by well-tested software engineering methods, it is possible to automatically generate Java interfaces using relatively common tools. 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 class and method affected. 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.

Correspondences Explained in @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 level. 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 Standard 19111. The second @UML annotation, this time applied to the getCoordinateSystem() method, indicates that this method is defined using the coordinateSystem association of ISO Standard 19111, 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 introspection methods allow access to this information during the execution of an application. This is useful for displaying names for users familiar with OGC standards, or for writing elements in an XML document. The following example displays the standard name for the method getTitle() from the Citation interface:

Class<?> type   = Citation.class;
Method   method = type.getMethod("getTitle", (Class<?>[]) null);
UML      annot  = method.getAnnotation(UML.class);
String   ident  = annot.identifier();
System.out.println("The standard name for the method " + method.getName() + " is " + ident);

The class org.apache.sis.util.iso.Types provides the commodity method getStandardName(Class) to perform this operation.

The reverse operation - getting the Java class and method of a standard name - is a bit more complicated. It requires reading the class-index.properties file provided in the org.opengis.annotation package. The following example reads the files just before searching for the name of the interface corresponding to CI_Citation. Users are always encouraged to only read this file once and then save its contents in their application's cache.

Properties isoToGeoAPI = new Properties();
try (InputStream in = UML.class.getResourceAsStream("class-index.properties")) {
    isoToGeoAPI.load(in);
}
String isoName = "CI_Citation";
String geoName = getProperty(isoName);
Class<?>  type = Class.forName(geoName);
System.out.println("The GeoAPI interface ISO " + isoName + " is " + type);

The class org.apache.sis.util.iso.Types provides the commodity method forStandardName(String) to perform this operation.

Implicit Correspondences to Standard JDK

Come 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 correspondence to ISO standards is implicit. The following table describes this correspondance for ISO Standard 19103 types. Standard Java's original types are preferred when applicable, but where necessary their equivalents in the form of objects are used in order to authorize null values.

Correspondences Between ISO 19103 et JDK
Type ISO Type JDK 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 (pas d’équivalent) 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 functions of Enum while being extensible. Extensions are derived from valueOf(String) static methods, 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 hide the previously requested values.";

Importing an Implementation of the Interfaces

GeoAPI defines factories (Factory) that can create implementations of its interfaces. For example, DatumFactory provides methods that can create instances which implement the interfaces of a org.opengis.referencing.datum package. A Factory must be implemented by a geospatial library, and declared as a service as defined by by the standard java.util.ServiceLoader class. The ServiceLoader javadoc explains this procedure. In brief, the library must create a file in the META-INF/services/ directory, with a name corresponding to the complete name of an interface in the factory (in the preceding example, org.opengis.referencing.datum.DatumFactory). On one line, this text file must include the complete name of the class that implements this interface. This class may be hidden from users, as they do not need to know of its existance.

If the library has correctly declared its factories as services, users may import them by using ServiceLoader, as in the example below. This example only takes the first factory located; if there is more than one factory - for example when multiple libraries coexist - then the choice is left to the user.

import org.opengis.referencing.GeodeticDatum;
import org.opengis.referencing.DatumFactory;
import java.util.ServiceLoader;

public class MyApplication {
    public void createMyDatum() {
        ServiceLoader  loader = ServiceLoader.load(DatumFactory.class);
        DatumFactory  factory = loader.iterator().next();
        GeodeticDatum myDatum = factory.createGeodeticDatum(…);
    }
}

Providing the Correct Implementation

Implementing GeoAPI oneself in order to meet very specific needs is not difficult. A developer might concentrate on a handful of interfaces among the hundreds available, while keeping other interfaces as extension points to eventually implement as needed.

The conceptual model that the interfaces represent is complex. But this complexity may be reduced by combining certain interfaces. For example, many libraries, even well-known ones, do not distinguish between a Coordinate System (CS) and a Coordinate Reference System (CRS). A developer that also wishes not to make this distinction may implement these two interfaces with the same class. The resulting implementation may have a simpler class hierarchy than that of GeoAPI interfaces. The geoapi-examples module, discussed later, provides such combinations. The following table lists a few possible combinations:

Main Interface Auxiliary Interface Use
CoordinateReferenceSystem CoordinateSystem Description of a spatial reference system (CRS).
GeodeticDatum Ellipsoid Description of the geodetic referential.
CoordinateOperation MathTransform Coordinate transformation operations.
IdentifiedObject ReferenceIdentifier An objet (usually a CRS) that we can identify by a code.
Citation InternationalString Bibliographic reference consisting of a simple title.
GeographicBoundingBox Extent Spatial area in degrees longitude and latitude.
ParameterValue ParameterDescriptor Description of a parameter (name, type) associated with its value.
ParameterValueGroup ParameterDescriptorGroup Description of a set of parameters associated with their values.

GeoAPI Modules

The GeoAPI project consists of a standardized part (geoapi) and an experimental part (geoapi-pending). As these two parts are mutually exclusive, users must take care not to mix them in the same project. This separation is guaranteed for all projects that depend only on the Maven central repository (including the final versions of Apache SIS), as the geoapi-pending module is never deployed on this central repository. By contrast, snapshots of certain SIS branches may depend on geoapi-pending.

GeoAPI modules are:

The Interfaces' Definition Modules

geoapi and geoapi-pending modules provide interfaces derived from the UML schemas of the international standards. The conceptual model will be explained in detail in the chapters describing the implementation of Apache SIS. However, we can get an overview of its content by consulting the page listing GeoAPI methods with their original standards.

The Conformance Tests Module

The geoapi-conformance module provides validators, a JUnit test suite, and report generators in the form of HTML pages. This module may be used with any GeoAPI implementation. For developers of a geospatial library, it offers the following advantages:

Validations of Instances

GeoAPI can validate an instance of its interfaces by checking that certain constraints are observed. These constraints, which may only be expressed in the method signature, are usually described textually in the abstract specifications or in the javadoc.

Example: The transformation of a coordinate (CC_CoordinateOperation) may require a sequence of several steps. In such a sequence of operations (CC_ConcatenatedOperation), for each step (CC_SingleOperation) the number of dimensions of the output must equal the number of dimensions of the input in the following operation. Expressed in Java, this constraint stipulates that for the entire index 0 < i < n where n is the number of operations, we have coordOperation[i].sourceDimensions == coordOperation[i-1].targetDimensions.

The easiest way to perform these verifications is to call the static methods validate(…) of the class org.opengis.test.Validators. As all of these methods bear the same name, it is enough to write “validate(value)” and then allow the compiler to choose the most appropriate method for the type of object given in the argument. If the object type is not known at the time of compilation, the method dispatch(Object) may redirect the work to the appropriate validate(…) method.

All validate(…) functions follow a chain of dependencies, meaning that they will also validate each component of the object to be validated. For example, the validation of a GeographicCRS implies the validation of its component GeodeticDatum, which itself implies the validation of its component Ellipsoid, and so on. Thus it is useless to validate the components themselves, unless you wish to isolate the test for a particular item known to cause problems.

By default, validations are as strict as possible. It is always possible to relax certain rules. The most common is to tolerate the absence of attributes that would normally be mandatory. This rule and a few others may be modified globally for all tests executed by the standard JVM, as in the following example:

import org.opengis.metadata.Metadata;
import org.opengis.test.Validators;
import org.junit.Test;

public class MyTest {
    /*
     * Tolerate the absence of mandatory attributes in metadata and citation packages.
     * This modification applies to all tests executed by the standard JVM.
     * If there are multiple test classes, this initialization may be performed
     * in a parent class to all test classes.
     */
    static {
        Validators.DEFAULT.metadata.requireMandatoryAttributes = false;
        Validators.DEFAULT.citation.requireMandatoryAttributes = false;
    }

    @Test
    public void testMyMetadata() {
        Metadata myObject = …; // Create an object here.
        Validators.validate(myObject);
    }
}

Rules may also be modified for a particular test suite without affecting the default configuration of the standard JVM. This approach requires the creation of a new instance of the validator that we wish to modify the configuration.

import org.opengis.metadata.Metadata;
import org.opengis.test.ValidatorContainer;
import org.junit.Test;

public class MyTest {
    private final ValidatorContainer validators;

    public MyTest() {
        validators = new ValidatorContainer();
        validators.metadata.requireMandatoryAttributes = false;
        validators.citation.requireMandatoryAttributes = false;
    }

    @Test
    public void testMyMetadata() {
        Metadata myObject = …; // Create an object here.
        validators.validate(myObject);
    }
}

Executing Pre-defined Tests

JUnit tests are defined in the org.opengis.test sub-packages. All test classes bear a name ending in "Test". In addition, an org.opengis.test.TestSuite class includes all test classes defined in the geoapi-conformance module. One way to execute all of these tests at once is to create a sub-class of TestSuite, perhaps including a static block performing a configuration of validators as in the previous example, and inherit the tests defined in GeoAPI.

Apache SIS inherits GeoAPI's *Test classes on a case-by-case basis, in the appropriate modules. The TestSuite class is used instead in an integration test encompassing all SIS modules. The example below gives an example of a personalizaed GeoAPI test: The parent test javadoc documents the tests performed in detail. In this example, only one test is modified and all the others are inherited as they are (it is not necessary to repeat them in the sub-class). However, this example adds a supplemental verrification, annotated with @After, which will be executed after each test.

import org.junit.*;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.opengis.test.referencing.ParameterizedTransformTest;
import static org.junit.Assert.*;

@RunWith(JUnit4.class)
public class MyTest extends ParameterizedTransformTest {
    /**
     * Specify our own coordinate transformation factory for the GeoAPI tests.
     * GeoAPI will test the objects created by this factory. 
     */
    public MyTest() {
        super(new MyMathTransformFactory());
    }

    /**
     * Changes the behaviour of a test. This example relaxes the requirements of this test a little,  
     * by accepting errors of up to 10 centimetres, rather than the default value of 1 cm.  
     * This change only applies to this method, and does not affect the other inherited tests.  
     */
    @Test
    @Override
    public void testLambertAzimuthalEqualArea() throws FactoryException, TransformException {
        tolerance = 0.1; // Tolérance de 10 cm.
        super.testLambertAzimuthalEqualArea();
    }

    /**
     * Supplemental verification performed after each test, inherited or not.  
     * In this example, we are verifying that the transformation tested 
     * works correctly in two-dimensional spaces.  
     */
    @After
    public void ensureAllTransformAreMath2D() {
        assertTrue(transform instanceof MathTransform2D);
    }
}

Example Modules

The geoapi-examples module provides examples of simple implementations. Plusieurs de ces classes implémentent plus d’une interface à la fois afin de proposer un modèle conceptuel plus simple. La Javadoc de ce module énumère les paquets et classes clés avec les combinaisons effectuées. Ce module illustre non-seulement comment GeoAPI peut-être implémenté, mais aussi comment l’implémentation peut être testée en utilisant geoapi-conformance.

Bien que sa mission première soit de servir d’inspiration aux implémenteurs, geoapi-examples a tout-de-même été conçu de manière à être utilisable par des applications ayant des besoins très simples. Tous les exemples étant dans le domaine publique, les développeurs sont invités à adapter librement des copies de ces classes si nécessaires. Toutefois si des modifications sont apportées hors du cadre du projet GeoAPI, le bon usage veut que les copies modifiées soient placées dans un paquet portant un autre nom que org.opengis.

Pour des besoins un peu plus poussés, les développeurs sont invités à examiner les modules geoapi-proj4 et geoapi-netcdf. Ces deux modules fournissent des exemples d’adaptateurs permettant d’utiliser, via les interfaces de GeoAPI, une partie des fonctionnalités de bibliothèques externes (Proj.4 et NetCDF). L’avantage de passer par ces interfaces est de disposer d’un modèle unifié pour exploiter deux API très différents, tout en se gardant la possibilité de basculer plus facilement à une autre bibliothèque si désiré.

Représentation des objets en XML

Les objets définis par les standards OGC/ISO doivent pouvoir être échangés sur internet entre des machines distantes, utilisant des logiciels différents écrits dans des langages différents. Quelques uns des formats les plus connus sont le WKT (Well Known Text) et le WKB (Well Known Binary). Mais le format le plus exhaustif et souvent considéré comme la référence est le XML, au point où la façon de représenter les objets ISO dans ce format fait parfois l’objet d’un standard international à part entière. Ainsi, les classes de méta-données sont décrites dans le standard ISO 19115 (une spécification dite abstraite), alors que la représentation de ces classes en XML est décrite par le standard ISO 19139.

Les différents standards OGC/ISO n’emploient pas tous la même stratégie pour exprimer les objets en XML. Le standard ISO 19139 en particulier emploie une approche plus verbeuse que les autres normes, et fera l’objet d’une section particulière. Mais la plupart des formats XML ont en commun de définir des types et des attributs supplémentaires qui ne font pas partie des spécifications abstraites d’origines. Ces attributs supplémentaires sont habituellement propres au XML et peuvent ne pas apparaître directement dans l’API de Apache SIS. Certains de ces attributs, notamment id, uuid et xlink:href, restent toutefois accessibles sous forme de paires clé-valeurs.

Les documents XML peuvent employer les préfixes de leur choix, mais les préfixes suivants sont couramment employés dans la pratique. Ils apparaissent donc par défaut dans les documents produits par Apache SIS. Ces préfixes sont définis dans la classe org.apache.sis.xml.Namespaces.

Préfixes d’espaces de noms XML courants
Préfixe Espace de nom
gco http://www.isotc211.org/2005/gco
gfc http://www.isotc211.org/2005/gfc
gmd http://www.isotc211.org/2005/gmd
gmi http://www.isotc211.org/2005/gmi
gmx http://www.isotc211.org/2005/gmx
gml http://www.opengis.net/gml/3.2
xlink http://www.w3.org/1999/xlink

Représentation des méta-données selon ISO 19139

Pour chaque classe de méta-donnée, il existe un type XML nommé comme dans la spécification abstraite (par exemple gmd:MD_Metadata et gmd:CI_Citation). Tous ces types peuvent être employés comme racine d’un document XML. Ainsi, il est possible d’écrire un document représentant un objet MD_Metadata complet, ou d’écrire un document représentant seulement un objet CI_Citation.

Le standard ISO 19139 dispose le contenu de ces objets d’une manière inhabituelle: pour chaque propriété dont le type de la valeur est lui-même une autre classe du standard ISO 19139, la valeur est enveloppée dans un élément qui représente son type plutôt que d’être écrite directement. Par exemple dans un objet de type CI_Citation, la valeur de la propriété citedResponsibleParty est enveloppée dans un élément CI_Responsibility. Cette pratique double la profondeur de l’arborescence, en introduisant une duplication à tous les niveaux pour chaque valeur, comme dans l’exemple suivant:

<MD_Metadata>
  <identificationInfo>
    <MD_DataIdentification>
      <citation>
        <CI_Citation>
          <citedResponsibleParty>
            <CI_Responsibility>
              <party>
                <CI_Party>
                  <contactInfo>
                    <CI_Contact>
                      <onlineResource>
                        <CI_OnlineResource>
                          <linkage>
                            <URL>http://www.opengeospatial.org</URL>
                          </linkage>
                        </CI_OnlineResource>
                      </onlineResource>
                    </CI_Contact>
                  </contactInfo>
                </CI_Party>
              </party>
            </CI_Responsibility>
          </citedResponsibleParty>
        </CI_Citation>
      </citation>
    </MD_DataIdentification>
  </identificationInfo>
</MD_Metadata>

L’exemple précédent, comme tous les documents conformes à ISO 19139, est constitué d’une alternance systématique de deux types d’éléments XML. Il y a d’abord le nom de la propriété, qui commence toujours par une lettre minuscule (en ignorant les préfixes). Dans les API Java, chaque propriété correspond à une méthode de la classe englobante. Par exemple dans l’exemple ci-haut, gmd:identificationInfo correspond à la méthode Metadata.getIdentificationInfo(). Contrairement aux API Java toutefois, les documents XML ne placent pas les propriétés filles directement en dessous. À la place, ces éléments n’acceptent que les informations suivantes:

Sous chaque propriété se trouve le type de la valeur, sauf si elle a été remplacée par une référence (la sous-section suivante approfondira ce sujet). Le type de la valeur est un élément XML dont le nom commence toujours par une lettre majuscule, en ignorant les préfixes. Dans l’exemple ci-haut nous avions MD_DataIdentification, qui correspond à l’interface Java DataIdentification. C’est cet élément XML qui contient les propriétés filles. Cet élément accepte aussi un groupe d’attributs (notamment id et uuid) que les schémas XSD de l’OGC nomment collectivement gco:ObjectIdentification. Ces attributs n’ont pas de méthodes Java dédiées, mais sont accessibles indirectement via l’interface IdentifiedObject décrite dans la sous-section suivante.

Afin de réduire la complexité des bibliothèques, GeoAPI et Apache SIS n’exposent publiquement qu’une vision unifiée de ces deux types d’éléments. L’API public correspond essentiellement au deuxième groupe. Toutefois, lors de l’écriture d’un document XML, des éléments du premier groupe doivent être temporairement recréés. Les classes qui y correspondent sont définies dans des paquets internes de SIS. Ces classes peuvent être ignorées, sauf si le développeur souhaite implémenter ses propres classes dont les instances devront être lus et écrits par JAXB.

Identification d’instances déjà définies

L’élément englobant peut contenir un attribut id, uuid ou xlink:href. Si un de ces attributs est présent, l’élément englobé peut être complètement omis; il sera remplacé au moment de la lecture par l’élément référencé par l’attribut. Dans l’exemple suivant, la partie gauche définie un élément associé à l’identifiant “mon_id”, alors que la partie droite référence cet élément:

Le choix de l’attribut à utiliser dépend de la portée de l’élément référencé:

Dans la bibliothèque SIS, tous les objets susceptibles d’être identifiés dans un document XML implémentent l’interface org.apache.sis.xml.IdentifiedObject. Chaque instance de cette interface fournit une vue de ses identifiants sous forme de Map<Citation,String>, dans lequel la clé Citation identifie le type d’identifiant et la valeur est l’identifiant lui-même. Quelques constantes représentant différents types d’identifiants sont énumérées dans IdentifierSpace, notamment ID, UUID et HREF. Chacune de ces clés peut être associée à une valeur d’un type différent (habituellement String, UUID ou URI) selon la clé. Par exemple le code suivant définit une valeur pour l’attribut uuid:

import org.apache.sis.metadata.iso.DefaultMetadata;
import org.apache.sis.xml.IdentifierSpace;
import java.util.UUID;

public class MyClass {
    public void myMethod() {
        UUID identifier = UUID.randomUUID();
        DefaultMetadata metadata = new DefaultMetadata();
        metadata.getIdentifierMap().putSpecialized(IdentifierSpace.UUID, identifier);
    }
}

Bien que ce mécanisme aie été définit dans le but de mieux supporter les représentations des attributs XML du groupe gco:ObjectIdentification, il permet aussi de manière opportuniste de manipuler d’autres types d’identifiants. Par exemple les attributs ISBN et ISSN de Citation peuvent être manipulés de cette manière. Les méthodes de l’interface IdentifiedObject fournissent donc un endroit unique où peuvent être manipulés tous types d’identifiants (pas seulement XML) associés à un objet.

Représentation de valeurs manquantes

Lorsqu’un attribut n’est pas défini, la méthode correspondante de GeoAPI retourne généralement null. Toutefois les choses se compliquent lorsque l’attribut manquant est une valeur considérée comme obligatoire par le standard ISO 19115. Le standard ISO 19139 autorise l’omission d’attributs obligatoires à la condition d’indiquer pourquoi la valeur est manquante. Les raisons peuvent être que l’attribut ne s’applique pas (inapplicable), que la valeur existe probablement mais n’est pas connue (unknown), que la valeur pourrait ne pas exister (missing), qu’elle ne peut pas être divulguée (withheld), etc. La transmission de cette information nécessite l’utilisation d’un objet non-nul même lorsque la valeur est manquante. SIS procède en retournant un objet qui, en plus d’implémenter l’interface GeoAPI attendue, implémente aussi l’interface org.apache.xml.NilObject. Cette interface marque les instances dont toutes les méthodes retournent une collection vide, un tableau vide, null, NaN, 0 ou false, dans cet ordre de préférence selon ce que les types de retours des méthodes permettent. Chaque instance implémentant NilObject fournit une méthode getNilReason() indiquant pourquoi l’objet est nul.

Dans l’exemple suivant, la partie gauche montre un élément CI_Citation contenant un élément CI_Series, alors que dans la partie droite la série est inconnue. Si l’élément CI_Series avait été complètement omis, alors la méthode Citation.getSeries() retournerait null en Java. Mais en présence d’un attribut nilReason, l’implémentation SIS de getSeries() retournera plutôt un objet implémentant à la fois les interfaces Series et NilReason, et dont la méthode getNilReason() retournera la constante UNKNOWN.

Classes et méthodes utilitaires

Ce chapitre décrit des aspects de Apache SIS qui s’appliquent à l’ensemble de la bibliothèque. La plupart de ces utilitaires ne sont pas spécifiques aux systèmes d’information spatiales.

Modes de comparaisons des objets

Il existe différentes opinions sur la façon d’implémenter la méthode Object.equals(Object) du Java standard. Selon certains, il doit être possible de comparer différentes implémentations d’une même interface ou classe de base. Mais cette politique nécessite que chaque interface ou classe de base définisse entièrement dans sa Javadoc les critères ou calculs que doivent employer les méthodes equals(Object) et hashCode() dans toutes les implémentations. Cette approche est choisie notamment par java.util.Collection et ses interfaces filles. La transposition de cette approche aux centaines d’interfaces de GeoAPI serait toutefois une entreprise ardue, qui risquerait d’être assez peu suivie par les diverses implémentations. En outre, elle se fait au détriment de la possibilité de prendre en compte des attributs supplémentaires dans les interfaces filles si cette possibilité n’a pas été spécifiée dans l’interface parente. Cette contrainte découle des points suivants du contrat des méthodes equals(Object) et hashCode():

Par exemple ces trois contraintes sont violées si A (et éventuellement C) peuvent contenir des attributs que B ignore. Pour contourner cette difficulté, une approche alternative consiste à exiger que les objets comparés par la méthode Object.equals(Object) soient exactement de la même classe, c’est-à-dire que A.getClass() == B.getClass(). Cette approche est parfois considérée contraire aux principes de la programmation orientée objets. Dans la pratique, pour des applications relativement complexes, l’importance accordée à ces principes dépend du contexte dans lequel les objets sont comparés: si les objets sont ajoutés à un HashSet ou utilisés comme clés dans un HashMap, alors nous avons besoin d’un strict respect du contrat de equals(Object) et hashCode(). Mais si le développeur compare les objets lui-même, par exemple pour vérifier si des informations qui l’intéresse ont changées, alors les contraintes de symétrie, transitivité ou de cohérence avec les valeurs de hachages peuvent ne pas être pertinentes pour lui. Des comparaisons plus permissives peuvent être souhaitables, allant parfois jusqu’à tolérer de légers écarts dans les valeurs numériques.

Afin de donner une certaine flexibilité aux développeurs, un grand nombre de classes de la bibliothèque SIS implémentent l’interface org.apache.sis.util.LenientComparable, qui défini une méthode equals(Object, ComparisonMode). Les principaux modes de comparaisons sont:

Le mode par défaut, utilisé par les toutes les méthodes equals(Object) de SIS, est STRICT. Ce mode est choisi pour une utilisation sécuritaire — notamment avec HashMap — sans nécessiter de définitions rigoureuses des méthodes equals(Object) et hashCode() dans toutes les interfaces. Avec ce mode, l’ordre des objets (A.equals(B) ou B.equals(A)) n’a pas d’importance. C’est toutefois le seul mode à offrir cette garantie. Dans l’expression A.equals(B), le mode BY_CONTRACT (et donc par extension tous les autres modes qui en dépendent) ne comparera que les propriétés connues de A, sans se soucier de savoir si B en connaît davantage.

Internationalisation

Dans une architecture où un programme exécuté sur un serveur fournit ses données à plusieurs clients, les conventions locales du serveur ne sont pas nécessairement les mêmes que celles des clients. Les conventions peuvent différer par la langue, mais aussi par la façon d’écrire les valeurs numériques (même entre deux pays parlant la même langue) ainsi que par le fuseau horaire. Pour produire des messages conformes aux conventions du client, SIS emploie deux approches qui diffèrent par leur niveau de granularité: au niveau des messages eux-mêmes, ou au niveau des objets produisant les messages. L’approche utilisée détermine aussi s’il est possible de partager une même instance d’un objet pour toutes les langues.

Chaînes de caractères distinctes pour chaque conventions locales

Certaines classes ne sont conçues que pour fonctionner selon une convention locale à la fois. C’est évidemment le cas des implémentations standards de java.text.Format, puisqu’elles sont entièrement dédiées au travail d’internationalisation. Mais c’est aussi le cas de d’autres classes moins évidentes comme javax.imageio.ImageReader/ImageWriter ainsi que les exceptions. Lorsque une de ces classes est implémentée par SIS, nous l’identifions en implémentant l’interface org.apache.sis.util.Localized. La méthode getLocale() de cette interface permet alors de déterminer selon quelles conventions locales l’instance produira ses messages.

Certaines sous-classes de Exception définies par SIS implémentent aussi l’interface Localized. Pour ces exceptions, le message d’erreur peut être produit selon deux conventions locales selon qu’il s’adresse à l’administrateur du système ou au client: getMessage() retourne le message de l’exception selon les conventions par défaut du système, alors que getLocalizedMessage() retourne le message de l’exception selon les conventions locales spécifiées par getLocale(). Ce Locale sera lui-même déterminé par l’objet Localized qui a lancé l’exception.

Exemple: Supposons que dans un environnement où la langue par défaut serait l’anglais, un objet AngleFormat est construit pour lire des angles selon les conventions françaises. Si une ParseException est lancée lors de l’utilisation de ce formateur, alors getMessage() retournera le message d’erreur en anglais tandis que getLocalizedMessage() retournera le message d’erreur en français.

Les exceptions définies par SIS n’implémentent pas toutes l’interface Localized. Seules celles dont le message est le plus susceptible d’être montré à l’utilisateur sont ainsi localisées. Les ParseException sont de bonnes candidates puisqu’elles surviennent souvent suite à une saisie incorrecte du client. En revanche les NullPointerException sont généralement la conséquence d’une erreur de programmation; elles peuvent être localisées dans la langue par défaut du système, mais ça sera généralement tout.

La classe utilitaire org.apache.sis.util.Exceptions fournit des méthodes de commodité pour obtenir des messages selon des conventions locales données lorsque cette information est disponible.

Instance unique pour toutes les conventions locales

Les API définit par SIS ou hérités de GeoAPI privilégient plutôt l’utilisation du type InternationalString là où une valeur de type String serait susceptible d’être localisée. Cette approche permet de différer le processus d’internationalisation au moment d’obtenir une chaîne de caractères plutôt qu’au moment de construire l’objet qui les contient. C’est particulièrement utile pour les classes immutables dont les instances existent en un seul exemplaire indépendamment des conventions locales.

Exemple: Il existe dans SIS une seule instance de type OperationMethod représentant la projection de Mercator, quelle que soit la langue du client. Mais sa méthode getName() fournit (indirectement) une instance de InternationalString telle que toString(Locale.ENGLISH) retourne Mercator Projection alors que toString(Locale.FRENCH) retourne Projection de Mercator.

En définissant des objets spatiaux indépendemment des conventions locales, on réduit les risques de sur-coûts de calculs. Par exemple il est plus facile de détecter que deux cartes emploient la même projection cartographique si cette dernière est représentée par la même instance de CoordinateOperation, même si la projection porte différents noms selon les pays. En outre, certain types de CoordinateOperation peuvent nécessiter des grilles de transformation de coordonnées, ce qui accroît l’intérêt de partager une instance unique pour des raisons d’économie de mémoire.

Convention Locale.ROOT

Toutes les méthodes SIS recevant ou retournant une valeur de type Locale acceptent la valeur Locale.ROOT. Cette valeur est interprétée comme signifiant de ne pas localiser le texte. La notion de texte non-localisé est un peu fausse, puisqu’il faut bien choisir une convention de format. Mais cette convention, bien que très proche de l’anglais, sera généralement légèrement différente. Par exemple:

Traitement des caractères

Les chaînes de caractères en Java utilisent l’encodage UTF-16. Il existe une correspondance directe entre les valeurs de type char et la très grande majorité des caractères, ce qui facilite l’utilisation des chaînes lorsque ces caractères suffisent. Mais certains caractères Unicode ne sont pas représentables par un seul char. Ces caractères supplémentaires comprennent certains idéogrammes, mais aussi des symboles routiers et géographiques dans la plage 1F680 à 1F700. Le support de ces caractères supplémentaires nécessite des itérations un peu plus complexes que le cas classique où l’on supposait une correspondance directe. Ainsi, au lieu de la boucle de gauche ci-dessous, les applications internationales devraient généralement utiliser la boucle de droite:

SIS supporte les caractères supplémentaires en utilisant la boucle de droite lorsque nécessaire. Mais la boucle de gauche reste occasionnellement utilisée lorsqu’il est connu que les caractères recherchés ne sont pas des caractères supplémentaires, même si la chaîne dans laquelle on fait la recherche peut en contenir.

Interprétation des espaces blancs

Le Java standard fournit deux méthodes pour déterminer si un caractères est un espace blanc: Character.isWhitespace(…) et Character.isSpaceChar(…). Ces deux méthodes diffèrent dans leurs interprétations des espaces insécables, des tabulations et des retours à la ligne. La première méthode est conforme à l’interprétation couramment utilisée dans des langages telles que le Java, C/C++ et XML, qui considère les tabulations et retours à la ligne comme des espaces blancs, alors que les espaces insécables sont interprétés comme des caractères non-blanc. La seconde méthode — strictement conforme à la définition Unicode — fait l’interprétation inverse.

SIS emploie ces deux méthodes dans des contextes différents. isWhitespace(…) est utilisée pour séparer les éléments d’une liste (nombres, dates, mots, etc.), tandis que isSpaceChar(…) est utilisée pour ignorer les espaces blancs à l’intérieur d’un seul élément.

Exemple: Supposons une liste de nombres représentés selon les conventions françaises. Chaque nombre peut contenir des espace insécables comme séparateurs des milliers, tandis que les différents nombres de la liste peuvent être séparés par des espaces ordinaires, des tabulations ou des retours à la ligne. Pendant l’analyse d’un nombre, on veut considérer les espaces insécables comme faisant partie du nombre, alors qu’une tabulation ou un retour à la ligne indique très probablement une séparation entre ce nombre et le nombre suivant. On utilisera donc isSpaceChar(…). Inversement, lors de la séparation des nombres de la liste, on veut considérer les tabulations et les retours à la ligne comme des séparateurs mais pas les espaces insécables. On utilisera donc isWhitespace(…). Le rôle des espaces ordinaires, qui pourraient s’appliquer aux deux cas, doit être décidé en amont.

Dans la pratique, cette distinction se traduit pas une utilisation de isSpaceChar(…) dans les implémentations de java.text.Format, et une utilisation de isWhitespace(…) dans pratiquement tout le reste de la bibliothèque SIS.

Géométries

Ce chapitre introduit quelques aspects de la norme ISO 19107 (Spatial schema) et les classes de Apache SIS qui les implémentent.

Classes de base

Chaque objet géométrique est considéré comme un ensemble infini de points. En tant qu’ensemble, leurs opérations les plus fondamentales sont de même nature que les opérations standards des collections du Java. On pourrait donc voir une géométrie comme une sorte de java.util.Set dont les éléments seraient des points, à ceci près que le nombre d’éléments contenus dans cet ensemble est infini (à l’exception des géométries représentant un simple point). Pour mieux représenter ce concept, la norme ISO et GeoAPI définissent une interface TransfiniteSet que l’on peut voir comme un Set de taille infini. Bien qu’un lien de parenté existe conceptuellement entre ces interfaces, GeoAPI ne définit pas TransfiniteSet comme une sous-interface de java.util.Collection car la définition de certaines méthodes telles que size() et iterator() serait problématique. On y retrouve toutefois des méthodes très similaires telles que contains(…) et intersects(…).

La classe parente de toutes les géométries est appelée GM_Object dans la norme ISO 19107. Les interfaces de GeoAPI utilisent plutôt le nom Geometry, car l’omission du préfixe GM_ (comme le veut la convention dans GeoAPI) aurait laissé un nom trop proche de la classe Object du Java. Toutes les géométries sont des spécialisations de TransfiniteSet.

Points et positions directes

ISO 19107 définit deux types de structures pour représenter un point: GM_Point et DirectPosition. Le premier type est une véritable géométrie et peut donc être relativement lourd, selon les implémentations. Le second type n’est pas considéré formellement comme une géométrie; il n’étend ni GM_Object ni TransfiniteSet. Il ne définit pratiquement pas d’opérations autres que le stockage d’une séquence de nombres représentant une coordonnée. Il peut donc être un objet plus léger.

Afin de permettre à l’API de travailler indifféremment avec ces deux types de positions, ISO 19107 définit Position comme une union de DirectPosition et GM_Point. Il s’agit d’une union au sens du C/C++. Pour le langage Java, GeoAPI obtient le même effet en définissant Position comme l’interface parente de DirectPosition et Point. Dans la pratique, la grande majorité des API de Apache SIS travaillent sur des DirectPosition, ou occasionnellement des Position quand il semble utile d’autoriser aussi des points géométriques.

Enveloppes

Les enveloppes stockent les valeurs minimales et maximales des coordonnées d’une géométrie. Les enveloppes ne sont pas elles-mêmes des géométries; ce ne sont pas des ensembles infinis de points (TransfiniteSet). Il n’y a aucune garantie que toutes les positions contenues dans les limites d’une enveloppe soient géographiquement valides. Il faut voir les enveloppes comme une information sur les valeurs extrêmes que peuvent prendre les coordonnées d’une géométrie en faisant comme si chaque dimension était indépendante des autres, rien de plus. Nous assimilons néanmoins les enveloppes à des rectangles, cubes ou hyper-cubes (selon le nombre de dimensions) afin de faciliter la discussion, mais en gardant à l’esprit leur nature non-géométrique.

Exemple: Nous pouvons tester si une position est à l’intérieur des limites de l’enveloppe. Un résultat positif ne garantie pas que la position est à l’intérieur de la géométrie délimitée par l’enveloppe, mais un résultat négatif garantie qu’elle est à l’extérieur. De même on peut effectuer des tests d’intersections. En revanche appliquer une rotation n’a pas beaucoup de sens pour une enveloppe, car le résultat peut être très différent de celui que nous aurions obtenu en effectuant une rotation de la géométrie originale, puis en recalculant son enveloppe.

Une enveloppe peut être représentée par deux positions correspondant à deux coins opposés d’un rectangle, cube ou hyper-cube. On prend souvent comme premier coin celui dont toutes les ordonnées ont la valeur minimale (lowerCorner), et comme second coin celui dont toutes les ordonnées ont la valeur maximale (upperCorner). Lors d’un affichage utilisant un système de coordonnées classique (valeurs de l’axe des y augmentant vers le haut), ces deux positions apparaissent respectivement dans le coin inférieur gauche et dans le coin supérieur droit d’un rectangle. Attention toutefois aux différents systèmes de coordonnées, qui peuvent faire varier les positions de ces coins à l’écran. Les expressions lower corner et upper corner doivent être comprises au sens mathématique plutôt que visuel.

Enveloppes traversant l’antiméridien

Les minimums et maximums sont les valeurs les plus souvent assignées aux lowerCorner et upperCorner. Mais les choses se compliquent dans le cas d’une enveloppe traversant l’antiméridien (-180° ou 180° de longitude). Par exemple, une enveloppe de 10° de largeur peut commencer à 175° de longitude et se terminer à -175°. Dans ce cas, la valeur de longitude assignée au lowerCorner est supérieure à celle qui est assignée à l’upperCorner. Apache SIS emploie donc une définition légèrement différente de ces deux coins:

Lorsque l’enveloppe ne traverse par l’antiméridien, ces deux définitions sont équivalentes à la sélection des valeurs minimales et maximales respectivement. C’est le cas du rectangle vert dans la figure ci-dessous. Lorsque l’enveloppe traverse l’antiméridien, les coins lowerCorner et upperCorner apparaissent encore en bas et en haut du rectangle (en supposant un système de coordonnées classique), donc leurs noms restent appropriés d’un point de vue visuel. Mais les positions gauche et droite sont interchangées. Ce cas est représenté par le rectangle rouge dans la figure ci-dessous.

Exemples d’enveloppes avec et sans croisement de l’antiméridien.

Les notions d’inclusion et d’intersection s’interprètent toutefois de manière légèrement différente dans ces deux cas. Dans le cas habituel où l’on ne traverse par l’antiméridien, le rectangle vert délimite bien une région d’inclusion. Les régions exclues de ce rectangle se propagent à l’infini dans toutes les directions. En d’autres mots, la région d’inclusion n’est pas répétée tous les 360°. Mais dans le cas du rectangle rouge, l’information fournie par l’enveloppe délimite plutôt la région d’exclusion qui se trouve entre les deux bords du rectangle. La région d’inclusion se propage à l’infini des côtés gauche et droit. Nous pourrions stipuler que toute longitude inférieure à -180° ou supérieure à 180° est considérée exclue, mais ça serait une décision arbitraire qui ne serait pas un reflet symétrique du cas habituel (rectangle vert). Un développeur pourrait vouloir utiliser ces valeurs, par exemple dans une mosaïque où la carte du monde est répétée plusieurs fois horizontalement sans pour autant les confondre. Si un développeur souhaite effectuer des opérations comme si les régions d’inclusions ou d’exclusions étaient répétées tous les 360°, alors il doit lui-même ramener ses valeurs de longitudes entre -180° et 180° au préalable. Toutes les fonctions add(…), contains(…), intersect(…), etc. de toutes les enveloppes définies dans le paquet org.apache.sis.geometry effectuent leurs calculs selon cette convention.

Pour que les fonctions telles que add(…) fonctionnent correctement, tous les objets impliqués doivent utiliser le même système de référence des coordonnées, y compris la même plage de valeurs. Ainsi, une enveloppe exprimant les longitudes dans la plage [-180 … +180]° n’est pas compatible avec une enveloppe exprimant les longitudes dans la plage [0 … 360]°. Les conversions, si nécessaires, sont à la charge de l’utilisateur (la classe Envelopes fournit des méthodes de commodités pour ce faire). En outre, les coordonnées de l’enveloppe doivent être comprises dans les limites du système de coordonnées, sauf si le développeur souhaite volontairement considérer (par exemple) 300° de longitude comme un position distincte de -60°. La classe GeneralEnvelope fournit une méthode normalize() pour ramener les coordonnées dans les limites attendues, au prix parfois de valeurs lower supérieures à la valeur upper.

Couvertures de données (Coverages)

Les images, souvent nommées rasters en anglais, sont des cas particuliers d’une structure de données appelée coverages. On pourrait traduire ce terme anglais par « couverture de données ». Le titre du standard les décrivant, “Coverage geometry and functions” (ISO 19123), résume bien les deux éléments essentiels des couvertures de données:

Les caractéristiques du domaine spatial sont définies par le standard ISO 19123, alors que les caractéristiques du range ne font pas parties du standard. Le standard mentionne simplement que les ranges peuvent être finis ou infinis, et ne sont pas nécessairement numériques. Par exemple les valeurs retournées par une couverture peuvent provenir d’une énumération (« ceci est une forêt », « ceci est un lac », etc.). Toutefois, le standard définit deux grands types de couvertures qui ont un impact sur les types de ranges autorisés: les couvertures discrètes et les couvertures continues. Présentées simplement, les couvertures continues sont des fonctions pouvant utiliser des méthodes d’interpolations. Or, les interpolations n’étant possibles qu’avec des valeurs numériques, les ranges de valeurs non-numériques ne peuvent être utilisés qu’avec des couvertures de type CV_DiscreteCoverage. En revanche, les ranges de valeurs numériques peuvent être utilisés aussi avec des couvertures de type CV_ContinuousCoverage.