org.mwc.debrief.track_shift.views.StackedDotHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.mwc.debrief.track_shift.views.StackedDotHelper.java

Source

/*
 *    Debrief - the Open Source Maritime Analysis Application
 *    http://debrief.info
 *
 *    (C) 2000-2014, PlanetMayo Ltd
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the Eclipse Public License v1.0
 *    (http://www.eclipse.org/legal/epl-v10.html)
 *
 *    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. 
 */
package org.mwc.debrief.track_shift.views;

import java.awt.Color;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Vector;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.swt.widgets.Composite;
import org.jfree.chart.plot.XYPlot;
import org.jfree.data.general.SeriesException;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.mwc.cmap.core.CorePlugin;
import org.mwc.cmap.core.DataTypes.TrackData.TrackDataProvider;

import Debrief.Wrappers.FixWrapper;
import Debrief.Wrappers.ISecondaryTrack;
import Debrief.Wrappers.SensorContactWrapper;
import Debrief.Wrappers.SensorWrapper;
import Debrief.Wrappers.TrackWrapper;
import Debrief.Wrappers.Track.Doublet;
import Debrief.Wrappers.Track.TrackSegment;
import Debrief.Wrappers.Track.TrackWrapper_Support.SegmentList;
import MWC.GUI.Editable;
import MWC.GUI.ErrorLogger;
import MWC.GUI.JFreeChart.ColouredDataItem;
import MWC.GenericData.HiResDate;
import MWC.GenericData.TimePeriod;
import MWC.GenericData.Watchable;
import MWC.GenericData.WatchableList;
import MWC.GenericData.WorldLocation;
import MWC.TacticalData.Fix;

public final class StackedDotHelper {
    /**
     * the track being dragged
     */
    private TrackWrapper _primaryTrack;

    /**
     * the secondary track we're monitoring
     */
    private ISecondaryTrack _secondaryTrack;

    /**
     * the set of points to watch on the primary track. This is stored as a sorted
     * set because if we have multiple sensors they may be suppled in
     * chronological order, or they may represent overlapping time periods
     */
    private TreeSet<Doublet> _primaryDoublets;

    // ////////////////////////////////////////////////
    // CONSTRUCTOR
    // ////////////////////////////////////////////////

    // ////////////////////////////////////////////////
    // MEMBER METHODS
    // ////////////////////////////////////////////////

    public TreeSet<Doublet> getDoublets(final boolean onlyVis, final boolean needBearing,
            final boolean needFrequency) {
        return getDoublets(_primaryTrack, _secondaryTrack, onlyVis, needBearing, needFrequency);
    }

    /**
     * sort out data of interest
     * 
     */
    public static TreeSet<Doublet> getDoublets(final TrackWrapper sensorHost, final ISecondaryTrack targetTrack,
            final boolean onlyVis, final boolean needBearing, final boolean needFrequency) {
        final TreeSet<Doublet> res = new TreeSet<Doublet>();

        // friendly fix-wrapper to save us repeatedly creating it
        final FixWrapper index = new FixWrapper(new Fix(null, new WorldLocation(0, 0, 0), 0.0, 0.0));

        // loop through our sensor data
        final Enumeration<Editable> sensors = sensorHost.getSensors().elements();
        if (sensors != null) {
            while (sensors.hasMoreElements()) {
                final SensorWrapper wrapper = (SensorWrapper) sensors.nextElement();
                if (!onlyVis || (onlyVis && wrapper.getVisible())) {
                    final Enumeration<Editable> cuts = wrapper.elements();
                    while (cuts.hasMoreElements()) {
                        final SensorContactWrapper scw = (SensorContactWrapper) cuts.nextElement();
                        if (!onlyVis || (onlyVis && scw.getVisible())) {
                            // is this cut suitable for what we're looking for?
                            if (needBearing) {
                                if (!scw.getHasBearing())
                                    continue;
                            }

                            // aaah, but does it meet the frequency requirement?
                            if (needFrequency) {
                                if (!scw.getHasFrequency())
                                    continue;
                            }

                            FixWrapper targetFix = null;
                            TrackSegment targetParent = null;

                            if (targetTrack != null) {
                                // right, get the track segment and fix nearest to
                                // this
                                // DTG
                                final Enumeration<Editable> trkData = targetTrack.segments();
                                final Vector<TrackSegment> _theSegments = new Vector<TrackSegment>();

                                while (trkData.hasMoreElements()) {

                                    final Editable thisI = trkData.nextElement();
                                    if (thisI instanceof SegmentList) {
                                        final SegmentList thisList = (SegmentList) thisI;
                                        final Enumeration<Editable> theElements = thisList.elements();
                                        while (theElements.hasMoreElements()) {
                                            final TrackSegment ts = (TrackSegment) theElements.nextElement();
                                            _theSegments.add(ts);
                                        }

                                    }
                                    if (thisI instanceof TrackSegment) {
                                        final TrackSegment ts = (TrackSegment) thisI;
                                        _theSegments.add(ts);
                                    }
                                }

                                if (_theSegments.size() > 0) {
                                    final Iterator<TrackSegment> iter = _theSegments.iterator();
                                    while (iter.hasNext()) {
                                        final TrackSegment ts = iter.next();

                                        final TimePeriod validPeriod = new TimePeriod.BaseTimePeriod(ts.startDTG(),
                                                ts.endDTG());
                                        if (validPeriod.contains(scw.getDTG())) {
                                            // sorted. here we go
                                            targetParent = ts;

                                            // create an object with the right time
                                            index.getFix().setTime(scw.getDTG());

                                            // and find any matching items
                                            final SortedSet<Editable> items = ts.tailSet(index);
                                            if (items.size() > 0) {
                                                targetFix = (FixWrapper) items.first();
                                            }
                                        }

                                    }
                                }
                            }

                            final Watchable[] matches = sensorHost.getNearestTo(scw.getDTG());
                            if ((matches != null) && (matches.length > 0) && (targetFix != null)) {
                                final FixWrapper hostFix = (FixWrapper) matches[0];

                                final Doublet thisDub = new Doublet(scw, targetFix, targetParent, hostFix);

                                // if we've no target track add all the points
                                if (targetTrack == null) {
                                    // store our data
                                    res.add(thisDub);
                                } else {
                                    // if we've got a target track we only add points
                                    // for which we
                                    // have
                                    // a target location
                                    if (targetFix != null) {
                                        // store our data
                                        res.add(thisDub);
                                    }
                                } // if we know the track
                            } // if there are any matching items
                              // if we find a match
                        } // if cut is visible
                    } // loop through cuts
                } // if sensor is visible
            } // loop through sensors
        } // if there are sensors

        return res;
    }

    /**
     * ok, our track has been dragged, calculate the new series of offsets
     * 
     * @param linePlot
     * @param dotPlot
     * @param onlyVis
     * @param showCourse
     * @param b
     * @param holder
     * @param logger
     * 
     * @param currentOffset
     *          how far the current track has been dragged
     */
    public void updateBearingData(final XYPlot dotPlot, final XYPlot linePlot, final TrackDataProvider tracks,
            final boolean onlyVis, final boolean showCourse, final boolean flipAxes, final Composite holder,
            final ErrorLogger logger, final boolean updateDoublets) {
        // do we even have a primary track
        if (_primaryTrack == null)
            return;

        // ok, find the track wrappers
        if (_secondaryTrack == null)
            initialise(tracks, false, onlyVis, holder, logger, "Bearing", true, false);

        // did it work?
        // if (_secondaryTrack == null)
        // return;

        // ok - the tracks have moved. better update the doublets
        if (updateDoublets)
            updateDoublets(onlyVis, true, false);

        // aah - but what if we've ditched our doublets?
        if ((_primaryDoublets == null) || (_primaryDoublets.size() == 0)) {
            // better clear the plot
            dotPlot.setDataset(null);
            linePlot.setDataset(null);
            return;
        }

        // create the collection of series
        final TimeSeriesCollection errorSeries = new TimeSeriesCollection();
        final TimeSeriesCollection actualSeries = new TimeSeriesCollection();

        // produce a dataset for each track
        final TimeSeries errorValues = new TimeSeries(_primaryTrack.getName());

        final TimeSeries measuredValues = new TimeSeries("Measured");
        final TimeSeries ambigValues = new TimeSeries("Ambiguous Bearing");
        final TimeSeries calculatedValues = new TimeSeries("Calculated");

        final TimeSeries osCourseValues = new TimeSeries("Course");

        // ok, run through the points on the primary track
        final Iterator<Doublet> iter = _primaryDoublets.iterator();
        while (iter.hasNext()) {
            final Doublet thisD = iter.next();

            try {
                // obvious stuff first (stuff that doesn't need the tgt data)
                final Color thisColor = thisD.getColor();
                double measuredBearing = thisD.getMeasuredBearing();
                double ambigBearing = thisD.getAmbiguousMeasuredBearing();
                final HiResDate currentTime = thisD.getDTG();
                final FixedMillisecond thisMilli = new FixedMillisecond(currentTime.getDate().getTime());

                // put the measured bearing back in the positive domain
                if (measuredBearing < 0)
                    measuredBearing += 360d;

                // stop, stop, stop - do we wish to plot bearings in the +/- 180 domain?
                if (flipAxes)
                    if (measuredBearing > 180)
                        measuredBearing -= 360;

                final ColouredDataItem mBearing = new ColouredDataItem(thisMilli, measuredBearing, thisColor, false,
                        null);

                // and add them to the series
                measuredValues.add(mBearing);

                if (ambigBearing != Doublet.INVALID_BASE_FREQUENCY) {
                    if (flipAxes)
                        if (ambigBearing > 180)
                            ambigBearing -= 360;

                    final ColouredDataItem amBearing = new ColouredDataItem(thisMilli, ambigBearing, thisColor,
                            false, null);
                    ambigValues.add(amBearing);
                }

                // do we have target data?
                if (thisD.getTarget() != null) {
                    double calculatedBearing = thisD.getCalculatedBearing(null, null);
                    final Color calcColor = thisD.getTarget().getColor();
                    final double thisError = thisD.calculateBearingError(measuredBearing, calculatedBearing);
                    final ColouredDataItem newError = new ColouredDataItem(thisMilli, thisError, thisColor, false,
                            null);

                    if (flipAxes)
                        if (calculatedBearing > 180)
                            calculatedBearing -= 360;

                    final ColouredDataItem cBearing = new ColouredDataItem(thisMilli, calculatedBearing, calcColor,
                            true, null);

                    errorValues.add(newError);
                    calculatedValues.add(cBearing);
                }

            } catch (final SeriesException e) {
                CorePlugin.logError(Status.INFO, "some kind of trip whilst updating bearing plot", e);
            }

        }

        // right, we do course in a special way, since it isn't dependent on the
        // target track. Do course here.
        HiResDate startDTG, endDTG;

        // just double-check we've still got our primary doublets
        if (_primaryDoublets == null) {
            CorePlugin.logError(Status.WARNING, "FOR SOME REASON PRIMARY DOUBLETS IS NULL - INVESTIGATE", null);
            return;
        }

        if (_primaryDoublets.size() == 0) {
            CorePlugin.logError(Status.WARNING, "FOR SOME REASON PRIMARY DOUBLETS IS ZERO LENGTH - INVESTIGATE",
                    null);
            return;
        }

        startDTG = _primaryDoublets.first().getDTG();
        endDTG = _primaryDoublets.last().getDTG();

        if (startDTG.greaterThan(endDTG)) {
            System.err.println("in the wrong order, start:" + startDTG + " end:" + endDTG);
            return;
        }

        final Collection<Editable> hostFixes = _primaryTrack.getItemsBetween(startDTG, endDTG);

        // loop through th items
        for (final Iterator<Editable> iterator = hostFixes.iterator(); iterator.hasNext();) {
            final Editable editable = (Editable) iterator.next();
            final FixWrapper fw = (FixWrapper) editable;
            final FixedMillisecond thisMilli = new FixedMillisecond(fw.getDateTimeGroup().getDate().getTime());
            double ownshipCourse = MWC.Algorithms.Conversions.Rads2Degs(fw.getCourse());

            // stop, stop, stop - do we wish to plot bearings in the +/- 180 domain?
            if (flipAxes)
                if (ownshipCourse > 180)
                    ownshipCourse -= 360;

            final ColouredDataItem crseBearing = new ColouredDataItem(thisMilli, ownshipCourse, fw.getColor(), true,
                    null);
            osCourseValues.add(crseBearing);
        }

        // ok, add these new series

        if (showCourse) {
            actualSeries.addSeries(osCourseValues);
        }

        if (errorValues.getItemCount() > 0)
            errorSeries.addSeries(errorValues);

        actualSeries.addSeries(measuredValues);

        if (ambigValues.getItemCount() > 0)
            actualSeries.addSeries(ambigValues);

        if (calculatedValues.getItemCount() > 0)
            actualSeries.addSeries(calculatedValues);

        dotPlot.setDataset(errorSeries);
        linePlot.setDataset(actualSeries);
    }

    /**
     * initialise the data, check we've got sensor data & the correct number of
     * visible tracks
     * 
     * @param showError
     * @param onlyVis
     * @param holder
     */
    void initialise(final TrackDataProvider tracks, final boolean showError, final boolean onlyVis,
            final Composite holder, final ErrorLogger logger, final String dataType, final boolean needBrg,
            final boolean needFreq) {

        // have we been created?
        if (holder == null)
            return;

        // are we visible?
        if (holder.isDisposed())
            return;

        _secondaryTrack = null;
        _primaryTrack = null;

        // do we have some data?
        if (tracks == null) {
            // output error message
            logger.logError(IStatus.INFO, "Please open a Debrief plot", null);
            return;
        }

        // check we have a primary track
        final WatchableList priTrk = tracks.getPrimaryTrack();
        if (priTrk == null) {
            logger.logError(IStatus.INFO, "A primary track must be placed on the Tote", null);
            return;
        } else {
            if (!(priTrk instanceof TrackWrapper)) {
                logger.logError(IStatus.INFO, "The primary track must be a vehicle track", null);
                return;
            } else
                _primaryTrack = (TrackWrapper) priTrk;
        }

        // now the sec track
        final WatchableList[] secs = tracks.getSecondaryTracks();

        // any?
        if ((secs == null) || (secs.length == 0)) {
        } else {
            // too many?
            if (secs.length > 1) {
                logger.logError(IStatus.INFO, "Only 1 secondary track may be on the tote", null);
                return;
            }

            // correct sort?
            final WatchableList secTrk = secs[0];
            if (!(secTrk instanceof ISecondaryTrack)) {
                logger.logError(IStatus.INFO, "The secondary track must be a vehicle track", null);
                return;
            } else {
                _secondaryTrack = (ISecondaryTrack) secTrk;
            }

        }

        // must have worked, hooray
        logger.logError(IStatus.OK, dataType + " error", null);

        // ok, get the positions
        updateDoublets(onlyVis, needBrg, needFreq);

    }

    /**
     * clear our data, all is finished
     */
    public void reset() {
        if (_primaryDoublets != null)
            _primaryDoublets.clear();
        _primaryDoublets = null;
        _primaryTrack = null;
        _secondaryTrack = null;
    }

    /**
     * go through the tracks, finding the relevant position on the other track.
     * 
     */
    private void updateDoublets(final boolean onlyVis, final boolean needBearing, final boolean needFreq) {
        // ok - we're now there
        // so, do we have primary and secondary tracks?
        if (_primaryTrack != null) {
            // cool sort out the list of sensor locations for these tracks
            _primaryDoublets = getDoublets(_primaryTrack, _secondaryTrack, onlyVis, needBearing, needFreq);
        }
    }

    /**
     * ok, our track has been dragged, calculate the new series of offsets
     * 
     * @param linePlot
     * @param dotPlot
     * @param onlyVis
     * @param holder
     * @param logger
     * 
     * @param currentOffset
     *          how far the current track has been dragged
     */
    public void updateFrequencyData(final XYPlot dotPlot, final XYPlot linePlot, final TrackDataProvider tracks,
            final boolean onlyVis, final Composite holder, final ErrorLogger logger, final boolean updateDoublets) {

        // do we have anything?
        if (_primaryTrack == null)
            return;

        // ok, find the track wrappers
        if (_secondaryTrack == null)
            initialise(tracks, false, onlyVis, holder, logger, "Frequency", false, true);

        // ok - the tracks have moved. better update the doublets
        if (updateDoublets)
            updateDoublets(onlyVis, false, true);

        // aah - but what if we've ditched our doublets?
        // aah - but what if we've ditched our doublets?
        if ((_primaryDoublets == null) || (_primaryDoublets.size() == 0)) {
            // better clear the plot
            dotPlot.setDataset(null);
            linePlot.setDataset(null);
            return;
        }

        // create the collection of series
        final TimeSeriesCollection errorSeries = new TimeSeriesCollection();
        final TimeSeriesCollection actualSeries = new TimeSeriesCollection();

        // produce a dataset for each track
        final TimeSeries errorValues = new TimeSeries(_primaryTrack.getName());

        final TimeSeries measuredValues = new TimeSeries("Measured");
        final TimeSeries correctedValues = new TimeSeries("Corrected");
        final TimeSeries predictedValues = new TimeSeries("Predicted");
        final TimeSeries baseValues = new TimeSeries("Base");

        // ok, run through the points on the primary track
        final Iterator<Doublet> iter = _primaryDoublets.iterator();
        while (iter.hasNext()) {
            final Doublet thisD = iter.next();
            try {

                final Color thisColor = thisD.getColor();
                final double measuredFreq = thisD.getMeasuredFrequency();
                final HiResDate currentTime = thisD.getDTG();
                final FixedMillisecond thisMilli = new FixedMillisecond(currentTime.getDate().getTime());

                final ColouredDataItem mFreq = new ColouredDataItem(thisMilli, measuredFreq, thisColor, false,
                        null);

                // final ColouredDataItem corrFreq = new ColouredDataItem(
                // new FixedMillisecond(currentTime.getDate().getTime()),
                // correctedFreq, thisColor, false, null);
                measuredValues.add(mFreq);

                // do we have target data?
                if (thisD.getTarget() != null) {
                    final double correctedFreq = thisD.getCorrectedFrequency();
                    final double baseFreq = thisD.getBaseFrequency();
                    final Color calcColor = thisD.getTarget().getColor();

                    final ColouredDataItem corrFreq = new ColouredDataItem(thisMilli, correctedFreq, thisColor,
                            true, null);

                    // did we get a base frequency? We may have a track
                    // with a section of data that doesn't have frequency, you see.
                    if (baseFreq != Doublet.INVALID_BASE_FREQUENCY) {
                        final double predictedFreq = thisD.getPredictedFrequency();
                        final double thisError = thisD.calculateFreqError(measuredFreq, predictedFreq);
                        final ColouredDataItem bFreq = new ColouredDataItem(thisMilli, baseFreq, thisColor, true,
                                null);
                        final ColouredDataItem pFreq = new ColouredDataItem(thisMilli, predictedFreq, calcColor,
                                false, null);
                        final ColouredDataItem eFreq = new ColouredDataItem(thisMilli, thisError, thisColor, false,
                                null);
                        baseValues.add(bFreq);
                        predictedValues.add(pFreq);
                        errorValues.add(eFreq);
                    }

                    correctedValues.add(corrFreq);
                }

            } catch (final SeriesException e) {
                CorePlugin.logError(Status.INFO, "some kind of trip whilst updating frequency plot", e);
            }

        }

        // ok, add these new series
        if (errorValues.getItemCount() > 0)
            errorSeries.addSeries(errorValues);

        actualSeries.addSeries(measuredValues);
        actualSeries.addSeries(correctedValues);

        if (predictedValues.getItemCount() > 0)
            actualSeries.addSeries(predictedValues);
        if (baseValues.getItemCount() > 0)
            actualSeries.addSeries(baseValues);

        dotPlot.setDataset(errorSeries);
        linePlot.setDataset(actualSeries);
    }

    public ISecondaryTrack getSecondaryTrack() {
        return _secondaryTrack;
    }

}