001/*
002 *    GeoAPI - Java interfaces for OGC/ISO standards
003 *    http://www.geoapi.org
004 *
005 *    Copyright (C) 2016-2019 Open Geospatial Consortium, Inc.
006 *    All Rights Reserved. http://www.opengeospatial.org/ogc/legal
007 *
008 *    Permission to use, copy, and modify this software and its documentation, with
009 *    or without modification, for any purpose and without fee or royalty is hereby
010 *    granted, provided that you include the following on ALL copies of the software
011 *    and documentation or portions thereof, including modifications, that you make:
012 *
013 *    1. The full text of this NOTICE in a location viewable to users of the
014 *       redistributed or derivative work.
015 *    2. Notice of any changes or modifications to the OGC files, including the
016 *       date changes were made.
017 *
018 *    THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE
019 *    NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
020 *    TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT
021 *    THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY
022 *    PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
023 *
024 *    COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR
025 *    CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
026 *
027 *    The name and trademarks of copyright holders may NOT be used in advertising or
028 *    publicity pertaining to the software without specific, written prior permission.
029 *    Title to copyright in this software and any associated documentation will at all
030 *    times remain with copyright holders.
031 */
032package org.opengis.test;
033
034import javax.measure.Unit;
035import javax.measure.quantity.Time;
036import javax.measure.quantity.Angle;
037import javax.measure.quantity.Length;
038import javax.measure.quantity.Pressure;
039import javax.measure.quantity.Dimensionless;
040import javax.measure.spi.ServiceProvider;
041import javax.measure.spi.SystemOfUnits;
042import org.opengis.test.util.PseudoFactory;
043
044
045/**
046 * Pre-defined constants for the units of measurement used by the conformance tests.
047 * This pseudo-factory provides separated methods for all units needed by {@code geoapi-conformance}.
048 *
049 * @author  Martin Desruisseaux (Geomatys)
050 * @version 3.1
051 * @since   3.0.1
052 */
053public class Units extends PseudoFactory {
054    /**
055     * The default instance, created when first needed.
056     *
057     * @see #getDefault()
058     */
059    private static Units DEFAULT;
060
061    /**
062     * Linear units used in the tests.
063     */
064    private final Unit<Length> metre, kilometre, foot, footSurveyUS;
065
066    /**
067     * Angular units used in the tests.
068     */
069    private final Unit<Angle> radian, microradian, degree, grad, arcSecond;
070
071    /**
072     * Temporal units used in the tests.
073     */
074    private final Unit<Time> second, day;
075
076    /**
077     * Pressure units used in the tests.
078     */
079    private final Unit<Pressure> pascal, hectopascal;
080
081    /**
082     * Scale units used in the tests.
083     */
084    private final Unit<Dimensionless> one, ppm;
085
086    /**
087     * Creates a new factory which will use the given system of units.
088     *
089     * @param  system  the system of units to use for creating base units.
090     */
091    public Units(final SystemOfUnits system) {
092        metre        = system.getUnit(Length.class);
093        radian       = system.getUnit(Angle.class);
094        second       = system.getUnit(Time.class);
095        pascal       = system.getUnit(Pressure.class);
096        one          = getDimensionless(system);
097        kilometre    = metre .multiply(1000);
098        foot         = metre .multiply(0.3048);
099        footSurveyUS = metre .multiply(12 / 39.37);
100        degree       = radian.multiply(Math.PI / 180);
101        grad         = radian.multiply(Math.PI / 200);
102        arcSecond    = radian.multiply(Math.PI / (180*60*60));
103        microradian  = radian.divide(1E6);
104        day          = second.multiply(24*60*60);
105        hectopascal  = pascal.multiply(100);
106        ppm          = one   .divide(1000000);
107    }
108
109    /**
110     * Returns the default units factory. This factory uses the unit service provider which is
111     * {@linkplain ServiceProvider#current() current} at the time of the first invocation of this method.
112     *
113     * @return the default units factory.
114     */
115    public static synchronized Units getDefault() {
116        if (DEFAULT == null) {
117            DEFAULT = new Units(ServiceProvider.current().getSystemOfUnitsService().getSystemOfUnits());
118        }
119        return DEFAULT;
120    }
121
122    /**
123     * Returns the dimensionless unit. This is a workaround for what seems to be a bug
124     * in the reference implementation 1.0.1 of unit API.
125     */
126    private static Unit<Dimensionless> getDimensionless(final SystemOfUnits system) {
127        Unit<Dimensionless> unit = system.getUnit(Dimensionless.class);
128        if (unit == null) try {
129            unit = ((Unit<?>) Class.forName("tec.units.ri.AbstractUnit").getField("ONE").get(null)).asType(Dimensionless.class);
130        } catch (ReflectiveOperationException | ClassCastException e) {
131            throw new IllegalArgumentException("Can not create a dimensionless unit from the given provider.");
132        }
133        return unit;
134    }
135
136    /** Base unit of measurement for lengths.            */  public Unit<Length>   metre()        {return metre;}
137    /** Unit of measurement defined as 1000 metres.      */  public Unit<Length>   kilometre()    {return kilometre;}
138    /** Unit of measurement defined as 0.3048 metres.    */  public Unit<Length>   foot()         {return foot;}
139    /** Unit of measurement defined as 12/39.37 metres.  */  public Unit<Length>   footSurveyUS() {return footSurveyUS;}
140    /** Base unit of measurement for angle.              */  public Unit<Angle>    radian()       {return radian;}
141    /** Unit of measurement defined as 1E-6 radians.     */  public Unit<Angle>    microradian()  {return microradian;}
142    /** Unit of measurement defined as π/180 radians.    */  public Unit<Angle>    degree()       {return degree;}
143    /** Unit of measurement defined as π/200 radians.    */  public Unit<Angle>    grad()         {return grad;}
144    /** Unit of measurement defined as 1/(60×60) degree. */  public Unit<Angle>    arcSecond()    {return arcSecond;}
145    /** Base unit of measurement for durations.          */  public Unit<Time>     second()       {return second;}
146    /** Unit of measurement defined as 24×60×60 seconds. */  public Unit<Time>     day()          {return day;}
147    /** Base unit of measurement for pressure.           */  public Unit<Pressure> pascal()       {return pascal;}
148    /** Unit of measurement defined as 100 pascals.      */  public Unit<Pressure> hectopascal()  {return hectopascal;}
149    /** Dimensionless unit for scale measurements.       */  public Unit<Dimensionless> one()     {return one;}
150    /** The "parts per million" unit.                    */  public Unit<Dimensionless> ppm()     {return ppm;}
151}