AggregatedPVCoordinatesProvider.java

  1. /* Copyright 2002-2025 Joseph Reed
  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.  * Joseph Reed 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.utils;

  18. import java.util.Objects;

  19. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  20. import org.orekit.errors.OrekitException;
  21. import org.orekit.errors.OrekitMessages;
  22. import org.orekit.frames.Frame;
  23. import org.orekit.time.AbsoluteDate;

  24. /** Aggregate multiple {@link PVCoordinatesProvider} instances together.
  25.  * <p>
  26.  * This can be used to describe an aircraft or surface vehicle.
  27.  * </p>
  28.  * @author Joe Reed
  29.  * @since 11.3
  30.  */
  31. public class AggregatedPVCoordinatesProvider implements PVCoordinatesProvider {

  32.     /** Map of provider instances by transition time. */
  33.     private final TimeSpanMap<PVCoordinatesProvider> pvProvMap;

  34.     /** Earliest date at which {@link #getPVCoordinates(AbsoluteDate, Frame)} will return a valid result. */
  35.     private final AbsoluteDate minDate;

  36.     /** Latest date at which {@link #getPVCoordinates(AbsoluteDate, Frame)} will return a valid result. */
  37.     private final AbsoluteDate maxDate;

  38.     /** Class constructor.
  39.      * <p>
  40.      * Note the provided {@code map} is used directly. Modification of the
  41.      * map after calling this constructor may result in undefined behavior.
  42.      * </p>
  43.      * @param map the map of {@link PVCoordinatesProvider} instances by time.
  44.      */
  45.     public AggregatedPVCoordinatesProvider(final TimeSpanMap<PVCoordinatesProvider> map) {
  46.         this(map, null, null);
  47.     }

  48.     /** Class constructor.
  49.      * <p>
  50.      * Note the provided {@code map} is used directly. Modification of the
  51.      * map after calling this constructor may result in undefined behavior.
  52.      * </p>
  53.      * @param map the map of {@link PVCoordinatesProvider} instances by time.
  54.      * @param minDate the earliest valid date, {@code null} if always valid
  55.      * @param maxDate the latest valid date, {@code null} if always valid
  56.      */
  57.     public AggregatedPVCoordinatesProvider(final TimeSpanMap<PVCoordinatesProvider> map,
  58.                                            final AbsoluteDate minDate, final AbsoluteDate maxDate) {
  59.         this.pvProvMap = Objects.requireNonNull(map, "PVCoordinatesProvider map must be non-null");
  60.         this.minDate = minDate == null ? AbsoluteDate.PAST_INFINITY : minDate;
  61.         this.maxDate = maxDate == null ? AbsoluteDate.FUTURE_INFINITY : maxDate;
  62.     }

  63.     /** Get the first date of the range.
  64.      * @return the first date of the range
  65.      */
  66.     public AbsoluteDate getMinDate() {
  67.         return minDate;
  68.     }

  69.     /** Get the last date of the range.
  70.      * @return the last date of the range
  71.      */
  72.     public AbsoluteDate getMaxDate() {
  73.         return maxDate;
  74.     }

  75.     @Override
  76.     public Vector3D getPosition(final AbsoluteDate date, final Frame frame) {
  77.         if (date.isBefore(minDate) || date.isAfter(maxDate)) {
  78.             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_DATE, date, minDate, maxDate);
  79.         }
  80.         return pvProvMap.get(date).getPosition(date, frame);
  81.     }

  82.     @Override
  83.     public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {
  84.         if (date.isBefore(minDate) || date.isAfter(maxDate)) {
  85.             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_DATE, date, minDate, maxDate);
  86.         }
  87.         return pvProvMap.get(date).getPVCoordinates(date, frame);
  88.     }

  89.     /**
  90.      * Builder class for {@link AggregatedPVCoordinatesProvider}.
  91.      */
  92.     public static class Builder {

  93.         /** Time span map holding the incremental values. */
  94.         private final TimeSpanMap<PVCoordinatesProvider> pvProvMap;

  95.         /**
  96.          * Create a builder using the {@link InvalidPVProvider} as the initial provider.
  97.          */
  98.         public Builder() {
  99.             this(new InvalidPVProvider());
  100.         }

  101.         /**
  102.          * Create a builder using the provided initial provider.
  103.          *
  104.          * @param initialProvider the inital provider
  105.          */
  106.         public Builder(final PVCoordinatesProvider initialProvider) {
  107.             pvProvMap = new TimeSpanMap<>(initialProvider);
  108.         }

  109.         /** Add a {@link PVCoordinatesProvider} to the collection.
  110.          * <p>
  111.          * The provided date is the transition time, at which this provider will be used.
  112.          * </p>
  113.          * @param date the transition date
  114.          * @param pvProv the provider
  115.          * @param erasesLater if true, the entry erases all existing transitions that are later than {@code date}
  116.          * @return this builder instance
  117.          * @see TimeSpanMap#addValidAfter(Object, AbsoluteDate, boolean)
  118.          */
  119.         public Builder addPVProviderAfter(final AbsoluteDate date,
  120.                                           final PVCoordinatesProvider pvProv,
  121.                                           final boolean erasesLater) {
  122.             pvProvMap.addValidAfter(pvProv, date, erasesLater);
  123.             return this;
  124.         }

  125.         /** Add a {@link PVCoordinatesProvider} to the collection.
  126.          * <p>
  127.          * The provided date is the final transition time, before which this provider will be used.
  128.          * </p>
  129.          * @param date the transition date
  130.          * @param pvProv the provider
  131.          * @param erasesEarlier if true, the entry erases all existing transitions that are earlier than {@code date}
  132.          * @return this builder instance
  133.          * @see TimeSpanMap#addValidBefore(Object, AbsoluteDate, boolean)
  134.          */
  135.         public Builder addPVProviderBefore(final AbsoluteDate date, final PVCoordinatesProvider pvProv, final boolean erasesEarlier) {
  136.             pvProvMap.addValidBefore(pvProv, date, erasesEarlier);
  137.             return this;
  138.         }

  139.         /** Indicate the date before which the resulting PVCoordinatesProvider is invalid.
  140.          *
  141.          * @param firstValidDate first date at which the resuling provider should be valid
  142.          * @return this instance
  143.          */
  144.         public Builder invalidBefore(final AbsoluteDate firstValidDate) {
  145.             pvProvMap.addValidBefore(new InvalidPVProvider(), firstValidDate, true);
  146.             return this;
  147.         }

  148.         /** Indicate the date after which the resulting PVCoordinatesProvider is invalid.
  149.          *
  150.          * @param lastValidDate last date at which the resuling provider should be valid
  151.          * @return this instance
  152.          */
  153.         public Builder invalidAfter(final AbsoluteDate lastValidDate) {
  154.             pvProvMap.addValidAfter(new InvalidPVProvider(), lastValidDate, true);
  155.             return this;
  156.         }

  157.         /** Build the aggregated PVCoordinatesProvider.
  158.          *
  159.          * @return the new provider instance.
  160.          */
  161.         public AggregatedPVCoordinatesProvider build() {
  162.             AbsoluteDate minDate = null;
  163.             AbsoluteDate maxDate = null;
  164.             // check the first span
  165.             if (pvProvMap.getFirstTransition() != null) {
  166.                 if (pvProvMap.getFirstTransition().getBefore() instanceof InvalidPVProvider) {
  167.                     minDate = pvProvMap.getFirstTransition().getDate();
  168.                 }
  169.             }
  170.             if (pvProvMap.getLastTransition() != null) {
  171.                 if (pvProvMap.getLastTransition().getAfter() instanceof InvalidPVProvider) {
  172.                     maxDate = pvProvMap.getLastTransition().getDate();
  173.                 }
  174.             }
  175.             return new AggregatedPVCoordinatesProvider(pvProvMap, minDate, maxDate);
  176.         }
  177.     }

  178.     /** Implementation of {@link PVCoordinatesProvider} that throws an {@link OrekitException} exception.
  179.      *
  180.      */
  181.     public static class InvalidPVProvider implements PVCoordinatesProvider {

  182.         /** Empty constructor.
  183.          * <p>
  184.          * This constructor is not strictly necessary, but it prevents spurious
  185.          * javadoc warnings with JDK 18 and later.
  186.          * </p>
  187.          * @since 12.0
  188.          */
  189.         public InvalidPVProvider() {
  190.             // nothing to do
  191.         }

  192.         @Override
  193.         public TimeStampedPVCoordinates getPVCoordinates(final AbsoluteDate date, final Frame frame) {
  194.             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_DATE, date);
  195.         }

  196.     }

  197. }