Skip to content

Commit a7cb33c

Browse files
committed
refactored into separate files, tests
1 parent d4de471 commit a7cb33c

File tree

6 files changed

+138
-89
lines changed

6 files changed

+138
-89
lines changed

server/src/main/java/org/elasticsearch/common/geo/GeoHashUtils.java

Lines changed: 0 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@
2525
import java.util.ArrayList;
2626
import java.util.Collection;
2727

28-
import static org.elasticsearch.common.geo.GeoUtils.normalizeLat;
29-
import static org.elasticsearch.common.geo.GeoUtils.normalizeLon;
30-
3128
/**
3229
* Utilities for converting to/from the GeoHash standard
3330
*
@@ -354,83 +351,4 @@ public static final double decodeLatitude(final String geohash) {
354351
public static final double decodeLongitude(final String geohash) {
355352
return decodeLongitude(mortonEncode(geohash));
356353
}
357-
358-
359-
/* ****** Converting geopoint to Spherical Mercator as tiles ****** */
360-
361-
public static final int MAX_ZOOM = 26;
362-
363-
/**
364-
* Convert [longitude, latitude] to a hash tha combines zoom, x, and y of the tile.
365-
*/
366-
public static long geoToMapTileHash(final double longitude, final double latitude, final int zoom) {
367-
// Adapted from https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Java
368-
369-
if (zoom < 0 || zoom > MAX_ZOOM) {
370-
throw new IllegalArgumentException("zoom");
371-
}
372-
// How many tiles in X and in Y
373-
final int tiles = 1 << zoom;
374-
final double lon = normalizeLon(longitude);
375-
final double lat = normalizeLat(latitude);
376-
377-
int xtile = (int) Math.floor((lon + 180) / 360 * tiles);
378-
int ytile = (int) Math.floor(
379-
(1 - Math.log(
380-
Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))
381-
) / Math.PI) / 2 * tiles);
382-
if (xtile < 0)
383-
xtile = 0;
384-
if (xtile >= tiles)
385-
xtile = (tiles - 1);
386-
if (ytile < 0)
387-
ytile = 0;
388-
if (ytile >= tiles)
389-
ytile = (tiles - 1);
390-
391-
// with max zoom being 26, the largest index would be 2^52 (51st..0th),
392-
// leaving 12 bits unused. Zoom cannot be >26, so it can fit into 5 bits (56th..52nd)
393-
return BitUtil.interleave(xtile, ytile) | ((long) zoom << 52);
394-
}
395-
396-
private static int[] parseMapTileHash(final long hash) {
397-
int zoom = (int) (hash >> 52);
398-
if (zoom < 0 || zoom > MAX_ZOOM) {
399-
throw new IllegalArgumentException("hash-zoom");
400-
}
401-
402-
final int tiles = 1 << zoom;
403-
// decode last 52 bits as xtile and ytile
404-
long val = hash & 0x000FFFFFFFFFFFFFL;
405-
int xtile = (int) BitUtil.deinterleave(val);
406-
int ytile = (int) BitUtil.deinterleave(val >> 1);
407-
if (xtile < 0 || ytile < 0 || xtile >= tiles || ytile >= tiles) {
408-
throw new IllegalArgumentException("hash-tile");
409-
}
410-
411-
return new int[]{zoom, xtile, ytile};
412-
}
413-
414-
public static String geoTileMapHashToKey(final long geohashAsLong) {
415-
int[] res = parseMapTileHash(geohashAsLong);
416-
return "" + res[0] + "/" + res[1] + "/" + res[2];
417-
}
418-
419-
private static double tile2lon(final int x, final double tiles) {
420-
return x / tiles * 360.0 - 180;
421-
}
422-
423-
private static double tile2lat(final int y, final double tiles) {
424-
double n = Math.PI - (2.0 * Math.PI * y) / tiles;
425-
return Math.toDegrees(Math.atan(Math.sinh(n)));
426-
}
427-
428-
public static Rectangle bboxFromTileIndex(long geohashAsLong) {
429-
int[] res = parseMapTileHash(geohashAsLong);
430-
int zoom = res[0], x = res[1], y = res[2];
431-
double tiles = Math.pow(2.0, zoom); // optimization
432-
433-
return new Rectangle(tile2lat(y, tiles), tile2lat(y + 1, tiles),
434-
tile2lon(x, tiles), tile2lon(x + 1, tiles));
435-
}
436354
}

server/src/main/java/org/elasticsearch/common/geo/GeoUtils.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -545,10 +545,7 @@ public static int checkPrecisionRange(int precision, GeoHashType type) {
545545
PluscodeHash.validatePrecision(precision);
546546
break;
547547
case maptile:
548-
if (precision < 0 || precision > GeoHashUtils.MAX_ZOOM) {
549-
throw new IllegalArgumentException("Invalid geohash maptile aggregation precision of " + precision +
550-
". Must be between 0 and " + GeoHashUtils.MAX_ZOOM + ".");
551-
}
548+
MaptileHash.validatePrecision(precision);
552549
break;
553550
default:
554551
throw new IllegalArgumentException("Unknown type " + type.toString());
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.common.geo;
20+
21+
import org.apache.lucene.geo.Rectangle;
22+
import org.apache.lucene.util.BitUtil;
23+
24+
import static org.elasticsearch.common.geo.GeoUtils.normalizeLat;
25+
import static org.elasticsearch.common.geo.GeoUtils.normalizeLon;
26+
27+
28+
/**
29+
* Converting geopoints to Spherical Mercator in the same way as map tile services
30+
*/
31+
public class MaptileHash {
32+
33+
private static final int MAX_ZOOM = 26;
34+
35+
/**
36+
* Convert [longitude, latitude] to a hash tha combines zoom, x, and y of the tile.
37+
*/
38+
public static long geoToMapTileHash(final double longitude, final double latitude, final int zoom) {
39+
// Adapted from https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Java
40+
41+
if (zoom < 0 || zoom > MAX_ZOOM) {
42+
throw new IllegalArgumentException("zoom");
43+
}
44+
// How many tiles in X and in Y
45+
final int tiles = 1 << zoom;
46+
final double lon = normalizeLon(longitude);
47+
final double lat = normalizeLat(latitude);
48+
49+
int xtile = (int) Math.floor((lon + 180) / 360 * tiles);
50+
int ytile = (int) Math.floor(
51+
(1 - Math.log(
52+
Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))
53+
) / Math.PI) / 2 * tiles);
54+
if (xtile < 0)
55+
xtile = 0;
56+
if (xtile >= tiles)
57+
xtile = (tiles - 1);
58+
if (ytile < 0)
59+
ytile = 0;
60+
if (ytile >= tiles)
61+
ytile = (tiles - 1);
62+
63+
// with max zoom being 26, the largest index would be 2^52 (51st..0th),
64+
// leaving 12 bits unused. Zoom cannot be >26, so it can fit into 5 bits (56th..52nd)
65+
return BitUtil.interleave(xtile, ytile) | ((long) zoom << 52);
66+
}
67+
68+
private static int[] parseMapTileHash(final long hash) {
69+
int zoom = (int) (hash >> 52);
70+
if (zoom < 0 || zoom > MAX_ZOOM) {
71+
throw new IllegalArgumentException("hash-zoom");
72+
}
73+
74+
final int tiles = 1 << zoom;
75+
// decode last 52 bits as xtile and ytile
76+
long val = hash & 0x000FFFFFFFFFFFFFL;
77+
int xtile = (int) BitUtil.deinterleave(val);
78+
int ytile = (int) BitUtil.deinterleave(val >> 1);
79+
if (xtile < 0 || ytile < 0 || xtile >= tiles || ytile >= tiles) {
80+
throw new IllegalArgumentException("hash-tile");
81+
}
82+
83+
return new int[]{zoom, xtile, ytile};
84+
}
85+
86+
public static String geoTileMapHashToKey(final long geohashAsLong) {
87+
int[] res = parseMapTileHash(geohashAsLong);
88+
return "" + res[0] + "/" + res[1] + "/" + res[2];
89+
}
90+
91+
private static double tile2lon(final int x, final double tiles) {
92+
return x / tiles * 360.0 - 180;
93+
}
94+
95+
private static double tile2lat(final int y, final double tiles) {
96+
double n = Math.PI - (2.0 * Math.PI * y) / tiles;
97+
return Math.toDegrees(Math.atan(Math.sinh(n)));
98+
}
99+
100+
public static Rectangle bboxFromTileIndex(long geohashAsLong) {
101+
int[] res = parseMapTileHash(geohashAsLong);
102+
int zoom = res[0], x = res[1], y = res[2];
103+
double tiles = Math.pow(2.0, zoom); // optimization
104+
105+
return new Rectangle(tile2lat(y, tiles), tile2lat(y + 1, tiles),
106+
tile2lon(x, tiles), tile2lon(x + 1, tiles));
107+
}
108+
109+
/**
110+
* Validate precision parameter
111+
* @param precision as submitted by the user
112+
*/
113+
public static void validatePrecision(int precision) {
114+
if (precision < 0 || precision > MAX_ZOOM) {
115+
throw new IllegalArgumentException("Invalid geohash maptile aggregation precision of " + precision +
116+
". Must be between 0 and " + MAX_ZOOM + ".");
117+
}
118+
}
119+
120+
private MaptileHash() {
121+
}
122+
123+
}

server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoGridAggregationBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.elasticsearch.common.geo.GeoPoint;
2727
import org.elasticsearch.common.geo.GeoUtils;
2828
import org.elasticsearch.common.geo.PluscodeHash;
29+
import org.elasticsearch.common.geo.MaptileHash;
2930
import org.elasticsearch.common.io.stream.StreamInput;
3031
import org.elasticsearch.common.io.stream.StreamOutput;
3132
import org.elasticsearch.common.xcontent.ObjectParser;
@@ -258,7 +259,7 @@ public boolean advanceExact(int docId) throws IOException {
258259
values[i] = PluscodeHash.latLngToPluscodeHash(target.getLon(), target.getLat(), precision);
259260
break;
260261
case maptile:
261-
values[i] = GeoHashUtils.geoToMapTileHash(target.getLon(), target.getLat(), precision);
262+
values[i] = MaptileHash.geoToMapTileHash(target.getLon(), target.getLat(), precision);
262263
break;
263264
default:
264265
throw new IllegalArgumentException();

server/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/InternalGeoHashGrid.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.apache.lucene.util.PriorityQueue;
2222
import org.elasticsearch.common.geo.GeoHashUtils;
2323
import org.elasticsearch.common.geo.GeoPoint;
24+
import org.elasticsearch.common.geo.MaptileHash;
2425
import org.elasticsearch.common.geo.PluscodeHash;
2526
import org.elasticsearch.common.io.stream.StreamInput;
2627
import org.elasticsearch.common.io.stream.StreamOutput;
@@ -88,7 +89,7 @@ public String getKeyAsString() {
8889
case pluscode:
8990
return PluscodeHash.decodePluscode(geohashAsLong);
9091
case maptile:
91-
return GeoHashUtils.geoTileMapHashToKey(geohashAsLong);
92+
return MaptileHash.geoTileMapHashToKey(geohashAsLong);
9293
default:
9394
throw new IllegalArgumentException();
9495
}
@@ -102,7 +103,7 @@ public Object getKey() {
102103
case pluscode:
103104
return PluscodeHash.bboxFromPluscode(geohashAsLong);
104105
case maptile:
105-
return GeoHashUtils.bboxFromTileIndex(geohashAsLong);
106+
return MaptileHash.bboxFromTileIndex(geohashAsLong);
106107
default:
107108
throw new IllegalArgumentException();
108109
}

server/src/test/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridAggregatorTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.lucene.search.Query;
2929
import org.apache.lucene.store.Directory;
3030
import org.elasticsearch.common.CheckedConsumer;
31+
import org.elasticsearch.common.geo.MaptileHash;
3132
import org.elasticsearch.common.geo.PluscodeHash;
3233
import org.elasticsearch.index.mapper.GeoPointFieldMapper;
3334
import org.elasticsearch.index.mapper.MappedFieldType;
@@ -84,6 +85,14 @@ public void testPluscodeWithSeveralDocs() throws IOException {
8485
});
8586
}
8687

88+
public void testMaptileWithSeveralDocs() throws IOException {
89+
final int precision = randomIntBetween(0, 26);
90+
91+
testWithSeveralDocs(GeoHashType.maptile, precision, (lng, lat) -> {
92+
return MaptileHash.geoTileMapHashToKey(MaptileHash.geoToMapTileHash(lng, lat, precision));
93+
});
94+
}
95+
8796
private void testWithSeveralDocs(GeoHashType type, int precision, BiFunction<Double, Double, String> hasher)
8897
throws IOException {
8998
int numPoints = randomIntBetween(8, 128);

0 commit comments

Comments
 (0)