/*
* Constellation - An open source and standard compliant SDI
* http://www.constellation-sdi.org
*
* Copyright 2014 Geomatys.
*
* Licensed 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.constellation.openoffice;
// J2SE dependencies
import com.sun.star.beans.XPropertySet;
import com.sun.star.comp.loader.FactoryHelper;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.lang.XSingleServiceFactory;
import com.sun.star.registry.XRegistryKey;
import org.constellation.observation.CatalogException;
import org.constellation.observation.coverage.DynamicCoverage;
import org.geotoolkit.coverage.SpatioTemporalCoverage3D;
import org.geotools.openoffice.Formulas;
import org.geotools.openoffice.MethodInfo;
import org.opengis.geometry.DirectPosition;
import java.awt.geom.Point2D;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.apache.sis.util.logging.Logging;
/**
* Implémentation de l'interface {@link XObservations} qui sera exportée vers
* <A HREF="http://www.openoffice.org">OpenOffice</A>.
*
* @version $Id$
* @author Martin Desruisseaux
*/
public class Observations extends Formulas implements XObservations {
/**
* The logger to use for all message to log in this package.
*/
private static final Logger LOGGER = Logging.getLogger("org.constellation.openoffice");
/**
* Le nom sous lequel sera enregistré cette composante.
*
* <strong>Note:</strong> Bien que ce nom de correspondent pas aux conventions
* du Java, on ne peut le changer car il correspond au nom attendu par OpenOffice.
*/
private static final String __serviceName = "org.constellation.openoffice.Observations";
/**
* Le service que l'on étend.
*/
private static final String ADDIN_SERVICE = "com.sun.star.sheet.AddIn";
/**
* Ensemble des couvertures construites pour un descripteur donné.
*/
private final Map<String, Reference<SpatioTemporalCoverage3D>> descriptors =
new HashMap<String, Reference<SpatioTemporalCoverage3D>>();
/**
* Tableau pré-alloué pour la méthode {@link #getDescriptorValue}.
*/
private transient double[] values;
/**
* Construit une nouvelle instance du service.
*/
@SuppressWarnings("unchecked")
public Observations() {
methods.put("getDescriptorValue", new MethodInfo("Observations", "EVALUATE",
"Évalue la valeur d'un descripteur du paysage océanique " +
"à une position spatio-temporelle donnée.",
new String[] {
"xOptions", "Fournit par OpenOffice.",
"Descripteur", "Nom du descripteur du paysage océanique.",
"Date", "Date et heure à laquelle évaluer le descripteur.",
"Longitude", "Longitude (en degrés) à laquelle évaluer le descripteur.",
"Latitude", "Latitude (en degrés) à laquelle évaluer le descripteur."
}));
methods.put("getVoxelCenter", new MethodInfo("Observations", "VOXEL.CENTER",
"Retourne la coordonnée spatio-temporelle au centre du voxel le plus proche. " +
"L'appel de la fonction EVALUATE à cette coordonnée ne devrait pas impliquer d'interpolations.",
new String[] {
"xOptions", "Fournit par OpenOffice.",
"Descripteur", "Nom du descripteur du paysage océanique.",
"Date", "Date et heure.",
"Longitude", "Longitude (en degrés).",
"Latitude", "Latitude (en degrés)."
}));
}
/**
* Returns a factory for creating the service.
* This method is called by the {@code com.sun.star.comp.loader.JavaLoader}.
*
* @param implementation The name of the implementation for which a service is desired.
* @param factories The service manager to be used if needed.
* @param registry The registryKey
* @return A factory for creating the component.
*/
public static XSingleServiceFactory __getServiceFactory(
final String implementation,
final XMultiServiceFactory factories,
final XRegistryKey registry)
{
if (implementation.equals(Observations.class.getName())) {
return FactoryHelper.getServiceFactory(Observations.class, __serviceName, factories, registry);
}
return Numeric.__getServiceFactory(implementation, factories, registry);
}
/**
* Writes the service information into the given registry key.
* This method is called by the {@code com.sun.star.comp.loader.JavaLoader}.
*
* @param registry The registry key.
* @return {@code true} if the operation succeeded.
*/
public static boolean __writeRegistryServiceInfo(final XRegistryKey registry) {
final String classname = Observations.class.getName();
return FactoryHelper.writeRegistryServiceInfo(classname, __serviceName, registry)
&& FactoryHelper.writeRegistryServiceInfo(classname, ADDIN_SERVICE, registry)
&& Numeric.__writeRegistryServiceInfo(registry);
}
/**
* The service name that can be used to create such an object by a factory.
*/
public String getServiceName() {
return __serviceName;
}
/**
* Provides the supported service names of the implementation, including also
* indirect service names.
*
* @return Sequence of service names that are supported.
*/
public String[] getSupportedServiceNames() {
return new String[] {ADDIN_SERVICE, __serviceName};
}
/**
* Tests whether the specified service is supported, i.e. implemented by the implementation.
*
* @param name Name of service to be tested.
* @return {@code true} if the service is supported, {@code false} otherwise.
*/
public boolean supportsService(final String name) {
return name.equals(ADDIN_SERVICE) || name.equals(__serviceName);
}
/**
* Retourne les données pour un descripteur du nom spécifié.
*
* @param name Le nom du {@linkplain Descriptor descripteur}.
* @return La converture des données pour le descripteur spécifié.
* @throws NoSuchRecordException si aucun descripteur n'a été trouvée pour le nom spécifié.
* @throws CatalogException si une erreur est survenue lors de l'interrogation du catalogue.
*/
private SpatioTemporalCoverage3D getDescriptorCoverage(final String descriptor)
throws CatalogException
{
final org.constellation.observation.Observations obs = org.constellation.observation.Observations.getDefault();
// Pour la synchronisation, utilise le même verrou que 'Observations.getDescriptorCoverage'.
synchronized (obs) {
Reference<SpatioTemporalCoverage3D> ref = descriptors.get(descriptor);
if (ref != null) {
final SpatioTemporalCoverage3D coverage = ref.get();
if (coverage != null) {
return coverage;
}
}
final SpatioTemporalCoverage3D coverage = new SpatioTemporalCoverage3D(descriptor,
obs.getDescriptorCoverage(descriptor));
ref = new SoftReference<SpatioTemporalCoverage3D>(coverage);
descriptors.put(descriptor, ref);
return coverage;
}
}
/**
* {inheritDoc}
*
* The XPropertySet contains a PropertyValue named "NullDate",
* which is the mentioned base for all date calculations in Calc.
*/
public Object getDescriptorValue(final XPropertySet xOptions,
final String descriptor,
final double t,
final double x,
final double y)
{
try {
final Date time = toDate(xOptions, t);
final Point2D.Double position = new Point2D.Double(x,y);
final SpatioTemporalCoverage3D coverage = getDescriptorCoverage(descriptor);
synchronized (coverage) {
values = coverage.evaluate(position, time, values);
}
return new Double(values[0]);
} catch (Throwable exception) {
// Attrape aussi NoClassDefFoundError, qui se produit souvent.
return getLocalizedMessage(exception);
}
}
/**
* {inheritDoc}
*/
public double[][] getVoxelCenter(final XPropertySet xOptions,
final String descriptor,
final double t,
final double x,
final double y)
{
try {
Point2D position = new Point2D.Double(x,y);
Date date = getEpoch(xOptions);
if (date == null) {
// Un message a déjà été enregistré dans le journal par getEpoch.
return null;
}
final long epoch = date.getTime();
date.setTime(date.getTime() + Math.round(t * DAY_TO_MILLIS));
final SpatioTemporalCoverage3D coverage = getDescriptorCoverage(descriptor);
final DirectPosition coord = ((DynamicCoverage) coverage.getWrappedCoverage())
.snap(coverage.toDirectPosition(position, date));
position = coverage.toPoint2D(coord);
date = coverage.toDate (coord);
return new double[][] {{(date.getTime() - epoch) / (double)DAY_TO_MILLIS,
position.getX(), position.getY()}};
} catch (Throwable exception) {
// Attrape aussi NoClassDefFoundError, qui se produit souvent.
reportException("getVoxelCenter", exception);
return null;
}
}
/**
* Retourne le journal dans lequel écrire d'éventuels avertissements.
*/
@Override
protected Logger getLogger() {
return LOGGER;
}
}