1 package au.gov.amsa.navigation;
2
3 import static com.google.common.base.Optional.absent;
4 import static com.google.common.base.Optional.of;
5
6 import java.io.InputStreamReader;
7 import java.io.Reader;
8 import java.util.Map;
9
10 import com.github.davidmoten.rx.Checked;
11 import com.google.common.base.Charsets;
12 import com.google.common.base.Optional;
13
14 import au.gov.amsa.ais.message.AisShipStaticUtil;
15 import au.gov.amsa.risky.format.AisClass;
16 import au.gov.amsa.streams.Strings;
17 import rx.Observable;
18
19 public final class ShipStaticData {
20
21 public static class Info {
22 public final int mmsi;
23 public final Optional<String> imo;
24 public final AisClass cls;
25 public final Optional<Integer> shipType;
26 public final Optional<Float> maxDraftMetres;
27 public final Optional<Integer> dimensionAMetres;
28 public final Optional<Integer> dimensionBMetres;
29 public final Optional<Integer> dimensionCMetres;
30 public final Optional<Integer> dimensionDMetres;
31 public final Optional<String> name;
32
33 public Info(int mmsi, Optional<String> imo, AisClass cls, Optional<Integer> shipType,
34 Optional<Float> maxDraftMetres, Optional<Integer> dimensionAMetres,
35 Optional<Integer> dimensionBMetres, Optional<Integer> dimensionCMetres,
36 Optional<Integer> dimensionDMetres, Optional<String> name) {
37 this.mmsi = mmsi;
38 this.imo = imo;
39 this.cls = cls;
40 this.shipType = shipType;
41 this.maxDraftMetres = maxDraftMetres;
42 this.dimensionAMetres = dimensionAMetres;
43 this.dimensionBMetres = dimensionBMetres;
44 this.dimensionCMetres = dimensionCMetres;
45 this.dimensionDMetres = dimensionDMetres;
46 this.name = name;
47 }
48
49 public Optional<Integer> lengthMetres() {
50 return AisShipStaticUtil.lengthMetres(dimensionAMetres, dimensionBMetres,
51 dimensionCMetres, dimensionDMetres);
52 }
53
54 public Optional<Integer> widthMetres() {
55 return AisShipStaticUtil.widthMetres(dimensionAMetres, dimensionBMetres,
56 dimensionCMetres, dimensionDMetres);
57 }
58
59 @Override
60 public String toString() {
61 StringBuilder builder = new StringBuilder();
62 builder.append("Info [mmsi=");
63 builder.append(mmsi);
64 builder.append(", imo=");
65 builder.append(imo);
66 builder.append(", cls=");
67 builder.append(cls);
68 builder.append(", shipType=");
69 builder.append(shipType);
70 builder.append(", maxDraftMetres=");
71 builder.append(maxDraftMetres);
72 builder.append(", dimensionAMetres=");
73 builder.append(dimensionAMetres.or(-1));
74 builder.append(", dimensionBMetres=");
75 builder.append(dimensionBMetres.or(-1));
76 builder.append(", dimensionCMetres=");
77 builder.append(dimensionCMetres.or(-1));
78 builder.append(", dimensionDMetres=");
79 builder.append(dimensionDMetres.or(-1));
80 builder.append(", name=");
81 builder.append(name.or(""));
82 builder.append("]");
83 return builder.toString();
84 }
85
86 }
87
88 public static Map<Integer, Info> getMapFromResource(String resource) {
89 return getMapFromReader(new InputStreamReader(
90 ShipStaticData.class.getResourceAsStream(resource), Charsets.UTF_8));
91 }
92
93 public static Map<Integer, Info> getMapFromReader(Reader reader) {
94 return ShipStaticData
95
96 .from(reader)
97
98 .toMap(info -> info.mmsi)
99
100 .toBlocking().single();
101 }
102
103 public static Observable<Info> fromAndClose(Reader reader) {
104 return Observable.using(() -> reader, r -> from(r), Checked.a1(r -> r.close()));
105 }
106
107 public static Observable<Info> from(String resource) {
108 return Observable.using(
109 () -> new InputStreamReader(ShipStaticData.class.getResourceAsStream(resource)),
110 r -> from(r), Checked.a1(r -> r.close()));
111 }
112
113 public static Observable<Info> from(Reader reader) {
114 return Strings.lines(reader)
115
116 .filter(line -> !line.startsWith("#"))
117
118 .map(line -> line.trim())
119
120 .filter(line -> line.length() > 0)
121
122 .map(line -> line.split("\t"))
123
124 .map(items -> {
125 int i = 0;
126 int mmsi = Integer.parseInt(items[i++]);
127 String imoTemp = items[i++];
128 Optional<String> imo;
129 if (imoTemp.trim().length() == 0 || Integer.parseInt(imoTemp.trim()) == -1)
130 imo = Optional.absent();
131 else
132 imo = Optional.of(imoTemp);
133 AisClass cls = items[i++].equals("A") ? AisClass.A : AisClass.B;
134 int type = Integer.parseInt(items[i++]);
135 Optional<Integer> shipType = type == -1 ? absent() : of(type);
136 float draft = Float.parseFloat(items[i++]);
137 Optional<Float> maxDraftMetres = draft == -1 ? absent() : of(draft);
138 int a = Integer.parseInt(items[i++]);
139 int b = Integer.parseInt(items[i++]);
140 int c = Integer.parseInt(items[i++]);
141 int d = Integer.parseInt(items[i++]);
142 Optional<Integer> dimensionAMetres = a == -1 ? absent() : of(a);
143 Optional<Integer> dimensionBMetres = b == -1 ? absent() : of(b);
144 Optional<Integer> dimensionCMetres = c == -1 ? absent() : of(c);
145 Optional<Integer> dimensionDMetres = d == -1 ? absent() : of(d);
146
147 i += 2;
148 Optional<String> name;
149 if (i >= items.length || items[i].trim().length() == 0)
150 name = Optional.absent();
151 else
152 name = Optional.of(items[i].trim());
153 return new Info(mmsi, imo, cls, shipType, maxDraftMetres, dimensionAMetres,
154 dimensionBMetres, dimensionCMetres, dimensionDMetres, name);
155 });
156 }
157 }