/* Copyright 2002-2017 CS Systèmes d'Information * Licensed to CS Systèmes d'Information (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * CS 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.orekit.bodies; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.orekit.errors.OrekitException; import org.orekit.errors.OrekitMessages; /** Factory class for bodies of the solar system. * <p>The {@link #getSun() Sun}, the {@link #getMoon() Moon} and the planets * (including the Pluto dwarf planet) are provided by this factory. In addition, * two important points are provided for convenience: the {@link * #getSolarSystemBarycenter() solar system barycenter} and the {@link * #getEarthMoonBarycenter() Earth-Moon barycenter}.</p> * <p>The underlying body-centered frames are either direct children of {@link * org.orekit.frames.FramesFactory#getEME2000() EME2000} (for {@link #getMoon() Moon} * and {@link #getEarthMoonBarycenter() Earth-Moon barycenter}) or children from other * body-centered frames. For example, the path from EME2000 to * Jupiter-centered frame is: EME2000, Earth-Moon barycenter centered, * solar system barycenter centered, Jupiter-centered. The defining transforms * of these frames are combinations of simple linear {@link * org.orekit.frames.Transform#Transform(org.orekit.time.AbsoluteDate, * org.hipparchus.geometry.euclidean.threed.Vector3D, * org.hipparchus.geometry.euclidean.threed.Vector3D) translation/velocity} transforms * without any rotation. The frame axes are therefore always parallel to * {@link org.orekit.frames.FramesFactory#getEME2000() EME2000} frame axes.</p> * <p>The position of the bodies provided by this class are interpolated using * the JPL DE 405/DE 406 ephemerides.</p> * @author Luc Maisonobe */ public class CelestialBodyFactory { /** Predefined name for solar system barycenter. * @see #getBody(String) */ public static final String SOLAR_SYSTEM_BARYCENTER = "solar system barycenter"; /** Predefined name for Sun. * @see #getBody(String) */ public static final String SUN = "Sun"; /** Predefined name for Mercury. * @see #getBody(String) */ public static final String MERCURY = "Mercury"; /** Predefined name for Venus. * @see #getBody(String) */ public static final String VENUS = "Venus"; /** Predefined name for Earth-Moon barycenter. * @see #getBody(String) */ public static final String EARTH_MOON = "Earth-Moon barycenter"; /** Predefined name for Earth. * @see #getBody(String) */ public static final String EARTH = "Earth"; /** Predefined name for Moon. * @see #getBody(String) */ public static final String MOON = "Moon"; /** Predefined name for Mars. * @see #getBody(String) */ public static final String MARS = "Mars"; /** Predefined name for Jupiter. * @see #getBody(String) */ public static final String JUPITER = "Jupiter"; /** Predefined name for Saturn. * @see #getBody(String) */ public static final String SATURN = "Saturn"; /** Predefined name for Uranus. * @see #getBody(String) */ public static final String URANUS = "Uranus"; /** Predefined name for Neptune. * @see #getBody(String) */ public static final String NEPTUNE = "Neptune"; /** Predefined name for Pluto. * @see #getBody(String) */ public static final String PLUTO = "Pluto"; /** Celestial body loaders map. */ private static final Map<String, List<CelestialBodyLoader>> LOADERS_MAP = new HashMap<String, List<CelestialBodyLoader>>(); /** Celestial body map. */ private static final Map<String, CelestialBody> CELESTIAL_BODIES_MAP = new HashMap<String, CelestialBody>(); /** Private constructor. * <p>This class is a utility class, it should neither have a public * nor a default constructor. This private constructor prevents * the compiler from generating one automatically.</p> */ private CelestialBodyFactory() { } /** Add a loader for celestial bodies. * @param name name of the body (may be one of the predefined names or a user-defined name) * @param loader custom loader to add for the body * @see #addDefaultCelestialBodyLoader(String) * @see #clearCelestialBodyLoaders(String) * @see #clearCelestialBodyLoaders() */ public static void addCelestialBodyLoader(final String name, final CelestialBodyLoader loader) { synchronized (LOADERS_MAP) { List<CelestialBodyLoader> loaders = LOADERS_MAP.get(name); if (loaders == null) { loaders = new ArrayList<CelestialBodyLoader>(); LOADERS_MAP.put(name, loaders); } loaders.add(loader); } } /** Add the default loaders for all predefined celestial bodies. * @param supportedNames regular expression for supported files names * (may be null if the default JPL file names are used) * <p> * The default loaders look for DE405 or DE406 JPL ephemerides. * </p> * @see <a href="ftp://ssd.jpl.nasa.gov/pub/eph/planets/unix/de405">DE405 JPL ephemerides</a> * @see <a href="ftp://ssd.jpl.nasa.gov/pub/eph/planets/unix/de406">DE406 JPL ephemerides</a> * @see #addCelestialBodyLoader(String, CelestialBodyLoader) * @see #addDefaultCelestialBodyLoader(String) * @see #clearCelestialBodyLoaders(String) * @see #clearCelestialBodyLoaders() * @exception OrekitException if the header constants cannot be read */ public static void addDefaultCelestialBodyLoader(final String supportedNames) throws OrekitException { addDefaultCelestialBodyLoader(SOLAR_SYSTEM_BARYCENTER, supportedNames); addDefaultCelestialBodyLoader(SUN, supportedNames); addDefaultCelestialBodyLoader(MERCURY, supportedNames); addDefaultCelestialBodyLoader(VENUS, supportedNames); addDefaultCelestialBodyLoader(EARTH_MOON, supportedNames); addDefaultCelestialBodyLoader(EARTH, supportedNames); addDefaultCelestialBodyLoader(MOON, supportedNames); addDefaultCelestialBodyLoader(MARS, supportedNames); addDefaultCelestialBodyLoader(JUPITER, supportedNames); addDefaultCelestialBodyLoader(SATURN, supportedNames); addDefaultCelestialBodyLoader(URANUS, supportedNames); addDefaultCelestialBodyLoader(NEPTUNE, supportedNames); addDefaultCelestialBodyLoader(PLUTO, supportedNames); } /** Add the default loaders for celestial bodies. * @param name name of the body (if not one of the predefined names, the method does nothing) * @param supportedNames regular expression for supported files names * (may be null if the default JPL file names are used) * <p> * The default loaders look for DE405 or DE406 JPL ephemerides. * </p> * @see <a href="ftp://ssd.jpl.nasa.gov/pub/eph/planets/unix/de405">DE405 JPL ephemerides</a> * @see <a href="ftp://ssd.jpl.nasa.gov/pub/eph/planets/unix/de406">DE406 JPL ephemerides</a> * @see #addCelestialBodyLoader(String, CelestialBodyLoader) * @see #addDefaultCelestialBodyLoader(String) * @see #clearCelestialBodyLoaders(String) * @see #clearCelestialBodyLoaders() * @exception OrekitException if the header constants cannot be read */ public static void addDefaultCelestialBodyLoader(final String name, final String supportedNames) throws OrekitException { CelestialBodyLoader loader = null; if (name.equalsIgnoreCase(SOLAR_SYSTEM_BARYCENTER)) { loader = new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.SOLAR_SYSTEM_BARYCENTER); } else if (name.equalsIgnoreCase(SUN)) { loader = new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.SUN); } else if (name.equalsIgnoreCase(MERCURY)) { loader = new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.MERCURY); } else if (name.equalsIgnoreCase(VENUS)) { loader = new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.VENUS); } else if (name.equalsIgnoreCase(EARTH_MOON)) { loader = new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.EARTH_MOON); } else if (name.equalsIgnoreCase(EARTH)) { loader = new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.EARTH); } else if (name.equalsIgnoreCase(MOON)) { loader = new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.MOON); } else if (name.equalsIgnoreCase(MARS)) { loader = new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.MARS); } else if (name.equalsIgnoreCase(JUPITER)) { loader = new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.JUPITER); } else if (name.equalsIgnoreCase(SATURN)) { loader = new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.SATURN); } else if (name.equalsIgnoreCase(URANUS)) { loader = new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.URANUS); } else if (name.equalsIgnoreCase(NEPTUNE)) { loader = new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.NEPTUNE); } else if (name.equalsIgnoreCase(PLUTO)) { loader = new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.PLUTO); } if (loader != null) { addCelestialBodyLoader(name, loader); } } /** Clear loaders for one celestial body. * <p> * Calling this method also clears the celestial body that * has been loaded via this {@link CelestialBodyLoader}. * </p> * @param name name of the body * @see #addCelestialBodyLoader(String, CelestialBodyLoader) * @see #clearCelestialBodyLoaders() * @see #clearCelestialBodyCache(String) */ public static void clearCelestialBodyLoaders(final String name) { // use same synchronization order as in getBody to prevent deadlocks synchronized (CELESTIAL_BODIES_MAP) { // take advantage of reentrent synchronization as // clearCelestialBodyCache uses the same lock inside clearCelestialBodyCache(name); synchronized (LOADERS_MAP) { LOADERS_MAP.remove(name); } } } /** Clear loaders for all celestial bodies. * <p> * Calling this method also clears all loaded celestial bodies. * </p> * @see #addCelestialBodyLoader(String, CelestialBodyLoader) * @see #clearCelestialBodyLoaders(String) * @see #clearCelestialBodyCache() */ public static void clearCelestialBodyLoaders() { synchronized (CELESTIAL_BODIES_MAP) { clearCelestialBodyCache(); synchronized (LOADERS_MAP) { LOADERS_MAP.clear(); } } } /** Clear the specified celestial body from the internal cache. * @param name name of the body */ public static void clearCelestialBodyCache(final String name) { synchronized (CELESTIAL_BODIES_MAP) { CELESTIAL_BODIES_MAP.remove(name); } } /** Clear all loaded celestial bodies. * <p> * Calling this method will remove all loaded bodies from the internal * cache. Subsequent calls to {@link #getBody(String)} or similar methods * will result in a reload of the requested body from the configured loader(s). * </p> */ public static void clearCelestialBodyCache() { synchronized (CELESTIAL_BODIES_MAP) { CELESTIAL_BODIES_MAP.clear(); } } /** Get the solar system barycenter aggregated body. * @return solar system barycenter aggregated body * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getSolarSystemBarycenter() throws OrekitException { return getBody(SOLAR_SYSTEM_BARYCENTER); } /** Get the Sun singleton body. * @return Sun body * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getSun() throws OrekitException { return getBody(SUN); } /** Get the Mercury singleton body. * @return Sun body * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getMercury() throws OrekitException { return getBody(MERCURY); } /** Get the Venus singleton body. * @return Venus body * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getVenus() throws OrekitException { return getBody(VENUS); } /** Get the Earth-Moon barycenter singleton bodies pair. * @return Earth-Moon barycenter bodies pair * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getEarthMoonBarycenter() throws OrekitException { return getBody(EARTH_MOON); } /** Get the Earth singleton body. * @return Earth body * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getEarth() throws OrekitException { return getBody(EARTH); } /** Get the Moon singleton body. * @return Moon body * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getMoon() throws OrekitException { return getBody(MOON); } /** Get the Mars singleton body. * @return Mars body * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getMars() throws OrekitException { return getBody(MARS); } /** Get the Jupiter singleton body. * @return Jupiter body * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getJupiter() throws OrekitException { return getBody(JUPITER); } /** Get the Saturn singleton body. * @return Saturn body * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getSaturn() throws OrekitException { return getBody(SATURN); } /** Get the Uranus singleton body. * @return Uranus body * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getUranus() throws OrekitException { return getBody(URANUS); } /** Get the Neptune singleton body. * @return Neptune body * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getNeptune() throws OrekitException { return getBody(NEPTUNE); } /** Get the Pluto singleton body. * @return Pluto body * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getPluto() throws OrekitException { return getBody(PLUTO); } /** Get a celestial body. * <p> * If no {@link CelestialBodyLoader} has been added by calling {@link * #addCelestialBodyLoader(String, CelestialBodyLoader) * addCelestialBodyLoader} or if {@link #clearCelestialBodyLoaders(String) * clearCelestialBodyLoaders} has been called afterwards, * the {@link #addDefaultCelestialBodyLoader(String, String) * addDefaultCelestialBodyLoader} method will be called automatically, * once with the default name for JPL DE ephemerides and once with the * default name for IMCCE INPOP files. * </p> * @param name name of the celestial body * @return celestial body * @exception OrekitException if the celestial body cannot be built */ public static CelestialBody getBody(final String name) throws OrekitException { synchronized (CELESTIAL_BODIES_MAP) { CelestialBody body = CELESTIAL_BODIES_MAP.get(name); if (body == null) { synchronized (LOADERS_MAP) { List<CelestialBodyLoader> loaders = LOADERS_MAP.get(name); if ((loaders == null) || loaders.isEmpty()) { addDefaultCelestialBodyLoader(name, JPLEphemeridesLoader.DEFAULT_DE_SUPPORTED_NAMES); addDefaultCelestialBodyLoader(name, JPLEphemeridesLoader.DEFAULT_INPOP_SUPPORTED_NAMES); loaders = LOADERS_MAP.get(name); } OrekitException delayedException = null; for (CelestialBodyLoader loader : loaders) { try { body = loader.loadCelestialBody(name); if (body != null) { break; } } catch (OrekitException oe) { delayedException = oe; } } if (body == null) { throw (delayedException != null) ? delayedException : new OrekitException(OrekitMessages.NO_DATA_LOADED_FOR_CELESTIAL_BODY, name); } } // save the body CELESTIAL_BODIES_MAP.put(name, body); } return body; } } }