/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.voltdb.types;
import java.nio.ByteBuffer;
import java.util.regex.Pattern;
import junit.framework.TestCase;
public class TestGeographyPointValue extends TestCase {
// Points should have this much precision.
private final double EPSILON = 1.0e-14;
private void assertConstructorThrows(String expectedMessage, double lng, double lat) {
try {
new GeographyPointValue(lng, lat);
fail("Expected constructor to throw an exception");
}
catch (IllegalArgumentException iae) {
assertTrue("Didn't find expected message in exception thrown by PointType constructor",
iae.getMessage().contains(expectedMessage));
}
}
public void testPointCtor() {
assertEquals(16, GeographyPointValue.getLengthInBytes());
GeographyPointValue point = new GeographyPointValue(10.333, 20.666);
assertEquals(20.666, point.getLatitude(), EPSILON);
assertEquals(10.333, point.getLongitude(), EPSILON);
assertEquals("POINT (10.333 20.666)", point.toString());
// Make sure that it's not possible to create points
// with bogus latitude or longitude.
assertConstructorThrows("Latitude out of range", 100, -91.0);
assertConstructorThrows("Latitude out of range", 100, 91.0);
assertConstructorThrows("Longitude out of range", 181.0, 45.0);
assertConstructorThrows("Longitude out of range", -181.0, 45.0);
}
public void testPointEquals() {
// We Points within 12 digits of precision should be considered equal for our purposes.
final double lessThanEps = 1e-13;
final double moreThanEps = 2 * GeographyPointValue.EPSILON;
GeographyPointValue point = new GeographyPointValue(10.333, 20.666);
GeographyPointValue closePoint = new GeographyPointValue(10.333 + lessThanEps, 20.666 - lessThanEps);
assertTrue(point.equals(point));
assertTrue(point.equals(closePoint));
assertTrue(closePoint.equals(point));
GeographyPointValue farPoint = new GeographyPointValue(0.0, 10.0);
assertFalse(point.equals(farPoint));
assertFalse(farPoint.equals(point));
// Points at the north (or south) pole should compare equal regardless of longitude.
GeographyPointValue northPole1 = new GeographyPointValue( 50.0, 90.0);
GeographyPointValue northPole2 = new GeographyPointValue(-70.0, 90.0);
GeographyPointValue northPole3 = new GeographyPointValue( 10.0, 90.0 - lessThanEps);
GeographyPointValue northPole4 = new GeographyPointValue( 180.0, 90.0 - lessThanEps);
GeographyPointValue northPole5 = new GeographyPointValue( -180.0, 90.0);
assertTrue(northPole1.equals(northPole2));
assertTrue(northPole2.equals(northPole1));
assertTrue(northPole1.equals(northPole3));
assertTrue(northPole3.equals(northPole1));
assertTrue(northPole1.equals(northPole4));
assertTrue(northPole4.equals(northPole1));
assertTrue(northPole3.equals(northPole5));
assertTrue(northPole5.equals(northPole3));
GeographyPointValue notNorthPole = new GeographyPointValue( 10.0, 90.0 - moreThanEps);
assertFalse(notNorthPole.equals(northPole1));
assertFalse(northPole1.equals(notNorthPole));
GeographyPointValue southPole1 = new GeographyPointValue( 50.0, -90.0);
GeographyPointValue southPole2 = new GeographyPointValue(-70.0, -90.0);
GeographyPointValue southPole3 = new GeographyPointValue( 10.0, -90.0 + lessThanEps);
GeographyPointValue southPole4 = new GeographyPointValue( 180.0, -90.0);
GeographyPointValue southPole5 = new GeographyPointValue( -180.0, -90.0 + lessThanEps);
assertTrue(southPole1.equals(southPole2));
assertTrue(southPole2.equals(southPole1));
assertTrue(southPole1.equals(southPole3));
assertTrue(southPole3.equals(southPole1));
assertTrue(southPole3.equals(southPole5));
assertTrue(southPole5.equals(southPole4));
assertTrue(southPole3.equals(southPole5));
assertTrue(southPole1.equals(southPole4));
GeographyPointValue notSouthPole = new GeographyPointValue( 10.0, -90.0 + moreThanEps);
assertFalse(notSouthPole.equals(southPole1));
assertFalse(southPole1.equals(notSouthPole));
assertFalse(southPole2.equals(northPole2));
// For a given latitude, points at 180 and -180 lontitude are the same.
GeographyPointValue onIDLNeg1 = new GeographyPointValue(-180.0 , 37.0);
GeographyPointValue onIDLNeg2 = new GeographyPointValue(-180.0 + lessThanEps, 37.0);
GeographyPointValue onIDLPos1 = new GeographyPointValue( 180.0 , 37.0);
GeographyPointValue onIDLPos2 = new GeographyPointValue( 180.0 - lessThanEps, 37.0);
assertTrue(onIDLNeg1.equals(onIDLPos1));
assertTrue(onIDLNeg2.equals(onIDLPos2));
assertTrue(onIDLNeg1.equals(onIDLPos2));
assertTrue(onIDLNeg2.equals(onIDLPos1));
GeographyPointValue notOnIDLNeg = new GeographyPointValue(-180.0 + moreThanEps, 37.0);
GeographyPointValue notOnIDLPos = new GeographyPointValue( 180.0 - moreThanEps, 37.0);
assertFalse(onIDLNeg1.equals(notOnIDLPos));
assertFalse(onIDLPos1.equals(notOnIDLNeg));
}
public void testPointSerialization() {
int len = GeographyPointValue.getLengthInBytes();
assertEquals(16, len);
ByteBuffer bb = ByteBuffer.allocate(len);
bb.putDouble(33.0);
bb.putDouble(45.0);
// Test deserialization
bb.position(0);
GeographyPointValue pt = GeographyPointValue.unflattenFromBuffer(bb);
assertEquals("POINT (33.0 45.0)", pt.toString());
// Test deserialization with offset argument
bb.position(0);
pt = GeographyPointValue.unflattenFromBuffer(bb, 0);
assertEquals("POINT (33.0 45.0)", pt.toString());
// Test serialization
pt = new GeographyPointValue(-64.0, -77.0);
bb.position(0);
pt.flattenToBuffer(bb);
bb.position(0);
assertEquals(-64.0, bb.getDouble());
assertEquals(-77.0, bb.getDouble());
// Null serialization puts 360.0 in both lat and long
bb.position(0);
GeographyPointValue.serializeNull(bb);
bb.position(0);
assertEquals(360.0, bb.getDouble());
assertEquals(360.0, bb.getDouble());
}
/*
* Note: The parameters are (latitude, longitude), which is the
* opposite of order in which we construct points, and
* the order used in WKT. This is to try to test that we
* have gotten it right.
*/
private void testOnePointFromFactory(String aWKT, double aLatitude, double aLongitude, double aEpsilon, String aErrMsg) {
try {
GeographyPointValue point = GeographyPointValue.fromWKT(aWKT);
assertEquals(aLatitude, point.getLatitude(), aEpsilon);
if (aErrMsg != null) {
assertTrue(String.format("Expected error message matching \"%s\", but got no error.", aErrMsg), aErrMsg == null);
}
} catch (Exception ex) {
if (aErrMsg != null) {
assertTrue(String.format("Expected error message matching \"%s\", but got \"%s\"",
aErrMsg, ex.getMessage()),
Pattern.matches(aErrMsg, ex.getMessage()));
} else {
assertTrue(String.format("Unexpected error message: \"%s\"", ex.getMessage()), false);
}
}
}
public void testPointFactory() {
// Note that the WKT strings and the test factory parameters are swapped.
// This is purposeful.
testOnePointFromFactory("point(0 0)", 0.0, 0.0, EPSILON, null);
testOnePointFromFactory("point(10.3330000000 20.6660000000)", 20.6660000000, 10.3330000000, EPSILON, null);
testOnePointFromFactory(" point (10.3330000000 20.6660000000) ", 20.666, 10.333, EPSILON, null);
testOnePointFromFactory("point(10.333 20.666)", 20.666, 10.333, EPSILON, null);
testOnePointFromFactory(" point (10.333 20.666) ", 20.666, 10.333, EPSILON, null);
testOnePointFromFactory("point(-10.333 -20.666)", -20.666, -10.333, EPSILON, null);
testOnePointFromFactory(" point (-10.333 -20.666) ", -20.666, -10.333, EPSILON, null);
testOnePointFromFactory("point(10 10)", 10.0, 10.0, EPSILON, null);
// Test latitude/longitude ranges.
testOnePointFromFactory("point( 100.0 100.0)", 100.0, 100.0, EPSILON, "Latitude \"100.0+\" out of bounds.");
testOnePointFromFactory("point( 360.0 45.0)", 360.0, 45.0, EPSILON, "Longitude \"360.0+\" out of bounds.");
testOnePointFromFactory("point( 270.0 45.0)", 360.0, 45.0, EPSILON, "Longitude \"270.0+\" out of bounds.");
testOnePointFromFactory("point(-100.0 -100.0)",
-100.0,
-100.0,
EPSILON,
"Latitude \"-100.0+\" out of bounds.");
testOnePointFromFactory("point(-360.0 -45.0)",
-45.0,
-360.0,
EPSILON,
"Longitude \"-360.0+\" out of bounds.");
testOnePointFromFactory("point(-270.0 -45.0)",
-45.0,
-360.0,
EPSILON,
"Longitude \"-270.0+\" out of bounds.");
// Syntax errors
// Comma separating the coordinates.
testOnePointFromFactory("point(0.0, 0.0)",
0.0,
0.0,
EPSILON,
"Cannot construct GeographyPointValue value from \"point\\(0[.]0, 0[.]0\\)\"");
}
public void checkGeographyPointAdd(double lng1, double lat1,
double scale,
double lng2, double lat2,
double lngans, double latans,
double epsilon) {
GeographyPointValue p1 = GeographyPointValue.normalizeLngLat(lng1, lat1);
GeographyPointValue p2 = GeographyPointValue.normalizeLngLat(lng2, lat2);
GeographyPointValue p3 = p1.add(p2, scale);
assertEquals(lngans, p3.getLongitude(), epsilon);
assertEquals(latans, p3.getLatitude(), epsilon);
}
public void checkGeographyPointSub(double lng1, double lat1,
double scale,
double lng2, double lat2,
double lngans, double latans,
double epsilon) {
GeographyPointValue p1 = GeographyPointValue.normalizeLngLat(lng1, lat1);
GeographyPointValue p2 = GeographyPointValue.normalizeLngLat(lng2, lat2);
GeographyPointValue p3 = p1.sub(p2, scale);
assertEquals(lngans, p3.getLongitude(), epsilon);
assertEquals(latans, p3.getLatitude(), epsilon);
}
public void checkGeographyPointMul(double lng1, double lat1,
double scale,
double lngans, double latans,
double epsilon) {
GeographyPointValue p1 = GeographyPointValue.normalizeLngLat(lng1, lat1);
GeographyPointValue p2 = p1.mul(scale);
assertEquals(lngans, p2.getLongitude(), epsilon);
assertEquals(latans, p2.getLatitude(), epsilon);
}
public void checkGeographyPointRotate(double lng1, double lat1,
double phi,
double ctrlng, double ctrlat,
double explng, double explat,
double epsilon) {
GeographyPointValue p1 = GeographyPointValue.normalizeLngLat(lng1, lat1);
GeographyPointValue ctr = GeographyPointValue.normalizeLngLat(ctrlng, ctrlat);
GeographyPointValue ans = p1.rotate(phi, ctr);
assertEquals(explng, ans.getLongitude(), epsilon);
assertEquals(explat, ans.getLatitude(), epsilon);
}
public void testGeographyPointAdd() throws Exception {
checkGeographyPointAdd(0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 2.0, EPSILON);
checkGeographyPointAdd(0.0, 1.0, 2.0, 2.0, 4.0, 4.0, 9.0, EPSILON);
}
public void testGeographyPointSub() throws Exception {
checkGeographyPointSub(2.0, 4.0,
1.0,
0.0, 2.0,
2.0, 2.0,
EPSILON);
checkGeographyPointSub(10.0, 20.0,
2.0,
2.0, 40.0,
6.0, -60.0, EPSILON);
}
public void testGeographyPointMul() throws Exception {
checkGeographyPointMul( 10.0, 20.0,
4.0,
40.0, 80.0,
EPSILON);
checkGeographyPointMul( 20.0, 80.0,
4.0,
80.0, -40.0, EPSILON);
checkGeographyPointMul( 20.0, 70.0,
4.0,
80.0, -80.0, EPSILON);
}
public void testGeographyPointRotate() throws Exception {
checkGeographyPointRotate(10.0, 20.0, 90, 0.0, 0.0, -20.0, 10.0, EPSILON);
}
}