dk.dma.ais.packet.AisPacketCSVOutputSink.java Source code

Java tutorial

Introduction

Here is the source code for dk.dma.ais.packet.AisPacketCSVOutputSink.java

Source

/* Copyright (c) 2011 Danish Maritime Authority
 *
 * 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 3 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 General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */
package dk.dma.ais.packet;

import com.google.common.collect.Lists;
import dk.dma.ais.message.AisMessage;
import dk.dma.ais.message.AisMessage5;
import dk.dma.ais.message.AisPosition;
import dk.dma.ais.message.AisStaticCommon;
import dk.dma.ais.message.AisTargetType;
import dk.dma.ais.message.IDimensionMessage;
import dk.dma.ais.message.INameMessage;
import dk.dma.ais.message.IPositionMessage;
import dk.dma.ais.message.IVesselPositionMessage;
import dk.dma.ais.message.ShipTypeCargo;
import dk.dma.commons.util.io.OutputStreamSink;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.concurrent.GuardedBy;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;

import static org.apache.commons.lang3.StringUtils.isBlank;

/**
 * Transform AisPackets into csv objects with only specific columns
 * @author Thomas Borg Salling
 *
 */
public class AisPacketCSVOutputSink extends OutputStreamSink<AisPacket> {

    private static final Logger LOG = LoggerFactory.getLogger(AisPacketCSVOutputSink.class);
    {
        LOG.info("AisPacketCSVOutputSink created.");
    }

    /** The column we are writing. */
    private final String[] columns;

    @GuardedBy("csvLock")
    private CSVPrinter csv;

    private Lock csvLock = new ReentrantLock();

    protected static final String NULL_INDICATOR = "null";

    public AisPacketCSVOutputSink() {
        this("timestamp_pretty;timestamp;targetType;mmsi;msgid;posacc;lat;lon;sog;cog;draught;name;dimBow;dimPort;dimStarboard;dimStern;shipTypeCargoTypeCode;shipType;shipCargo;destination;eta;imo;callsign",
                ";");
    }

    public AisPacketCSVOutputSink(String format) {
        this(format, ";");
    }

    public AisPacketCSVOutputSink(String format, String separator) {
        LOG.info("Exporting CSV columns: " + format);
        columns = format.split(separator);
    }

    @Override
    public void process(OutputStream stream, AisPacket packet, long count) throws IOException {

        csvLock.lock();
        try {
            if (csv == null) {
                csv = new CSVPrinter(new OutputStreamWriter(stream), CSVFormat.DEFAULT.withHeader(columns));
            }

            AisMessage m = packet.tryGetAisMessage();
            if (m != null) {
                List fields = Lists.newArrayList();

                for (String column : columns) {
                    switch (column) {
                    case "timestamp":
                        addTimestamp(fields, packet);
                        break;
                    case "timestamp_pretty":
                        addTimestampPretty(fields, packet);
                        break;
                    case "msgid":
                        addMsgId(fields, m);
                        break;
                    case "mmsi":
                        addMmsi(fields, m);
                        break;
                    case "posacc":
                        addPositionAccuracy(fields, m);
                        break;
                    case "lat":
                        addLatitude(fields, m);
                        break;
                    case "lon":
                        addLongitude(fields, m);
                        break;
                    case "targetType":
                        addTargetType(fields, m);
                        break;
                    case "sog":
                        addSog(fields, m);
                        break;
                    case "cog":
                        addCog(fields, m);
                        break;
                    case "trueHeading":
                        addTrueHeading(fields, m);
                        break;
                    case "draught":
                        addDraught(fields, m);
                        break;
                    case "name":
                        addName(fields, m);
                        break;
                    case "dimBow":
                        addDimBow(fields, m);
                        break;
                    case "dimPort":
                        addDimPort(fields, m);
                        break;
                    case "dimStarboard":
                        addDimStarboard(fields, m);
                        break;
                    case "dimStern":
                        addDimStern(fields, m);
                        break;
                    case "shipTypeCargoTypeCode":
                        addShipTypeCargoTypeCode(fields, m);
                        break;
                    case "shipType":
                        addShipType(fields, m);
                        break;
                    case "shipCargo":
                        addShipCargo(fields, m);
                        break;
                    case "callsign":
                        addCallsign(fields, m);
                        break;
                    case "imo":
                        addImo(fields, m);
                        break;
                    case "destination":
                        addDestination(fields, m);
                        break;
                    case "eta":
                        addEta(fields, m);
                        break;
                    default:
                        LOG.warn("Unknown column: " + column);
                        fields.add(NULL_INDICATOR);
                    }
                }

                csv.printRecord(fields);
            }
        } finally {
            csvLock.unlock();
        }
    }

    // ---

    private void addTimestamp(List fields, AisPacket packet) {
        final long timestamp = packet.getBestTimestamp();
        if (timestamp >= 0)
            fields.add(timestamp);
        else
            fields.add("null");
    }

    private void addTimestampPretty(List fields, AisPacket packet) {
        final long timestamp = packet.getBestTimestamp();
        if (timestamp >= 0)
            fields.add(DateTimeFormatter.ofPattern("dd/MM/uuuu HH:mm:ss")
                    .format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.of("UTC"))));
        else
            fields.add("null");
    }

    private void addMmsi(List fields, AisMessage m) {
        fields.add(m.getUserId());
    }

    private void addMsgId(List fields, AisMessage m) {
        fields.add(m.getMsgId());
    }

    private void addPositionAccuracy(List fields, AisMessage m) {
        if (m instanceof IPositionMessage) {
            fields.add(((IPositionMessage) m).getPosAcc());
        } else {
            fields.add(nullValueForPositionAccuracy.apply(m));
        }
    }

    private void addLatitude(List fields, AisMessage m) {
        if (m instanceof IPositionMessage) {
            AisPosition pos = ((IPositionMessage) m).getPos();
            if (pos != null) {
                fields.add((float) pos.getLatitudeDouble());
            } else {
                fields.add(nullValueForLatitude.apply(m));
            }
        } else {
            fields.add(nullValueForLatitude.apply(m));
        }
    }

    private void addLongitude(List fields, AisMessage m) {
        if (m instanceof IPositionMessage) {
            AisPosition pos = ((IPositionMessage) m).getPos();
            if (pos != null) {
                fields.add((float) pos.getLongitudeDouble());
            } else {
                fields.add(nullValueForLongitude.apply(m));
            }
        } else {
            fields.add(nullValueForLongitude.apply(m));
        }
    }

    private void addTargetType(List fields, AisMessage m) {
        AisTargetType targetType = m.getTargetType();
        fields.add(targetType != null ? targetType.toString() : nullValueForTargetType.apply(m));
    }

    private void addSog(List fields, AisMessage m) {
        if (m instanceof IVesselPositionMessage) {
            fields.add(((IVesselPositionMessage) m).getSog() / 10f);
        } else {
            fields.add(nullValueForSog.apply(m));
        }
    }

    private void addCog(List fields, AisMessage m) {
        if (m instanceof IVesselPositionMessage) {
            fields.add(((IVesselPositionMessage) m).getCog() / 10f);
        } else {
            fields.add(nullValueForCog.apply(m));
        }
    }

    private void addTrueHeading(List fields, AisMessage m) {
        if (m instanceof IVesselPositionMessage) {
            fields.add(((IVesselPositionMessage) m).getTrueHeading());
        } else {
            fields.add(nullValueForTrueHeading.apply(m));
        }
    }

    private void addDraught(List fields, AisMessage m) {
        if (m instanceof AisMessage5) {
            fields.add(((AisMessage5) m).getDraught() / 10f);
        } else {
            fields.add(nullValueForDraught.apply(m));
        }
    }

    private void addName(List fields, AisMessage m) {
        if (m instanceof INameMessage) {
            String name = AisMessage.trimText(((INameMessage) m).getName());
            if (!isBlank(name)) {
                fields.add(name);
            } else {
                fields.add(nullValueForName.apply(m));
            }
        } else {
            fields.add(nullValueForName.apply(m));
        }
    }

    private void addDimBow(List fields, AisMessage m) {
        if (m instanceof IDimensionMessage) {
            fields.add(((IDimensionMessage) m).getDimBow());
        } else {
            fields.add(nullValueForDimBow.apply(m));
        }
    }

    private void addDimPort(List fields, AisMessage m) {
        if (m instanceof IDimensionMessage) {
            fields.add(((IDimensionMessage) m).getDimPort());
        } else {
            fields.add(nullValueForDimPort.apply(m));
        }
    }

    private void addDimStarboard(List fields, AisMessage m) {
        if (m instanceof IDimensionMessage) {
            fields.add(((IDimensionMessage) m).getDimStarboard());
        } else {
            fields.add(nullValueForDimStarboard.apply(m));
        }
    }

    private void addDimStern(List fields, AisMessage m) {
        if (m instanceof IDimensionMessage) {
            fields.add(((IDimensionMessage) m).getDimStern());
        } else {
            fields.add(nullValueForDimStern.apply(m));
        }
    }

    private void addShipTypeCargoTypeCode(List fields, AisMessage m) {
        if (m instanceof AisStaticCommon) {
            fields.add(((AisStaticCommon) m).getShipType());
        } else {
            fields.add(nullValueForShipTypeCargoTypeCode.apply(m));
        }
    }

    private void addShipType(List fields, AisMessage m) {
        if (m instanceof AisStaticCommon) {
            ShipTypeCargo stc = new ShipTypeCargo(((AisStaticCommon) m).getShipType());
            fields.add(stc.getShipType().toString());
        } else {
            fields.add(nullValueForShipType.apply(m));
        }
    }

    private void addShipCargo(List fields, AisMessage m) {
        if (m instanceof AisStaticCommon) {
            ShipTypeCargo stc = new ShipTypeCargo(((AisStaticCommon) m).getShipType());
            fields.add(stc.getShipCargo().toString());
        } else {
            fields.add(nullValueForShipCargo.apply(m));
        }
    }

    private void addCallsign(List fields, AisMessage m) {
        if (m instanceof AisStaticCommon) {
            fields.add(AisMessage.trimText(((AisStaticCommon) m).getCallsign()));
        } else {
            fields.add(nullValueForCallsign.apply(m));
        }
    }

    private void addImo(List fields, AisMessage m) {
        if (m instanceof AisMessage5) {
            long imo = ((AisMessage5) m).getImo();
            if (imo > 0) {
                fields.add(imo);
            } else {
                fields.add(nullValueForImo.apply(m));
            }
        } else {
            fields.add(nullValueForImo.apply(m));
        }
    }

    private void addDestination(List fields, AisMessage m) {
        if (m instanceof AisMessage5) {
            String destination = AisMessage.trimText(((AisMessage5) m).getDest());
            if (!isBlank(destination)) {
                fields.add(destination);
            } else {
                fields.add(nullValueForDestination.apply(m));
            }
        } else {
            fields.add(nullValueForDestination.apply(m));
        }
    }

    private void addEta(List fields, AisMessage m) {
        if (m instanceof AisMessage5) {
            fields.add(formatEta(((AisMessage5) m).getEta()));
        } else {
            fields.add(nullValueForEta.apply(m));
        }
    }

    /** {@inheritDoc} */
    @Override
    public void footer(OutputStream stream, long count) throws IOException {
        if (csv != null) {
            csv.flush();
            csv.close();
            csv = null;
        }
    }

    // ---

    protected String formatEta(long eta) {
        /*
        Estimated time of arrival; MMDDHHMM UTC
        Bits 19-16: month; 1-12; 0 = not available = default
        Bits 15-11: day; 1-31; 0 = not available = default
        Bits 10-6: hour; 0-23; 24 = not available = default
        Bits 5-0: minute; 0-59; 60 = not available = default
        For SAR aircraft, the use of this field may be decided by the responsible administration
        */

        int minute = (int) (eta & 63);
        int hour = (int) ((eta >> 6) & 31);
        int day = (int) ((eta >> 11) & 31);
        int month = (int) ((eta >> 16) & 15);
        return formatEta(day, month, hour, minute);
    }

    protected String formatEta(Date eta) {
        LocalDateTime time = LocalDateTime.ofInstant(Instant.ofEpochMilli(eta.getTime()), ZoneId.systemDefault());
        return formatEta(time.getDayOfMonth(), time.getMonthValue(), time.getHour(), time.getMinute());
    }

    protected String formatEta(int day, int month, int hour, int minute) {
        return String.format("%02d/%02d %02d:%02d UTC", day, month, hour, minute);
    }

    // ---

    protected void setNullValueForPositionAccuracy(Function<AisMessage, Object> nullValueForPositionAccuracy) {
        this.nullValueForPositionAccuracy = nullValueForPositionAccuracy;
    }

    protected void setNullValueForLatitude(Function<AisMessage, Object> nullValueForLatitude) {
        this.nullValueForLatitude = nullValueForLatitude;
    }

    protected void setNullValueForLongitude(Function<AisMessage, Object> nullValueForLongitude) {
        this.nullValueForLongitude = nullValueForLongitude;
    }

    protected void setNullValueForSog(Function<AisMessage, Object> nullValueForSog) {
        this.nullValueForSog = nullValueForSog;
    }

    protected void setNullValueForCog(Function<AisMessage, Object> nullValueForCog) {
        this.nullValueForCog = nullValueForCog;
    }

    protected void setNullValueForTrueHeading(Function<AisMessage, Object> nullValueForTrueHeading) {
        this.nullValueForTrueHeading = nullValueForTrueHeading;
    }

    protected void setNullValueForDraught(Function<AisMessage, Object> nullValueForDraught) {
        this.nullValueForDraught = nullValueForDraught;
    }

    protected void setNullValueForName(Function<AisMessage, Object> nullValueForName) {
        this.nullValueForName = nullValueForName;
    }

    protected void setNullValueForDimBow(Function<AisMessage, Object> nullValueForDimBow) {
        this.nullValueForDimBow = nullValueForDimBow;
    }

    protected void setNullValueForDimPort(Function<AisMessage, Object> nullValueForDimPort) {
        this.nullValueForDimPort = nullValueForDimPort;
    }

    protected void setNullValueForDimStarboard(Function<AisMessage, Object> nullValueForDimStarboard) {
        this.nullValueForDimStarboard = nullValueForDimStarboard;
    }

    protected void setNullValueForDimStern(Function<AisMessage, Object> nullValueForDimStern) {
        this.nullValueForDimStern = nullValueForDimStern;
    }

    protected void setNullValueForShipTypeCargoTypeCode(
            Function<AisMessage, Object> nullValueForShipTypeCargoTypeCode) {
        this.nullValueForShipTypeCargoTypeCode = nullValueForShipTypeCargoTypeCode;
    }

    protected void setNullValueForShipType(Function<AisMessage, Object> nullValueForShipType) {
        this.nullValueForShipType = nullValueForShipType;
    }

    protected void setNullValueForShipCargo(Function<AisMessage, Object> nullValueForShipCargo) {
        this.nullValueForShipCargo = nullValueForShipCargo;
    }

    protected void setNullValueForCallsign(Function<AisMessage, Object> nullValueForCallsign) {
        this.nullValueForCallsign = nullValueForCallsign;
    }

    protected void setNullValueForImo(Function<AisMessage, Object> nullValueForImo) {
        this.nullValueForImo = nullValueForImo;
    }

    protected void setNullValueForDestination(Function<AisMessage, Object> nullValueForDestination) {
        this.nullValueForDestination = nullValueForDestination;
    }

    protected void setNullValueForEta(Function<AisMessage, Object> nullValueForEta) {
        this.nullValueForEta = nullValueForEta;
    }

    // ---

    private Function<AisMessage, Object> nullValueForTargetType = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForPositionAccuracy = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForLatitude = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForLongitude = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForSog = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForCog = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForTrueHeading = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForDraught = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForName = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForDimBow = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForDimPort = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForDimStarboard = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForDimStern = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForShipTypeCargoTypeCode = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForShipType = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForShipCargo = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForCallsign = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForImo = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForDestination = m -> NULL_INDICATOR;
    private Function<AisMessage, Object> nullValueForEta = m -> NULL_INDICATOR;

}