Skip to content

Commit bdec457

Browse files
committed
Fixed Point implementation for exceptional lon values
1 parent 2679816 commit bdec457

File tree

3 files changed

+85
-61
lines changed

3 files changed

+85
-61
lines changed

src/main/java/com/mapcode/Point.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ public class Point {
5555
/**
5656
* Create a point from lat/lon in degrees (may be precision!)
5757
*
58-
* @param latDeg Longitude in degrees.
59-
* @param lonDeg Latitude in degrees.
58+
* @param latDeg Latitude in degrees. Range: [-90, 90].
59+
* @param lonDeg Longitude in degrees. Range: [-180, 180).
6060
* @return A defined point.
6161
*/
6262
@Nonnull
@@ -285,7 +285,11 @@ else if (lat > 180) {
285285
latFractionOnlyDeg = (int) latFractionOnly;
286286
latMicroDeg = latMicroDeg - 90000000;
287287

288-
double lon = lonDeg - (360.0 * Math.floor(lonDeg / 360));
288+
// Math.floor has limited precision for really large values, so we need to limit the lon explicitly.
289+
double lon = Math.min(360.0, Math.max(0.0, lonDeg - (360.0 * Math.floor(lonDeg / 360.0))));
290+
if (Double.compare(lon, 360.0) == 0) {
291+
lon = 0.0;
292+
}
289293

290294
// Lon now in [0..360>.
291295
lon = lon * LON_TO_FRACTIONS_FACTOR;

src/site/apt/ReleaseNotes.apt.vm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
In any case, never depend on them for your own non-<<<SNAPSHOT>>> releases.
1010
#end
1111

12+
* 2.2.2
13+
* Fixed error in <<<Point>>> which in rare cases would allow longitudes outside proper range.
14+
15+
[]
16+
1217
* 2.2.1
1318

1419
* Fixed unit test. Reduced size of files for unit tests considerably. Improved unit test speed.

src/test/java/com/mapcode/PointTest.java

Lines changed: 73 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,17 @@
1616

1717
package com.mapcode;
1818

19-
import org.junit.Assert;
2019
import org.junit.Test;
2120
import org.slf4j.Logger;
2221
import org.slf4j.LoggerFactory;
2322

23+
import java.util.Random;
24+
2425
import static org.junit.Assert.assertEquals;
26+
import static org.junit.Assert.assertTrue;
2527

2628
public class PointTest {
27-
private static final Logger LOG = LoggerFactory.getLogger(PointTest.class);
29+
private static final Logger LOG = LoggerFactory.getLogger(PointTest.class);
2830
private static final double DELTA = 0.000001;
2931

3032
@Test
@@ -83,17 +85,17 @@ public void testDegreesLonToMeters() {
8385

8486
assertEquals(0, Double.compare(0, Point.degreesLonToMetersAtLat(0, 0)));
8587
assertEquals(0,
86-
Double.compare(Point.METERS_PER_DEGREE_LON_EQUATOR / 2.0, Point.degreesLonToMetersAtLat(0.5, 0)));
88+
Double.compare(Point.METERS_PER_DEGREE_LON_EQUATOR / 2.0, Point.degreesLonToMetersAtLat(0.5, 0)));
8789
assertEquals(0,
88-
Double.compare(Point.METERS_PER_DEGREE_LON_EQUATOR, Point.degreesLonToMetersAtLat(1, 0)));
90+
Double.compare(Point.METERS_PER_DEGREE_LON_EQUATOR, Point.degreesLonToMetersAtLat(1, 0)));
8991
assertEquals(0,
90-
Double.compare(Point.METERS_PER_DEGREE_LON_EQUATOR * 180, Point.degreesLonToMetersAtLat(180, 0)));
92+
Double.compare(Point.METERS_PER_DEGREE_LON_EQUATOR * 180, Point.degreesLonToMetersAtLat(180, 0)));
9193
assertEquals(0,
92-
Double.compare(-Point.METERS_PER_DEGREE_LON_EQUATOR * 180, Point.degreesLonToMetersAtLat(-180, 0)));
93-
Assert.assertTrue(Math.abs((Point.METERS_PER_DEGREE_LON_EQUATOR / 2.0) -
94-
Point.degreesLonToMetersAtLat(1, 60)) < DELTA);
95-
Assert.assertTrue(Math.abs((Point.METERS_PER_DEGREE_LON_EQUATOR / 2.0) -
96-
Point.degreesLonToMetersAtLat(1, -60)) < DELTA);
94+
Double.compare(-Point.METERS_PER_DEGREE_LON_EQUATOR * 180, Point.degreesLonToMetersAtLat(-180, 0)));
95+
assertTrue(Math.abs((Point.METERS_PER_DEGREE_LON_EQUATOR / 2.0) -
96+
Point.degreesLonToMetersAtLat(1, 60)) < DELTA);
97+
assertTrue(Math.abs((Point.METERS_PER_DEGREE_LON_EQUATOR / 2.0) -
98+
Point.degreesLonToMetersAtLat(1, -60)) < DELTA);
9799
}
98100

99101
@Test
@@ -102,139 +104,139 @@ public void testMetersToDegreesLon() {
102104

103105
assertEquals(0, Double.compare(0, Point.metersToDegreesLonAtLat(0, 0)));
104106
assertEquals(0,
105-
Double.compare(0.5, Point.metersToDegreesLonAtLat(Point.METERS_PER_DEGREE_LON_EQUATOR / 2, 0)));
107+
Double.compare(0.5, Point.metersToDegreesLonAtLat(Point.METERS_PER_DEGREE_LON_EQUATOR / 2, 0)));
106108
assertEquals(0,
107-
Double.compare(1, Point.metersToDegreesLonAtLat(Point.METERS_PER_DEGREE_LON_EQUATOR, 0)));
109+
Double.compare(1, Point.metersToDegreesLonAtLat(Point.METERS_PER_DEGREE_LON_EQUATOR, 0)));
108110
assertEquals(0,
109-
Double.compare(180, Point.metersToDegreesLonAtLat(Point.METERS_PER_DEGREE_LON_EQUATOR * 180, 0)));
111+
Double.compare(180, Point.metersToDegreesLonAtLat(Point.METERS_PER_DEGREE_LON_EQUATOR * 180, 0)));
110112
assertEquals(0, Double.compare(-180,
111-
Point.metersToDegreesLonAtLat(Point.METERS_PER_DEGREE_LON_EQUATOR * -180, 0)));
112-
Assert.assertTrue(
113-
Math.abs(2.0 - Point.metersToDegreesLonAtLat(Point.METERS_PER_DEGREE_LON_EQUATOR, 60)) < DELTA);
114-
Assert.assertTrue(
115-
Math.abs(2.0 - Point.metersToDegreesLonAtLat(Point.METERS_PER_DEGREE_LON_EQUATOR, -60)) < DELTA);
113+
Point.metersToDegreesLonAtLat(Point.METERS_PER_DEGREE_LON_EQUATOR * -180, 0)));
114+
assertTrue(
115+
Math.abs(2.0 - Point.metersToDegreesLonAtLat(Point.METERS_PER_DEGREE_LON_EQUATOR, 60)) < DELTA);
116+
assertTrue(
117+
Math.abs(2.0 - Point.metersToDegreesLonAtLat(Point.METERS_PER_DEGREE_LON_EQUATOR, -60)) < DELTA);
116118
}
117119

118120
@Test
119121
public void testDistanceInMeters() {
120122
LOG.info("testDistanceInMeters");
121123

122124
double d = Point.distanceInMeters(
123-
Point.fromMicroDeg(-500000, 0), Point.fromMicroDeg(500000, 0));
125+
Point.fromMicroDeg(-500000, 0), Point.fromMicroDeg(500000, 0));
124126
double delta = Math.abs(Point.METERS_PER_DEGREE_LAT - d);
125127
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
126-
Assert.assertTrue(delta <= DELTA);
128+
assertTrue(delta <= DELTA);
127129

128130
d = Point.distanceInMeters(
129-
Point.fromMicroDeg(80000000, 0), Point.fromMicroDeg(81000000, 0));
131+
Point.fromMicroDeg(80000000, 0), Point.fromMicroDeg(81000000, 0));
130132
delta = Math.abs(Point.METERS_PER_DEGREE_LAT - d);
131133
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
132-
Assert.assertTrue(delta <= DELTA);
134+
assertTrue(delta <= DELTA);
133135

134136
d = Point.distanceInMeters(
135-
Point.fromMicroDeg(59000000, 0), Point.fromMicroDeg(61000000, 0));
137+
Point.fromMicroDeg(59000000, 0), Point.fromMicroDeg(61000000, 0));
136138
delta = Math.abs((Point.METERS_PER_DEGREE_LAT * 2) - d);
137139
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
138-
Assert.assertTrue(delta <= DELTA);
140+
assertTrue(delta <= DELTA);
139141

140142
d = Point.distanceInMeters(
141-
Point.fromMicroDeg(0, 1000000), Point.fromMicroDeg(0, 0));
143+
Point.fromMicroDeg(0, 1000000), Point.fromMicroDeg(0, 0));
142144
delta = Math.abs(Point.METERS_PER_DEGREE_LON_EQUATOR - d);
143145
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
144-
Assert.assertTrue(delta <= DELTA);
146+
assertTrue(delta <= DELTA);
145147

146148
d = Point.distanceInMeters(
147-
Point.fromMicroDeg(0, 2000000), Point.fromMicroDeg(0, 0));
149+
Point.fromMicroDeg(0, 2000000), Point.fromMicroDeg(0, 0));
148150
delta = Math.abs((Point.METERS_PER_DEGREE_LON_EQUATOR * 2) - d);
149151
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
150-
Assert.assertTrue(delta <= DELTA);
152+
assertTrue(delta <= DELTA);
151153

152154
d = Point.distanceInMeters(
153-
Point.fromMicroDeg(0, 2000000), Point.fromMicroDeg(0, -1000000));
155+
Point.fromMicroDeg(0, 2000000), Point.fromMicroDeg(0, -1000000));
154156
delta = Math.abs((Point.METERS_PER_DEGREE_LON_EQUATOR * 3) - d);
155157
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
156-
Assert.assertTrue(delta <= DELTA);
158+
assertTrue(delta <= DELTA);
157159

158160
d = Point.distanceInMeters(
159-
Point.fromMicroDeg(0, -1000000), Point.fromMicroDeg(0, 0));
161+
Point.fromMicroDeg(0, -1000000), Point.fromMicroDeg(0, 0));
160162
delta = Math.abs(Point.METERS_PER_DEGREE_LON_EQUATOR - d);
161163
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
162-
Assert.assertTrue(delta <= DELTA);
164+
assertTrue(delta <= DELTA);
163165

164166
d = Point.distanceInMeters(
165-
Point.fromMicroDeg(0, -1000000), Point.fromMicroDeg(0, 1000000));
167+
Point.fromMicroDeg(0, -1000000), Point.fromMicroDeg(0, 1000000));
166168
delta = Math.abs((Point.METERS_PER_DEGREE_LON_EQUATOR * 2) - d);
167169
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
168-
Assert.assertTrue(delta <= DELTA);
170+
assertTrue(delta <= DELTA);
169171

170172
d = Point.distanceInMeters(
171-
Point.fromMicroDeg(0, -1000000), Point.fromMicroDeg(0, 2000000));
173+
Point.fromMicroDeg(0, -1000000), Point.fromMicroDeg(0, 2000000));
172174
delta = Math.abs((Point.METERS_PER_DEGREE_LON_EQUATOR * 3) - d);
173175
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
174-
Assert.assertTrue(delta <= DELTA);
176+
assertTrue(delta <= DELTA);
175177

176178
d = Point.distanceInMeters(
177-
Point.fromMicroDeg(0, -500000), Point.fromMicroDeg(0, 500000));
179+
Point.fromMicroDeg(0, -500000), Point.fromMicroDeg(0, 500000));
178180
delta = Math.abs(Point.METERS_PER_DEGREE_LON_EQUATOR - d);
179181
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
180-
Assert.assertTrue(delta <= DELTA);
182+
assertTrue(delta <= DELTA);
181183

182184
d = Point.distanceInMeters(
183-
Point.fromMicroDeg(0, 80000000), Point.fromMicroDeg(0, 81000000));
185+
Point.fromMicroDeg(0, 80000000), Point.fromMicroDeg(0, 81000000));
184186
delta = Math.abs(Point.METERS_PER_DEGREE_LON_EQUATOR - d);
185187
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
186-
Assert.assertTrue(delta <= DELTA);
188+
assertTrue(delta <= DELTA);
187189

188190
d = Point.distanceInMeters(
189-
Point.fromMicroDeg(60000000, 80000000), Point.fromMicroDeg(60000000, 81000000));
191+
Point.fromMicroDeg(60000000, 80000000), Point.fromMicroDeg(60000000, 81000000));
190192
delta = Math.abs((Point.METERS_PER_DEGREE_LON_EQUATOR / 2.0) - d);
191193
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
192-
Assert.assertTrue(delta <= DELTA);
194+
assertTrue(delta <= DELTA);
193195

194196
d = Point.distanceInMeters(
195-
Point.fromMicroDeg(0, -1000000), Point.fromMicroDeg(0, 1000000));
197+
Point.fromMicroDeg(0, -1000000), Point.fromMicroDeg(0, 1000000));
196198
delta = Math.abs((Point.METERS_PER_DEGREE_LON_EQUATOR * 2) - d);
197199
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
198-
Assert.assertTrue(delta <= DELTA);
200+
assertTrue(delta <= DELTA);
199201

200202
d = Point.distanceInMeters(
201-
Point.fromMicroDeg(0, 1000000), Point.fromMicroDeg(0, 2000000));
203+
Point.fromMicroDeg(0, 1000000), Point.fromMicroDeg(0, 2000000));
202204
delta = Math.abs(Point.METERS_PER_DEGREE_LON_EQUATOR - d);
203205
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
204-
Assert.assertTrue(delta <= DELTA);
206+
assertTrue(delta <= DELTA);
205207

206208
d = Point.distanceInMeters(
207-
Point.fromMicroDeg(0, 178000000), Point.fromMicroDeg(0, 179000000));
209+
Point.fromMicroDeg(0, 178000000), Point.fromMicroDeg(0, 179000000));
208210
delta = Math.abs(Point.METERS_PER_DEGREE_LON_EQUATOR - d);
209211
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
210-
Assert.assertTrue(delta <= DELTA);
212+
assertTrue(delta <= DELTA);
211213

212214
d = Point.distanceInMeters(
213-
Point.fromMicroDeg(0, 179000000), Point.fromMicroDeg(0, 180000000));
215+
Point.fromMicroDeg(0, 179000000), Point.fromMicroDeg(0, 180000000));
214216
delta = Math.abs(Point.METERS_PER_DEGREE_LON_EQUATOR - d);
215217
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
216-
Assert.assertTrue(delta <= DELTA);
218+
assertTrue(delta <= DELTA);
217219

218220
d = Point.distanceInMeters(
219-
Point.fromMicroDeg(0, 179500000), Point.fromMicroDeg(0, -179500000));
221+
Point.fromMicroDeg(0, 179500000), Point.fromMicroDeg(0, -179500000));
220222
delta = Math.abs(Point.METERS_PER_DEGREE_LON_EQUATOR - d);
221223
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
222-
Assert.assertTrue(delta <= DELTA);
224+
assertTrue(delta <= DELTA);
223225

224226
d = Point.distanceInMeters(
225-
Point.fromMicroDeg(0, -179500000), Point.fromMicroDeg(0, 179500000));
227+
Point.fromMicroDeg(0, -179500000), Point.fromMicroDeg(0, 179500000));
226228
delta = Math.abs(Point.METERS_PER_DEGREE_LON_EQUATOR - d);
227229
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
228-
Assert.assertTrue(delta <= DELTA);
230+
assertTrue(delta <= DELTA);
229231

230232
d = Point.distanceInMeters(
231-
Point.fromMicroDeg(0, -179000000), Point.fromMicroDeg(0, -180000000));
233+
Point.fromMicroDeg(0, -179000000), Point.fromMicroDeg(0, -180000000));
232234
delta = Math.abs(Point.METERS_PER_DEGREE_LON_EQUATOR - d);
233235
LOG.debug("testDistanceInMeters: d={}, delta={}", d, delta);
234-
Assert.assertTrue(delta <= DELTA);
236+
assertTrue(delta <= DELTA);
235237

236-
Assert.assertTrue(Point.distanceInMeters(Point.fromDeg(0.0, 180.0), Point.fromDeg(0.0, -179.999977)) < 10.0);
237-
Assert.assertTrue(Point.distanceInMeters(Point.fromDeg(0.0, -179.999977), Point.fromDeg(0.0, 180.0)) < 10.0);
238+
assertTrue(Point.distanceInMeters(Point.fromDeg(0.0, 180.0), Point.fromDeg(0.0, -179.999977)) < 10.0);
239+
assertTrue(Point.distanceInMeters(Point.fromDeg(0.0, -179.999977), Point.fromDeg(0.0, 180.0)) < 10.0);
238240
}
239241

240242
@Test
@@ -259,5 +261,18 @@ public void testWrap() {
259261
assertEquals(Point.fromDeg(0.0, (i * 360) + 179.99), Point.fromDeg(0.0, 179.99).wrap());
260262
assertEquals(Point.fromDeg(0.0, (i * 360) + 180.01), Point.fromDeg(0.0, -179.99).wrap());
261263
}
264+
265+
// Has caused a failure once:
266+
assertPointOK(Point.fromDeg(1.0e307, 1.0e307));
267+
assertPointOK(Point.fromDeg(Double.MIN_VALUE, Double.MIN_VALUE));
268+
assertPointOK(Point.fromDeg(Double.MAX_VALUE, Double.MAX_VALUE));
269+
assertPointOK(Point.fromDeg(8.988465674311579e307, 6.964924833637635e307));
270+
}
271+
272+
private static void assertPointOK(final Point p) {
273+
assertTrue("Lat = " + p.getLatDeg(), p.getLatDeg() >= -90);
274+
assertTrue("Lat = " + p.getLatDeg(), p.getLatDeg() <= 90);
275+
assertTrue("Lon = " + p.getLonDeg(), p.getLonDeg() >= -180);
276+
assertTrue("Lon = " + p.getLonDeg(), p.getLonDeg() < 180);
262277
}
263278
}

0 commit comments

Comments
 (0)