package au.gov.amsa.navigation; import java.io.File; import java.io.PrintStream; import java.util.List; import com.github.davidmoten.rx.Checked; import com.github.davidmoten.rx.Transformers; import com.github.davidmoten.rx.slf4j.Logging; import com.google.common.base.Optional; import au.gov.amsa.ais.AisMessage; import au.gov.amsa.ais.Timestamped; import au.gov.amsa.ais.message.AisShipStatic; import au.gov.amsa.ais.message.AisShipStaticA; import au.gov.amsa.ais.message.AisShipStaticUtil; import au.gov.amsa.ais.rx.Streams; import au.gov.amsa.ais.rx.Streams.TimestampedAndLine; import rx.Observable; import rx.Scheduler; import rx.functions.Action1; import rx.functions.Func0; import rx.functions.Func1; import rx.schedulers.Schedulers; public final class ShipStaticDataCreator { public static Observable<AisShipStatic> writeStaticDataToFile(List<File> files, File outputFile) { return writeStaticDataToFile(files, outputFile, Schedulers.computation()); } public static Observable<AisShipStatic> writeStaticDataToFile(List<File> files, File outputFile, Scheduler scheduler) { Func0<PrintStream> resourceFactory = Checked.f0(() -> new PrintStream(outputFile)); Func1<PrintStream, Observable<AisShipStatic>> observableFactory = out -> Observable .from(files) // buffer into chunks for each processor .buffer(Math.max(1, files.size() / Runtime.getRuntime().availableProcessors()) - 1) .flatMap( list -> Observable.from(list) // .lift(Logging.<File> logger().showValue().showMemory().log()) // .concatMap( file -> Streams.extract(Streams.nmeaFromGzip(file)) // .flatMap(aisShipStaticOnly) // .map(m -> m.getMessage().get().message()) // .filter(m -> m instanceof AisShipStatic) // .cast(AisShipStatic.class) // .distinct(m -> m.getMmsi()) // .doOnError(e -> System.err.println("could not read " + file + ": " + e.getMessage())) // .onErrorResumeNext(Observable.<AisShipStatic> empty())) // .distinct(m -> m.getMmsi()) // .subscribeOn(scheduler)) // .distinct(m -> m.getMmsi()) // .compose(Transformers.mapWithIndex()) // .doOnNext(indexed -> { if (indexed.index() == 0) { out.println( "# MMSI, IMO, AisClass, AisShipType, MaxPresentStaticDraughtMetres, DimAMetres, DimBMetres, DimCMetres, DimDMetres, LengthMetres, WidthMetres, Name"); out.println("# columns are tab delimited"); out.println("# -1 = not present"); } }) // .map(indexed -> indexed.value()) // .doOnNext(m -> { out.format("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", m.getMmsi(), getImo(m).or(-1), m instanceof AisShipStaticA ? "A" : "B", m.getShipType(), getMaximumPresentStaticDraughtMetres(m).or(-1F), m.getDimensionA().or(-1), m.getDimensionB().or(-1), m.getDimensionC().or(-1), m.getDimensionD().or(-1), AisShipStaticUtil.lengthMetres(m).or(-1), AisShipStaticUtil.widthMetres(m).or(-1), prepareName(m.getName())); out.flush(); }); Action1<PrintStream> disposeAction = out -> out.close(); return Observable.using(resourceFactory, observableFactory, disposeAction); } private static String prepareName(String name) { if (name == null) return ""; else return name.replace("\t", " ").trim(); } private static Optional<Integer> getImo(AisShipStatic m) { if (m instanceof AisShipStaticA) { return ((AisShipStaticA) m).getImo(); } else return Optional.absent(); } private static Optional<Float> getMaximumPresentStaticDraughtMetres(AisShipStatic m) { if (m instanceof AisShipStaticA) { return Optional.of((float) ((AisShipStaticA) m).getMaximumPresentStaticDraughtMetres()); } else return Optional.absent(); } private static Func1<TimestampedAndLine<AisMessage>, Observable<TimestampedAndLine<AisShipStatic>>> aisShipStaticOnly = m -> { Optional<Timestamped<AisMessage>> message = m.getMessage(); if (message.isPresent() && message.get().message() instanceof AisShipStatic) { @SuppressWarnings("unchecked") TimestampedAndLine<AisShipStatic> m2 = (TimestampedAndLine<AisShipStatic>) (TimestampedAndLine<?>) m; return Observable.just(m2); } else return Observable.empty(); }; }