001 package ps2.test;
002
003 import ps2.*;
004 import static ps2.test.TestValues.TOLERANCE;
005
006 import org.junit.Test;
007 import static org.junit.Assert.assertTrue;
008 import static org.junit.Assert.fail;
009 import static org.junit.Assert.assertThat;
010 import static org.junit.Assert.assertFalse;
011 import static org.junit.Assert.assertEquals;
012 import static org.hamcrest.CoreMatchers.*;
013
014
015 /**
016 * Unit tests for the GeoPoint class.
017 **/
018 public class GeoPointTest {
019
020 /** A GeoPoint object with the same data as eq2 **/
021 private final GeoPoint eq1;
022
023 /** A GeoPoint object with the same data as eq1 **/
024 private final GeoPoint eq2;
025
026 /** A GeoPoint object with different data than eq1 **/
027 private final GeoPoint diff;
028
029
030 /** A GeoPoint representing the center of Seattle **/
031 private final GeoPoint p1;
032
033
034 /** A GeoPoint due north of Seattle **/
035 private final GeoPoint north;
036
037 /** Heading, in degrees, between p1 and north **/
038 private final double nHeading;
039
040 /** Distance, in miles, between p1 and north **/
041 private final double nDist;
042
043
044 /** A GeoPoint due south of Seattle **/
045 private final GeoPoint south;
046
047 /** Heading, in degrees, between p1 and south **/
048 private final double sHeading;
049
050 /** Distance, in miles, between p1 and south **/
051 private final double sDist;
052
053
054 /** A GeoPoint due east of Seattle **/
055 private final GeoPoint east;
056
057 /** Heading, in degrees, between p1 and east **/
058 private final double eHeading;
059
060 /** Distance, in miles, between p1 and east **/
061 private final double eDist;
062
063
064 /** A GeoPoint due west of Seattle **/
065 private final GeoPoint west;
066
067 /** Heading, in degrees, between p1 and west **/
068 private final double wHeading;
069
070 /** Distance, in miles, between p1 and west **/
071 private final double wDist;
072
073
074 /** A GeoPoint due north east of Seattle **/
075 private final GeoPoint northEast;
076
077 /** Heading, in degrees, between p1 and north east **/
078 private final double neHeading;
079
080 /** Distance, in miles, between p1 and north east **/
081 private final double neDist;
082
083
084 /** A GeoPoint due south west of Seattle **/
085 private final GeoPoint southWest;
086
087 /** Heading, in degrees, between p1 and south west **/
088 private final double swHeading;
089
090 /** Distance, in miles, between p1 and south west **/
091 private final double swDist;
092
093
094 public GeoPointTest() {
095
096 int eqLat = locationToInt(47, 36, 35);
097 int eqLong = locationToInt(-122, 19, 59);
098 eq1 = new GeoPoint(eqLat, eqLong);
099 eq2 = new GeoPoint(eqLat, eqLong);
100 diff = new GeoPoint(eqLat + 50, eqLong + 50);
101
102
103 // Center of Seattle:
104 p1 = new GeoPoint(locationToInt(47, 36, 35),
105 locationToInt(-122, 19, 59));
106 // due north
107 north = new GeoPoint(locationToInt(47, 42, 35),
108 locationToInt(-122, 19, 59));
109 nHeading = 0;
110 nDist = 6.904;
111
112 // due south
113 south = new GeoPoint(locationToInt(47, 30, 35),
114 locationToInt(-122, 19, 59));
115 sHeading = 180;
116 sDist = 6.904;
117
118 // due east
119 east = new GeoPoint(locationToInt(47, 36, 35),
120 locationToInt(-122, 13, 59));
121 eHeading = 90;
122 eDist = 4.6574;
123
124 // due west
125 west = new GeoPoint(locationToInt(47, 36, 35),
126 locationToInt(-122, 25, 59));
127 wHeading = 270;
128 wDist = 4.6574;
129
130 // north east
131 northEast = new GeoPoint(locationToInt(47, 42, 35),
132 locationToInt(-122, 13, 59));
133 neHeading = 34.0033834;
134 neDist = 8.328060444;
135
136 // south west
137 southWest = new GeoPoint(locationToInt(47, 30, 35),
138 locationToInt(-122, 25, 59));
139 swHeading = 214.0033834;
140 swDist = 8.328060444;
141 }
142
143
144 /**
145 * Convert a degree, minutes, seconds latitude or longitude value
146 * to its associated integer value (to millionths of a degree).
147 * Allows invalid locations to be specified for testing, but
148 * doesn't allow negative minute/second values.
149 **/
150 static int locationToInt(int deg, int min, int sec) {
151 if (min < 0 || sec < 0) {
152 throw new RuntimeException("min and sec must be positive!");
153 }
154
155 double dmin = (double)min / 60;
156 double dsec = (double)sec / (60 * 60);
157 if (deg >= 0) {
158 return ((int)Math.round(1000000 * (deg + dmin + dsec)));
159 } else {
160 return ((int)Math.round(1000000 * (deg - dmin - dsec)));
161 }
162 }
163
164
165
166 /**
167 * Sanity check for our locationToInt() method
168 **/
169 @Test
170 public void testLocationToIntMethod() {
171 int lat = locationToInt(42, 21, 30);
172 int lng = locationToInt(-71, 3, 37);
173 if (lat != 42358333) {
174 fail("42 21 30 latitude must be 42358333, is " + lat);
175 } else if (lng != -71060278) {
176 fail("-71 3 37 longitude must be -71060278, is " + lng);
177 }
178 }
179
180
181
182 ////////////////////////////////////////////////////
183 // TEST LEGAL/ILLEGAL VALUES IN GeoPoint CREATION //
184 ////////////////////////////////////////////////////
185
186
187 /**
188 * Tests that GeoPoints can be created with some legal latitude
189 * and longitude values that are in the greater Seattle area, and
190 * that the values it's provided are returned unchanged.
191 **/
192 @Test
193 public void testLegalValues1() {
194 int valLat = locationToInt(47, 36, 0);
195 int valLong = locationToInt(-122, 19, 50);
196 checkLegal(valLat, valLong);
197 }
198
199
200 /**
201 * Tests that GeoPoints can be created with some legal latitude
202 * and longitude values that are in the greater Seattle area, and
203 * that the values it's provided are returned unchanged.
204 **/
205 @Test
206 public void testLegalValues2() {
207 int valLat = locationToInt(47, 35, 58);
208 int valLong = locationToInt(-122, 20, 0);
209 checkLegal(valLat, valLong);
210 }
211
212 /**
213 * Tests that GeoPoints can be created with the max/min latitudes
214 * and longitudes
215 **/
216 @Test
217 public void testLegalValuesExtreme() {
218 checkLegal(GeoPoint.MAX_LATITUDE, GeoPoint.MAX_LONGITUDE);
219 checkLegal(GeoPoint.MIN_LATITUDE, GeoPoint.MAX_LONGITUDE);
220 checkLegal(GeoPoint.MAX_LATITUDE, GeoPoint.MIN_LONGITUDE);
221 checkLegal(GeoPoint.MIN_LATITUDE, GeoPoint.MIN_LONGITUDE);
222 }
223
224 /**
225 * Helper method for asserting that a legal latitude/longitude
226 * pair can be used to create a GeoPoint and that those exact same
227 * values are then returned.
228 **/
229 void checkLegal(int lat, int lng) {
230 GeoPoint g = null;
231 try {
232 g = new GeoPoint(lat, lng);
233 } catch (Exception ex) {
234 // Failed
235 fail("Didn't allow legal (latitude,longitude) of (" +
236 lat + ", " + lng + ")");
237 return;
238 }
239
240 if (g.getLatitude() != lat) {
241 fail("Latitude should have been set to " + lat +
242 ", was set to " + g.getLatitude() + " instead.");
243
244 } else if (g.getLongitude() != lng) {
245 fail("Longitude should have been set to " + lng +
246 ", was set to " + g.getLongitude() + " instead.");
247 }
248 }
249
250
251 //////////////////////////////
252 // TEST GeoPoint.distanceTo //
253 //////////////////////////////
254
255 /**
256 * Test that distance 0 is handled correctly: same object
257 **/
258 @Test
259 public void testDistance01() {
260 assertEquals("Distance between a GeoPoint and itself must be 0",
261 0.0, p1.distanceTo(p1), TOLERANCE);
262 }
263
264
265 /**
266 * Test that distance 0 is handled correctly: equal objects
267 **/
268 @Test
269 public void testDistance02() {
270 assertEquals("Distance between two equal GeoPoints must be 0",
271 0.0, eq1.distanceTo(eq2), TOLERANCE);
272 }
273
274
275 /**
276 * Test that points due north are handled correctly
277 **/
278 @Test
279 public void testDistanceDueNorth() {
280 checkDist(p1, north, nDist);
281 }
282
283
284 /**
285 * Test that points due south are handled correctly
286 **/
287 @Test
288 public void testDistanceDueSouth() {
289 checkDist(p1, south, sDist);
290 }
291
292
293 /**
294 * Test that points due east are handled correctly
295 **/
296 @Test
297 public void testDistanceDueEast() {
298 checkDist(p1, east, eDist);
299 }
300
301
302 /**
303 * Test that points due west are handled correctly
304 **/
305 @Test
306 public void testDistanceDueWest() {
307 checkDist(p1, west, wDist);
308 }
309
310
311 /**
312 * Test that points north east are handled correctly
313 **/
314 @Test
315 public void testDistanceDueNorthEast() {
316 checkDist(p1, northEast, neDist);
317 }
318
319
320 /**
321 * Test that points south west are handled correctly
322 **/
323 @Test
324 public void testDistanceDueSouthWest() {
325 checkDist(p1, southWest, swDist);
326 }
327
328
329 /**
330 * Helper method that asserts that the distance between p1 and p2
331 * is dist AND that the distance between p2 and p1 is also dist.
332 **/
333 void checkDist(GeoPoint p1, GeoPoint p2, double dist) {
334 assertEquals("distanceTo()",
335 dist, p1.distanceTo(p2), TOLERANCE);
336 assertEquals("distanceTo()",
337 dist, p2.distanceTo(p1), TOLERANCE);
338 }
339
340
341 /////////////////////////////
342 // TEST GeoPoint.headingTo //
343 /////////////////////////////
344
345 /**
346 * Test that points due north are handled correctly
347 **/
348 @Test
349 public void testHeadingDueNorth() {
350 checkHeading(p1, north, nHeading);
351 }
352
353
354 /**
355 * Test that points due south are handled correctly
356 **/
357 @Test
358 public void testHeadingDueSouth() {
359 checkHeading(p1, south, sHeading);
360 }
361
362
363 /**
364 * Test that points due east are handled correctly
365 **/
366 @Test
367 public void testHeadingDueEast() {
368 checkHeading(p1, east, eHeading);
369 }
370
371
372 /**
373 * Test that points due west are handled correctly
374 **/
375 @Test
376 public void testHeadingDueWest() {
377 checkHeading(p1, west, wHeading);
378 }
379
380
381 /**
382 * Test that points north east are handled correctly
383 **/
384 @Test
385 public void testHeadingNorthEast() {
386 checkHeading(p1, northEast, neHeading);
387 }
388
389
390 /**
391 * Test that points south west are handled correctly
392 **/
393 @Test
394 public void testHeadingSouthWest() {
395 checkHeading(p1, southWest, swHeading);
396 }
397
398
399 /**
400 * Helper method that asserts that the heading between p1 and p2
401 * is heading AND that the distance between p2 and p1 is heading + 180.
402 **/
403 void checkHeading(GeoPoint p1, GeoPoint p2, double heading) {
404 assertEquals("headingTo()",
405 heading, p1.headingTo(p2), TOLERANCE);
406 assertEquals("headingTo()",
407 (heading + 180) % 360, p2.headingTo(p1), TOLERANCE);
408 }
409
410
411 //////////////////////////
412 // TEST GeoPoint.equals //
413 //////////////////////////
414
415 /**
416 * Test positive case: same object
417 **/
418 @Test
419 public void testEquals1() {
420 assertEquals("A GeoPoint must be equal() to itself",
421 eq1, eq1);
422 }
423
424
425 /**
426 * Test positive case: equal objects
427 **/
428 @Test
429 public void testEquals2() {
430 assertEquals("Two GeoPoints with the same data must be equal()",
431 eq1, eq2);
432 }
433
434
435 /**
436 * Test that null values are handled correctly
437 **/
438 @Test
439 public void testNotEqualsNull() {
440 if (!eq1.equals(null))
441 return; // gp != null which is good.
442 fail("Null not handled: equals(null) returns true");
443 }
444
445 /**
446 * Test that objects of a different type are handled appropriately
447 **/
448 @Test
449 public void testNotEqualsWrongType()
450 {
451 assertThat("Equality with objects of different type is incorrect.",
452 eq1, not(new Object()));
453 }
454
455
456 /**
457 * Test that two GeoPoints with different data do not register as
458 * equal.
459 **/
460 @Test
461 public void testNotEqualsDiff() {
462 assertThat("Two GeoPoints with different data must not be equal()",
463 eq1, not(diff));
464 }
465
466
467 /**
468 * Test that two GeoPoints with "swapped" latitude and longitude
469 * values (i.e., they both sum to the same value) do not register
470 * as equal.
471 **/
472 @Test
473 public void testNotEqualsSwapped() {
474 int lat = locationToInt(1, 2, 3);
475 int lng = locationToInt(4, 5, 6);
476 GeoPoint e1 = new GeoPoint(lat, lng);
477 GeoPoint e2 = new GeoPoint(lng, lat);
478 assertThat("Two GeoPoints with swapped data must not be equal()",
479 e1, not(e2));
480 }
481
482
483 ////////////////////////////
484 // TEST GeoPoint.hashCode //
485 ////////////////////////////
486
487 /**
488 * Test positive case: same object
489 **/
490 @Test
491 public void testHashCode1() {
492 assertEquals("A GeoPoint's hashCode must remain constant",
493 eq1.hashCode(), eq1.hashCode());
494 }
495
496
497 /**
498 * Test positive case: equal objects
499 **/
500 @Test
501 public void testHashCode2() {
502 assertEquals("GeoPoints with same data must have the same hashCode",
503 eq1.hashCode(), eq2.hashCode());
504 }
505 }