blob: f5e023e46f5ca9666affb1a0bd9e178bb05585ee [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sis.referencing.cs;
import java.util.Map;
import java.util.List;
import javax.xml.bind.annotation.XmlTransient;
import org.opengis.referencing.cs.CoordinateSystem;
import org.opengis.referencing.cs.CoordinateSystemAxis;
import org.apache.sis.internal.referencing.AxisDirections;
import org.apache.sis.internal.util.UnmodifiableArrayList;
import org.apache.sis.util.ComparisonMode;
import org.apache.sis.util.Workaround;
import static java.util.Collections.singletonMap;
import static org.apache.sis.util.ArgumentChecks.*;
import static org.apache.sis.util.Utilities.deepEquals;
/**
* A coordinate system made of two or more independent coordinate systems.
*
* <table class="sis">
* <caption>Permitted associations</caption>
* <tr>
* <th>Used with CRS</th>
* <th>Permitted axis names</th>
* </tr><tr>
* <td>{@linkplain org.apache.sis.referencing.crs.DefaultCompoundCRS Compound}</td>
* <td>(not applicable)</td>
* </tr></table>
*
* <h2>Immutability and thread safety</h2>
* This class is immutable and thus thread-safe if the property <em>values</em> (not necessarily the map itself)
* and the {@link CoordinateSystemAxis} instances given to the constructor are also immutable. Unless otherwise
* noted in the javadoc, this condition holds if all components were created using only SIS factories and static
* constants.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 0.6
* @since 0.4
* @module
*/
@XmlTransient
public class DefaultCompoundCS extends AbstractCS {
/**
* Serial number for inter-operability with different versions.
*/
private static final long serialVersionUID = -5726410275278843373L;
/**
* The coordinate systems.
*/
private final List<CoordinateSystem> components;
/**
* Constructs a coordinate system from a set of properties and a sequence of coordinate systems.
* The properties map is given unchanged to the
* {@linkplain AbstractCS#AbstractCS(Map,CoordinateSystemAxis[]) super-class constructor}.
* The following table is a reminder of main (not all) properties:
*
* <table class="sis">
* <caption>Recognized properties (non exhaustive list)</caption>
* <tr>
* <th>Property name</th>
* <th>Value type</th>
* <th>Returned by</th>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td>
* <td>{@link org.opengis.metadata.Identifier} or {@link String}</td>
* <td>{@link #getName()}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.IdentifiedObject#ALIAS_KEY}</td>
* <td>{@link org.opengis.util.GenericName} or {@link CharSequence} (optionally as array)</td>
* <td>{@link #getAlias()}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td>
* <td>{@link org.opengis.metadata.Identifier} (optionally as array)</td>
* <td>{@link #getIdentifiers()}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.IdentifiedObject#REMARKS_KEY}</td>
* <td>{@link org.opengis.util.InternationalString} or {@link String}</td>
* <td>{@link #getRemarks()}</td>
* </tr>
* </table>
*
* @param properties the properties to be given to the identified object.
* @param components the set of coordinate system.
*/
public DefaultCompoundCS(final Map<String,?> properties, CoordinateSystem... components) {
super(properties, getAxes(components = clone(components)));
this.components = UnmodifiableArrayList.wrap(components);
}
/**
* Constructs a compound coordinate system from a sequence of coordinate systems.
* A default name for this CS will be inferred from the names of all specified CS.
*
* @param components the set of coordinate system.
*/
public DefaultCompoundCS(CoordinateSystem... components) {
this(components = clone(components), getAxes(components));
}
/**
* This is a work around for RFE #4093999 in Sun's bug database
* ("Relax constraint on placement of this()/super() call in constructors").
*
* @param components the coordinate systems.
*/
@Workaround(library="JDK", version="1.7")
private DefaultCompoundCS(final CoordinateSystem[] components, final CoordinateSystemAxis[] axes) {
super(singletonMap(NAME_KEY, AxisDirections.appendTo(new StringBuilder(60).append("Compound CS"), axes)), axes);
this.components = UnmodifiableArrayList.wrap(components);
}
/**
* Returns a clone of the given array, making sure that it contains only non-null elements.
*/
private static CoordinateSystem[] clone(CoordinateSystem[] components) {
ensureNonNull("components", components);
components = components.clone();
for (int i=0; i<components.length; i++) {
ensureNonNullElement("components", i, components[i]);
}
return components;
}
/**
* Returns all axes in the given sequence of components.
*/
@SuppressWarnings("ForLoopReplaceableByForEach")
private static CoordinateSystemAxis[] getAxes(final CoordinateSystem[] components) {
int count = 0;
for (int i=0; i<components.length; i++) {
count += components[i].getDimension();
}
final CoordinateSystemAxis[] axis = new CoordinateSystemAxis[count];
count = 0;
for (final CoordinateSystem c : components) {
final int dim = c.getDimension();
for (int j=0; j<dim; j++) {
axis[count++] = c.getAxis(j);
}
}
assert count == axis.length;
return axis;
}
/**
* Returns all coordinate systems in this compound CS.
*
* @return all coordinate systems in this compound CS.
*/
@SuppressWarnings("ReturnOfCollectionOrArrayField")
public List<CoordinateSystem> getComponents() {
return components; // Unmodifiable.
}
/*
* Do not override createForAxes(…) and forConvention(…) because we can not create a new DefaultCompoundCS
* without knownledge of the CoordinateSystem components to give to it. It would be possible to recursively
* invoke components[i].forConvention(…), but it would be useless to perform such decomposition here because
* DefaultCompoundCRS will need to perform its own decomposition anyway.
*
* If the user invokes DefaultCompoundCS.forConvention(…) anyway, he will get an AbstractCS instance, which
* is not that bad.
*/
/**
* Compares this coordinate system with the specified object for equality.
*
* @param object the object to compare to {@code this}.
* @param mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
* {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only
* properties relevant to coordinate transformations.
* @return {@code true} if both objects are equal.
*/
@Override
public boolean equals(final Object object, final ComparisonMode mode) {
if (!(object instanceof DefaultCompoundCS && super.equals(object, mode))) {
return false;
}
return deepEquals(components, ((DefaultCompoundCS) object).components, mode);
}
}