LazyLoadedCelestialBodies.java

  1. /* Contributed in the public domain.
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.bodies;

  18. import java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;

  22. import org.orekit.data.DataProvidersManager;
  23. import org.orekit.errors.OrekitException;
  24. import org.orekit.errors.OrekitMessages;
  25. import org.orekit.frames.Frame;
  26. import org.orekit.time.TimeScales;

  27. /**
  28.  * This class lazily loads auxiliary data when it is needed by a requested body. It is
  29.  * designed to match the behavior of {@link CelestialBodyFactory} in Orekit 10.0.
  30.  *
  31.  * @author Luc Maisonobe
  32.  * @author Evan Ward
  33.  * @see CelestialBodyFactory
  34.  * @since 10.1
  35.  */
  36. public class LazyLoadedCelestialBodies implements CelestialBodies {

  37.     /** Supplies the auxiliary data files. */
  38.     private final DataProvidersManager dataProvidersManager;
  39.     /** Provides access to time scales when parsing bodies. */
  40.     private final TimeScales timeScales;
  41.     /** Earth centered frame aligned with ICRF. */
  42.     private final Frame gcrf;
  43.     /** Celestial body loaders map. */
  44.     private final Map<String, List<CelestialBodyLoader>> loadersMap = new HashMap<>();

  45.     /** Celestial body map. */
  46.     private final Map<String, CelestialBody> celestialBodyMap = new HashMap<>();

  47.     /**
  48.      * Create a celestial body factory with the given auxiliary data sources.
  49.      *
  50.      * @param dataProvidersManager supplies JPL ephemerides auxiliary data files.
  51.      * @param timeScales           set of time scales to use when loading bodies.
  52.      * @param gcrf                 Earth centered frame aligned with ICRF.
  53.      */
  54.     public LazyLoadedCelestialBodies(final DataProvidersManager dataProvidersManager,
  55.                                      final TimeScales timeScales,
  56.                                      final Frame gcrf) {
  57.         this.dataProvidersManager = dataProvidersManager;
  58.         this.timeScales = timeScales;
  59.         this.gcrf = gcrf;
  60.     }

  61.     /** Add a loader for celestial bodies.
  62.      * @param name name of the body (may be one of the predefined names or a user-defined name)
  63.      * @param loader custom loader to add for the body
  64.      * @see #addDefaultCelestialBodyLoader(String)
  65.      * @see #clearCelestialBodyLoaders(String)
  66.      * @see #clearCelestialBodyLoaders()
  67.      */
  68.     public void addCelestialBodyLoader(final String name,
  69.                                        final CelestialBodyLoader loader) {
  70.         synchronized (loadersMap) {
  71.             loadersMap.computeIfAbsent(name, k -> new ArrayList<>()).add(loader);
  72.         }
  73.     }

  74.     /** Add the default loaders for all predefined celestial bodies.
  75.      * @param supportedNames regular expression for supported files names
  76.      * (may be null if the default JPL file names are used)
  77.      * <p>
  78.      * The default loaders look for DE405 or DE406 JPL ephemerides.
  79.      * </p>
  80.      * @see <a href="ftp://ssd.jpl.nasa.gov/pub/eph/planets/Linux/de405/">DE405 JPL ephemerides</a>
  81.      * @see <a href="ftp://ssd.jpl.nasa.gov/pub/eph/planets/Linux/de406/">DE406 JPL ephemerides</a>
  82.      * @see #addCelestialBodyLoader(String, CelestialBodyLoader)
  83.      * @see #addDefaultCelestialBodyLoader(String)
  84.      * @see #clearCelestialBodyLoaders(String)
  85.      * @see #clearCelestialBodyLoaders()
  86.      */
  87.     public void addDefaultCelestialBodyLoader(final String supportedNames) {
  88.         addDefaultCelestialBodyLoader(CelestialBodyFactory.SOLAR_SYSTEM_BARYCENTER, supportedNames);
  89.         addDefaultCelestialBodyLoader(CelestialBodyFactory.SUN,                     supportedNames);
  90.         addDefaultCelestialBodyLoader(CelestialBodyFactory.MERCURY,                 supportedNames);
  91.         addDefaultCelestialBodyLoader(CelestialBodyFactory.VENUS,                   supportedNames);
  92.         addDefaultCelestialBodyLoader(CelestialBodyFactory.EARTH_MOON,              supportedNames);
  93.         addDefaultCelestialBodyLoader(CelestialBodyFactory.EARTH,                   supportedNames);
  94.         addDefaultCelestialBodyLoader(CelestialBodyFactory.MOON,                    supportedNames);
  95.         addDefaultCelestialBodyLoader(CelestialBodyFactory.MARS,                    supportedNames);
  96.         addDefaultCelestialBodyLoader(CelestialBodyFactory.JUPITER,                 supportedNames);
  97.         addDefaultCelestialBodyLoader(CelestialBodyFactory.SATURN,                  supportedNames);
  98.         addDefaultCelestialBodyLoader(CelestialBodyFactory.URANUS,                  supportedNames);
  99.         addDefaultCelestialBodyLoader(CelestialBodyFactory.NEPTUNE,                 supportedNames);
  100.         addDefaultCelestialBodyLoader(CelestialBodyFactory.PLUTO,                   supportedNames);
  101.     }

  102.     /** Add the default loaders for celestial bodies.
  103.      * @param name name of the body (if not one of the predefined names, the method does nothing)
  104.      * @param supportedNames regular expression for supported files names
  105.      * (may be null if the default JPL file names are used)
  106.      * <p>
  107.      * The default loaders look for DE405 or DE406 JPL ephemerides.
  108.      * </p>
  109.      * @see <a href="ftp://ssd.jpl.nasa.gov/pub/eph/planets/Linux/de405/">DE405 JPL ephemerides</a>
  110.      * @see <a href="ftp://ssd.jpl.nasa.gov/pub/eph/planets/Linux/de406/">DE406 JPL ephemerides</a>
  111.      * @see #addCelestialBodyLoader(String, CelestialBodyLoader)
  112.      * @see #addDefaultCelestialBodyLoader(String)
  113.      * @see #clearCelestialBodyLoaders(String)
  114.      * @see #clearCelestialBodyLoaders()
  115.      */
  116.     public void addDefaultCelestialBodyLoader(final String name,
  117.                                               final String supportedNames) {

  118.         CelestialBodyLoader loader = null;
  119.         if (name.equalsIgnoreCase(CelestialBodyFactory.SOLAR_SYSTEM_BARYCENTER)) {
  120.             loader =
  121.                     new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.SOLAR_SYSTEM_BARYCENTER, dataProvidersManager, timeScales, gcrf);
  122.         } else if (name.equalsIgnoreCase(CelestialBodyFactory.SUN)) {
  123.             loader =
  124.                     new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.SUN, dataProvidersManager, timeScales, gcrf);
  125.         } else if (name.equalsIgnoreCase(CelestialBodyFactory.MERCURY)) {
  126.             loader =
  127.                     new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.MERCURY, dataProvidersManager, timeScales, gcrf);
  128.         } else if (name.equalsIgnoreCase(CelestialBodyFactory.VENUS)) {
  129.             loader =
  130.                     new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.VENUS, dataProvidersManager, timeScales, gcrf);
  131.         } else if (name.equalsIgnoreCase(CelestialBodyFactory.EARTH_MOON)) {
  132.             loader =
  133.                     new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.EARTH_MOON, dataProvidersManager, timeScales, gcrf);
  134.         } else if (name.equalsIgnoreCase(CelestialBodyFactory.EARTH)) {
  135.             loader =
  136.                     new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.EARTH, dataProvidersManager, timeScales, gcrf);
  137.         } else if (name.equalsIgnoreCase(CelestialBodyFactory.MOON)) {
  138.             loader =
  139.                     new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.MOON, dataProvidersManager, timeScales, gcrf);
  140.         } else if (name.equalsIgnoreCase(CelestialBodyFactory.MARS)) {
  141.             loader =
  142.                     new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.MARS, dataProvidersManager, timeScales, gcrf);
  143.         } else if (name.equalsIgnoreCase(CelestialBodyFactory.JUPITER)) {
  144.             loader =
  145.                     new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.JUPITER, dataProvidersManager, timeScales, gcrf);
  146.         } else if (name.equalsIgnoreCase(CelestialBodyFactory.SATURN)) {
  147.             loader =
  148.                     new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.SATURN, dataProvidersManager, timeScales, gcrf);
  149.         } else if (name.equalsIgnoreCase(CelestialBodyFactory.URANUS)) {
  150.             loader =
  151.                     new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.URANUS, dataProvidersManager, timeScales, gcrf);
  152.         } else if (name.equalsIgnoreCase(CelestialBodyFactory.NEPTUNE)) {
  153.             loader =
  154.                     new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.NEPTUNE, dataProvidersManager, timeScales, gcrf);
  155.         } else if (name.equalsIgnoreCase(CelestialBodyFactory.PLUTO)) {
  156.             loader =
  157.                     new JPLEphemeridesLoader(supportedNames, JPLEphemeridesLoader.EphemerisType.PLUTO, dataProvidersManager, timeScales, gcrf);
  158.         }

  159.         if (loader != null) {
  160.             addCelestialBodyLoader(name, loader);
  161.         }

  162.     }

  163.     /** Clear loaders for one celestial body.
  164.      * <p>
  165.      * Calling this method also clears the celestial body that
  166.      * has been loaded via this {@link CelestialBodyLoader}.
  167.      * </p>
  168.      * @param name name of the body
  169.      * @see #addCelestialBodyLoader(String, CelestialBodyLoader)
  170.      * @see #clearCelestialBodyLoaders()
  171.      * @see #clearCelestialBodyCache(String)
  172.      */
  173.     public void clearCelestialBodyLoaders(final String name) {
  174.         // use same synchronization order as in getBody to prevent deadlocks
  175.         synchronized (celestialBodyMap) {
  176.             // take advantage of reentrent synchronization as
  177.             // clearCelestialBodyCache uses the same lock inside
  178.             clearCelestialBodyCache(name);

  179.             synchronized (loadersMap) {
  180.                 loadersMap.remove(name);
  181.             }
  182.         }
  183.     }

  184.     /** Clear loaders for all celestial bodies.
  185.      * <p>
  186.      * Calling this method also clears all loaded celestial bodies.
  187.      * </p>
  188.      * @see #addCelestialBodyLoader(String, CelestialBodyLoader)
  189.      * @see #clearCelestialBodyLoaders(String)
  190.      * @see #clearCelestialBodyCache()
  191.      */
  192.     public void clearCelestialBodyLoaders() {
  193.         synchronized (celestialBodyMap) {
  194.             clearCelestialBodyCache();

  195.             synchronized (loadersMap) {
  196.                 loadersMap.clear();
  197.             }
  198.         }
  199.     }

  200.     /** Clear the specified celestial body from the internal cache.
  201.      * @param name name of the body
  202.      */
  203.     public void clearCelestialBodyCache(final String name) {
  204.         synchronized (celestialBodyMap) {
  205.             celestialBodyMap.remove(name);
  206.         }
  207.     }

  208.     /** Clear all loaded celestial bodies.
  209.      * <p>
  210.      * Calling this method will remove all loaded bodies from the internal
  211.      * cache. Subsequent calls to {@link #getBody(String)} or similar methods
  212.      * will result in a reload of the requested body from the configured loader(s).
  213.      * </p>
  214.      */
  215.     public void clearCelestialBodyCache() {
  216.         synchronized (celestialBodyMap) {
  217.             celestialBodyMap.clear();
  218.         }
  219.     }

  220.     @Override
  221.     public CelestialBody getSolarSystemBarycenter() {
  222.         return getBody(CelestialBodyFactory.SOLAR_SYSTEM_BARYCENTER);
  223.     }

  224.     @Override
  225.     public CelestialBody getSun() {
  226.         return getBody(CelestialBodyFactory.SUN);
  227.     }

  228.     @Override
  229.     public CelestialBody getMercury() {
  230.         return getBody(CelestialBodyFactory.MERCURY);
  231.     }

  232.     @Override
  233.     public CelestialBody getVenus() {
  234.         return getBody(CelestialBodyFactory.VENUS);
  235.     }

  236.     @Override
  237.     public CelestialBody getEarthMoonBarycenter() {
  238.         return getBody(CelestialBodyFactory.EARTH_MOON);
  239.     }

  240.     @Override
  241.     public CelestialBody getEarth() {
  242.         return getBody(CelestialBodyFactory.EARTH);
  243.     }

  244.     @Override
  245.     public CelestialBody getMoon() {
  246.         return getBody(CelestialBodyFactory.MOON);
  247.     }

  248.     @Override
  249.     public CelestialBody getMars() {
  250.         return getBody(CelestialBodyFactory.MARS);
  251.     }

  252.     @Override
  253.     public CelestialBody getJupiter() {
  254.         return getBody(CelestialBodyFactory.JUPITER);
  255.     }

  256.     @Override
  257.     public CelestialBody getSaturn() {
  258.         return getBody(CelestialBodyFactory.SATURN);
  259.     }

  260.     @Override
  261.     public CelestialBody getUranus() {
  262.         return getBody(CelestialBodyFactory.URANUS);
  263.     }

  264.     @Override
  265.     public CelestialBody getNeptune() {
  266.         return getBody(CelestialBodyFactory.NEPTUNE);
  267.     }

  268.     @Override
  269.     public CelestialBody getPluto() {
  270.         return getBody(CelestialBodyFactory.PLUTO);
  271.     }

  272.     /**
  273.      * {@inheritDoc}
  274.      *
  275.      * <p>
  276.      * If no {@link CelestialBodyLoader} has been added by calling {@link
  277.      * #addCelestialBodyLoader(String, CelestialBodyLoader) addCelestialBodyLoader} or if
  278.      * {@link #clearCelestialBodyLoaders(String) clearCelestialBodyLoaders} has been
  279.      * called afterwards, the {@link #addDefaultCelestialBodyLoader(String, String)
  280.      * addDefaultCelestialBodyLoader} method will be called automatically, once with the
  281.      * default name for JPL DE ephemerides and once with the default name for IMCCE INPOP
  282.      * files.
  283.      * </p>
  284.      */
  285.     @Override
  286.     public CelestialBody getBody(final String name) {
  287.         synchronized (celestialBodyMap) {
  288.             CelestialBody body = celestialBodyMap.get(name);
  289.             if (body == null) {
  290.                 synchronized (loadersMap) {
  291.                     List<CelestialBodyLoader> loaders = loadersMap.get(name);
  292.                     if (loaders == null || loaders.isEmpty()) {
  293.                         addDefaultCelestialBodyLoader(name, JPLEphemeridesLoader.DEFAULT_DE_SUPPORTED_NAMES);
  294.                         addDefaultCelestialBodyLoader(name, JPLEphemeridesLoader.DEFAULT_INPOP_SUPPORTED_NAMES);
  295.                         loaders = loadersMap.get(name);
  296.                     }
  297.                     OrekitException delayedException = null;
  298.                     for (CelestialBodyLoader loader : loaders) {
  299.                         try {
  300.                             body = loader.loadCelestialBody(name);
  301.                             if (body != null) {
  302.                                 break;
  303.                             }
  304.                         } catch (OrekitException oe) {
  305.                             delayedException = oe;
  306.                         }
  307.                     }
  308.                     if (body == null) {
  309.                         throw (delayedException != null) ?
  310.                                 delayedException :
  311.                                 new OrekitException(OrekitMessages.NO_DATA_LOADED_FOR_CELESTIAL_BODY, name);
  312.                     }

  313.                 }

  314.                 // save the body
  315.                 celestialBodyMap.put(name, body);

  316.             }

  317.             return body;

  318.         }
  319.     }

  320. }