1 package au.gov.amsa.geo.model;
2
3 import static au.gov.amsa.geo.model.Util.formatDate;
4
5 import java.math.BigDecimal;
6
7 import org.joda.time.DateTime;
8
9 import com.google.common.base.Optional;
10 import com.google.common.base.Preconditions;
11
12 public class Options {
13
14 private static final double KM_PER_NM = 1.852;
15 private final BigDecimal originLat;
16 private final BigDecimal originLon;
17 private final BigDecimal cellSizeDegrees;
18 private final double cellSizeDegreesDouble;
19 private final Bounds bounds;
20 private final Grid grid;
21 private final Bounds filterBounds;
22 private final SegmentOptions segmentOptions;
23 private final Optional<Long> startTime;
24 private final Optional<Long> finishTime;
25
26 public Options(BigDecimal originLat, BigDecimal originLon,
27 BigDecimal cellSizeDegrees, Bounds bounds,
28 Optional<Bounds> filterBounds, SegmentOptions segmentOptions,
29 Optional<Long> startTime, Optional<Long> finishTime) {
30 Preconditions.checkNotNull(originLat);
31 Preconditions.checkNotNull(originLon);
32 Preconditions.checkNotNull(cellSizeDegrees);
33 Preconditions.checkArgument(cellSizeDegrees.doubleValue() > 0);
34 Preconditions.checkNotNull(filterBounds);
35 Preconditions.checkNotNull(startTime);
36 Preconditions.checkNotNull(finishTime);
37 this.originLat = originLat;
38 this.originLon = originLon;
39 this.cellSizeDegrees = cellSizeDegrees;
40 this.bounds = bounds;
41 if (filterBounds.isPresent())
42 this.filterBounds = filterBounds.get();
43 else
44 this.filterBounds = bounds;
45 this.segmentOptions = segmentOptions;
46 this.startTime = startTime;
47 this.finishTime = finishTime;
48 grid = new Grid(this);
49 this.cellSizeDegreesDouble = cellSizeDegrees.doubleValue();
50 }
51
52 public int maxCells() {
53 return (int) Math.round(bounds.getWidthDegrees()
54 * bounds.getHeightDegrees() / cellSizeDegreesDouble
55 / cellSizeDegreesDouble);
56 }
57
58 public static Bounds createFilterBounds(Bounds bounds,
59 SegmentOptions segmentOptions) {
60 double maxTimeTravellingHours = 4.0;
61 double maxDistanceKm = maxTimeTravellingHours
62 * segmentOptions.maxSpeedKnots() * KM_PER_NM;
63 au.gov.amsa.util.navigation.Position topLeftBounds = au.gov.amsa.util.navigation.Position
64 .create(bounds.getTopLeftLat(), bounds.getTopLeftLon());
65 double topLat = topLeftBounds.predict(maxDistanceKm, 0).getLat();
66 double leftLon = topLeftBounds.predict(maxDistanceKm, -90).getLon();
67 au.gov.amsa.util.navigation.Position bottomRightBounds = au.gov.amsa.util.navigation.Position
68 .create(bounds.getBottomRightLat(), bounds.getBottomRightLon());
69
70 double bottomLat = bottomRightBounds.predict(maxDistanceKm, 180)
71 .getLat();
72 double rightLon = bottomRightBounds.predict(maxDistanceKm, 90).getLon();
73
74 return new Bounds(topLat, leftLon, bottomLat, rightLon);
75 }
76
77 public Optional<Long> getStartTime() {
78 return startTime;
79 }
80
81 public Optional<Long> getFinishTime() {
82 return finishTime;
83 }
84
85 public BigDecimal getOriginLat() {
86 return originLat;
87 }
88
89 public BigDecimal getOriginLon() {
90 return originLon;
91 }
92
93 public BigDecimal getCellSizeDegrees() {
94 return cellSizeDegrees;
95 }
96
97 public double getCellSizeDegreesAsDouble() {
98 return cellSizeDegreesDouble;
99 }
100
101 public Bounds getBounds() {
102 return bounds;
103 }
104
105 public Bounds getFilterBounds() {
106 return filterBounds;
107 }
108
109 public Grid getGrid() {
110 return grid;
111 }
112
113 @Override
114 public String toString() {
115 StringBuilder builder = new StringBuilder();
116 builder.append("Options [originLat=");
117 builder.append(originLat);
118 builder.append(", originLon=");
119 builder.append(originLon);
120 builder.append(", cellSizeDegrees=");
121 builder.append(cellSizeDegrees);
122 builder.append(", bounds=");
123 builder.append(bounds);
124 builder.append(", filterBounds=");
125 builder.append(filterBounds);
126 builder.append(", startTime=");
127 builder.append(formatDate(startTime));
128 builder.append(", finishTime=");
129 builder.append(formatDate(finishTime));
130 builder.append(", segmentOptions=");
131 builder.append(segmentOptions);
132 builder.append("]");
133 return builder.toString();
134 }
135
136 public static Builder builder() {
137 return new Builder();
138 }
139
140 public static Builder builder(Options o) {
141 return builder().bounds(o.getBounds())
142 .cellSizeDegrees(o.getCellSizeDegrees())
143 .filterBounds(o.getFilterBounds())
144 .finishTime(o.getFinishTime()).startTime(o.getStartTime())
145 .originLat(o.getOriginLat()).originLon(o.getOriginLon())
146 .segmentOptions(o.getSegmentOptions());
147 }
148
149 public SegmentOptions getSegmentOptions() {
150 return segmentOptions;
151 }
152
153 public static class Builder {
154
155 private BigDecimal originLat = BigDecimal.ZERO;
156 private BigDecimal originLon = BigDecimal.ZERO;
157 private BigDecimal cellSizeDegrees = BigDecimal.ONE;
158 private Bounds bounds = new Bounds(15, 67, -60, 179);
159 private SegmentOptions segmentOptions = SegmentOptions.builder()
160 .build();
161 private Optional<Bounds> filterBounds = Optional.absent();
162 private Optional<Long> startTime = Optional.absent();
163 private Optional<Long> finishTime = Optional.absent();
164
165 private Builder() {
166 }
167
168 public Builder originLat(BigDecimal originLat) {
169 this.originLat = originLat;
170 return this;
171 }
172
173 public Builder originLat(double originLat) {
174 this.originLat = BigDecimal.valueOf(originLat);
175 return this;
176 }
177
178 public Builder originLon(BigDecimal originLon) {
179 this.originLon = originLon;
180 return this;
181 }
182
183 public Builder originLon(double originLon) {
184 this.originLon = BigDecimal.valueOf(originLon);
185 return this;
186 }
187
188 public Builder cellSizeDegrees(BigDecimal cellSizeDegrees) {
189 this.cellSizeDegrees = cellSizeDegrees;
190 return this;
191 }
192
193 public Builder cellSizeDegrees(double cellSizeDegrees) {
194 this.cellSizeDegrees = BigDecimal.valueOf(cellSizeDegrees)
195 .setScale(30);
196 return this;
197 }
198
199 public Builder bounds(Bounds bounds) {
200 this.bounds = bounds;
201 return this;
202 }
203
204 public Builder filterBounds(Bounds bounds) {
205 this.filterBounds = Optional.of(bounds);
206 return this;
207 }
208
209 public Builder startTime(Optional<Long> startTime) {
210 this.startTime = startTime;
211 return this;
212 }
213
214 public Builder startTime(String isoDateTimeFormat) {
215 this.startTime = Optional.of(DateTime.parse(isoDateTimeFormat)
216 .getMillis());
217 return this;
218 }
219
220 public Builder finishTime(Optional<Long> finishTime) {
221 this.finishTime = finishTime;
222 return this;
223 }
224
225 public Builder finishTime(String isoDateTimeFormat) {
226 this.finishTime = Optional.of(DateTime.parse(isoDateTimeFormat)
227 .getMillis());
228 return this;
229 }
230
231 public Builder segmentOptions(SegmentOptions o) {
232 this.segmentOptions = o;
233 return this;
234 }
235
236 public Options build() {
237 return new Options(originLat, originLon, cellSizeDegrees, bounds,
238 filterBounds, segmentOptions, startTime, finishTime);
239 }
240 }
241
242 public Builder buildFrom() {
243 return builder(this);
244 }
245
246 }