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 }