Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit b29091a
Merge: 9b64610 9aaaf4c
Author: Wen Jun Jie <132931864+WenJJ2000@users.noreply.github.com>
Date:   Mon Mar 4 10:09:16 2024 +0100

    Merge pull request Esri#55 from DD2480-Group-3/54-coveringGeohash

    54 covering geohash

commit 9aaaf4c
Author: wenjj2000 <wenjj2000@gmail.com>
Date:   Mon Mar 4 10:00:46 2024 +0100

    Squashed commit of the following:

    commit 279212f
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Mon Mar 4 09:46:08 2024 +0100

        fix: solves bug where precision is allowed to be too high

        Limits the precision in containingGeohash function to 6 from 24
        since the toGeohash function has been updated to only work for
        highest precision 6.

    commit 2aa650f
    Merge: 263f8e2 dcdb58d
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Mon Mar 4 09:45:04 2024 +0100

        Merge branch '213-geometry-to-geohash' of github.com:DD2480-Group-3/geometry-api-java into 54-coveringGeohash

    commit 263f8e2
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Sun Mar 3 19:47:34 2024 +0100

        refactor: removed missed empty line

    commit 72b84f4
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Sun Mar 3 19:47:05 2024 +0100

        refactor: removed empty line

    commit 6fef312
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Sun Mar 3 19:45:34 2024 +0100

        refactor: removed empty lines

    commit 1287b2c
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Sun Mar 3 19:39:49 2024 +0100

        test: changed envelope to encompass 4 different parts of the geo grid

        The envelope was previously assigned wrong max x and y values, which
        resulted in errors as the coveringGeohash function didn't return
        the expected amount of geo hashes.

    commit 69ca9c0
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Sun Mar 3 19:30:38 2024 +0100

        fix: solves bug which resulted in wrong geohashes

    commit 2a8d131
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Sun Mar 3 19:24:32 2024 +0100

        test: updated tests to match the changes of the function in last commit

    commit bb45871
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Sun Mar 3 19:24:02 2024 +0100

        refactor: made the return string array dynamic

    commit 708542e
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Sun Mar 3 18:42:15 2024 +0100

        refactor: removes indentation error caused by merge

    commit a216691
    Merge: 4420765 62f7d5f
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Sun Mar 3 18:41:36 2024 +0100

        Merge branch '213-geometry-to-geohash' of github.com:DD2480-Group-3/geometry-api-java into 54-coveringGeohash

    commit 4420765
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Sun Mar 3 18:37:30 2024 +0100

        test: added test cases for coveringGeohash function

    commit 2c31979
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Sun Mar 3 18:15:40 2024 +0100

        refactor: removes unnecessary if statement

        Removes if statement which didn't change the outcome of the program.

    commit e0b04d3
    Author: Linus Wallin <linuswallin@live.se>
    Date:   Sun Mar 3 17:39:37 2024 +0100

        feat: coveringGeohash funciton added

        Adds funtion which given an envelope returns up to four geohashes
        which cover the envelope.

commit 9b64610
Author: wenjj2000 <wenjj2000@gmail.com>
Date:   Mon Mar 4 09:24:34 2024 +0100

    docs : update doucmentation for helper function and removed commented code

commit dcdb58d
Author: wenjj2000 <wenjj2000@gmail.com>
Date:   Mon Mar 4 07:52:31 2024 +0100

    refactor : Changed toGeoHash function to use bitwise operations instead of strings to improve time complexity

commit 80aa932
Author: wenjj2000 <wenjj2000@gmail.com>
Date:   Mon Mar 4 00:33:36 2024 +0100

    refactor : changing helper functions to private instead of public and removing their tests

commit 62f7d5f
Author: --replace-all <muchembled.martin@gmail.com>
Date:   Sun Mar 3 16:48:48 2024 +0100

    Test: added some tests for containingGeohash

commit 7191907
Author: --replace-all <muchembled.martin@gmail.com>
Date:   Sun Mar 3 16:47:58 2024 +0100

    Fix: lat and lon were inverted in test for toGeohash

commit b21e5b9
Author: --replace-all <muchembled.martin@gmail.com>
Date:   Sun Mar 3 16:45:28 2024 +0100

    Feat: Implemented containingGeohash

commit c30e1e5
Merge: 84ef58e dbe750f
Author: --replace-all <muchembled.martin@gmail.com>
Date:   Sun Mar 3 16:44:34 2024 +0100

    Fix: lat and lon were inverted in tooGeoHash

commit 84ef58e
Author: --replace-all <muchembled.martin@gmail.com>
Date:   Sun Mar 3 11:02:32 2024 +0100

    test: Added some tests for toGeohash Esri#213

commit dbe750f
Author: wenjj2000 <wenjj2000@gmail.com>
Date:   Sun Mar 3 00:21:25 2024 +0100

    tests : added test for TestToGeoHash

commit e4adae4
Author: wenjj2000 <wenjj2000@gmail.com>
Date:   Sat Mar 2 23:32:43 2024 +0100

    tests : added test for binaryToBase32 and TestCovertToBinary

commit 42b5287
Author: wenjj2000 <wenjj2000@gmail.com>
Date:   Sat Mar 2 23:14:06 2024 +0100

    feat : Added toGeoHash function with 2 helper function BinaryToBase32 and converyTobinary

commit 5fd7cb0
Author: --replace-all <muchembled.martin@gmail.com>
Date:   Sat Mar 2 17:33:31 2024 +0100

    Feat: Implemented geohashToEnvelope Esri#213

commit cac7dfd
Author: --replace-all <muchembled.martin@gmail.com>
Date:   Sat Mar 2 17:30:08 2024 +0100

    test: added some tests for geohashToEnvelope Esri#213

commit 8461a3c
Author: --replace-all <muchembled.martin@gmail.com>
Date:   Sat Mar 2 16:15:02 2024 +0100

    Test: Created a test file for geohash Esri#213

commit af667b6
Author: --replace-all <muchembled.martin@gmail.com>
Date:   Sat Mar 2 14:45:05 2024 +0100

    Feat: Created Geohash class and its skeletton Esri#213
  • Loading branch information
WenJJ2000 committed Mar 4, 2024
1 parent e0bef0b commit a56f6ae
Show file tree
Hide file tree
Showing 2 changed files with 434 additions and 0 deletions.
252 changes: 252 additions & 0 deletions src/main/java/com/esri/core/geometry/Geohash.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
package com.esri.core.geometry;

import java.security.InvalidParameterException;

/**
* Helper class to work with geohash
*/
public class Geohash {

private static final String base32 = "0123456789bcdefghjkmnpqrstuvwxyz";

private static final String INVALID_CHARACTER_MESSAGE =
"Invalid character in geohash: ";
private static final String GEOHASH_EXCEED_MAX_PRECISION_MESSAGE =
"Precision to high in geohash (max 24)";

/**
* Create an evelope from a given geohash
* @param geoHash
* @return The envelope that corresponds to the geohash
* @throws InvalidParameterException if the precision of geoHash is greater than 24 characters
*/
public static Envelope2D geohashToEnvelope(String geoHash) {
if (geoHash.length() > 24) {
throw new InvalidParameterException(GEOHASH_EXCEED_MAX_PRECISION_MESSAGE);
}

long latBits = 0;
long lonBits = 0;
for (int i = 0; i < geoHash.length(); i++) {
int pos = base32.indexOf(geoHash.charAt(i));
if (pos == -1) {
throw new InvalidParameterException(
new StringBuilder(INVALID_CHARACTER_MESSAGE)
.append('\'')
.append(geoHash.charAt(i))
.append('\'')
.toString()
);
}

if (i % 2 == 0) {
lonBits =
(lonBits << 3) | ((pos >> 2) & 4) | ((pos >> 1) & 2) | (pos & 1);
latBits = (latBits << 2) | ((pos >> 2) & 2) | ((pos >> 1) & 1);
} else {
latBits =
(latBits << 3) | ((pos >> 2) & 4) | ((pos >> 1) & 2) | (pos & 1);
lonBits = (lonBits << 2) | ((pos >> 2) & 2) | ((pos >> 1) & 1);
}
}

int lonBitsSize = (int) Math.ceil(geoHash.length() * 5 / 2.0);
int latBitsSize = geoHash.length() * 5 - lonBitsSize;

double lat = -90;
double latPrecision = 90;
for (int i = 0; i < latBitsSize; i++) {
if (((1 << (latBitsSize - 1 - i)) & latBits) != 0) {
lat += latPrecision;
}
latPrecision /= 2;
}

double lon = -180;
double lonPrecision = 180;
for (int i = 0; i < lonBitsSize; i++) {
if (((1 << (lonBitsSize - 1 - i)) & lonBits) != 0) {
lon += lonPrecision;
}
lonPrecision /= 2;
}

return new Envelope2D(
lon,
lat,
lon + lonPrecision * 2,
lat + latPrecision * 2
);
}

/**
* Computes the geohash that contains a point at a certain precision
* @param pt A point represented as lat/long pair
* @param characterLength - The precision of the geohash
* @return The geohash of containing pt as a String
*/
public static String toGeohash(Point2D pt, int characterLength) {
if (characterLength < 1) {
throw new InvalidParameterException(
"CharacterLength cannot be less than 1"
);
}
if (characterLength > 6) {
throw new InvalidParameterException("Max characterLength of 6");
}
int precision = characterLength * 5;
double lat = pt.y;
double lon = pt.x;
long latBit = Geohash.convertToBinary(
lat,
new double[] { -90, 90 },
precision
);

long lonBit = Geohash.convertToBinary(
lon,
new double[] { -180, 180 },
precision
);

long interwovenBin = 1;
for (int i = precision - 1; i >= 0; i--) {
long currLon = (lonBit >>> i) & 1;
long currLat = (latBit >>> i) & 1;
interwovenBin <<= 1;
interwovenBin |= currLon;
interwovenBin <<= 1;
interwovenBin |= currLat;
}

return Geohash
.binaryToBase32(interwovenBin, precision * 2)
.substring(0, characterLength);
}

/**
* Computes the base32 value of the binary string given
* @param binStr (long) Binary number that is to be converted to a base32 string
* @param len (int) number of bits
* @return base32 string of the binStr in chunks of 5 binary digits
*/

private static String binaryToBase32(long binStr, int len) {
StringBuilder base32Str = new StringBuilder();

for (int i = len - 5; i >= 0; i -= 5) {
// Extract a group of 5 bits
int group = (int) (binStr >>> i) & 0x1F;

// Use the extracted group as an index to fetch the corresponding base32 character
base32Str.append(base32.charAt(group));
}

return base32Str.toString();
}

/**
* Converts the value given to a binary string with the given precision and range
* @param value (double) The value to be converted to a binary number
* @param r (double[]) The range at which the value is to be compared with
* @param precision (int) The Precision (number of bits) that the binary number needs
* @return (String) A binary number representation of the value with the given range and precision
*/

private static long convertToBinary(double value, double[] r, int precision) {
int binVal = 1;
for (int i = 0; i < precision; i++) {
double mid = (r[0] + r[1]) / 2;
if (value >= mid) {
binVal = binVal << 1;
binVal = binVal | 1;
r[0] = mid;
} else {
binVal = binVal << 1;
r[1] = mid;
}
}
return binVal;
}

/**
* Compute the longest geohash that contains the envelope
* @param envelope
* @return the geohash as a string
*/
public static String containingGeohash(Envelope2D envelope) {
double posMinX = envelope.xmin + 180;
double posMaxX = envelope.xmax + 180;
double posMinY = envelope.ymin + 90;
double posMaxY = envelope.ymax + 90;
int chars = 0;
double xmin = 0;
double xmax = 0;
double ymin = 0;
double ymax = 0;
double deltaLon = 360;
double deltaLat = 180;

while (xmin == xmax && ymin == ymax && chars < 7) {
if (chars % 2 == 0) {
deltaLon = deltaLon / 8;
deltaLat = deltaLat / 4;
} else {
deltaLon = deltaLon / 4;
deltaLat = deltaLat / 8;
}

xmin = Math.floor(posMinX / deltaLon);
xmax = Math.floor(posMaxX / deltaLon);
ymin = Math.floor(posMinY / deltaLat);
ymax = Math.floor(posMaxY / deltaLat);

chars++;
}

if (chars == 1) return "";

return toGeohash(new Point2D(envelope.xmin, envelope.ymin), chars - 1);
}

/**
*
* @param envelope
* @return up to four geohashes that completely cover given envelope
*/
public static String[] coveringGeohash(Envelope2D envelope) {
double xmin = envelope.xmin;
double ymin = envelope.ymin;
double xmax = envelope.xmax;
double ymax = envelope.ymax;

if (NumberUtils.isNaN(xmax)) {
return new String[] {""};
}
String[] geoHash = {containingGeohash(envelope)};
if (geoHash[0] != ""){
return geoHash;
}

int grid = 45;
int gridMaxLon = (int)Math.floor(xmax/grid);
int gridMinLon = (int)Math.floor(xmin/grid);
int gridMaxLat = (int)Math.floor(ymax/grid);
int gridMinLat = (int)Math.floor(ymin/grid);
int deltaLon = gridMaxLon - gridMinLon + 1;
int deltaLat = gridMaxLat - gridMinLat + 1;
String[] geoHashes = new String[deltaLon * deltaLat];

if (deltaLon * deltaLat > 4){
return new String[] {""};
} else {
for (int i = 0; i < deltaLon; i++){
for (int j = 0; j < deltaLat; j++){
Point2D p = new Point2D(xmin + i * grid, ymin + j * grid);
geoHashes[i*deltaLat + j] = toGeohash(p, 1);
}
}
}
return geoHashes;
}
}
Loading

0 comments on commit a56f6ae

Please sign in to comment.