sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1662874 - in /sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing: DeprecatedName.java provider/MapProjectionDescriptor.java provider/MapProjectionParameters.java provider/UniversalParameters.java
Date Sat, 28 Feb 2015 01:14:06 GMT
Author: desruisseaux
Date: Sat Feb 28 01:14:06 2015
New Revision: 1662874

URL: http://svn.apache.org/r1662874
Log:
Initial port of a collection of map projection parameters.

Added:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeprecatedName.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionDescriptor.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionParameters.java   (with props)
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/UniversalParameters.java   (with props)

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeprecatedName.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeprecatedName.java?rev=1662874&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeprecatedName.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeprecatedName.java [UTF-8] Sat Feb 28 01:14:06 2015
@@ -0,0 +1,66 @@
+/*
+ * 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.internal.referencing;
+
+import org.opengis.metadata.citation.Citation;
+import org.opengis.metadata.Identifier;
+import org.apache.sis.referencing.NamedIdentifier;
+
+
+/**
+ * A name which is deprecated (when associated to a given object) in the EPSG database.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ */
+public final class DeprecatedName extends NamedIdentifier {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 1792369861343798471L;
+
+    /**
+     * Creates a new deprecated name with the same authority, code, version and remarks than the identifier.
+     *
+     * @param identifier The identifier.
+     */
+    public DeprecatedName(final Identifier identifier) {
+        super(identifier);
+    }
+
+    /**
+     * Creates a new deprecated name for the given code.
+     *
+     * @param authority The authority, or {@code null} if not available.
+     * @param code      The code.
+     */
+    public DeprecatedName(final Citation authority, final String code) {
+        super(authority, code);
+    }
+
+    /**
+     * Returns {@code true} since this name is deprecated.
+     *
+     * @return {@code true}.
+     */
+    @Override
+    public boolean isDeprecated() {
+        return true;
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeprecatedName.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeprecatedName.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionDescriptor.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionDescriptor.java?rev=1662874&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionDescriptor.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionDescriptor.java [UTF-8] Sat Feb 28 01:14:06 2015
@@ -0,0 +1,144 @@
+/*
+ * 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.internal.referencing.provider;
+
+import java.util.Map;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.GeneralParameterDescriptor;
+import org.opengis.parameter.ParameterNotFoundException;
+import org.apache.sis.parameter.DefaultParameterDescriptorGroup;
+import org.apache.sis.util.CharSequences;
+
+
+/**
+ * Map projection parameters, with special processing for alternative ways to express the ellipsoid axis length
+ * and the standard parallels. Those alternative ways are non-standard; when a value is set to such alternative
+ * parameter, the value is translated to standard parameter values as soon as possible.
+ *
+ * <p>The non-standard parameters are:</p>
+ * <ul>
+ *   <li>{@code "earth_radius"} and {@code "inverse_flattening"}, which are mapped to the
+ *       {@link UniversalParameters#SEMI_MAJOR} and {@link UniversalParameters#SEMI_MINOR} parameters.</li>
+ *   <li>{@code "standard_parallel"} with an array value of 1 or 2 elements, which is mapped to
+ *       {@link UniversalParameters#STANDARD_PARALLEL_1} and
+ *       {@link UniversalParameters#STANDARD_PARALLEL_2}</li>
+ * </ul>
+ *
+ * The main purpose of this class is to supported transparently the NetCDF ways to express some parameter values.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ *
+ * @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/reference/StandardCoordinateTransforms.html">NetCDF projection parameters</a>
+ */
+final class MapProjectionDescriptor extends DefaultParameterDescriptorGroup {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -9142116135803309453L;
+
+    /**
+     * The NetCDF parameter name for the Earth radius.
+     */
+    static final String EARTH_RADIUS = "earth_radius";
+
+    /**
+     * The NetCDF parameter name for inverse flattening.
+     */
+    static final String INVERSE_FLATTENING = "inverse_flattening";
+
+    /**
+     * The NetCDF parameter name for the standard parallels.
+     */
+    static final String STANDARD_PARALLEL = "standard_parallel";
+
+    /**
+     * The OGC parameter name for the standard parallels.
+     */
+    static final String STANDARD_PARALLEL_1 = "standard_parallel_1",
+                        STANDARD_PARALLEL_2 = "standard_parallel_2";
+
+    /**
+     * A constant for the {@linkplain UniversalParameters#createDescriptorGroup factory method}
+     * method which indicate that the {@link #EARTH_RADIUS} parameter needs to be added.
+     */
+    static final int ADD_EARTH_RADIUS = 1;
+
+    /**
+     * A constant for the {@linkplain UniversalParameters#createDescriptorGroup factory method}
+     * method which indicate that the {@link #STANDARD_PARALLEL} parameter needs to be added.
+     */
+    static final int ADD_STANDARD_PARALLEL = 2;
+
+    /**
+     * Bitwise combination of {@code ADD_*} constants indicating which dynamic parameters to add.
+     */
+    final int dynamicParameters;
+
+    /**
+     * Creates a new parameter descriptor from the given properties and parameters.
+     *
+     * @param properties Names, aliases and identifiers of the parameter group.
+     * @param parameters The "real" parameters.
+     * @param dynamicParameters Bitwise combination of {@code ADD_*} constants
+     *        indicating which dynamic parameters to add.
+     */
+    MapProjectionDescriptor(final Map<String,?> properties, final ParameterDescriptor<?>[] parameters,
+            final int dynamicParameters)
+    {
+        super(properties, 1, 1, parameters);
+        this.dynamicParameters = dynamicParameters;
+    }
+
+    /**
+     * Returns the parameter descriptor for the given name. If the given name is one of the dynamic parameters,
+     * returns a descriptor for that parameter without adding it to the list of parameter values.
+     *
+     * @param  name The case insensitive name of the parameter to search for.
+     * @return The parameter for the given name.
+     * @throws ParameterNotFoundException if there is no parameter for the given name.
+     */
+    @Override
+    public GeneralParameterDescriptor descriptor(String name) throws ParameterNotFoundException {
+        name = CharSequences.trimWhitespaces(name);
+        if ((dynamicParameters & ADD_EARTH_RADIUS) != 0) {
+            if (name.equalsIgnoreCase(EARTH_RADIUS)) {
+                return UniversalParameters.EARTH_RADIUS;
+            }
+            if (name.equalsIgnoreCase(INVERSE_FLATTENING)) {
+                return UniversalParameters.INVERSE_FLATTENING;
+            }
+        }
+        if ((dynamicParameters & ADD_STANDARD_PARALLEL) != 0) {
+            if (name.equalsIgnoreCase(STANDARD_PARALLEL)) {
+                return UniversalParameters.STANDARD_PARALLEL;
+            }
+        }
+        return super.descriptor(name);
+    }
+
+    /**
+     * Returns the parameter group implementation which can handle the dynamic parameters.
+     */
+    @Override
+    public ParameterValueGroup createValue() {
+        return new MapProjectionParameters(this);
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionDescriptor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionDescriptor.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionParameters.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionParameters.java?rev=1662874&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionParameters.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionParameters.java [UTF-8] Sat Feb 28 01:14:06 2015
@@ -0,0 +1,307 @@
+/*
+ * 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.internal.referencing.provider;
+
+import javax.measure.unit.Unit;
+import org.opengis.parameter.ParameterValue;
+import org.opengis.parameter.ParameterNotFoundException;
+import org.apache.sis.parameter.DefaultParameterValue;
+import org.apache.sis.parameter.DefaultParameterValueGroup;
+import org.apache.sis.internal.referencing.Formulas;
+import org.apache.sis.internal.util.Constants;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.ArraysExt;
+
+
+/**
+ * Map projection parameters, with special processing for alternative ways to express the ellipsoid axis length
+ * and the standard parallels. See {@link MapProjectionDescriptor} for more information about those non-standard
+ * parameters.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ */
+final class MapProjectionParameters extends DefaultParameterValueGroup {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -6801091012335717139L;
+
+    /**
+     * The earth radius parameter. This parameter is computed automatically from the {@code "semi_major"}
+     * and {@code "semi_minor"} parameters. When explicitely set, this parameter value is also assigned
+     * to the {@code "semi_major"} and {@code "semi_minor"} axis lengths.
+     *
+     * @see org.apache.sis.referencing.datum.DefaultEllipsoid#getAuthalicRadius()
+     */
+    private final class EarthRadius extends DefaultParameterValue<Double> {
+        /**
+         * For cross-version compatibility. Actually instances of this class
+         * are not expected to be serialized, but we try to be a bit safer here.
+         */
+        private static final long serialVersionUID = 5848432458976184182L;
+
+        /**
+         * Creates a new parameter.
+         */
+        EarthRadius() {
+            super(UniversalParameters.EARTH_RADIUS);
+        }
+
+        /**
+         * Invoked when a new parameter value is set. This method sets both axis length to the given radius.
+         */
+        @Override
+        protected void setValue(final Object value, final Unit<?> unit) {
+            super.setValue(value, unit);   // Perform argument check.
+            final double r = (Double) value;
+            parameter(Constants.SEMI_MAJOR).setValue(r, unit);
+            parameter(Constants.SEMI_MINOR).setValue(r, unit);
+        }
+
+        /**
+         * Invoked when the parameter value is requested. Unconditionally computes the authalic radius.
+         * If an Earth radius has been explicitely specified, the result will be the same unless the user
+         * overwrote it with explicit semi-major or semi-minor axis length.
+         */
+        @Override
+        public double doubleValue() {
+            final ParameterValue<?> semiMajor = parameter(Constants.SEMI_MAJOR);
+            final ParameterValue<?> semiMinor = parameter(Constants.SEMI_MINOR);
+            double r = semiMajor.doubleValue();
+            if (semiMinor.getValue() != null) {
+                // Compute in unit of the semi-major axis.
+                r = Formulas.getAuthalicRadius(r, semiMinor.doubleValue(semiMajor.getUnit()));
+            }
+            return r;
+        }
+
+        /**
+         * Unconditionally returns the unit of the semi-major axis, which is the unit
+         * in which {@link #doubleValue()} performs its computation.
+         */
+        @Override
+        public Unit<?> getUnit() {
+            return parameter(Constants.SEMI_MAJOR).getUnit();
+        }
+
+        /**
+         * Getters other than the above {@code doubleValue()} delegate to this method.
+         */
+        @Override
+        public Double getValue() {
+            return doubleValue();
+        }
+    }
+
+    /**
+     * The inverse flattening parameter. This parameter is computed automatically from the {@code "semi_major"}
+     * and {@code "semi_minor"} parameters. When explicitly set, this parameter value is used for computing the
+     * semi-minor axis length.
+     *
+     * @see org.apache.sis.referencing.datum.DefaultEllipsoid#getInverseFlattening()
+     */
+    private final class InverseFlattening extends DefaultParameterValue<Double> {
+        /**
+         * For cross-version compatibility. Actually instances of this class
+         * are not expected to be serialized, but we try to be a bit safer here.
+         */
+        private static final long serialVersionUID = 4490056024453509851L;
+
+        /**
+         * Creates a new parameter.
+         */
+        InverseFlattening() {
+            super(UniversalParameters.INVERSE_FLATTENING);
+        }
+
+        /**
+         * Invoked when a new parameter value is set.
+         * This method computes the semi-minor axis length from the given value.
+         */
+        @Override
+        protected void setValue(final Object value, final Unit<?> unit) {
+            super.setValue(value, unit);   // Perform argument check.
+            final double ivf = (Double) value;
+            if (!Double.isNaN(ivf)) {
+                final ParameterValue<?> semiMajor = parameter(Constants.SEMI_MAJOR);
+                final ParameterValue<?> semiMinor = parameter(Constants.SEMI_MINOR);
+                final Double a = (Double) semiMajor.getValue();
+                if (a != null) {
+                    semiMinor.setValue(a * (1 - 1/ivf), semiMajor.getUnit());
+                }
+            }
+        }
+
+        /**
+         * Invoked when the parameter value is requested.
+         * Unconditionally computes the inverse flattening factor from the axis lengths.
+         */
+        @Override
+        public double doubleValue() {
+            final ParameterValue<?> semiMajor = parameter(Constants.SEMI_MAJOR);
+            final ParameterValue<?> semiMinor = parameter(Constants.SEMI_MINOR);
+            final Double a = (Double) semiMajor.getValue();
+            if (a != null && semiMinor.getValue() != null) {
+                final double b = semiMinor.doubleValue(semiMajor.getUnit());
+                return a / (a - b);
+            }
+            return Double.NaN;
+        }
+
+        /**
+         * Getters other than the above {@code doubleValue()} delegate to this method.
+         */
+        @Override
+        public Double getValue() {
+            return doubleValue();
+        }
+    }
+
+    /**
+     * The standard parallels parameter as an array of {@code double}. This parameter is computed automatically
+     * from the {@code "standard_parallel_1"} and {@code "standard_parallel_1"} standard parameters. When this
+     * non-standard parameter is explicitely set, the array elements are given to the above-cited standard parameters.
+     */
+    private final class StandardParallel extends DefaultParameterValue<double[]> {
+        /**
+         * For cross-version compatibility. Actually instances of this class
+         * are not expected to be serialized, but we try to be a bit safer here.
+         */
+        private static final long serialVersionUID = -1379566730374843040L;
+
+        /**
+         * Creates a new parameter.
+         */
+        StandardParallel() {
+            super(UniversalParameters.STANDARD_PARALLEL);
+        }
+
+        /**
+         * Invoked when a new parameter value is set. This method assign the array elements
+         * to {@code "standard_parallel_1"} and {@code "standard_parallel_1"} parameters.
+         */
+        @Override
+        @SuppressWarnings("fallthrough")
+        protected void setValue(final Object value, final Unit<?> unit) {
+            super.setValue(value, unit);   // Perform argument check.
+            double standardParallel1 = Double.NaN;
+            double standardParallel2 = Double.NaN;
+            if (value != null) {
+                final double[] values = (double[]) value;
+                switch (values.length) {
+                    default: {
+                        throw new IllegalArgumentException(Errors.format(
+                                Errors.Keys.UnexpectedArrayLength_2, 2, values.length));
+                    }
+                    case 2: standardParallel2 = values[1]; // Fallthrough
+                    case 1: standardParallel1 = values[0]; // Fallthrough
+                    case 0: break;
+                }
+            }
+            parameter(MapProjectionDescriptor.STANDARD_PARALLEL_1).setValue(standardParallel1, unit);
+            parameter(MapProjectionDescriptor.STANDARD_PARALLEL_2).setValue(standardParallel2, unit);
+        }
+
+        /**
+         * Invoked when the parameter value is requested. Unconditionally computes the array
+         * from the {@code "standard_parallel_1"} and {@code "standard_parallel_1"} parameters.
+         */
+        @Override
+        public double[] getValue() {
+            final Double standardParallel1 = (Double) parameter(MapProjectionDescriptor.STANDARD_PARALLEL_1).getValue();
+            final Double standardParallel2 = (Double) parameter(MapProjectionDescriptor.STANDARD_PARALLEL_2).getValue();
+            if (standardParallel2 == null) {
+                if (standardParallel1 == null) {
+                    return ArraysExt.EMPTY_DOUBLE;
+                }
+                return new double[] {standardParallel1};
+            }
+            return new double[] {(standardParallel1 != null) ? standardParallel1 : Double.NaN, standardParallel2};
+        }
+    }
+
+    /**
+     * The {@link EarthRadius} parameter instance, created when first needed.
+     * This is an "invisible" parameter, never shown in the {@link #values()} list.
+     */
+    private transient ParameterValue<Double> earthRadius;
+
+    /**
+     * The {@link InverseFlattening} parameter instance, created when first needed.
+     * This is an "invisible" parameter, never shown in the {@link #values()} list.
+     */
+    private transient ParameterValue<Double> inverseFlattening;
+
+    /**
+     * The {@link StandardParallel} parameter instance, created when first needed.
+     * This is an "invisible" parameter, never shown in the {@link #values()} list.
+     */
+    private transient ParameterValue<double[]> standardParallel;
+
+    /**
+     * Creates a new parameter value group. An instance of {@link MapProjectionDescriptor}
+     * is mandatory, because some method in this class will need to cast the descriptor.
+     */
+    MapProjectionParameters(final MapProjectionDescriptor descriptor) {
+        super(descriptor);
+    }
+
+    /**
+     * Returns the value in this group for the specified name. If the given name is one of the
+     * "invisible" parameters, returns a dynamic parameter view without adding it to the list
+     * of real parameter values.
+     *
+     * @param  name The case insensitive name of the parameter to search for.
+     * @return The parameter value for the given name.
+     * @throws ParameterNotFoundException if there is no parameter value for the given name.
+     */
+    @Override
+    public ParameterValue<?> parameter(String name) throws ParameterNotFoundException {
+        name = CharSequences.trimWhitespaces(name);
+        final int dynamicParameters = ((MapProjectionDescriptor) getDescriptor()).dynamicParameters;
+        if ((dynamicParameters & MapProjectionDescriptor.ADD_EARTH_RADIUS) != 0) {
+            if (name.equalsIgnoreCase(MapProjectionDescriptor.EARTH_RADIUS)) {
+                ParameterValue<?> value = earthRadius;
+                if (value == null) {
+                    value = earthRadius = new EarthRadius();
+                }
+                return value;
+            }
+            if (name.equalsIgnoreCase(MapProjectionDescriptor.INVERSE_FLATTENING)) {
+                ParameterValue<?> value = inverseFlattening;
+                if (value == null) {
+                    value = inverseFlattening = new InverseFlattening();
+                }
+                return value;
+            }
+        }
+        if ((dynamicParameters & MapProjectionDescriptor.ADD_STANDARD_PARALLEL) != 0) {
+            if (name.equalsIgnoreCase(MapProjectionDescriptor.STANDARD_PARALLEL)) {
+                ParameterValue<?> value = standardParallel;
+                if (value == null) {
+                    value = standardParallel = new StandardParallel();
+                }
+                return value;
+            }
+        }
+        return super.parameter(name);
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionParameters.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/MapProjectionParameters.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/UniversalParameters.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/UniversalParameters.java?rev=1662874&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/UniversalParameters.java (added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/UniversalParameters.java [UTF-8] Sat Feb 28 01:14:06 2015
@@ -0,0 +1,812 @@
+/*
+ * 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.internal.referencing.provider;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Arrays;
+import javax.measure.unit.SI;
+import javax.measure.unit.NonSI;
+import javax.measure.unit.Unit;
+import org.opengis.util.GenericName;
+import org.opengis.metadata.Identifier;
+import org.opengis.metadata.citation.Citation;
+import org.opengis.parameter.ParameterValueGroup;
+import org.opengis.parameter.ParameterDescriptor;
+import org.opengis.parameter.ParameterDescriptorGroup;
+import org.opengis.parameter.GeneralParameterDescriptor;
+import org.apache.sis.parameter.DefaultParameterDescriptor;
+import org.apache.sis.parameter.DefaultParameterDescriptorGroup;
+import org.apache.sis.metadata.iso.citation.Citations;
+import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.referencing.NamedIdentifier;
+import org.apache.sis.internal.referencing.DeprecatedName;
+import org.apache.sis.internal.util.Constants;
+import org.apache.sis.measure.MeasurementRange;
+import org.apache.sis.util.collection.Containers;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.ArraysExt;
+
+import static org.opengis.referencing.operation.SingleOperation.*;
+import static org.apache.sis.metadata.iso.citation.Citations.*;
+
+
+/**
+ * Collection of {@linkplain MapProjection map projection} parameters containing every names known to Apache SIS.
+ * This class can be used for lenient parsing of projection parameters, when they are not used in a way strictly
+ * compliant to their standard.
+ *
+ * <p>The same parameter may have different names according different authorities ({@linkplain Citations#EPSG EPSG},
+ * {@linkplain Citations#OGC OGC}, {@linkplain Citations#ESRI ESRI}, <cite>etc.</cite>). But in addition, the same
+ * authority may use different names for a parameter which, from a computational point of view, serves the same purpose
+ * in every projections. For example the EPSG database uses all the following names for the {@link #CENTRAL_MERIDIAN}
+ * parameter, even if the value is always used in the same way:</p>
+ *
+ * <ul>
+ *   <li>Longitude of origin</li>
+ *   <li>Longitude of false origin</li>
+ *   <li>Longitude of natural origin</li>
+ *   <li>Spherical longitude of origin</li>
+ *   <li>Longitude of projection centre</li>
+ * </ul>
+ *
+ * In every {@link MapProjection} subclass, only the official parameter names are declared.
+ * For example the {@link Mercator1SP} class uses "<cite>Longitude of natural origin</cite>"
+ * for the above-cited {@code CENTRAL_MERIDIAN} parameter, while {@link ObliqueMercator} uses
+ * "<cite>Longitude of projection centre</cite>". However not every softwares use the right
+ * parameter name with the right projection. This {@code UniversalParameters} class can be
+ * used for processing parameters which may have the wrong name.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.6
+ * @version 0.6
+ * @module
+ */
+public final class UniversalParameters extends DefaultParameterDescriptor<Double> {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -4608976443553166518L;
+
+    /**
+     * All names known to Apache SIS for the
+     * {@linkplain org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#semiMajor semi-major}
+     * parameter. This parameter is mandatory and has no default value. The range of valid values is (0 … ∞).
+     *
+     * <p>Some names for this parameter are {@code "semi_major"}, {@code "SemiMajor"} and {@code "a"}.</p>
+     *
+     * @see org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#semiMajor
+     */
+    public static final UniversalParameters SEMI_MAJOR = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(OGC,     Constants.SEMI_MAJOR),
+            new NamedIdentifier(ESRI,    "Semi_Major"),
+            new NamedIdentifier(NETCDF,  "semi_major_axis"),
+            new NamedIdentifier(GEOTIFF, "SemiMajor"),
+            new NamedIdentifier(PROJ4,   "a")
+        }, Double.NaN, 0, Double.POSITIVE_INFINITY, SI.METRE, true);
+
+    /**
+     * All names known to Apache SIS for the
+     * {@linkplain org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#semiMinor semi-minor}
+     * parameter. This parameter is mandatory and has no default value. The range of valid values is (0 … ∞).
+     *
+     * <p>Some names for this parameter are {@code "semi_minor"}, {@code "SemiMinor"} and {@code "b"}.</p>
+     *
+     * @see org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#semiMinor
+     */
+    public static final UniversalParameters SEMI_MINOR = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(OGC,     Constants.SEMI_MINOR),
+            new NamedIdentifier(ESRI,    "Semi_Minor"),
+            new NamedIdentifier(NETCDF,  "semi_minor_axis"),
+            new NamedIdentifier(GEOTIFF, "SemiMinor"),
+            new NamedIdentifier(PROJ4,   "b")
+        }, Double.NaN, 0, Double.POSITIVE_INFINITY, SI.METRE, true);
+
+    /**
+     * All names known to Apache SIS for the Earth radius parameter.
+     * This is used in some NetCDF files instead of {@link #SEMI_MAJOR} and {@link #SEMI_MINOR}.
+     * This is not a standard parameter.
+     */
+    static final ParameterDescriptor<Double> EARTH_RADIUS = createDescriptor(new NamedIdentifier[] {
+            new NamedIdentifier(NETCDF, MapProjectionDescriptor.EARTH_RADIUS)
+        }, Double.NaN, 0.0, Double.POSITIVE_INFINITY, SI.METRE, false);
+
+    /**
+     * All known names for the inverse flattening parameter.
+     * This is used in some NetCDF files instead of {@link #SEMI_MINOR}.
+     * This is not a standard parameter.
+     */
+    static final ParameterDescriptor<Double> INVERSE_FLATTENING = createDescriptor(new NamedIdentifier[] {
+            new NamedIdentifier(NETCDF, MapProjectionDescriptor.INVERSE_FLATTENING)
+        }, Double.NaN, 0.0, Double.POSITIVE_INFINITY, Unit.ONE, false);
+
+    /**
+     * All known names for the
+     * {@linkplain org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#centralMeridian
+     * central meridian} parameter.
+     * This parameter is mandatory - meaning that it appears in {@link ParameterValueGroup}
+     * even if the user didn't set it explicitly - and its default value is 0°.
+     * The range of valid values is [-180 … 180]°.
+     *
+     * <p>Some names for this parameter are {@code "Longitude of origin"}, {@code "Longitude of false origin"},
+     * {@code "Longitude of natural origin"}, {@code "Spherical longitude of origin"},
+     * {@code "Longitude of projection centre"}, {@code "Longitude_Of_Center"},
+     * {@code "longitude_of_projection_origin"}, {@code "central_meridian"}, {@code "longitude_of_central_meridian"},
+     * {@code "NatOriginLong"}, {@code "FalseOriginLong"}, {@code "ProjCenterLong"}, {@code "CenterLong"}
+     * and {@code "lon_0"}.</p>
+     *
+     * @see org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#centralMeridian
+     */
+    public static final UniversalParameters CENTRAL_MERIDIAN = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(EPSG,    "Longitude of origin"),
+            new NamedIdentifier(EPSG,    "Longitude of false origin"),
+            new NamedIdentifier(EPSG,    "Longitude of natural origin"),
+            new NamedIdentifier(EPSG,    "Spherical longitude of origin"),
+            new NamedIdentifier(EPSG,    "Longitude of projection centre"),
+            new NamedIdentifier(OGC,     "central_meridian"),
+            new NamedIdentifier(OGC,     "longitude_of_center"),
+            new NamedIdentifier(ESRI,    "Central_Meridian"),
+            new NamedIdentifier(ESRI,    "Longitude_Of_Center"),
+            new NamedIdentifier(ESRI,    "Longitude_Of_Origin"),
+            new NamedIdentifier(NETCDF,  "longitude_of_projection_origin"),
+            new NamedIdentifier(NETCDF,  "longitude_of_central_meridian"),
+            new NamedIdentifier(GEOTIFF, "NatOriginLong"),
+            new NamedIdentifier(GEOTIFF, "FalseOriginLong"),
+            new NamedIdentifier(GEOTIFF, "ProjCenterLong"),
+            new NamedIdentifier(GEOTIFF, "CenterLong"),
+            new NamedIdentifier(GEOTIFF, "StraightVertPoleLong"),
+            new NamedIdentifier(PROJ4,   "lon_0")
+        }, 0, -180, 180, NonSI.DEGREE_ANGLE, true);
+
+    /**
+     * All known names for the
+     * {@linkplain org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#latitudeOfOrigin
+     * latitude of origin} parameter.
+     * This parameter is mandatory - meaning that it appears in {@link ParameterValueGroup}
+     * even if the user didn't set it explicitly - and its default value is 0°.
+     * The range of valid values is [-90 … 90]°.
+     *
+     * <p>Some names for this parameter are {@code "Latitude of false origin"},
+     * {@code "Latitude of natural origin"}, {@code "Spherical latitude of origin"},
+     * {@code "Latitude of projection centre"}, {@code "latitude_of_center"},
+     * {@code "latitude_of_projection_origin"}, {@code "latitude_of_origin"},
+     * {@code "NatOriginLat"}, {@code "FalseOriginLat"}, {@code "ProjCenterLat"}, {@code "CenterLat"}
+     * and @code "lat_0"}.</p>
+     *
+     * @see org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#latitudeOfOrigin
+     */
+    public static final UniversalParameters LATITUDE_OF_ORIGIN;
+
+    /**
+     * All known names for the standard parallels parameter, as an array of 1 or 2 elements.
+     * This is used in some NetCDF files instead of {@link #STANDARD_PARALLEL_1} and
+     * {@link #STANDARD_PARALLEL_2}. This is not a standard parameter.
+     */
+    static final ParameterDescriptor<double[]> STANDARD_PARALLEL;
+
+    /**
+     * All known names for the standard parallel 1 parameter.
+     * This parameter is optional. The range of valid values is [-90 … 90]°.
+     *
+     * <blockquote><b>EPSG description:</b> For a conic projection with two standard parallels,
+     * this is the latitude of intersection of the cone with the ellipsoid that is nearest the pole.
+     * Scale is true along this parallel.</blockquote>
+     *
+     * <p>Some names for this parameter are {@code "Latitude of standard parallel"},
+     * {@code "Latitude of pseudo standard parallel"}, {@code "standard_parallel_1"},
+     * {@code "pseudo_standard_parallel_1"}, {@code "StdParallel1"} and {@code "lat_1"}.</p>
+     */
+    public static final UniversalParameters STANDARD_PARALLEL_1;
+
+    /**
+     * Creates the above constants together in order to share instances of identifiers
+     * that appear in both cases. Those common identifiers are misplaced for historical
+     * reasons (in the EPSG case, one of them is usually deprecated). We still need to
+     * declare them in both places for compatibility with historical data.
+     */
+    static {
+        final NamedIdentifier esri = new NamedIdentifier(ESRI, "Standard_Parallel_1");
+        final NamedIdentifier epsg = new NamedIdentifier(EPSG, "Latitude of 1st standard parallel");
+        final NamedIdentifier nc   = new NamedIdentifier(NETCDF, MapProjectionDescriptor.STANDARD_PARALLEL);
+
+        LATITUDE_OF_ORIGIN = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(EPSG,    "Latitude of false origin"),
+            new NamedIdentifier(EPSG,    "Latitude of natural origin"),
+            new NamedIdentifier(EPSG,    "Spherical latitude of origin"),
+            new NamedIdentifier(EPSG,    "Latitude of projection centre"), epsg,
+            new NamedIdentifier(OGC,     "latitude_of_origin"),
+            new NamedIdentifier(OGC,     "latitude_of_center"),
+            new NamedIdentifier(ESRI,    "Latitude_Of_Origin"),
+            new NamedIdentifier(ESRI,    "Latitude_Of_Center"), esri,
+            new NamedIdentifier(NETCDF,  "latitude_of_projection_origin"),
+            new NamedIdentifier(GEOTIFF, "NatOriginLat"),
+            new NamedIdentifier(GEOTIFF, "FalseOriginLat"),
+            new NamedIdentifier(GEOTIFF, "ProjCenterLat"),
+            new NamedIdentifier(GEOTIFF, "CenterLat"),
+            new NamedIdentifier(PROJ4,   "lat_0")
+        }, 0, -90, 90, NonSI.DEGREE_ANGLE, true);
+
+        STANDARD_PARALLEL = new DefaultParameterDescriptor<>(Collections.singletonMap(NAME_KEY, nc), 1, 1,
+            double[].class, null, null, null);
+
+        STANDARD_PARALLEL_1 = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(EPSG,    "Latitude of standard parallel"), epsg,
+            new NamedIdentifier(EPSG,    "Latitude of pseudo standard parallel"),
+            new NamedIdentifier(OGC,     MapProjectionDescriptor.STANDARD_PARALLEL_1),
+            new NamedIdentifier(OGC,     "pseudo_standard_parallel_1"),
+            new NamedIdentifier(ESRI,    "Pseudo_Standard_Parallel_1"), esri,
+            new NamedIdentifier(NETCDF,  "standard_parallel[1]"), nc, // Because this parameter is an array.
+            new NamedIdentifier(GEOTIFF, "StdParallel1"),
+            new NamedIdentifier(PROJ4,   "lat_1")
+        }, Double.NaN, -90, 90, NonSI.DEGREE_ANGLE, false);
+    }
+
+    /**
+     * All known names for the standard parallel 2 parameter.
+     * This parameter is optional. The range of valid values is [-90 … 90]°.
+     *
+     * <blockquote><b>EPSG description:</b> For a conic projection with two standard parallels,
+     * this is the latitude of intersection of the cone with the ellipsoid that is furthest from the pole.
+     * Scale is true along this parallel.</blockquote>
+     *
+     * <p>Some names for this parameter are {@code "Latitude of 2nd standard parallel"},
+     * {@code "standard_parallel_2"}, {@code "StdParallel2"} and {@code "lat_2"}.</p>
+     */
+    public static final UniversalParameters STANDARD_PARALLEL_2 = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(EPSG,    "Latitude of 2nd standard parallel"),
+            new NamedIdentifier(OGC,     MapProjectionDescriptor.STANDARD_PARALLEL_2),
+            new NamedIdentifier(ESRI,    "Standard_Parallel_2"),
+            new NamedIdentifier(NETCDF,  "standard_parallel[2]"),
+            new NamedIdentifier(GEOTIFF, "StdParallel2"),
+            new NamedIdentifier(PROJ4,   "lat_2")
+        }, Double.NaN, -90, 90, NonSI.DEGREE_ANGLE, false);
+
+    /**
+     * All known names for the {@code latitudeOf1stPoint} parameter.
+     * This parameter is mandatory and has no default value.
+     * The range of valid values is [-90 … 90]°.
+     */
+    public static final UniversalParameters LAT_OF_1ST_POINT = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(Citations.ESRI, "Latitude_Of_1st_Point")
+        }, Double.NaN, -90, 90, NonSI.DEGREE_ANGLE, true);
+
+    /**
+     * All known names for the {@code longitudeOf1stPoint} parameter.
+     * This parameter is mandatory and has no default value.
+     * The range of valid values is [-180 … 180]°.
+     */
+    public static final UniversalParameters LONG_OF_1ST_POINT = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(Citations.ESRI, "Longitude_Of_1st_Point")
+        }, Double.NaN, -180, 180, NonSI.DEGREE_ANGLE, true);
+
+    /**
+     * All known names for the {@code latitudeOf2ndPoint} parameter.
+     * This parameter is mandatory and has no default value.
+     * The range of valid values is [-90 … 90]°.
+     */
+    public static final UniversalParameters LAT_OF_2ND_POINT = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(Citations.ESRI, "Latitude_Of_2nd_Point")
+        }, Double.NaN, -90, 90, NonSI.DEGREE_ANGLE, true);
+
+    /**
+     * All known names for the {@code longitudeOf2ndPoint} parameter.
+     * This parameter is mandatory and has no default value.
+     * The range of valid values is [-180 … 180]°.
+     */
+    public static final UniversalParameters LONG_OF_2ND_POINT = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(Citations.ESRI, "Longitude_Of_2nd_Point")
+        }, Double.NaN, -180, 180, NonSI.DEGREE_ANGLE, true);
+
+    /**
+     * All known names for the {@code azimuth} parameter.
+     * This parameter is mandatory and has no default value.
+     *
+     * <blockquote><b>EPSG description:</b> The azimuthal direction (north zero, east of north being positive)
+     * of the great circle which is the centre line of an oblique projection.
+     * The azimuth is given at the projection center.</blockquote>
+     *
+     * <p>Some names for this parameter are {@code "Azimuth of initial line"},
+     * {@code "Co-latitude of cone axis"}, {@code "azimuth"} and {@code "AzimuthAngle"}.</p>
+     */
+    public static final UniversalParameters AZIMUTH = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(EPSG,     "Azimuth of initial line"),
+            new NamedIdentifier(EPSG,     "Co-latitude of cone axis"), // Used in Krovak projection.
+            new NamedIdentifier(OGC,      "azimuth"),
+            new NamedIdentifier(ESRI,     "Azimuth"),
+            new NamedIdentifier(GEOTIFF,  "AzimuthAngle")
+        }, Double.NaN, -360, 360, NonSI.DEGREE_ANGLE, true);
+
+    /**
+     * All known names for the {@code rectifiedGridAngle} parameter.
+     * This is an optional parameter with valid values ranging [-360 … 360]°.
+     * The default value is the value of the {@linkplain #AZIMUTH azimuth} parameter.
+     *
+     * <blockquote><b>EPSG description:</b> The angle at the natural origin of an oblique projection through which
+     * the natural coordinate reference system is rotated to make the projection north axis parallel with true north.
+     * </blockquote>
+     *
+     * <p>Some names for this parameter are {@code "Angle from Rectified to Skew Grid"},
+     * {@code "rectified_grid_angle"}, {@code "RectifiedGridAngle"} and {@code "XY_Plane_Rotation"}.</p>
+     */
+    public static final UniversalParameters RECTIFIED_GRID_ANGLE = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(Citations.EPSG,     "Angle from Rectified to Skew Grid"),
+            new NamedIdentifier(Citations.OGC,      "rectified_grid_angle"),
+            new NamedIdentifier(Citations.ESRI,     "XY_Plane_Rotation"),
+            new NamedIdentifier(Citations.GEOTIFF,  "RectifiedGridAngle")
+        }, Double.NaN, -360, 360, NonSI.DEGREE_ANGLE, false);
+
+    /**
+     * All known names for the
+     * {@linkplain org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#scaleFactor
+     * scale factor} parameter.
+     * This parameter is mandatory - meaning that it appears in {@link ParameterValueGroup}
+     * even if the user didn't set it explicitly - and its default value is 1.
+     * The range of valid values is (0 … ∞).
+     *
+     * <p>Some names for this parameter are {@code "Scale factor at natural origin"},
+     * {@code "Scale factor on initial line"}, {@code "Scale factor on pseudo standard parallel"},
+     * {@code "scale_factor"}, {@code "scale_factor_at_projection_origin"}, {@code "scale_factor_at_central_meridian"},
+     * {@code "ScaleAtNatOrigin"}, {@code "ScaleAtCenter"} and {@code "k"}.</p>
+     *
+     * @see org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#scaleFactor
+     */
+    public static final UniversalParameters SCALE_FACTOR = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(EPSG,    "Scale factor at natural origin"),
+            new NamedIdentifier(EPSG,    "Scale factor on initial line"),
+            new NamedIdentifier(EPSG,    "Scale factor on pseudo standard parallel"),
+            new NamedIdentifier(OGC,     "scale_factor"),
+            new NamedIdentifier(ESRI,    "Scale_Factor"),
+            new NamedIdentifier(NETCDF,  "scale_factor_at_projection_origin"),
+            new NamedIdentifier(NETCDF,  "scale_factor_at_central_meridian"),
+            new NamedIdentifier(GEOTIFF, "ScaleAtNatOrigin"),
+            new NamedIdentifier(GEOTIFF, "ScaleAtCenter"),
+            new NamedIdentifier(PROJ4,   "k")
+        }, 1, 0, Double.POSITIVE_INFINITY, Unit.ONE, true);
+
+    /**
+     * All known names for the {@code "X_Scale"} parameter.
+     * This parameter is optional and its default value is 1.
+     * The range of valid values is unrestricted (but value 0 is not recommended).
+     * In particular, negative values can be used for reverting the axis orientation.
+     *
+     * <p>This is an ESRI-specific parameter, sometime used instead of {@code "AXIS"} elements
+     * in <cite>Well Known Text</cite> for resolving axis orientation (especially for the
+     * {@linkplain Krovak} projection). However its usage could be extended to any projection.
+     * The choice to allow this parameter or not is taken on a projection-by-projection basis.</p>
+     */
+    public static final UniversalParameters X_SCALE = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(ESRI, "X_Scale")
+        }, 1, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Unit.ONE, false);
+
+    /**
+     * All known names for the {@code "Y_Scale"} parameter.
+     * This parameter is optional and its default value is 1.
+     * The range of valid values is unrestricted (but value 0 is not recommended).
+     * In particular, negative values can be used for reverting the axis orientation.
+     *
+     * <p>This is an ESRI-specific parameter, sometime used instead of {@code "AXIS"} elements
+     * in <cite>Well Known Text</cite> for resolving axis orientation (especially for the
+     * {@linkplain Krovak} projection). However its usage could be extended to any projection.
+     * The choice to allow this parameter or not is taken on a projection-by-projection basis.</p>
+     */
+    public static final UniversalParameters Y_SCALE = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(ESRI, "Y_Scale")
+        }, 1, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Unit.ONE, false);
+
+    /**
+     * All known names for the
+     * {@linkplain org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#falseEasting
+     * false easting} parameter.
+     * This parameter is mandatory - meaning that it appears in {@link ParameterValueGroup}
+     * even if the user didn't set it explicitly - and its default value is 0 metres.
+     * The range of valid values is unrestricted.
+     *
+     * <p>Some names for this parameter are {@code "Easting at false origin"}, {@code "Easting at projection centre"},
+     * {@code "false_easting"}, {@code "FalseEasting"}, {@code "FalseOriginEasting"} and {@code "x_0"}.</p>
+     *
+     * @see org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#falseEasting
+     */
+    public static final UniversalParameters FALSE_EASTING = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(EPSG,    "False easting"),
+            new NamedIdentifier(EPSG,    "Easting at false origin"),
+            new NamedIdentifier(EPSG,    "Easting at projection centre"),
+            new NamedIdentifier(OGC,     "false_easting"),
+            new NamedIdentifier(ESRI,    "False_Easting"),
+            new NamedIdentifier(NETCDF,  "false_easting"),
+            new NamedIdentifier(GEOTIFF, "FalseEasting"),
+            new NamedIdentifier(GEOTIFF, "FalseOriginEasting"),
+            new NamedIdentifier(PROJ4,   "x_0")
+        }, 0, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, SI.METRE, true);
+
+    /**
+     * All known names for the
+     * {@linkplain org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#falseNorthing
+     * false northing} parameter.
+     * This parameter is mandatory - meaning that it appears in {@link ParameterValueGroup}
+     * even if the user didn't set it explicitly - and its default value is 0 metres.
+     * The range of valid values is unrestricted.
+     *
+     * <p>Some names for this parameter are {@code "Northing at false origin"}, {@code "Northing at projection centre"},
+     * {@code "false_northing"}, {@code "FalseNorthing"}, {@code "FalseOriginNorthing"} and {@code "y_0"}.</p>
+     *
+     * @see org.apache.sis.referencing.operation.projection.UnitaryProjection.Parameters#falseNorthing
+     */
+    public static final UniversalParameters FALSE_NORTHING = new UniversalParameters(new NamedIdentifier[] {
+            new NamedIdentifier(EPSG,    "False northing"),
+            new NamedIdentifier(EPSG,    "Northing at false origin"),
+            new NamedIdentifier(EPSG,    "Northing at projection centre"),
+            new NamedIdentifier(OGC,     "false_northing"),
+            new NamedIdentifier(ESRI,    "False_Northing"),
+            new NamedIdentifier(NETCDF,  "false_northing"),
+            new NamedIdentifier(GEOTIFF, "FalseNorthing"),
+            new NamedIdentifier(GEOTIFF, "FalseOriginNorthing"),
+            new NamedIdentifier(PROJ4,   "y_0")
+        }, 0, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, SI.METRE, true);
+
+    /**
+     * The identifiers which can be declared to the descriptor. Only a subset of those values
+     * will actually be used. The subset is specified by a call to a {@code select} method.
+     */
+    private final NamedIdentifier[] identifiers;
+
+    /**
+     * Locates the identifiers by their {@linkplain Identifier#getCode() code}.
+     * If there is more than one parameter instance for the same name, this map contains
+     * only the first occurrence. The other occurrences can be obtained by {@link #nextSameName}.
+     */
+    private final Map<String,NamedIdentifier> identifiersMap;
+
+    /**
+     * If there is many parameter instances for the same name, allow to iterate over the
+     * other instances. Otherwise, {@code null}.
+     */
+    private final Map<NamedIdentifier,NamedIdentifier> nextSameName;
+
+    /**
+     * Creates a new instance of {@code UniversalParameters} for the given identifiers.
+     * The array given in argument should never be modified, since it will not be cloned.
+     *
+     * @param identifiers  The parameter identifiers. Must contains at least one entry.
+     * @param defaultValue The default value for the parameter, or {@link Double#NaN} if none.
+     * @param minimum      The minimum parameter value, or {@link Double#NEGATIVE_INFINITY} if none.
+     * @param maximum      The maximum parameter value, or {@link Double#POSITIVE_INFINITY} if none.
+     * @param unit         The unit for default, minimum and maximum values.
+     * @param required     {@code true} if the parameter is mandatory.
+     */
+    private UniversalParameters(final NamedIdentifier[] identifiers, final double defaultValue,
+            final double minimum, final double maximum, final Unit<?> unit, final boolean required)
+    {
+        super(toMap(identifiers), required ? 1 : 0, 1, Double.class,
+                MeasurementRange.create(minimum, true, maximum, true, unit), null,
+                Double.isNaN(defaultValue) ? null : Double.valueOf(defaultValue));
+        this.identifiers = identifiers;
+        identifiersMap = new HashMap<>(Containers.hashMapCapacity(identifiers.length));
+        Map<NamedIdentifier,NamedIdentifier> nextSameName = null;
+        /*
+         * Put elements in reverse order in order to give precedence to the first occurrence.
+         */
+        for (int i=identifiers.length; --i >= 0;) {
+            final NamedIdentifier id = identifiers[i];
+            final NamedIdentifier old = identifiersMap.put(id.getCode(), id);
+            if (old != null) {
+                if (nextSameName == null) {
+                    nextSameName = new IdentityHashMap<>(4);
+                }
+                nextSameName.put(id, old);
+            }
+        }
+        this.nextSameName = nextSameName;
+    }
+
+    /**
+     * Returns a new descriptor having the same identifiers than this descriptor.
+     * The given array is used for disambiguation when the same authority defines many names.
+     *
+     * @param  excludes The authorities to exclude, or {@code null} if none.
+     * @param  names    The names to be used for disambiguation.
+     * @return The requested identifiers.
+     */
+    final ParameterDescriptor<Double> select(final Citation[] excludes, final String... names) {
+        return select(getMinimumOccurs() != 0, getDefaultValue(), excludes, null, names);
+    }
+
+    /**
+     * Returns a new descriptor having the same identifiers than this descriptor but a different
+     * {@code mandatory} status and default value. The given array is used for disambiguation when
+     * the same authority defines many names.
+     *
+     * @param  required     Whatever the parameter shall be mandatory or not, or {@code null} if unchanged.
+     * @param  defaultValue The default value, or {@code null} for keeping it unchanged.
+     * @param  excludes     The authorities to exclude, or {@code null} if none.
+     * @param  deprecated   The names of deprecated identifiers, or {@code null} if none.
+     * @param  names        The names to be used for disambiguation.
+     *                      The same name may be used for more than one authority.
+     * @return The requested identifiers.
+     */
+    final ParameterDescriptor<Double> select(final Boolean required, final Double defaultValue,
+            final Citation[] excludes, final String[] deprecated, final String... names)
+    {
+        final Map<Citation,Boolean> authorities = new HashMap<>();
+        NamedIdentifier[] selected = new NamedIdentifier[identifiers.length];
+        long usedIdent = 0; // A bitmask of elements from the 'identifiers' array which have been used.
+        long usedNames = 0; // A bitmask of elements from the given 'names' array which have been used.
+        /*
+         * Finds every identifiers which have not been excluded. In this process, also take note
+         * of every identifiers explicitly requested by the names array given in argument.
+         */
+        int included = 0;
+        for (final NamedIdentifier candidate : identifiers) {
+            final Citation authority = candidate.getAuthority();
+            if (ArraysExt.contains(excludes, authority)) {
+                continue;
+            }
+            selected[included] = candidate;
+            final String code = candidate.getCode();
+            for (int j=names.length; --j>=0;) {
+                if (code.equals(names[j])) {
+                    if (authorities.put(authority, Boolean.TRUE) != null) {
+                        throw new IllegalArgumentException(Errors.format(
+                                Errors.Keys.ValueAlreadyDefined_1, authority));
+                    }
+                    usedNames |= (1 << j);
+                    usedIdent |= (1 << included);
+                    break;
+                }
+            }
+            included++;
+        }
+        /*
+         * If a name has not been used, this is considered as an error. We perform
+         * this check for reducing the risk of erroneous declaration in providers.
+         * Note that the same name may be used for more than one authority.
+         */
+        if (usedNames != (1 << names.length) - 1) {
+            throw new IllegalArgumentException(Errors.format(
+                    Errors.Keys.UnexpectedParameter_1, names[Long.numberOfTrailingZeros(~usedNames)]));
+        }
+        /*
+         * If some identifiers were selected as a result of explicit requirement through the
+         * names array, discards all other identifiers of that authority. Otherwise if there
+         * is some remaining authorities declaring exactly one identifier, inherits that
+         * identifier silently. If more than one identifier is found for the same authority,
+         * this is considered an error.
+         */
+        int n = 0;
+        for (int i=0; i<included; i++) {
+            final NamedIdentifier candidate = selected[i];
+            if ((usedIdent & (1 << i)) == 0) {
+                final Citation authority = candidate.getAuthority();
+                final Boolean explicit = authorities.put(authority, Boolean.FALSE);
+                if (explicit != null) {
+                    // An identifier was already specified for this authority.
+                    // If the identifier was specified explicitly by the user,
+                    // do nothing. Otherwise we have ambiguity.
+                    if (explicit) {
+                        authorities.put(authority, Boolean.TRUE); // Restore the previous value.
+                        continue;
+                    }
+                    throw new IllegalStateException(String.valueOf(candidate));
+                }
+            }
+            selected[n++] = candidate;
+        }
+        /*
+         * Adds deprecated names, if any. Those names will appears last in the names array.
+         * Note that at the difference of ordinary names, we don't share deprecated names
+         * between different provider. Deprecated names are rare enough that this is not needed.
+         */
+        if (deprecated != null) {
+            selected = ArraysExt.resize(selected, n + deprecated.length);
+            for (final String code : deprecated) {
+                selected[n++] = new DeprecatedName(identifiersMap.get(code));
+            }
+        }
+        selected = ArraysExt.resize(selected, n);
+        return new DefaultParameterDescriptor<>(toMap(selected),
+                (required != null) ? (required ? 1 : 0) : getMinimumOccurs(), 1,
+                Double.class, getValueDomain(), null,
+                (defaultValue != null) ? defaultValue : getDefaultValue());
+    }
+
+    /**
+     * Returns the element from the given collection having at least one of the names known to
+     * this {@code UniversalParameters} instance. If no such element is found, returns {@code null}.
+     *
+     * @param  candidates The collection of descriptors to compare with the names known to this
+     *         {@code UniversalParameters} instance.
+     * @return A descriptor from the given collection, or {@code null} if this method did not
+     *         found any descriptor having at least one known name.
+     * @throws IllegalArgumentException If more than one descriptor having a known name is found.
+     */
+    public ParameterDescriptor<?> find(final Collection<GeneralParameterDescriptor> candidates)
+            throws IllegalArgumentException
+    {
+        ParameterDescriptor<?> found = null;
+        for (final GeneralParameterDescriptor candidate : candidates) {
+            final Identifier candidateId = candidate.getName();
+            NamedIdentifier identifier = identifiersMap.get(candidateId.getCode());
+            while (identifier != null) {
+                final Citation authority = candidateId.getAuthority();
+                if (authority == null || identifierMatches(authority, identifier.getAuthority())) {
+                    if (candidate instanceof ParameterDescriptor<?>) {
+                        if (found != null) {
+                            throw new IllegalArgumentException(Errors.format(Errors.Keys.AmbiguousName_3,
+                                    IdentifiedObjects.toString(found.getName()),
+                                    IdentifiedObjects.toString(candidate.getName()),
+                                    getName().getCode()));
+                        }
+                        found = (ParameterDescriptor<?>) candidate;
+                        break; // Continue the 'for' loop.
+                    } else {
+                        // Name matches, but this is not an instance of parameter descriptor.
+                        // It is probably an error. For now continue the search, but future
+                        // implementations may do some other action here.
+                    }
+                }
+                if (nextSameName == null) break;
+                identifier = nextSameName.get(identifier);
+            }
+        }
+        return found;
+    }
+
+    /**
+     * Constructs a parameter descriptor for a floating point value. The parameter is
+     * identified by codes in the namespace of one or more authorities ({@link Citations#OGC OGC},
+     * {@link Citations#EPSG EPSG}, <i>etc.</i>). Those codes are declared as elements in the
+     * {@code identifiers} array argument. The first element ({@code identifiers[0]}) is both the
+     * {@linkplain ParameterDescriptor#getName main name} and the
+     * {@linkplain ParameterDescriptor#getIdentifiers identifiers}.
+     * All others elements are {@linkplain ParameterDescriptor#getAlias aliases}.
+     *
+     * <p>The {@code required} argument is handled as below:</p>
+     * <ul>
+     *   <li><p>If {@code true}, then the descriptor created by this method is flagged as
+     *   <cite>mandatory</cite>, meaning that it will always appear in the list of parameter values
+     *   that a user shall provide. However the value will be initialized with the given default
+     *   value (if different than {@linkplain Double#NaN NaN}), so the user may not needs to supply
+     *   explicitly a value.</p></li>
+     *
+     *   <li><p>If {@code false}, then the descriptor created by this method is flagged as
+     *   <cite>optional</cite>, meaning that it will appear in the list of parameter values
+     *   only if set to a value different than the default value.</p></li>
+     * </ul>
+     *
+     * @param  identifiers  The parameter identifiers. Must contains at least one entry.
+     * @param  defaultValue The default value for the parameter, or {@link Double#NaN} if none.
+     * @param  minimum      The minimum parameter value, or {@link Double#NEGATIVE_INFINITY} if none.
+     * @param  maximum      The maximum parameter value, or {@link Double#POSITIVE_INFINITY} if none.
+     * @param  unit         The unit for default, minimum and maximum values.
+     * @param  required     {@code true} if the parameter is mandatory, or {@code false} if optional.
+     * @return The descriptor for the given identifiers.
+     */
+    static ParameterDescriptor<Double> createDescriptor(
+            final Identifier[] identifiers, final double defaultValue,
+            final double minimum, final double maximum, final Unit<?> unit, final boolean required)
+    {
+        final MeasurementRange<Double> valueDomain = MeasurementRange.create(minimum, true, maximum, true, unit);
+        return new DefaultParameterDescriptor<>(toMap(identifiers), required ? 1 : 0, 1,
+                Double.class, valueDomain, null, Double.isNaN(defaultValue) ? null : defaultValue);
+    }
+
+    /**
+     * Constructs a parameter group from a set of alias. The parameter group is
+     * identified by codes provided by one or more authorities. Common authorities are
+     * {@link Citations#OGC OGC} and {@link Citations#EPSG EPSG} for example.
+     *
+     * <p>Special rules:</p>
+     * <ul>
+     *   <li>The first entry in the {@code identifiers} array is the
+     *       {@linkplain ParameterDescriptorGroup#getName() primary name}.</li>
+     *   <li>If an identifier does not implements the {@link GenericName} interface, it is
+     *       used as an {@linkplain ParameterDescriptorGroup#getIdentifiers identifiers}.</li>
+     *   <li>All others are {@linkplain ParameterDescriptorGroup#getAlias aliases}.</li>
+     * </ul>
+     *
+     * <b>Note:</b> This method may modify in-place the given parameters array.
+     * Do not pass a cached array.
+     *
+     * @param  identifiers  The operation identifiers. Must contains at least one entry.
+     * @param  excludes     The authorities to exclude from all parameters, or {@code null} if none.
+     * @param  parameters   The set of parameters, or {@code null} or an empty array if none.
+     * @param  dynamicParameters   Non-standard dynamic parameters to add as bitwise combination of
+     *                      {@link MapProjectionDescriptor}, or 0 if none.
+     * @return The descriptor for the given identifiers.
+     */
+    static ParameterDescriptorGroup createDescriptorGroup(final Identifier[] identifiers,
+            final Citation[] excludes, final DefaultParameterDescriptor<?>[] parameters, final int dynamicParameters)
+    {
+        if (excludes != null) {
+            final Map<String,Object> properties = new HashMap<>();
+            for (int i=0; i<parameters.length; i++) {
+                final DefaultParameterDescriptor<?> param = parameters[i];
+                if (param.getValueClass() != Double.class) {
+                    continue;
+                }
+                properties.putAll(IdentifiedObjects.getProperties(param));
+                boolean forAlias = false;
+                boolean modified = false;
+                Object[] aliases;
+                do { // Executed exactly twice: once for identifier, then once for aliases.
+                    final String key = forAlias ? ALIAS_KEY : IDENTIFIERS_KEY;
+                    aliases = (Object[]) properties.get(key);
+                    if (aliases != null) {
+                        int n = 0;
+                        for (final Object alias : aliases) {
+                            if (alias instanceof Identifier) {
+                                if (ArraysExt.contains(excludes, ((Identifier) alias).getAuthority())) {
+                                    continue;
+                                }
+                            }
+                            aliases[n++] = alias;
+                        }
+                        // If at least one alias or identifier has been removed, remember that we
+                        // will need to create a new parameter in replacement to the provided one.
+                        if (n != aliases.length) {
+                            properties.put(key, Arrays.copyOf(aliases, n));
+                            modified = true;
+                        }
+                    }
+                } while ((forAlias = !forAlias) == true);
+                if (modified) {
+                    properties.put(NAME_KEY, aliases[0]); // In case the primary name was one of the excluded names.
+                    parameters[i] = new DefaultParameterDescriptor<>(properties, param.getMinimumOccurs(), 1,
+                            Double.class, param.getValueDomain(), null, (Double) param.getDefaultValue());
+                }
+                properties.clear();
+            }
+        }
+        final Map<String,Object> properties = toMap(identifiers);
+        return (dynamicParameters == 0) ? new DefaultParameterDescriptorGroup(properties, 1, 1, parameters) :
+                new MapProjectionDescriptor(properties, parameters, dynamicParameters);
+    }
+
+    /**
+     * Puts the identifiers into a properties map suitable for {@link ParameterDescriptorGroup} constructor.
+     * The first identifier is used as the primary name. All other elements are aliases.
+     */
+    private static Map<String,Object> toMap(final Identifier[] identifiers) {
+        int idCount    = 0;
+        int aliasCount = 0;
+        GenericName[] alias = null;
+        Identifier[] id = null;
+        for (int i=0; i<identifiers.length; i++) {
+            final Identifier candidate = identifiers[i];
+            if (candidate instanceof GenericName) {
+                if (alias == null) {
+                    alias = new GenericName[identifiers.length - i];
+                }
+                alias[aliasCount++] = (GenericName) candidate;
+            } else {
+                if (id == null) {
+                    id = new Identifier[identifiers.length - i];
+                }
+                id[idCount++] = candidate;
+            }
+        }
+        id    = ArraysExt.resize(id,    idCount);
+        alias = ArraysExt.resize(alias, aliasCount);
+        final Map<String,Object> properties = new HashMap<>(4);
+        properties.put(NAME_KEY,        identifiers[0]);
+        properties.put(IDENTIFIERS_KEY, id);
+        properties.put(ALIAS_KEY,       alias);
+        return properties;
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/UniversalParameters.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/UniversalParameters.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8



Mime
View raw message