LazyLoadedGeoMagneticFields.java
/* Contributed in the public domain.
* Licensed to CS Group (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.models.earth;
import java.util.Collection;
import java.util.NavigableMap;
import java.util.SortedMap;
import java.util.TreeMap;
import org.hipparchus.util.FastMath;
import org.orekit.data.DataProvidersManager;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.models.earth.GeoMagneticFieldFactory.FieldModel;
/**
* Loads magnetic fields on request and can be configured after creation. Designed to
* match the behavior of {@link GeoMagneticFieldFactory} in Orekit 10.0
*
* @author Evan Ward
* @author Thomas Neidhart
* @since 10.1
*/
public class LazyLoadedGeoMagneticFields implements GeoMagneticFields {
/** Loaded IGRF models. */
private NavigableMap<Integer, GeoMagneticField> igrfModels = null;
/** Loaded WMM models. */
private NavigableMap<Integer, GeoMagneticField> wmmModels = null;
/** Provides access to auxiliary data files. */
private final DataProvidersManager dataProvidersManager;
/**
* Create a factory for magnetic fields that uses the given data manager to load
* magnetic field files.
*
* @param dataProvidersManager provides access to auxiliary data files.
*/
public LazyLoadedGeoMagneticFields(final DataProvidersManager dataProvidersManager) {
this.dataProvidersManager = dataProvidersManager;
}
@Override
public GeoMagneticField getField(final FieldModel type, final double year) {
switch (type) {
case WMM:
return getWMM(year);
case IGRF:
return getIGRF(year);
default:
throw new OrekitException(OrekitMessages.NON_EXISTENT_GEOMAGNETIC_MODEL, type.name(), year);
}
}
@Override
public GeoMagneticField getIGRF(final double year) {
synchronized (this) {
if (igrfModels == null) {
igrfModels = loadModels("^IGRF\\.COF$");
}
return getModel(FieldModel.IGRF, igrfModels, year);
}
}
@Override
public GeoMagneticField getWMM(final double year) {
synchronized (this) {
if (wmmModels == null) {
wmmModels = loadModels("^WMM\\.COF$");
}
return getModel(FieldModel.WMM, wmmModels, year);
}
}
/** Loads the geomagnetic model files from the given filename. The loaded
* models are inserted in a {@link NavigableMap} with their epoch as key in order
* to retrieve them in a sorted manner.
* @param supportedNames a regular expression for valid filenames
* @return a {@link NavigableMap} of all loaded models
*/
private NavigableMap<Integer, GeoMagneticField> loadModels(final String supportedNames) {
NavigableMap<Integer, GeoMagneticField> loadedModels = null;
final GeoMagneticModelLoader loader = new GeoMagneticModelLoader();
dataProvidersManager.feed(supportedNames, loader);
if (!loader.stillAcceptsData()) {
final Collection<GeoMagneticField> models = loader.getModels();
if (models != null) {
loadedModels = new TreeMap<>();
for (GeoMagneticField model : models) {
// round to a precision of two digits after the comma
final int epoch = (int) FastMath.round(model.getEpoch() * 100d);
loadedModels.put(epoch, model);
}
}
}
// if no models could be loaded -> throw exception
if (loadedModels == null || loadedModels.size() == 0) {
throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_RESOURCE, supportedNames);
}
return loadedModels;
}
/** Gets a geomagnetic field model for the given year. In case the specified
* year does not match an existing model epoch, the resulting field is
* generated by either time-transforming an existing model using its secular
* variation coefficients, or by linear interpolating two existing models.
* @param type the type of the field (e.g. WMM or IGRF)
* @param models all loaded field models, sorted by their epoch
* @param year the epoch of the resulting field model
* @return a {@link GeoMagneticField} model for the given year
*/
private static GeoMagneticField getModel(final FieldModel type,
final NavigableMap<Integer, GeoMagneticField> models,
final double year) {
final int epochKey = (int) (year * 100d);
final SortedMap<Integer, GeoMagneticField> head = models.headMap(epochKey, true);
if (head.isEmpty()) {
throw new OrekitException(OrekitMessages.NON_EXISTENT_GEOMAGNETIC_MODEL, type.name(), year);
}
GeoMagneticField model = models.get(head.lastKey());
if (model.getEpoch() < year) {
if (model.supportsTimeTransform()) {
model = model.transformModel(year);
} else {
final SortedMap<Integer, GeoMagneticField> tail = models.tailMap(epochKey, false);
if (tail.isEmpty()) {
throw new OrekitException(OrekitMessages.NON_EXISTENT_GEOMAGNETIC_MODEL, type.name(), year);
}
final GeoMagneticField secondModel = models.get(tail.firstKey());
if (secondModel != model) {
model = model.transformModel(secondModel, year);
}
}
}
return model;
}
}