1 package au.gov.amsa.geo.model;
2
3 import org.apache.log4j.Logger;
4
5 import au.gov.amsa.util.navigation.Position;
6 import au.gov.amsa.util.navigation.Position.LongitudePair;
7
8 public class GridTraversor {
9
10 private static Logger log = Logger.getLogger(GridTraversor.class);
11
12 private static final double SMALL_INCREMENT_DEGREES = 0.0000001;
13
14 private final Options options;
15
16 public GridTraversor(Options options) {
17 this.options = options;
18 }
19
20 private static double bearingDegrees(Position a, Position b) {
21 double val = a.getBearingDegrees(b);
22 if (val == 0 && a.getLat() == b.getLat()) {
23 if (b.getLon() > a.getLon())
24 val = 90;
25 else if (b.getLon() < a.getLon())
26 val = 270;
27 else
28
29 val = 0;
30 }
31 return val;
32 }
33
34 public Position nextPoint(Position a, Position b) {
35 if (a.equals(b))
36 return b;
37 double bearingDegrees = bearingDegrees(a, b);
38
39
40
41
42 Cell cell1 = Cell.cellAt(a.getLat(), a.getLon(), options).get();
43 if (bearingDegrees > 180
44 && a.getLon() == cell1.leftEdgeLongitude(options)) {
45 a = new Position(a.getLat(), a.getLon() - SMALL_INCREMENT_DEGREES);
46 cell1 = Cell.cellAt(a.getLat(), a.getLon(), options).get();
47 bearingDegrees = bearingDegrees(a, b);
48 } else if ((bearingDegrees > 270 || bearingDegrees < 90)
49 && a.getLat() == cell1.topEdgeLatitude(options)) {
50 a = new Position(a.getLat() + SMALL_INCREMENT_DEGREES, a.getLon());
51 cell1 = Cell.cellAt(a.getLat(), a.getLon(), options).get();
52 bearingDegrees = bearingDegrees(a, b);
53 }
54
55 Cell cell2 = Cell.cellAt(b.getLat(), b.getLon(), options).get();
56 if (cell1.equals(cell2))
57 return b;
58 else {
59
60 double targetLon = getTargetLon(cell1, bearingDegrees);
61
62
63 double leftLon = cell1.leftEdgeLongitude(options);
64 double rightLon = cell1.rightEdgeLongitude(options);
65 double targetLat = getTargetLat(cell1, bearingDegrees);
66
67 if (bearingDegrees == 0 || bearingDegrees == 180)
68 return Position.create(targetLat, a.getLon());
69
70 {
71 Position result = nextPointCrossingLatitude(a, b, leftLon,
72 rightLon, targetLat, bearingDegrees);
73 if (result != null)
74 return result;
75 }
76 {
77 double otherLat = getNonTargetLat(cell1, bearingDegrees);
78 Position result = nextPointCrossingLatitude(a, b, leftLon,
79 rightLon, otherLat, bearingDegrees);
80 if (result != null)
81 return result;
82 }
83
84
85 Double latCrossing = a.getLatitudeOnGreatCircle(b, targetLon);
86
87 if (latCrossing != null) {
88 double topLat = cell1.topEdgeLatitude(options);
89 double bottomLat = cell1.bottomEdgeLatitude(options);
90 if (topLat >= latCrossing && bottomLat <= latCrossing)
91 return Position.create(latCrossing, targetLon);
92 }
93
94 log.warn("unexpected! Could not calculate next point for segment between\n a = "
95 + a + " b = " + b + "\noptions=" + options);
96 return b;
97
98 }
99 }
100
101 private double getNonTargetLat(Cell cell, double bearingDegrees) {
102 if (bearingDegrees >= 270 || bearingDegrees < 90)
103 return cell.bottomEdgeLatitude(options);
104 else
105 return cell.topEdgeLatitude(options);
106 }
107
108 private Position nextPointCrossingLatitude(Position a, Position b,
109 double leftLon, double rightLon, double targetLat,
110 double bearingDegrees) {
111 LongitudePair lonCrossingCandidates = a.getLongitudeOnGreatCircle(b,
112 targetLat);
113 if (lonCrossingCandidates != null) {
114 double lonCrossing;
115
116 boolean candidate1ok = leftLon <= lonCrossingCandidates.getLon1()
117 && lonCrossingCandidates.getLon1() <= rightLon
118 && !(lonCrossingCandidates.getLon1() == a.getLon() && targetLat == a
119 .getLat());
120
121 boolean candidate2ok = leftLon <= lonCrossingCandidates.getLon2()
122 && lonCrossingCandidates.getLon2() <= rightLon
123 && !(lonCrossingCandidates.getLon2() == a.getLon() && targetLat == a
124 .getLat());
125 if (candidate1ok && candidate2ok) {
126
127
128
129
130 double distance1ToB = new Position(targetLat,
131 lonCrossingCandidates.getLon1()).getDistanceToKm(b);
132 double distance2ToB = new Position(targetLat,
133 lonCrossingCandidates.getLon2()).getDistanceToKm(b);
134 double distanceAToB = a.getDistanceToKm(b);
135 if (distance1ToB > distanceAToB)
136 candidate1ok = false;
137 if (distance2ToB > distanceAToB)
138 candidate2ok = false;
139 if (candidate1ok && candidate2ok) {
140 if (distance1ToB < distance2ToB) {
141 candidate1ok = false;
142 } else
143 candidate2ok = false;
144 }
145 if (candidate1ok)
146 lonCrossing = lonCrossingCandidates.getLon1();
147 else if (candidate2ok)
148 lonCrossing = lonCrossingCandidates.getLon2();
149 else
150
151 return null;
152 } else if (candidate1ok) {
153 lonCrossing = lonCrossingCandidates.getLon1();
154 } else if (candidate2ok)
155 lonCrossing = lonCrossingCandidates.getLon2();
156 else
157 return null;
158
159 double bearingDegreesTest = bearingDegrees(a,
160 Position.create(targetLat, lonCrossing));
161 double diff = Position.getBearingDifferenceDegrees(bearingDegrees,
162 bearingDegreesTest);
163 if (Math.abs(diff) > 90)
164 return null;
165 return Position.create(targetLat, lonCrossing);
166 } else
167 return null;
168 }
169
170 private double getTargetLon(Cell cell, double bearingDegrees) {
171 if (bearingDegrees >= 0 && bearingDegrees < 180)
172 return cell.rightEdgeLongitude(options);
173 else
174 return cell.leftEdgeLongitude(options);
175 }
176
177 private double getTargetLat(Cell cell, double bearingDegrees) {
178 if (bearingDegrees >= 270 || bearingDegrees < 90)
179 return cell.topEdgeLatitude(options);
180 else
181 return cell.bottomEdgeLatitude(options);
182 }
183
184 }