Package jaid.ais.data

Source Code of jaid.ais.data.Target

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package jaid.ais.data;

import gov.nasa.worldwind.geom.Angle;
import gov.nasa.worldwind.geom.LatLon;
import gov.nasa.worldwind.render.GlobeAnnotation;
import gov.nasa.worldwind.render.Polygon;
import gov.nasa.worldwind.render.Polyline;
import gov.nasa.worldwind.render.SurfaceIcon;
import jaid.ais.message.*;
import java.awt.Color;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import nav.position.Position;
import nav.util.math.NavCalculator;
import ui.layers.TargetLayer;
import ui.layers.TargetTextLayer;

/**
* The <code>Target</code> class represents the notion of a maritime or aerospace
* entity identified either via radar or AIS. Consequently, the class
* encapsulates the all properties that identify and entity and that define its
* position and physical characteristics. Every target object is associated with
* the data source(s) that are currently tracking / identifying it.
*
* @author Benjamin Jakobus
* @version 1.0
* @since 1.0
*/
public class Target {

    /**
     * Define constants that indicate the target's type.
     */
    /* Indicates that the target is a Class A Vessel. */
    public static final int TARGET_CLASSAVL = 0;

    /* Indicates that the target is a Class B Vessel. */
    public static final int TARGET_CLASSBVL = 1;

    /* Indicates that the target is an AtoN. */
    public static final int TARGET_AtoN = 2;

    /* Indicates that the target is a base station. */
    public static final int TARGET_BASESTN = 3;

    /* Indicates that the target is a a SAR Aircraft. */
    public static final int TARGET_SARACFT = 4;

    /* The number of historical positions to store (A limit of
     * 14440 equals an aprox 12 hours reporting every 3s).
     */
    private static final int HISTORY_SIZE_LIMIT = 1700; //7220 = 6hrs

    /* Entity Type - what type of AIS target's data is stored here... */
    private int targetType;

    /* The MMSI of this target. */
    private String mmsi;

    /* The target's name. */
    private String name;

    /* Last time this target was updated. */
    private long lastUpdate;

    /* Is this target considered lost by the system? */
    private boolean lost;

    /* Used to to store the previously reported position. */
    private Position latestPosition;

    /* The latest type 1,2 or 3 for class A, and type 18 for class B targets. */
    private AISMsg latestDynamic;

    /* The latest type 5 for class A targets, type 19 for class B, and used to store
     * the only message for type 4 & 21 messages.
     */
    private AISMsg latestMessage;

    /* The ID of the base station with which the target was acquired most recently. */
    private String rxID;

    /* Indicates whether or not the target has just been discovered (ie is a new target. */
    private boolean isNew;

    /* The target's movement trail. */
    private LinkedList<gov.nasa.worldwind.geom.Position> trail;
    private Polyline visibleTrail;

    /* The label that is visible to the user. */
    private GlobeAnnotation label;

    /* Denotes whether or not the target is currently being focused on by the user. */
    private boolean focused;

    /* A list of waypoints that define the target's predicted travel route.
     * The amount of waypoints is defined by the time that the user wants to
     * predict (e.g. a user may chose to predict the vessel's route 1 minute ahead
     * of time or 3 minutes ahead of time etc).
     */
    private List<Position> predictedRoute;

    /* A list of stations that are currently picking up the target paired with
     * a timestamp of when the target was last acquired by that station.
     */
    private HashMap<String, Long> receiverRxIDList;

    /* The target's width. */
    private int beam;

    /* The target's length. */
    private int length;

    /* The target's maximum draft. */
    private double draft;

    /* This is what's actually visible on NASA WorldWind. */
    private SurfaceIcon visibleTarget;

    /* The target's hull as specified through its AIS broadcast. */
    private Polygon hull;

    /* Indicates whether or not the target is missing target dimensions.
     * If the target is using the default dimensions, then this is set to true;
     * otherwise to false. True by default.
     */
    private boolean missingTargetDimensions;

    /* The distance between the target's GPS receiver and its bow. */
    private int distanceToBow;
    private int distanceToStern;
    private int a;
    private int b;
    private int c;
    private int d;

    /* The safety bubble that is surrounding the target. Safety bubbles are
     * virtual concepts that act to protect the target by assisting in collision
     * detection.
     */
//    private SafetyBubble safetyBubble;
    /**
     * Constructs a new <code>Target</code> object.
     *
     * @param msg       The AIS message containing the information
     *                  used to describe the target.
     * @param rxID      The rxID of the data source that identified the target
     *                  i.e. that received its AIS message.
     * @since 1.0
     */
    public Target(AISMsg msg, String rxID) {
        this.rxID = rxID;
        this.receiverRxIDList = new HashMap<String, Long>();
        receiverRxIDList.put(rxID, System.currentTimeMillis());
        latestPosition = msg.getPosition();
        isNew = true;
        focused = false;
        name = null;
        missingTargetDimensions = true;
        beam = 2;
        length = 2;
        distanceToBow = 2;
        distanceToStern = 2;
        a = b = c = d = 1;
//        safetyBubble = new SafetyBubble(-1, null, length, beam);
        hull = new Polygon();
        predictedRoute = new ArrayList<Position>(200);
        trail = new LinkedList<gov.nasa.worldwind.geom.Position>();
        visibleTrail = new Polyline(trail);
        visibleTrail.setColor(Color.WHITE);
        visibleTrail.setLineWidth(2.0);
        TargetLayer.getInstance().addRenderable(visibleTrail);
        switch (msg.getMsgType()) {
            case 1:
                targetType = this.TARGET_CLASSAVL;
                break;
            case 2:
                targetType = this.TARGET_CLASSAVL;
                break;
            case 3:
                targetType = this.TARGET_CLASSAVL;
                break;
            case 4:
                targetType = this.TARGET_BASESTN;
                break;
            case 5:
                targetType = this.TARGET_CLASSAVL;
                break;
            case 9:
                targetType = this.TARGET_SARACFT;
                break;
            case 18:
                targetType = this.TARGET_CLASSBVL;
                break;
            case 19:
                targetType = this.TARGET_CLASSBVL;
                break;
            case 21:
                targetType = this.TARGET_AtoN;
        }
        lost = false;
        mmsi = msg.getMMSI();
        update(msg);
        TargetLayer.getInstance().addRenderable(hull);
    }

    public void setFocused(boolean focused) {
        this.focused = focused;
    }

    public boolean isFocused() {
        return focused;
    }

    /**
     * Removes the target from the <code>TargetLayer</code>
     *
     * @since 2.0
     */
    public void detach() {
        TargetLayer.getInstance().removeRenderable(visibleTrail);
        TargetLayer.getInstance().removeRenderable(visibleTarget);
    }

    /**
     * Updates the target's motion vector that defines its
     * predicted route over time. This method is called every time
     * that the target moves (i.e. for every change in position)
     * @param minutes the minutes from now for which to predict the target's
     *        future route
     * @param map           The map used within the prediction context.
     * @param worldObjects  A list containing the <code>GeneralPath</code>
     *                      of objects within the world. Objects are entities
     *                      that are painted on the map e.g. country borders, boyes,
     *                      landmarks, etc.
     * @since 1.0
     */
    public void predictRoute(int minutes) {
        int heading = getHeading();
        float speed = getSpeed();
        if (speed < 0 || heading < 0 || heading == 511) {
            return;
        }
        predictedRoute.clear();
        // Convert speed to nautical miles per second
        double nauticalMilesPerMinute = speed / 60.0;
        predictedRoute.add(getPosition());
        // Calculate the future position for every minute
        for (int time = 1; time < minutes; time++) {
            predictedRoute.add(NavCalculator.predictPosition(time, nauticalMilesPerMinute, getPosition().getLatitude(),
                    getPosition().getLongitude(), heading));
        }
    }

    /**
     * Returns the target's predicted route.
     *
     * @param minutes           The time in the future for which to get the route
     *                          (in minutes).
     * @return                  The target's predicted route.
     * @since 1.0
     */
    public List<Position> getPredictedRoute(int time) {
        predictRoute(time);
        return predictedRoute;
    }

    /**
     * Returns the (AIS) icon that represents the target. Icons are dependent on
     * the target's current state and type. Refer to the software documentation for
     * further details.
     *
     * @return                  <code>BufferedImage</code> object representing the target.
     * @throws IOException
     * @since 1.0
     */
    private URL getIconURL() {
        try {
            AISMsg msg = (getLatestDynamicMsg() == null ? getLatestMessage()
                    : getLatestDynamicMsg());
            String navStatus = msg.getNavStatus();
            float speed = msg.getSpeed();
            if (speed < 0) {
                speed = 0;
            }
            int heading = msg.getHeading();

            if (isLost()) {
                return getClass().getResource("/assets/icons/ais/lost.png");
            }

            URL img = null;

            if (navStatus.equals("at anchor")) {
                if (speed > 0.2) {
                    if (heading < 0 || heading > 360) {
                        img = getClass().getResource("/assets/icons/ais/anchor-cog.png");
                    } else {
                        img = getClass().getResource("/assets/icons/ais/anchor-hdg.png");
                    }
                } else {
                    img = getClass().getResource("/assets/icons/ais/anchor-000.png");
                }
            } else if (navStatus.equals("moored")) {
                if (speed > 0.2) {
                    if (heading < 0 || heading > 360) {
                        img = getClass().getResource("/assets/icons/ais/moored-cog.png");
                    } else {
                        img = getClass().getResource("/assets/icons/ais/moored-hdg.png");
                    }
                } else {
                    img = getClass().getResource("/assets/icons/ais/moored-000.png");
                }
            } else if (navStatus.equals("under way")) {
                if (speed > 0.2) {
                    if (heading < 0 || heading > 360) {
                        img = getClass().getResource("/assets/icons/ais/uwy-cog.png");
                    } else {
                        img = getClass().getResource("/assets/icons/ais/uw-000.png");
                    }
                } else {
                    img = getClass().getResource("/assets/icons/ais/uwy-drift.png");
                }
            } else if (navStatus.equals("not defined")) {
                if (speed > 0.2) {
                    if (heading < 0 || heading > 360) {
                        img = getClass().getResource("/assets/icons/ais/notDef-cog.png");
                    } else {
                        img = getClass().getResource("/assets/icons/ais/notDef-hdg.png");
                    }
                } else {
                    img = getClass().getResource("/assets/icons/ais/notDef-drift.png");
                }
            } else if (getType() == TARGET_AtoN || getMMSI().startsWith("99")) {
                if (msg.getAidType().equals("Fixed structure")) {
                    img = getClass().getResource("/assets/icons/ais/AtoN-FM-V.png");
                } else if (msg.getAidType().equals("Fixed structure")) {
                    img = getClass().getResource("/assets/icons/ais/AtoN-FM-V.png");
                }
                if (msg.getAidType().equals("Safe water mark")) {
                    img = getClass().getResource("/assets/icons/ais/AtoN-SWM.png");
                } else {
                    img = getClass().getResource("/assets/icons/ais/baseStnAtoN.png");
                }
            } else if (getType() == TARGET_BASESTN) {
                if (msg.getAidType().equals("Fixed structure")) {
                    img = getClass().getResource("/assets/icons/ais/AtoN-FM-V.png");
                }
                if (msg.getAidType().equals("Safe water mark")) {
                    img = getClass().getResource("/assets/icons/ais/AtoN-SWM.png");
                } else {
                    img = getClass().getResource("/assets/icons/ais/baseStation.png");
                }
            } else if (getType() == TARGET_SARACFT) {
                img = getClass().getResource("/assets/icons/ais/sar-aircraft.png");
            }
            return img;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Updates the target's AIS message to its most recently received one.
     *
     * @param latestMsg         The most recently received AIS message.
     * @since 1.0
     */
    public void update(AISMsg latestMsg) {
        // Update the message depending on its type (dynamic vs static)
        if (latestMsg.getName() != null) {
            name = latestMsg.getName();
        }
        switch (latestMsg.getMsgType()) {
            case 1:
                // Error check code for right messages type!
                latestDynamic = latestMsg;
                lastUpdate = latestDynamic.getUtcTimeStamp();
                if (((AISMsg1_2_3) latestMsg).getNavStatus().compareTo("moored") != 0) {
                    updatePositionHistory(latestMsg.getPosition());
                }
                break;
            case 2:
                // Error check code for right messages type!
                latestDynamic = latestMsg;
                lastUpdate = latestDynamic.getUtcTimeStamp();
                if (((AISMsg1_2_3) latestMsg).getNavStatus().compareTo("moored") != 0) {
                    updatePositionHistory(latestMsg.getPosition());
                }
                break;
            case 3:
                // Error check code for right messages type!
                latestDynamic = latestMsg;
                lastUpdate = latestDynamic.getUtcTimeStamp();
                if (((AISMsg1_2_3) latestMsg).getNavStatus().compareTo("moored") != 0) {
                    updatePositionHistory(latestMsg.getPosition());
                }
                break;
            case 4:
                // Error check code for right messages type!
                latestMessage = latestMsg;
                lastUpdate = latestMessage.getUtcTimeStamp();
                //no position history
                break;
            case 5:
                // Error check code for right messages type!
                latestMessage = latestMsg;
                Calendar cal = Calendar.getInstance();
                cal.add(Calendar.MILLISECOND, -cal.get(Calendar.DST_OFFSET)
                        - cal.get(Calendar.ZONE_OFFSET));
                lastUpdate = cal.getTimeInMillis();
                // Calcukate vessek dimensions if they have not yet been set
                AISMsg5 msg = (AISMsg5) latestMsg;
                if (msg.getA() == 0 || msg.getB() == 0 || msg.getC() == 0
                        || msg.getD() == 0) {
                    break;
                }
                a = msg.getA();
                b = msg.getB();
                c = msg.getC();
                d = msg.getD();
                beam = msg.getC() + msg.getD();
                length = msg.getA() + msg.getB();
                distanceToBow = length / 2;
                distanceToStern = length / 2;
//                safetyBubble.setLength(length);
//                safetyBubble.setBeam(beam);
                missingTargetDimensions = false;
                break;
            case 9:
                // Error check code for right messages type!
                latestDynamic = latestMsg;
                lastUpdate = latestDynamic.getUtcTimeStamp();
                updatePositionHistory(latestMsg.getPosition());
                break;
            case 18:
                // Error check code for right messages type!
                latestDynamic = latestMsg;
                lastUpdate = latestDynamic.getUtcTimeStamp();
                break;
            case 19:
                // Error check code for right messages type!
                latestMessage = latestMsg;
                lastUpdate = latestMessage.getUtcTimeStamp();
                break;
            case 21:
                // Error check code for right messages type!
                latestMessage = latestMsg;
                if (latestMessage.getUtcTimeStampStr().compareTo("Not Available") == 0) {
                    cal = Calendar.getInstance();
                    cal.add(
                            Calendar.MILLISECOND, -cal.get(Calendar.DST_OFFSET)
                            - cal.get(Calendar.ZONE_OFFSET));
                    lastUpdate = cal.getTimeInMillis();
                } else {
                    lastUpdate = latestMessage.getUtcTimeStamp();
                }
                break;
            default:
        }
        lost = false;
//        safetyBubble.setTargetPosition(getCentroid());
        int heading = getHeading();
        int course = getCourse();
//        if (heading >= 0 && heading <= 360) {
//            safetyBubble.setHeading(heading);
//        } else if (course >= 0 && course <= 360) {
//            safetyBubble.setHeading(course);
//        }
        Position pos = getPosition();
        String txt = generateLabelText();
        if (visibleTarget == null) {
            visibleTarget = new SurfaceIcon(getIconURL(), LatLon.fromDegrees(pos.getLatitude(), pos.getLongitude()));
            TargetLayer.getInstance().addRenderable(visibleTarget);
            label = new GlobeAnnotation(txt, new gov.nasa.worldwind.geom.Position(LatLon.fromDegrees(pos.getLatitude(), pos.getLongitude()), 0.1));
            TargetTextLayer.getInstance().addAnnotation(label);
        } else {
            visibleTarget.setImageSource(getIconURL());
            visibleTarget.setLocation(LatLon.fromDegrees(pos.getLatitude(), pos.getLongitude()));
            label.setText(txt);
            label.setPosition(new gov.nasa.worldwind.geom.Position(LatLon.fromDegrees(pos.getLatitude(), pos.getLongitude()), 0.1));
        }
        visibleTarget.setHeading(Angle.fromDegrees(getCourse()));
    }

    /**
     *
     * @since 2.0
     */
    private String generateLabelText() {
        StringBuilder sb = new StringBuilder();
        sb.append("<b>MMSI:</b> ");
        sb.append(getMMSI());
        sb.append("<br/>");
        sb.append("<b>Dest.:</b> ");
        sb.append(getDestination());
        sb.append("<br/>");
        sb.append("<b>Heading:</b> ");
        sb.append(getHeading());
        sb.append("<br/>");
        sb.append("<b>Course:</b> ");
        sb.append(getCourse());
        sb.append("<br/>");
        sb.append("<b>Speed:</b> ");
        sb.append(getSpeed());
        sb.append("<br/>");
        sb.append("<b>Cargo:</b> ");
        sb.append(getCargoType());
        sb.append("<br/>");
        sb.append("<b>Location:</b> ");
        if (getPosition() != null) {
            sb.append(getPosition().toStringDec());
        } else {
            sb.append("Unknown");
        }
        sb.append("<br/>");
        return new String(sb);
    }

    /**
     * Finds and returns the target's centre position.
     *
     * @return              A <code>Position</code> representing the target's centre.
     * @since 1.0
     */
    public Position getCentroid() {
        Position centre = null;
        int direction = 0;
        int heading = getHeading();
        if (heading < 0 || heading > 360) {
            heading = getCourse();
        }
        if (heading <= 270) {
            direction = heading + 90;
        } else {
            direction = (heading + 90) - 360;
        }
        int dist = (length / 2) - b;
        int dir0 = heading;
        if (b > a) {
            if (heading >= 180) {
                dir0 = heading - 180;
            } else {
                dir0 = 360 + (heading - 180);
            }
        }
        centre = NavCalculator.computePosition(getPosition(), dir0, dist);
        centre = NavCalculator.computePosition(centre, direction, (beam / 2) - c);
        return centre;
    }

    public int getA() {
        return a;
    }

    public int getB() {
        return b;
    }

    public int getC() {
        return c;
    }

    public int getD() {
        return d;
    }

    /**
     * Updates the target's trail. A target's trail is a collection of
     * its past waypoints.
     *
     * @param t     The target's new trail.
     * @since 1.0
     */
    public void updateTrail(Trail t) {
        if (trail.size() >= HISTORY_SIZE_LIMIT) {
            for (int i = 0; i < 1000; i++) {
                trail.remove(i);
            }
        }
        trail.add(gov.nasa.worldwind.geom.Position.fromDegrees(t.getTrailPosition().getLatitude(), t.getTrailPosition().getLongitude(), 0));
        visibleTrail.setPositions(trail);
    }

    public Position getSternPosition() {
        // Calculate the bow's lat/long position if heading/course
        // are available
        Position sternPosition = null;
        int heading = getHeading();
        int course = getCourse();
        if (heading < 0 || heading > 360) {
            if (course >= 0 && course <= 360) {
                heading = course;
            } else {
                return sternPosition;
            }
        }
        if (heading <= 180) {
            heading = heading + 180;
        } else {
            heading = (heading + 180) - 360;
        }
        sternPosition = NavCalculator.computePosition(getCentroid(),
                heading, distanceToStern);
        return sternPosition;
    }

    /**
     * Returns the target's trail. A target's trail is a collection of
     * its past waypoints.
     *
     * @return                  <code>List</code> object containing the target's
     *                          trails.
     * @since 1.0
     */
    public List<gov.nasa.worldwind.geom.Position> getTrail() {
        return trail;
    }

    /**
     * Returns the target's navigational status.
     *
     * @return                  <code>String}</code> object representing the
     *                          target's naviational status.
     * @since 1.0
     */
    public String getNavStatus() {
        AISMsg msg = getLatestDynamicMsg();
        if (msg == null) {

            return "unknown";
        } else if (isLost()) {
            return "Lost";
        }
        return msg.getNavStatus();
    }

    /**
     * Returns the vessel's color. Color is defined according to the
     * visual perspectives in place.
     *
     * @return                  <code>Color</code> describing the vessel's color
     * @since 2.0
     */
    public static Color getColor(Target target) {
        Color color = null;
        AISMsg msg = (target.getLatestDynamicMsg() == null ? target.getLatestMessage()
                : target.getLatestDynamicMsg());
        String navStatus = msg.getNavStatus();

        if (target.isLost()) {
            return Color.RED;
        }
        if (navStatus.equals("at anchor")) {
            color = new Color(14, 77, 120);
        } else if (navStatus.equals("moored")) {
            color = new Color(0, 231, 0);
        } else if (navStatus.equals("under way")) {
            color = Color.WHITE;
        } else if (navStatus.equals("not defined")) {
            color = new Color(162, 162, 162);
        } else if (target.getType() == target.TARGET_AtoN || target.getMMSI().startsWith("99")) {
            if (msg.getAidType().equals("Fixed structure")) {
                color = Color.YELLOW;
            }
            if (msg.getAidType().equals("Safe water mark")) {
                color = Color.RED;
            } else {
                color = Color.MAGENTA;
            }
        } else if (target.getType() == target.TARGET_BASESTN) {
            if (msg.getAidType().equals("Fixed structure")) {
                color = Color.YELLOW;
            }
            if (msg.getAidType().equals("Safe water mark")) {
                color = new Color(205, 133, 0);
            } else {
                color = new Color(205, 173, 0);
            }
        }
        return color;
    }

    /**
     * Returns the number of seconds since the last update to this target.
     *
     * @return                      The number of seconds since the last
     *                              received update.
     * @since 1.0
     */
    public long getElapsedTimeSinceLastUpdate() {
        Calendar cal = Calendar.getInstance();
        // Current system time in UTC
        cal.add(Calendar.MILLISECOND, -cal.get(Calendar.DST_OFFSET)
                - cal.get(Calendar.ZONE_OFFSET));
        // Subtract last update time, converts to s & return
        return (Math.round((cal.getTimeInMillis() - lastUpdate) / 1000));
    }

    /**
     * Returns the MMSI for this target.
     *
     * @return                      <code>String</code> object representing the
     *                              target's MMSI.
     * @since 1.0
     */
    public String getMMSI() {
        return mmsi;
    }

    /**
     * Indicates whether or not the target is missing dimensional information.
     *
     * @return                      <code>True</code> if the <code>Target</code> is
     *                              missing dimensional information; <code>false</code>
     *                              if not.
     * @since 1.0
     */
    public boolean isMissingDimensions() {
        return missingTargetDimensions;
    }

    /**
     * Returns the target's width in meters.
     *
     * @return                      The target's width in meters.
     * @since 1.0
     */
    public int getBeam() {
        return beam;
    }

    /**
     * Returns the target's length in meters.
     *
     * @return                      The target's length in meters.
     * @since 1.0
     */
    public int getLength() {
        return length;
    }

    /**
     * Returns the distance between the vessel's GPS antenna and
     * its bow.
     * @return                      The distance between the vessel's GPS antenna
     *                              and its bow in meters.
     * @since 1.0
     */
    public int getDistanceToBow() {
        return distanceToBow;
    }

    /**
     * Returns the latest dynamic message that has been broadcasted by the target.
     *
     * @return                      <code>AISMsg</code> object representing the
     *                              target's most recent dynamic message.
     * @since 1.0
     */
    public AISMsg getLatestDynamicMsg() {
        return latestDynamic;
    }

    /**
     * Returns the most recent AIS message.
     *
     * @return                      <code>AISMsg</code> the most recent AIS message.
     * @since 1.0
     */
    public AISMsg getLatestMessage() {
        return latestMessage;
    }

    /**
     * Returns the target's position.
     *
     * @return                      <code>Position</code> object representing the
     *                              target's position.
     * @since 1.0
     */
    public Position getPosition() {
        return latestPosition;
    }

    /**
     * The time of the last update of this target.
     * @return                      The time of the last update of this target.
     * @since 1.0
     */
    public long getTimeOfLastUpdate() {
        //write code to compute time of last update!
        return lastUpdate;
    }

    /**
     * Removes all trails left by the target.
     *
     * @since 1.0
     */
    public void clearTrail() {
        for (int i = 0; i < trail.size(); i++) {
            trail.remove(i);
        }
    }

    /**
     * Set this target as lost.
     *
     * @since 1.0
     */
    public void setLost() {
        lost = true;
    }

    /**
     * Indicates whether or not the target has been lost.
     *
     * @return                              <code>True</code> if the target has
     *                                      been lost; <code>false</code> if not.
     * @since 1.0
     */
    public boolean isLost() {
        return lost;
    }

    @Override
    public void finalize() throws Throwable {
        super.finalize();
    }

    /**
     * Returns the type of target.
     *
     * @return          <code>Integer</code> flag indicating the target type.
     * @since 1.0
     */
    public int getType() {
        return targetType;
    }

    /**
     * Returns the vessel's draft in meters.
     *
     * @return          The vessel's draft (in meters).
     * @since 1.0
     */
    public float getDraft() {
        return (latestMessage != null ? latestMessage.getMaxDraft() : -1);
    }

    /**
     * Returns the position of the target's bow.
     *
     * @return          The <code>Position</code> of the target's bow.
     * @since 1.0
     */
    public Position getBowPosition() {
        // Calculate the bow's lat/long position if heading/course
        // are available
        Position bowPosition = null;
        int heading = getHeading();
        int course = getCourse();
        if (heading < 0 || heading > 360) {
            if (course >= 0 && course <= 360) {
                bowPosition = NavCalculator.computePosition(getCentroid(),
                        course, distanceToBow * 0.7);
            }
        } else {
            bowPosition = NavCalculator.computePosition(getCentroid(),
                    heading, distanceToBow * 0.7);

        }
        return bowPosition;
    }

    /**
     * Returns the position of the target's port.
     *
     * @return portPosition     The position of the target's port.
     * @since 1.0
     */
    public Position getPortPosition() {
        // Calculate the port's lat/long position if heading/course
        // are available
        Position portPosition = null;
        int heading = getHeading();
        int course = getCourse();
        if (heading < 0) {
            if (course >= 0) {
                heading = course;
            }
        }
        if (heading < 0 || heading > 360) {
            return portPosition;
        }
        if (heading >= 90) {
            portPosition = NavCalculator.computePosition(getCentroid(), heading - 90, c);
        } else if (heading < 90) {
            int portHeading = heading - 90;
            portHeading = 360 + portHeading;
            portPosition = NavCalculator.computePosition(getCentroid(), portHeading, c);
        }
        return portPosition;
    }

    /**
     * Returns the position of the target's starboard.
     *
     * @return starboardPosition     The position of the target's starboard.
     * @since 1.0
     */
    public Position getStarboardPosition() {
        // Calculate the port's lat/long position if heading/course
        // are available
        Position starboardPosition = null;
        int heading = getHeading();
        int course = getCourse();
        if (heading < 0) {
            if (course >= 0 && course != 511) {
                heading = course;
            }
        }
        if (heading < 0 || heading > 360) {
            return starboardPosition;
        }
        if (heading <= 270) {
            starboardPosition = NavCalculator.computePosition(getCentroid(), heading + 90, d);
        } else {
            int starboardHeading = heading + 90;
            starboardHeading = starboardHeading - 360;
            starboardPosition = NavCalculator.computePosition(getCentroid(), starboardHeading, d);
        }
        return starboardPosition;
    }

    /**
     * Returns an instance of the target's safety bubble.
     *
     * @return safetyBubble     The target's <code>SafetyBubble</code>
     * @see SafetyBubble
     * @since 1.0
     */
//    public SafetyBubble getSafetyBubble() {
//        return safetyBubble;
//    }
    /**
     * The RxID of the station that last received this target.
     *
     * @return rxID         The ID of the receiver that last received the target.
     * @since 1.0
     */
    public String getSourceRxID() {
        return rxID;
    }

    /**
     * Returns the target type in string format.
     *
     * @return              <code>String</code> object indicating the target type.
     * @since 1.0
     */
    public String getTypeStr() {
        switch (targetType) {
            case TARGET_CLASSAVL:
                return "CLASSAVL";
            case TARGET_BASESTN:
                return "BASE STATION";
            case TARGET_SARACFT:
                return "SARACFT";
            case TARGET_CLASSBVL:
                return "CLASSBVL";
            case TARGET_AtoN:
                return "AtoN";
        }
        return "unknow";
    }

    /**
     * Returns the target's speed in nautical miles per hour.
     *
     * @return              The target's speed.
     * @since 1.0
     */
    public float getSpeed() {
        AISMsg msg = getLatestDynamicMsg();
        if (msg == null) {
            return AISMsg.UNKNOWN;
        }
        return msg.getSpeed();
    }

    /**
     * Updates the target's position history.
     *
     * @param           <code>Position</code> to update to.
     * @since 1.0
     */
    private void updatePositionHistory(Position position) {
        if (latestPosition != null) {
            isNew = false;
        }
        if (position == null || latestPosition == null) {
            latestPosition = position;
            return;
        }
        // If the two positions are different, then add the current latest
        // position to the trail and replace it with the new position
        if (position.getLatitude() != latestPosition.getLatitude()
                || position.getLongitude() != latestPosition.getLongitude()) {
            try {
                updateTrail(new Trail(new Position(latestPosition.getLatitude(),
                        latestPosition.getLongitude())));
                drawHull();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        latestPosition = position;
    }

    /**
     * Draws the target's hull.
     *
     * @since 2.0
     */
    private void drawHull() {
        Color color;
        if (isMissingDimensions()) {
            color = Color.DARK_GRAY;
            return;
        } else {
            color = Color.BLACK;
        }

        Position bow = getBowPosition();
        Position stern = getSternPosition();
        int heading = getHeading();
        if (heading < 0 || heading > 360) {
            heading = getCourse();
            if (heading < 0 || heading > 360) {
                return;
            }
        }
        double dist = getDistanceToBow();
        dist = dist * 0.3;
        Position bowTip = NavCalculator.computePosition(bow, heading, dist);
        int direction;
       
        dist = getBeam() / 2;

        if (heading >= 90) {
            direction = heading - 90;
        } else {
            direction = (heading - 90) + 360;
        }
        Position bowPort = NavCalculator.computePosition(bow, direction, dist);
        Position sternPort = NavCalculator.computePosition(stern, direction, dist);
        if (heading <= 270) {
            direction = heading + 90;
        } else {
            direction = (heading + 90) - 360;
        }
        Position sternStarboard = NavCalculator.computePosition(stern, direction, dist);
        Position bowStarboard = NavCalculator.computePosition(bow, direction, dist);
        double elevation = visibleTrail.getPositions().iterator().next().getElevation();
        List<gov.nasa.worldwind.geom.Position> boundaries = new ArrayList<gov.nasa.worldwind.geom.Position>();
        boundaries.add(new gov.nasa.worldwind.geom.Position(LatLon.fromDegrees(bowPort.getLatitude(), bowPort.getLongitude()), elevation));
        boundaries.add(new gov.nasa.worldwind.geom.Position(LatLon.fromDegrees(sternPort.getLatitude(), sternPort.getLongitude()), elevation));
        boundaries.add(new gov.nasa.worldwind.geom.Position(LatLon.fromDegrees(sternStarboard.getLatitude(), sternStarboard.getLongitude()), elevation));

        boundaries.add(new gov.nasa.worldwind.geom.Position(LatLon.fromDegrees(bowStarboard.getLatitude(), bowStarboard.getLongitude()), elevation));
        boundaries.add(new gov.nasa.worldwind.geom.Position(LatLon.fromDegrees(bowTip.getLatitude(), bowTip.getLongitude()), elevation));
//        boundaries.add(new gov.nasa.worldwind.geom.Position(LatLon.fromDegrees(getPosition().getLatitude(), getPosition().getLongitude()), elevation));
//        boundaries.add(new gov.nasa.worldwind.geom.Position(LatLon.fromDegrees(getCentroid().getLatitude(), getCentroid().getLongitude()), elevation));


        hull.setOuterBoundary(boundaries);
//        g.draw(poly);
//        g.drawString("x", (int) p6.getX(), (int) p6.getY());
//        g.fillOval((int) p7.getX(), (int) p7.getY(), 2, 2);
//        if (target.isMissingDimensions()) {
//            Point p8 = map.toPixel(bow);
//            g.drawString("<No Dim>", (int) p8.getX() + 10,
//                    (int) p8.getY() + 10);
//        }
//        g.setColor(color);
    }

    /**
     * Indicates whether or not the target has just been received.
     *
     * @return              <code>True</code>} if the target is new;
     *                      <code>false</code> if not.
     * @since 1.0
     */
    public boolean isNew() {
        return isNew;
    }

    /**
     * Updates the rxID of the station receiving the target.
     *
     * @param rxID              The new rxID of the source station.
     * @since 1.0
     */
    public void updateSourceRxID(String rxID) {
        this.rxID = rxID;
        // Add the receiver's rxID to the list if it does not exist already
        // otherwise just update its timestamp
        receiverRxIDList.put(rxID, System.currentTimeMillis());
    }

    /**
     * Returns the target's name.
     * @return          <code>String</code> containing the target's name.
     * @since 1.0
     */
    public String getName() {
        if (name == null) {
            return "?";
        }
        return name;
    }

    /**
     * Returns a list of stations that are currently receiving this target.
     *
     * @return          <code>List</code> of stations that currently receive this target.
     * @since 1.0
     */
    public HashMap<String, Long> getSourceRxIDList() {
        return receiverRxIDList;
    }

    /**
     * Returns the target's course in degrees (from 0 to 359).
     *
     * @return          The target's course.
     * @since 1.0
     */
    public int getCourse() {
        AISMsg msg = getLatestDynamicMsg();
        int course;
        if (msg == null) {
            return AISMsg.UNKNOWN;
        }
        course = msg.getCourse();
        if (course == 511 || course < 0) {
            return AISMsg.UNKNOWN;
        }
        String strCourse = "" + course;
        if (strCourse.length() < 3) {
            for (int i = strCourse.length(); i < 3; i++) {
                strCourse = "0" + strCourse.length();
            }
            course = Integer.parseInt(strCourse);
        }


        return course;
    }

    /**
     * Returns the target's course.
     *
     * @return The target's course
     * @since 1.0
     */
    public int getHeading() {
        AISMsg msg = getLatestDynamicMsg();
        int heading;
        String strHeading;
        if (msg == null) {
            return AISMsg.UNKNOWN;
        }
        heading = msg.getHeading();
        if (heading == 511 || heading < 0) {
            return AISMsg.UNKNOWN;
        }
        strHeading = "" + heading;
        if (strHeading.length() < 3) {
            for (int i = strHeading.length(); i < 3; i++) {
                strHeading = "0" + strHeading;
            }
            heading = Integer.parseInt(strHeading);
        }

        return heading;
    }

    /**
     * Returns the type of cargo loaded by the target.
     *
     * @return              The type of cargo loaded by the target.
     * @since 1.0
     */
    public String getCargoType() {
        AISMsg msg = getLatestMessage();
        if (msg == null) {
            return "?";
        }
        return msg.getCargoType();
    }

    /**
     * Returns the target's destination.
     *
     * @return          The target's destination.
     * @since 1.0
     */
    public String getDestination() {
        AISMsg msg = getLatestMessage();
        if (msg == null) {
            return "?";
        }
        return msg.getDestination();
    }

    /**
     * Returns the target's Exptected Time of Arrival (ETA).
     *
     * @return          The target's ETA.
     * @since 1.0
     */
    public String getEta() {
        AISMsg msg = getLatestMessage();
        if (msg == null) {
            return "?";
        }
        return msg.getEta();
    }

    /**
     * Returns a string containing all target information.
     *
     * @return          Target information.
     * @since 1.0
     */
    public String print() {
        String info = "";
        info += "Name: " + getName() + "\n";
        info += "Destination: " + getDestination() + "\n";
        info += "ETA: " + getEta() + "\n";
        info += "\nMMSI: " + getMMSI() + "\n";
        info += "Heading: " + getHeading() + " (" + getCourse() + "(c))\n";
        info += "Speed: " + getSpeed() + "\n";
        info += "Cargo Type: " + getCargoType() + "\n";
        info += "LOA: " + getLength() + " Beam: " + getBeam() + " Draft:";

        info += "\nLast received by: " + getSourceRxID() + "\n";
        return info;
    }
}
TOP

Related Classes of jaid.ais.data.Target

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.