package au.gov.amsa.geo.adhoc;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.davidmoten.rx.Functions;
import au.gov.amsa.ais.ShipTypeDecoder;
import au.gov.amsa.gt.Shapefile;
import au.gov.amsa.navigation.ShipStaticData;
import au.gov.amsa.navigation.ShipStaticData.Info;
import au.gov.amsa.risky.format.BinaryFixes;
import au.gov.amsa.risky.format.Fix;
import au.gov.amsa.util.Files;
import rx.Observable;
import rx.functions.Func2;
public class LMSAdhocMain {
private static final Logger log = LoggerFactory.getLogger(LMSAdhocMain.class);
public static void main(String[] args) throws FileNotFoundException, IOException {
Map<Integer, Info> ships = ShipStaticData.getMapFromResource("/ship-data-2014.txt");
Pattern pattern = Pattern.compile(".*\\.track");
File base = new File("/media/an/binary-fixes-lms2");
List<File> files = Files.find(new File(base, "2016"), pattern);
log.info("files=" + files.size());
Func2<Fix, Fix, Integer> ascendingTime = (a, b) -> ((Long) a.time()).compareTo(b.time());
String[] shapes = new String[] { "fremantle_port_limits_pl.zip",
"dampier_port_limits_pl.zip", "port_hedland_port_limits_pl.zip" };
File shapeBase = new File("/media/an/shapefiles/port-boundaries");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")
.withZone(ZoneOffset.UTC);
System.out.println("region, mmsi, imo, class, type, lat, lon, timeUTC");
for (String shape : shapes) {
final String shapeName = shape.replace("_port_limits_pl.zip", "");
try (InputStream is = new FileInputStream(new File(shapeBase, shape))) {
Shapefile region = Shapefile.fromZip(is);
Observable.from(files)
// only international vessels
.filter(file -> !file.getName().startsWith("503"))
//
// .filter(file ->
// file.getName().startsWith("215040000")
// || file.getName().startsWith("351424000"))
// .doOnNext(System.out::println)
.concatMap(file -> detectCrossings(file, region))
// count out to in crossings
.toSortedList(ascendingTime) //
.flatMapIterable(Functions.identity()) //
// log
.doOnNext(fix -> {
Info info = ships.get(fix.mmsi());
String type = "";
if (info != null) {
if (info.shipType.isPresent())
type = ShipTypeDecoder.getShipType(info.shipType.get());
else
type = "";
}
String t = formatter
.format(ZonedDateTime.ofInstant(
Instant.ofEpochMilli(fix.time()), ZoneOffset.UTC))
.replace("[UTC]", "");
String imo = info == null ? "" : info.imo.or("");
System.out.format("%s,%s,%s,%s,\"%s\",%s,%s,%s\n", shapeName,
fix.mmsi(), imo, fix.aisClass().name(), type, fix.lat(),
fix.lon(), t);
}).count().toBlocking().single();
}
}
}
private static final DateTimeFormatter date = DateTimeFormatter.ofPattern("dd/MM/yyyy")
.withZone(ZoneOffset.UTC);
private static Observable<Fix> detectCrossings(File file, Shapefile region) {
return BinaryFixes.from(file)
// log
// .lift(Logging.<Fix> logger().onCompleted("read " +
// file).every(10000000)
// .showValue(false).log())
//
.filter(fix -> fix.mmsi() != 0)
// add in or out of region
.map(fix -> new FixAndRegion(fix, region.contains(fix.lat(), fix.lon())))
// detect changes in being in
// our out of region
.distinctUntilChanged(fix -> fix.inRegion)
// pair them up
.buffer(2)
// just get out of region to
// inside region
.filter(list -> list.size() == 2 && !list.get(0).inRegion && list.get(1).inRegion)
// get the fix inside the region
.map(list -> list.get(1).fix)
// one per day
.distinctUntilChanged(fix -> date.format(
ZonedDateTime.ofInstant(Instant.ofEpochMilli(fix.time()), ZoneOffset.UTC)));
}
private static class FixAndRegion {
final Fix fix;
final boolean inRegion;
FixAndRegion(Fix fix, boolean inRegion) {
super();
this.fix = fix;
this.inRegion = inRegion;
}
}
}