1 package au.gov.amsa.geo.model;
2
3 import static au.gov.amsa.util.navigation.Position.to180;
4
5 import java.math.BigDecimal;
6 import java.util.TreeSet;
7
8 import org.apache.log4j.Logger;
9
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.base.Optional;
12 import com.google.common.collect.BiMap;
13 import com.google.common.collect.HashBiMap;
14
15
16 public class Grid {
17
18 private static Logger log = Logger.getLogger(Grid.class);
19
20 private final TreeSet<Double> lats;
21
22
23
24
25 private final BiMap<Double, Long> latIndexes;
26
27 private final TreeSet<Double> lons;
28
29
30
31 private final BiMap<Double, Long> lonIndexes;
32
33 private final Options options;
34
35 public Grid(Options options) {
36 this.options = options;
37 lats = new TreeSet<Double>();
38 {
39 BigDecimal lat = getStartLat(options);
40 while (lat.doubleValue() >= options.getFilterBounds()
41 .getBottomRightLat()) {
42 lats.add(lat.doubleValue());
43 lat = lat.subtract(options.getCellSizeDegrees());
44 }
45 lat = lat.subtract(options.getCellSizeDegrees());
46 lats.add(lat.doubleValue());
47 }
48
49 {
50 latIndexes = HashBiMap.create();
51 long index = 0;
52 for (double lat : lats.descendingSet()) {
53 latIndexes.put(lat, index);
54 index++;
55 }
56 }
57
58 {
59 lons = new TreeSet<Double>();
60 BigDecimal lon = getStartLon(options);
61
62
63
64 double maxLon;
65 if (options.getFilterBounds().getBottomRightLon() < lon
66 .doubleValue())
67 maxLon = options.getFilterBounds().getBottomRightLon() + 360;
68 else
69 maxLon = options.getFilterBounds().getBottomRightLon();
70 while (lon.doubleValue() <= maxLon) {
71 lons.add(to180(lon.doubleValue()));
72 lon = lon.add(options.getCellSizeDegrees());
73 }
74 lon.add(options.getCellSizeDegrees());
75 lons.add(lon.doubleValue());
76 }
77 {
78 lonIndexes = HashBiMap.create();
79 long index = 0;
80 for (double lon : lons) {
81 lonIndexes.put(lon, index);
82 index++;
83 }
84 }
85
86 }
87
88 @VisibleForTesting
89 static BigDecimal getStartLat(Options options) {
90 final long moveStartLatUpByCells;
91 if (options.getFilterBounds().getTopLeftLat() == options.getOriginLat()
92 .doubleValue())
93 moveStartLatUpByCells = 0;
94 else
95 moveStartLatUpByCells = Math.max(0, Math.round(Math.floor((options
96 .getFilterBounds().getTopLeftLat() - options.getOriginLat()
97 .doubleValue())
98 / options.getCellSizeDegrees().doubleValue()) + 1));
99 BigDecimal result = options.getOriginLat();
100 for (int i = 0; i < moveStartLatUpByCells; i++)
101 result = result.add(options.getCellSizeDegrees());
102 return result;
103 }
104
105 @VisibleForTesting
106 static BigDecimal getStartLon(Options options) {
107 final long moveStartLonLeftByCells;
108 if (options.getFilterBounds().getTopLeftLon() == options.getOriginLon()
109 .doubleValue())
110 moveStartLonLeftByCells = 0;
111 else {
112 moveStartLonLeftByCells = Math.max(0, Math.round(Math
113 .floor((options.getOriginLon().doubleValue() - options
114 .getFilterBounds().getTopLeftLon())
115 / options.getCellSizeDegrees().doubleValue()) + 1));
116 }
117 BigDecimal result = options.getOriginLon();
118 for (int i = 0; i < moveStartLonLeftByCells; i++)
119 result = result.subtract(options.getCellSizeDegrees());
120 return result;
121 }
122
123 public Optional<Cell> cellAt(double lat, double lon) {
124 if (!options.getFilterBounds().contains(lat, lon))
125 return Optional.absent();
126 else {
127 Long latIndex = latIndexes.get(lats.ceiling(lat));
128 Long lonIndex = lonIndexes.get(lons.floor(lon));
129 return Optional.of(new Cell(latIndex, lonIndex));
130 }
131 }
132
133 public double leftEdgeLongitude(Cell cell) {
134 return leftEdgeLongitude(cell.getLonIndex());
135 }
136
137 private double leftEdgeLongitude(long lonIndex) {
138 return lonIndexes.inverse().get(lonIndex);
139 }
140
141 public double rightEdgeLongitude(Cell cell) {
142 try {
143 return lonIndexes.inverse().get(cell.getLonIndex() + 1);
144 } catch (RuntimeException e) {
145 log.warn("cell=" + cell + ", options=" + options);
146 throw e;
147 }
148 }
149
150 public double topEdgeLatitude(Cell cell) {
151 return topEdgeLatitude(cell.getLatIndex());
152 }
153
154 public double topEdgeLatitude(long latIndex) {
155 return latIndexes.inverse().get(latIndex);
156 }
157
158 public double bottomEdgeLatitude(Cell cell) {
159 return latIndexes.inverse().get(cell.getLatIndex() + 1);
160 }
161
162 public double centreLat(long latIndex) {
163 return topEdgeLatitude(latIndex)
164 - options.getCellSizeDegrees().doubleValue() / 2;
165 }
166
167 public double centreLon(long lonIndex) {
168 return leftEdgeLongitude(lonIndex)
169 + options.getCellSizeDegrees().doubleValue() / 2;
170 }
171
172 }