org.kalypso.ui.rrm.internal.conversion.to12_02.TimeseriesImporter.java Source code

Java tutorial

Introduction

Here is the source code for org.kalypso.ui.rrm.internal.conversion.to12_02.TimeseriesImporter.java

Source

/*----------------    FILE HEADER KALYPSO ------------------------------------------
 *
 *  This file is part of kalypso.
 *  Copyright (C) 2004 by:
 *
 *  Technical University Hamburg-Harburg (TUHH)
 *  Institute of River and coastal engineering
 *  Denickestrae 22
 *  21073 Hamburg, Germany
 *  http://www.tuhh.de/wb
 *
 *  and
 *
 *  Bjoernsen Consulting Engineers (BCE)
 *  Maria Trost 3
 *  56070 Koblenz, Germany
 *  http://www.bjoernsen.de
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  Contact:
 *
 *  E-Mail:
 *  belger@bjoernsen.de
 *  schlienger@bjoernsen.de
 *  v.doemming@tuhh.de
 *
 *  ---------------------------------------------------------------------------*/
package org.kalypso.ui.rrm.internal.conversion.to12_02;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;

import javax.xml.namespace.QName;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.joda.time.Interval;
import org.joda.time.LocalTime;
import org.joda.time.Period;
import org.kalypso.commons.java.io.FileUtilities;
import org.kalypso.commons.java.lang.Objects;
import org.kalypso.commons.time.PeriodUtils;
import org.kalypso.contribs.eclipse.core.runtime.IStatusCollector;
import org.kalypso.contribs.eclipse.core.runtime.StatusCollector;
import org.kalypso.contribs.java.util.CalendarUtilities.FIELD;
import org.kalypso.model.hydrology.binding.timeseries.IStation;
import org.kalypso.model.hydrology.binding.timeseries.IStationCollection;
import org.kalypso.model.hydrology.binding.timeseries.ITimeseries;
import org.kalypso.model.hydrology.binding.timeseries.StationUtils;
import org.kalypso.model.hydrology.project.RrmProject;
import org.kalypso.model.hydrology.timeseries.HydrologyTimeseriesImportWorker;
import org.kalypso.model.hydrology.timeseries.StationClassesCatalog;
import org.kalypso.ogc.gml.serialize.GmlSerializer;
import org.kalypso.ogc.sensor.DateRange;
import org.kalypso.ogc.sensor.IAxis;
import org.kalypso.ogc.sensor.IObservation;
import org.kalypso.ogc.sensor.ITupleModel;
import org.kalypso.ogc.sensor.ObservationUtilities;
import org.kalypso.ogc.sensor.SensorException;
import org.kalypso.ogc.sensor.metadata.ITimeseriesConstants;
import org.kalypso.ogc.sensor.metadata.MetadataHelper;
import org.kalypso.ogc.sensor.timeseries.AxisUtils;
import org.kalypso.ogc.sensor.timeseries.TimeseriesUtils;
import org.kalypso.ogc.sensor.util.Observations;
import org.kalypso.ogc.sensor.util.ZmlLink;
import org.kalypso.ogc.sensor.zml.ZmlFactory;
import org.kalypso.ui.rrm.internal.KalypsoUIRRMPlugin;
import org.kalypso.ui.rrm.internal.i18n.Messages;
import org.kalypso.ui.rrm.internal.timeseries.operations.StoreTimeseriesStatusOperation;
import org.kalypsodeegree.model.feature.GMLWorkspace;
import org.kalypsodeegree.model.feature.IFeatureBindingCollection;

import com.google.common.base.Charsets;

/**
 * Helper that imports the timeseries from the old 'Zeitreihen' folder into the new timeseries management.
 * 
 * @author Gernot Belger
 */
public class TimeseriesImporter {
    private final TimeseriesIndex m_timeseriesIndex = new TimeseriesIndex();

    private final File m_sourceDir;

    private IStationCollection m_stations;

    private final IStatusCollector m_log;

    private final IParameterTypeIndex m_parameterIndex;

    private final File m_stationsFile;

    public TimeseriesImporter(final File sourceDir, final File targetDir, final IStatusCollector log,
            final IParameterTypeIndex parameterIndex) {
        m_log = log;
        m_parameterIndex = parameterIndex;
        m_sourceDir = new File(sourceDir, INaProjectConstants.FOLDER_ZEITREIHEN);

        final IPath stationsGmlPath = RrmProject.getStationsGmlPath();
        m_stationsFile = new File(targetDir, stationsGmlPath.toOSString());
    }

    /** Read timeseries management */
    public void readStations() throws CoreException {
        try {
            final GMLWorkspace workspace = GmlSerializer.createGMLWorkspace(m_stationsFile, null);
            m_stations = (IStationCollection) workspace.getRootFeature();
        } catch (final Exception e) {
            e.printStackTrace();
            final IStatus status = new Status(IStatus.ERROR, KalypsoUIRRMPlugin.getID(),
                    Messages.getString("TimeseriesImporter_0"), e); //$NON-NLS-1$
            throw new CoreException(status);
        }
    }

    public void saveStations() throws CoreException {
        try {
            GmlSerializer.serializeWorkspace(m_stationsFile, m_stations.getWorkspace(), Charsets.UTF_8.name());
        } catch (final Exception e) {
            e.printStackTrace();
            final IStatus status = new Status(IStatus.ERROR, KalypsoUIRRMPlugin.getID(),
                    Messages.getString("TimeseriesImporter_1"), e); //$NON-NLS-1$
            throw new CoreException(status);
        }
    }

    public void copyTimeseries(final IProgressMonitor monitor) {
        final String name = Messages.getString("TimeseriesImporter_2", m_sourceDir.getName()); //$NON-NLS-1$
        monitor.beginTask(name, IProgressMonitor.UNKNOWN);

        final File sourceTimeseriesDir = m_sourceDir;

        /* Return, if directory does not exist */
        if (!sourceTimeseriesDir.isDirectory())
            return;

        final IStatusCollector stati = new StatusCollector(KalypsoUIRRMPlugin.getID());

        /* Find all .zml */
        final Iterator<File> zmlIterator = FileUtils.iterateFiles(sourceTimeseriesDir, new String[] { "zml" }, //$NON-NLS-1$
                true);
        for (final Iterator<File> iterator = zmlIterator; iterator.hasNext();) {
            final File zmlFile = iterator.next();
            monitor.subTask(zmlFile.getName());

            try {
                stati.add(importZml(sourceTimeseriesDir, zmlFile));
            } catch (final CoreException e) {
                stati.add(e.getStatus());
            } catch (final Exception e) {
                final String relativePath = FileUtilities.getRelativePathTo(sourceTimeseriesDir, zmlFile);

                final String message = String.format(Messages.getString("TimeseriesImporter_4"), relativePath); //$NON-NLS-1$
                final IStatus status = new Status(IStatus.WARNING, KalypsoUIRRMPlugin.getID(), message, e);
                stati.add(status);
            }

            monitor.worked(1);
        }

        /* Log it */
        final String message = Messages.getString("TimeseriesImporter_5", m_sourceDir.getName()); //$NON-NLS-1$
        final IStatus status = stati.asMultiStatusOrOK(message, message);
        m_log.add(status);

        monitor.done();
    }

    private IStatus importZml(final File baseDir, final File zmlFile)
            throws SensorException, CoreException, IOException {
        final IStatusCollector stati = new StatusCollector(KalypsoUIRRMPlugin.getID());

        final String baseName = FilenameUtils.removeExtension(zmlFile.getName());
        final String relativePath = FileUtilities.getRelativePathTo(baseDir, zmlFile);

        /* Read and check observation. */
        IObservation observation = readObservation(zmlFile, relativePath);

        if (observation == null) {
            stati.add(IStatus.INFO, Messages.getString("TimeseriesImporter.1")); //$NON-NLS-1$

            return stati.asMultiStatus(String.format(Messages.getString("TimeseriesImporter.0"), baseName)); //$NON-NLS-1$
        }

        final IAxis[] axes = observation.getAxes();

        final IAxis dateAxis = AxisUtils.findDateAxis(axes);
        if (dateAxis == null) {
            final String message = String.format(Messages.getString("TimeseriesImporter_6"), relativePath); //$NON-NLS-1$
            final IStatus status = new Status(IStatus.WARNING, KalypsoUIRRMPlugin.getID(), message);
            throw new CoreException(status);
        }

        final IAxis[] valueAxes = AxisUtils.findValueAxes(axes, true);
        if (valueAxes.length == 0) {
            final String message = String.format(Messages.getString("TimeseriesImporter_7"), relativePath); //$NON-NLS-1$
            final IStatus status = new Status(IStatus.WARNING, KalypsoUIRRMPlugin.getID(), message);
            throw new CoreException(status);
        } else if (valueAxes.length > 1) {
            final String message = String.format(Messages.getString("TimeseriesImporter_8"), relativePath); //$NON-NLS-1$
            final IStatus status = new Status(IStatus.WARNING, KalypsoUIRRMPlugin.getID(), message);
            throw new CoreException(status);
        }

        final IAxis valueAxis = valueAxes[0];
        final String parameterType = valueAxis.getType();

        final ITupleModel values = observation.getValues(null);
        final DateRange dateRange = Observations.findDateRange(values);

        /* Guess the timestep. */
        final Period timestep = TimeseriesUtils.guessTimestep(values);
        if (timestep != null) {
            final int amount = PeriodUtils.findCalendarAmount(timestep);
            final FIELD field = PeriodUtils.findCalendarField(timestep);
            MetadataHelper.setTimestep(observation.getMetadataList(), field.getField(), amount);
        }

        /* The timestamp is only relevant for day values. */
        final LocalTime timestamp = TimeseriesUtils.guessTimestamp(values, timestep);

        final RepairTimeseriesOperation repair = new RepairTimeseriesOperation(observation, timestep, timestamp,
                zmlFile.getName());
        final MultiStatus repairStatus = repair.execute(new NullProgressMonitor());
        /* add all children to avoid too deep status hierarchy */
        stati.addAll(Arrays.asList(repairStatus.getChildren()));

        observation = repair.getObservation();

        /* Assign station and timeseries parameters. */
        final String stationDescription = baseName;
        final String timeseriesDescription = baseName;
        final String groupName = findGroupName(relativePath);

        /* Add to timeseries management. */
        final IStation station = findOrCreateStation(stationDescription, groupName, parameterType, relativePath);

        /* Always create a new timeseries. */
        final ITimeseries newTimeseries = createTimeseries(station, timeseriesDescription, parameterType, timestep,
                dateRange);

        /* Copy observation file. */
        final ZmlLink dataLink = newTimeseries.getDataLink();

        /* We write the file from the read observation (instead of copy) */
        /* in order to compress the data and add status and source axes (now required). */
        final HydrologyTimeseriesImportWorker cleanupWorker = new HydrologyTimeseriesImportWorker(observation,
                dateRange);
        final IObservation observationWithSource = cleanupWorker.convert(newTimeseries.getTimestep(), timestamp);

        /* Save the observation. */
        dataLink.saveObservation(observationWithSource);

        /* Add as new entry into timeseries index (used later for catchment guessing). */
        final IPath sourceDirPath = new Path(m_sourceDir.getParent());
        final IPath sourcePath = new Path(zmlFile.getAbsolutePath());
        final IPath relativeSourcePath = sourcePath.makeRelativeTo(sourceDirPath);

        final Interval interval = toInterval(dateRange);

        final TimeseriesIndexEntry newEntry = new TimeseriesIndexEntry(relativeSourcePath, dataLink.getHref(),
                parameterType, timestep, timestamp, interval);
        m_timeseriesIndex.addEntry(newEntry);

        final MultiStatus status = stati
                .asMultiStatus(String.format(Messages.getString("TimeseriesImporter.0"), baseName)); //$NON-NLS-1$
        final StoreTimeseriesStatusOperation storeStatusOperation = new StoreTimeseriesStatusOperation(
                newTimeseries, status);
        stati.add(storeStatusOperation.execute(new NullProgressMonitor()));

        return status;
    }

    private Interval toInterval(final DateRange dateRange) {
        if (dateRange == null)
            return null;

        final Date to = dateRange.getTo();
        final Date from = dateRange.getFrom();

        if (Objects.isNull(from, to))
            return null;

        return new Interval(from.getTime(), to.getTime());
    }

    private IObservation readObservation(final File zmlFile, final String relativePath)
            throws SensorException, MalformedURLException {
        final IObservation observation = ZmlFactory.parseXML(zmlFile.toURI().toURL());
        if (observation.isEmpty())
            return null;

        final String forcedParmaterType = getForcedParameterType(observation, relativePath);
        if (forcedParmaterType == null)
            return observation;

        return ObservationUtilities.forceParameterType(observation, forcedParmaterType);
    }

    private String getForcedParameterType(final IObservation observation, final String relativePath) {
        final String parmeterType = m_parameterIndex.getParameterType(relativePath);
        if (StringUtils.equals(ITimeseriesConstants.TYPE_TEMPERATURE, parmeterType))
            return ITimeseriesConstants.TYPE_MEAN_TEMPERATURE;

        if (StringUtils.equals(ITimeseriesConstants.TYPE_EVAPORATION, parmeterType))
            return ITimeseriesConstants.TYPE_EVAPORATION_LAND_BASED;

        if (parmeterType == null) {
            final IAxis valueAxis = AxisUtils.findValueAxis(observation.getAxes(), true);
            if (valueAxis != null && StringUtils.equals(ITimeseriesConstants.TYPE_TEMPERATURE, valueAxis.getType()))
                return ITimeseriesConstants.TYPE_MEAN_TEMPERATURE;

            if (valueAxis != null && StringUtils.equals(ITimeseriesConstants.TYPE_EVAPORATION, valueAxis.getType()))
                return ITimeseriesConstants.TYPE_EVAPORATION_LAND_BASED;
        }

        return parmeterType;
    }

    private String findGroupName(final String relativePath) {
        final Path path = new Path(relativePath);
        if (path.segmentCount() < 2)
            return null;

        final IPath relativeFolders = path.removeLastSegments(1);
        final String portableRelativeFolders = relativeFolders.toPortableString();
        return portableRelativeFolders.replace(Path.SEPARATOR, '_');
    }

    private IStation findOrCreateStation(final String description, final String group, final String parameterType,
            final String relativePath) throws CoreException {
        final QName stationType = StationClassesCatalog.getTypeFor(parameterType);
        if (stationType == null) {
            final String message = String.format(Messages.getString("TimeseriesImporter_9"), parameterType, //$NON-NLS-1$
                    relativePath);
            final IStatus status = new Status(IStatus.WARNING, KalypsoUIRRMPlugin.getID(), message);
            throw new CoreException(status);
        }

        // REMARK: always create a new station, because we can never have two stations with the same station name
        int count = 1;
        while (true) {
            final String stationName = buildNewStationName(description, count++);

            /* Search for a station that does not exist */
            final IStation existingStation = findStation(stationName);
            if (existingStation == null)
                return createNewStation(stationName, group, stationType);
        }
    }

    private String buildNewStationName(final String description, final int count) {
        String stationName;
        if (count == 1)
            stationName = description;
        else
            stationName = String.format("%s (%s)", description, count); //$NON-NLS-1$
        return stationName;
    }

    private IStation findStation(final String description) {
        final String foldername = StationUtils.getTimeseriesFoldername(description);

        // REMARK: linear search here, we assume we do not have too many stations...

        final IFeatureBindingCollection<IStation> stations = m_stations.getStations();
        for (final IStation station : stations) {
            if (foldername.equals(station.getTimeseriesFoldername()))
                return station;
        }

        return null;
    }

    private IStation createNewStation(final String description, final String group, final QName stationType) {
        final IFeatureBindingCollection<IStation> stations = m_stations.getStations();

        final IStation newStation = stations.addNew(stationType);
        newStation.setDescription(description);
        newStation.setComment(Messages.getString("TimeseriesImporter_10")); //$NON-NLS-1$
        newStation.setGroup(group);

        return newStation;
    }

    private ITimeseries createTimeseries(final IStation station, final String timeseriesDescription,
            final String parameterType, final Period timestep, final DateRange daterange) {
        final ITimeseries newTimeseries = station.getTimeseries().addNew(ITimeseries.FEATURE_TIMESERIES);
        newTimeseries.setDescription(timeseriesDescription);

        final String quality = findUniqueQuality(station, parameterType, timestep);

        newTimeseries.setParameterType(parameterType);
        newTimeseries.setQuality(quality);
        newTimeseries.setTimestep(timestep);
        newTimeseries.setMeasurementStart(daterange.getFrom());
        newTimeseries.setMeasurementEnd(daterange.getTo());

        return newTimeseries;
    }

    /**
     * @return unique quality string - sometimes a time series with the same parameter type, time step, ... exists multi
     *         times in a project
     */
    private String findUniqueQuality(final IStation station, final String parameterType, final Period timestep) {
        final FindUniqueQualityVisitor visitor = new FindUniqueQualityVisitor(parameterType, timestep);

        final IFeatureBindingCollection<ITimeseries> timeseries = station.getTimeseries();
        timeseries.accept(visitor);

        final String base = Messages.getString("TimeseriesImporter_11"); //$NON-NLS-1$
        int count = 1;
        String quality = base;
        while (visitor.hasQuality(quality)) {
            quality = String.format("%s (%d)", base, count); //$NON-NLS-1$
            count++;
        }

        return quality;
    }

    public TimeseriesIndex getIndex() {
        return m_timeseriesIndex;
    }
}