001/*
002 *    GeoAPI - Java interfaces for OGC/ISO standards
003 *    Copyright © 2011-2023 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.test;
019
020import javax.measure.Unit;
021import javax.measure.quantity.Time;
022import javax.measure.quantity.Angle;
023import javax.measure.quantity.Length;
024import javax.measure.quantity.Pressure;
025import javax.measure.quantity.Dimensionless;
026import javax.measure.spi.ServiceProvider;
027import javax.measure.spi.SystemOfUnits;
028import org.opengis.test.util.PseudoFactory;
029
030
031/**
032 * Pre-defined constants for the units of measurement used by the conformance tests.
033 * This pseudo-factory provides separated methods for all units needed by {@code geoapi-conformance}.
034 *
035 * @author  Martin Desruisseaux (Geomatys)
036 * @version 3.1
037 * @since   3.0.1
038 */
039public class Units extends PseudoFactory {
040    /**
041     * The default instance, created when first needed.
042     *
043     * @see #getDefault()
044     */
045    private static Units DEFAULT;
046
047    /**
048     * Linear units used in the tests.
049     */
050    private final Unit<Length> metre, kilometre, foot, footSurveyUS;
051
052    /**
053     * Angular units used in the tests.
054     */
055    private final Unit<Angle> radian, microradian, degree, grad, arcSecond;
056
057    /**
058     * Temporal units used in the tests.
059     */
060    private final Unit<Time> second, day;
061
062    /**
063     * Pressure units used in the tests.
064     */
065    private final Unit<Pressure> pascal, hectopascal;
066
067    /**
068     * Scale units used in the tests.
069     */
070    private final Unit<Dimensionless> one, ppm;
071
072    /**
073     * Creates a new factory which will use the given system of units.
074     *
075     * @param  system  the system of units to use for creating base units.
076     */
077    public Units(final SystemOfUnits system) {
078        metre        = system.getUnit(Length.class);
079        radian       = system.getUnit(Angle.class);
080        second       = system.getUnit(Time.class);
081        pascal       = system.getUnit(Pressure.class);
082        one          = getDimensionless(system);
083        kilometre    = metre .multiply(1000);
084        foot         = metre .multiply(0.3048);
085        footSurveyUS = metre .multiply(12 / 39.37);
086        degree       = radian.multiply(Math.PI / 180);
087        grad         = radian.multiply(Math.PI / 200);
088        arcSecond    = radian.multiply(Math.PI / (180*60*60));
089        microradian  = radian.divide(1E6);
090        day          = second.multiply(24*60*60);
091        hectopascal  = pascal.multiply(100);
092        ppm          = one   .divide(1000000);
093    }
094
095    /**
096     * Returns the default units factory. This factory uses the unit service provider which is
097     * {@linkplain ServiceProvider#current() current} at the time of the first invocation of this method.
098     *
099     * @return the default units factory.
100     */
101    public static synchronized Units getDefault() {
102        if (DEFAULT == null) {
103            DEFAULT = new Units(ServiceProvider.current().getSystemOfUnitsService().getSystemOfUnits());
104        }
105        return DEFAULT;
106    }
107
108    /**
109     * {@return the dimensionless unit}. This is a workaround for what seems to be a bug
110     * in the reference implementation 1.0.1 of unit API.
111     *
112     * @param  system  the system of units to use for creating base units.
113     */
114    private static Unit<Dimensionless> getDimensionless(final SystemOfUnits system) {
115        Unit<Dimensionless> unit = system.getUnit(Dimensionless.class);
116        if (unit == null) try {
117            unit = ((Unit<?>) Class.forName("tec.units.ri.AbstractUnit").getField("ONE").get(null)).asType(Dimensionless.class);
118        } catch (ReflectiveOperationException | ClassCastException e) {
119            throw new IllegalArgumentException("Cannot create a dimensionless unit from the given provider.");
120        }
121        return unit;
122    }
123
124    /** {@return the base unit of measurement for lengths}.            */  public Unit<Length>   metre()        {return metre;}
125    /** {@return the unit of measurement defined as 1000 metres}.      */  public Unit<Length>   kilometre()    {return kilometre;}
126    /** {@return the unit of measurement defined as 0.3048 metres}.    */  public Unit<Length>   foot()         {return foot;}
127    /** {@return the unit of measurement defined as 12/39.37 metres}.  */  public Unit<Length>   footSurveyUS() {return footSurveyUS;}
128    /** {@return the base unit of measurement for angle}.              */  public Unit<Angle>    radian()       {return radian;}
129    /** {@return the unit of measurement defined as 1E-6 radians}.     */  public Unit<Angle>    microradian()  {return microradian;}
130    /** {@return the unit of measurement defined as π/180 radians}.    */  public Unit<Angle>    degree()       {return degree;}
131    /** {@return the unit of measurement defined as π/200 radians}.    */  public Unit<Angle>    grad()         {return grad;}
132    /** {@return the unit of measurement defined as 1/(60×60) degree}. */  public Unit<Angle>    arcSecond()    {return arcSecond;}
133    /** {@return the base unit of measurement for durations}.          */  public Unit<Time>     second()       {return second;}
134    /** {@return the unit of measurement defined as 24×60×60 seconds}. */  public Unit<Time>     day()          {return day;}
135    /** {@return the base unit of measurement for pressure}.           */  public Unit<Pressure> pascal()       {return pascal;}
136    /** {@return the unit of measurement defined as 100 pascals}.      */  public Unit<Pressure> hectopascal()  {return hectopascal;}
137    /** {@return the dimensionless unit for scale measurements}.       */  public Unit<Dimensionless> one()     {return one;}
138    /** {@return the "parts per million" unit}.                        */  public Unit<Dimensionless> ppm()     {return ppm;}
139}