CelestialBodyFactory.java
/* 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;
}
}
}