apache / apache/lucene

Faster GeoHashUtils [LUCENE-2965]

Open
#4,039 1 comment 0 reactions 0 assignees View on GitHub
affects-version:2.9.2 affects-version:2.9.4 affects-version:3.0 affects-version:3.0.1 affects-version:3.0.2 affects-version:3.0.3 legacy-jira-priority:Major module:spatial type:enhancement
Dominant language
Java
Stars
3.6k
Forks
1.4k
Avg merge
2d 11h
Merged PRs (30d)
88

Description

I found the current implement of org.apache.lucene.spatial.geohash.GeoHashUtils.encode and decode is slow and this is my improvement (400% faster )

/\*\*
- Encodes the given latitude and longitude into a geohash
\*
- `@param` latitude Latitude to encode
- `@param` longitude Longitude to encode
- `@return` Geohash encoding of the longitude and latitude
\*/
public static String encode(double latitude, double longitude) {
double latL = -90, latH = 90;
double lngL = -180, lngH = 180;
double mid;
// assert PRECISION % 2 == 0;
final char[] geohash = new char[PRECISION];
int len = 0;
int ch = 0;
while (len <PRECISION) {
if (longitude > (mid = (lngL + lngH) \* 0.5)) {
ch |= 16;
lngL = mid;
} else
lngH = mid;

if (longitude > (mid = (lngL + lngH) \* 0.5)) {
ch |= 4;
lngL = mid;
} else
lngH = mid;

if (longitude > (mid = (lngL + lngH) \* 0.5)) {
ch |= 1;
lngL = mid;
} else {
lngH = mid;
}

if (latitude > (mid = (latL + latH) \* 0.5)) {
ch |= 8;
latL = mid;
} else {
latH = mid;
}
if (latitude > (mid = (latL + latH) \* 0.5)) {
ch |= 2;
latL = mid;
} else {
latH = mid;
}

geohash[len++] = BASE_32[ch];
ch = 0;

if (latitude > (mid = (latL + latH) \* 0.5)) {
ch |= 16;
latL = mid;
} else
latH = mid;

if (longitude > (mid = (lngL + lngH) \* 0.5)) {
ch |= 8;
lngL = mid;
} else
lngH = mid;

if (latitude > (mid = (latL + latH) \* 0.5)) {
ch |= 4;
latL = mid;
} else
latH = mid;

if (longitude > (mid = (lngL + lngH) \* 0.5)) {
ch |= 2;
lngL = mid;
} else
lngH = mid;

if (latitude > (mid = (latL + latH) \* 0.5)) {
ch |= 1;
latL = mid;
} else
latH = mid;

geohash[len++] = BASE_32[ch];
ch = 0;
}

return new String(geohash);
}

/\*\*
- Decodes the given geohash into a latitude and longitude
\*
- `@param` geohash Geohash to deocde
- `@return` Array with the latitude at index 0, and longitude at index 1
\*/
public static double[] decode(String geohash) {
double latL = -90.0, latH = 90.0;
double lngL = -180.0, lngH = 180.0;
double gap;
int len = geohash.length();
for (int i = 0; i < len; ) {
switch (geohash.charAt(i++)) {
case '0':
latH -= (latH - latL) \* 0.75;
lngH -= (lngH - lngL) \* 0.875;
break;
case '1':
latH -= (latH - latL) \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.125;
lngH -= gap \* 0.75;
break;
case '2':
gap = latH - latL;
latL += gap \* 0.25;
latH -= gap \* 0.5;
lngH -= (lngH - lngL) \* 0.875;
break;
case '3':
gap = latH - latL;
latL += gap \* 0.25;
latH -= gap \* 0.5;
gap = lngH - lngL;
lngL += gap \* 0.125;
lngH -= gap \* 0.75;
break;
case '4':
latH -= (latH - latL) \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.25;
lngH -= gap \* 0.625;
break;
case '5':
latH -= (latH - latL) \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.375;
lngH -= gap \* 0.5;
break;
case '6':
gap = latH - latL;
latL += gap \* 0.25;
latH -= gap \* 0.5;
gap = lngH - lngL;
lngL += gap \* 0.25;
lngH -= gap \* 0.625;
break;
case '7':
gap = latH - latL;
latL += gap \* 0.25;
latH -= gap \* 0.5;
gap = lngH - lngL;
lngL += gap \* 0.375;
lngH -= gap \* 0.5;
break;
case '8':
gap = latH - latL;
latL += gap \* 0.5;
latH -= gap \* 0.25;
lngH -= (lngH - lngL) \* 0.875;
break;
case '9':
gap = latH - latL;
latL += gap \* 0.5;
latH -= gap \* 0.25;
gap = lngH - lngL;
lngL += gap \* 0.125;
lngH -= gap \* 0.75;
break;
case 'b':
latL += (latH - latL) \* 0.75;
lngH -= (lngH - lngL) \* 0.875;
break;
case 'c':
latL += (latH - latL) \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.125;
lngH -= gap \* 0.75;
break;
case 'd':
gap = latH - latL;
latL += gap \* 0.5;
latH -= gap \* 0.25;
gap = lngH - lngL;
lngL += gap \* 0.25;
lngH -= gap \* 0.625;
break;
case 'e':
gap = latH - latL;
latL += gap \* 0.5;
latH -= gap \* 0.25;
gap = lngH - lngL;
lngL += gap \* 0.375;
lngH -= gap \* 0.5;
break;
case 'f':
latL += (latH - latL) \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.25;
lngH -= gap \* 0.625;
break;
case 'g':
latL += (latH - latL) \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.375;
lngH -= gap \* 0.5;
break;
case 'h':
latH -= (latH - latL) \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.5;
lngH -= gap \* 0.375;
break;
case 'j':
latH -= (latH - latL) \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.625;
lngH -= gap \* 0.25;
break;
case 'k':
gap = latH - latL;
latL += gap \* 0.25;
latH -= gap \* 0.5;
gap = lngH - lngL;
lngL += gap \* 0.5;
lngH -= gap \* 0.375;
break;
case 'm':
gap = latH - latL;
latL += gap \* 0.25;
latH -= gap \* 0.5;
gap = lngH - lngL;
lngL += gap \* 0.625;
lngH -= gap \* 0.25;
break;
case 'n':
latH -= (latH - latL) \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.75;
lngH -= gap \* 0.125;
break;
case 'p':
latH -= (latH - latL) \* 0.75;
lngL += (lngH - lngL) \* 0.875;
break;
case 'q':
gap = latH - latL;
latL += gap \* 0.25;
latH -= gap \* 0.5;
gap = lngH - lngL;
lngL += gap \* 0.75;
lngH -= gap \* 0.125;
break;
case 'r':
gap = latH - latL;
latL += gap \* 0.25;
latH -= gap \* 0.5;
lngL += (lngH - lngL) \* 0.875;
break;
case 's':
gap = latH - latL;
latL += gap \* 0.5;
latH -= gap \* 0.25;
gap = lngH - lngL;
lngL += gap \* 0.5;
lngH -= gap \* 0.375;
break;
case 't':
gap = latH - latL;
latL += gap \* 0.5;
latH -= gap \* 0.25;
gap = lngH - lngL;
lngL += gap \* 0.625;
lngH -= gap \* 0.25;
break;
case 'u':
latL += (latH - latL) \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.5;
lngH -= gap \* 0.375;
break;
case 'v':
latL += (latH - latL) \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.625;
lngH -= gap \* 0.25;
break;
case 'w':
gap = latH - latL;
latL += gap \* 0.5;
latH -= gap \* 0.25;
gap = lngH - lngL;
lngL += gap \* 0.75;
lngH -= gap \* 0.125;
break;
case 'x':
gap = latH - latL;
latL += gap \* 0.5;
latH -= gap \* 0.25;
lngL += (lngH - lngL) \* 0.875;
break;
case 'y':
latL += (latH - latL) \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.75;
lngH -= gap \* 0.125;
break;
case 'z':
latL += (latH - latL) \* 0.75;
lngL += (lngH - lngL) \* 0.875;
break;
}
switch (geohash.charAt(i++)) {
case '0':
latH -= (latH - latL) \* 0.875;
lngH -= (lngH - lngL) \* 0.75;
break;
case '1':
gap = latH - latL;
latL += gap \* 0.125;
latH -= gap \* 0.75;
lngH -= (lngH - lngL) \* 0.75;
break;
case '2':
latH -= (latH - latL) \* 0.875;
gap = lngH - lngL;
lngL += gap \* 0.25;
lngH -= gap \* 0.5;
break;
case '3':
gap = latH - latL;
latL += gap \* 0.125;
latH -= gap \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.25;
lngH -= gap \* 0.5;
break;
case '4':
gap = latH - latL;
latL += gap \* 0.25;
latH -= gap \* 0.625;
lngH -= (lngH - lngL) \* 0.75;
break;
case '5':
gap = latH - latL;
latL += gap \* 0.375;
latH -= gap \* 0.5;
lngH -= (lngH - lngL) \* 0.75;
break;
case '6':
gap = latH - latL;
latL += gap \* 0.25;
latH -= gap \* 0.625;
gap = lngH - lngL;
lngL += gap \* 0.25;
lngH -= gap \* 0.5;
break;
case '7':
gap = latH - latL;
latL += gap \* 0.375;
latH -= gap \* 0.5;
gap = lngH - lngL;
lngL += gap \* 0.25;
lngH -= gap \* 0.5;
break;
case '8':
latH -= (latH - latL) \* 0.875;
gap = lngH - lngL;
lngL += gap \* 0.5;
lngH -= gap \* 0.25;
break;
case '9':
gap = latH - latL;
latL += gap \* 0.125;
latH -= gap \* 0.75;
gap = lngH - lngL;
lngL += gap \* 0.5;
lngH -= gap \* 0.25;
break;
case 'b':
latH -= (latH - latL) \* 0.875;
lngL += (lngH - lngL) \* 0.75;
break;
case 'c':
gap = latH - latL;
latL += gap \* 0.125;
latH -= gap \* 0.75;
lngL += (lngH - lngL) \* 0.75;
break;
case 'd':
gap = latH - latL;
latL += gap \* 0.25;
latH -= gap \* 0.625;
gap = lngH - lngL;
lngL += gap \* 0.5;
lngH -= gap \* 0.25;
break;
case 'e':
gap = latH - latL;
latL += gap \* 0.375;
latH -= gap \* 0.5;
gap = lngH - lngL;
lngL += gap \* 0.5;
lngH -= gap \* 0.25;
break;
case 'f':
gap = latH - latL;
latL += gap \* 0.25;
latH -= gap \* 0.625;
lngL += (lngH - lngL) \* 0.75;
break;
case 'g':
gap = latH - latL;
latL += gap \* 0.375;
latH -= gap \* 0.5;
lngL += (lngH - lngL) \* 0.75;
break;
case 'h':
gap = latH - latL;
latL += gap \* 0.5;
latH -= gap \* 0.375;
lngH -= (lngH - lngL) \* 0.75;
break;
case 'j':
gap = latH - latL;
latL += gap \* 0.625;
latH -= gap \* 0.25;
lngH -= (lngH - lngL) \* 0.75;
break;
case 'k':
gap = latH - latL;
latL += gap \* 0.5;
latH -= gap \* 0.375;
gap = lngH - lngL;
lngL += gap \* 0.25;
lngH -= gap \* 0.5;
break;
case 'm':
gap = latH - latL;
latL += gap \* 0.625;
latH -= gap \* 0.25;
gap = lngH - lngL;
lngL += gap \* 0.25;
lngH -= gap \* 0.5;
break;
case 'n':
gap = latH - latL;
latL += gap \* 0.75;
latH -= gap \* 0.125;
lngH -= (lngH - lngL) \* 0.75;
break;
case 'p':
latL += (latH - latL) \* 0.875;
lngH -= (lngH - lngL) \* 0.75;
break;
case 'q':
gap = latH - latL;
latL += gap \* 0.75;
latH -= gap \* 0.125;
gap = lngH - lngL;
lngL += gap \* 0.25;
lngH -= gap \* 0.5;
break;
case 'r':
latL += (latH - latL) \* 0.875;
gap = lngH - lngL;
lngL += gap \* 0.25;
lngH -= gap \* 0.5;
break;
case 's':
gap = latH - latL;
latL += gap \* 0.5;
latH -= gap \* 0.375;
gap = lngH - lngL;
lngL += gap \* 0.5;
lngH -= gap \* 0.25;
break;
case 't':
gap = latH - latL;
latL += gap \* 0.625;
latH -= gap \* 0.25;
gap = lngH - lngL;
lngL += gap \* 0.5;
lngH -= gap \* 0.25;
break;
case 'u':
gap = latH - latL;
latL += gap \* 0.5;
latH -= gap \* 0.375;
lngL += (lngH - lngL) \* 0.75;
break;
case 'v':
gap = latH - latL;
latL += gap \* 0.625;
latH -= gap \* 0.25;
lngL += (lngH - lngL) \* 0.75;
break;
case 'w':
gap = latH - latL;
latL += gap \* 0.75;
latH -= gap \* 0.125;
gap = lngH - lngL;
lngL += gap \* 0.5;
lngH -= gap \* 0.25;
break;
case 'x':
latL += (latH - latL) \* 0.875;
gap = lngH - lngL;
lngL += gap \* 0.5;
lngH -= gap \* 0.25;
break;
case 'y':
gap = latH - latL;
latL += gap \* 0.75;
latH -= gap \* 0.125;
lngL += (lngH - lngL) \* 0.75;
break;
case 'z':
latL += (latH - latL) \* 0.875;
lngL += (lngH - lngL) \* 0.75;
break;
}
}
return new double[] { (latL + latH) / 2D, (lngL + lngH) / 2D };
}

---
Migrated from [LUCENE-2965](https://issues.apache.org/jira/browse/LUCENE-2965) by 朱文彬

Contributor guide

Open the contributing guide

Research direction

Start by locating org.apache.lucene.spatial.geohash.GeoHashUtils and its encode and decode methods, then compare the current implementation with the proposed code in the issue. Verify that the optimized methods preserve existing geohash behavior and establish whether the reported speed improvement is reproducible before considering the work done.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
search
Issue type
Refactor
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.