001/* 002 * GeoAPI - Java interfaces for OGC/ISO standards 003 * Copyright © 2003-2024 Open Geospatial Consortium, Inc. 004 * http://www.geoapi.org 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.opengis.referencing.crs; 019 020import java.util.Map; 021import java.util.List; 022import java.util.ArrayList; 023import java.util.LinkedList; 024import java.util.Collections; 025import org.opengis.referencing.cs.CoordinateSystem; 026import org.opengis.annotation.UML; 027 028import static org.opengis.annotation.Obligation.*; 029import static org.opengis.annotation.Specification.*; 030 031 032/** 033 * A <abbr>CRS</abbr> describing the position of points through two or more independent <abbr>CRS</abbr>s. 034 * Two <abbr>CRS</abbr>s are independent of each other if coordinate values in one cannot be converted or 035 * transformed into coordinate values in the other. 036 * 037 * <p>For spatial coordinates, a number of constraints exist for the construction of compound <abbr>CRS</abbr>s. 038 * For example, the <abbr>CRS</abbr>s that are combined should not contain any duplicate or redundant axes. 039 * Valid combinations include (non-exhaustive list):</p> 040 * 041 * <ul> 042 * <li>Geographic 2D + Vertical</li> 043 * <li>Geographic 2D + Engineering 1D (near vertical)</li> 044 * <li>Projected 2D + Vertical</li> 045 * <li>Projected 2D + Engineering 1D (near vertical)</li> 046 * <li>Engineering (horizontal 2D) + Vertical</li> 047 * <li>Engineering (1D linear) + Vertical</li> 048 * </ul> 049 * 050 * Any coordinate reference system (<abbr>CRS</abbr>), or any of the above listed combinations of <abbr>CRS</abbr>s, 051 * can have a {@link TemporalCRS} added. More than one temporal <abbr>CRS</abbr> may be added if these axes represent 052 * different time quantities. 053 * 054 * @author OGC Topic 2 (for abstract model and documentation) 055 * @author Martin Desruisseaux (IRD, Geomatys) 056 * @version 3.1 057 * @since 1.0 058 * 059 * @see CRSAuthorityFactory#createCompoundCRS(String) 060 * @see CRSFactory#createCompoundCRS(Map, CoordinateReferenceSystem[]) 061 */ 062@UML(identifier="CompoundCRS", specification=ISO_19111) 063public interface CompoundCRS extends CoordinateReferenceSystem { 064 /** 065 * Returns the ordered list of <abbr>CRS</abbr> components. 066 * The returned list may contain nested compound <abbr>CRS</abbr>. 067 * For a list without nesting, as required by ISO 19111, see {@link #getSingleComponents()}. 068 * 069 * <h4>Why nested compound <abbr>CRS</abbr></h4> 070 * The use of nested compound <abbr>CRS</abbr>s can avoid metadata lost when a temporal <abbr>CRS</abbr> 071 * is appended to spatial components defined in a preexisting compound <abbr>CRS</abbr>. 072 * A three-dimensional compound <abbr>CRS</abbr> has its own metadata (e.g., an <abbr>EPSG</abbr> code) 073 * that may not be found in the individual horizontal and vertical components. 074 * A flatten list of horizontal, vertical and temporal components would lost those metadata. 075 * In particular, the lost of authority code reduces the scope of the 076 * {@linkplain org.opengis.referencing.operation.CoordinateOperationAuthorityFactory#createFromCoordinateReferenceSystemCodes 077 * search for coordinate operations} that an application can do. 078 * 079 * @return the ordered list of components of this compound <abbr>CRS</abbr>. 080 * 081 * @departure generalization 082 * Added as an alternative to the association defined by ISO 19111 for resolving the problem of metadata lost. 083 * The ISO 19111 requirement is still available as the {@link #getSingleComponents()} method. 084 */ 085 List<CoordinateReferenceSystem> getComponents(); 086 087 /** 088 * Returns the ordered list of <abbr>CRS</abbr> components, none of which itself compound. 089 * If this compound <abbr>CRS</abbr> contains nested compound <abbr>CRS</abbr> components, 090 * then those components are flattened recursively in a sequence of {@link SingleCRS} objects. 091 * 092 * @return the ordered list of components of this compound <abbr>CRS</abbr>, none of which itself compound. 093 * 094 * @since 3.1 095 */ 096 @UML(identifier="componentReferenceSystem", obligation=MANDATORY, specification=ISO_19111) 097 default List<SingleCRS> getSingleComponents() { 098 var singles = new ArrayList<SingleCRS>(5); 099 flatten(singles, new LinkedList<>()); // Linked list is cheap to construct and efficient with 0 or 1 element. 100 return Collections.unmodifiableList(singles); 101 } 102 103 /** 104 * Appends recursively all single components in the given list. 105 * 106 * @param singles the list where to add single components. 107 * @param safety a safety against infinite recursive method calls. 108 * @throws IllegalStateException if recursive components are detected. 109 */ 110 private void flatten(final List<SingleCRS> singles, final List<Object> safety) { 111 for (CoordinateReferenceSystem crs : getComponents()) { 112 if (crs instanceof SingleCRS) { 113 singles.add((SingleCRS) crs); 114 } else if (crs instanceof CompoundCRS) { 115 for (Object previous : safety) { 116 if (previous == this) { 117 throw new IllegalStateException("Recursive components detected."); 118 } 119 } 120 safety.add(this); 121 ((CompoundCRS) crs).flatten(singles, safety); 122 } 123 } 124 } 125 126 /** 127 * Returns a view over all coordinate systems of this compound CRS. 128 * The returned coordinate system shall have a {@linkplain CoordinateSystem#getDimension() dimension} 129 * equals to the sum of the dimensions of all {@linkplain #getComponents() components}, 130 * and axes obtained from the coordinate system of each component in the same order. 131 * 132 * @return view over all coordinate systems of this compound CRS. 133 * 134 * @departure generalization 135 * ISO 19111 defines this method for {@link SingleCRS} only. 136 * GeoAPI declares this method in {@code CompoundCRS} as well for user convenience, 137 * because <abbr>CS</abbr> dimension and axes are commonly requested information 138 * that are still available (indirectly) for compound CRS. 139 */ 140 @Override 141 default CoordinateSystem getCoordinateSystem() { 142 return new CompoundCS(this); 143 } 144}