GithubHelp home page GithubHelp logo

kungfoo / geohash-java Goto Github PK

View Code? Open in Web Editor NEW
979.0 979.0 313.0 579 KB

Implementation of GeoHashes in java. We try to be/stay compliant to the spec, as far as possible.

License: Other

Java 99.94% Shell 0.06%

geohash-java's People

Contributors

0mok avatar cchandler avatar chids avatar chosak avatar dependabot[bot] avatar frostbytten avatar grahamdennis avatar kungfoo avatar martin-mfg avatar mauhiz avatar robertpyke avatar satorg avatar twillouer avatar vinerich avatar vooft avatar zhyhooo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

geohash-java's Issues

`VincentyGeodesy.distanceInMeters` works incorrectly if one of the points is either South or North pole

Example (all results in meters are converted into kilometers for convenience):

VincentyGeodesy.distanceInMeters(new WGS84Point(90, 0), new WGS84Point(89, 0)) / 1000.0
  results to 19892.237593638412

whereas

VincentyGeodesy.distanceInMeters(new WGS84Point(-90, 0), new WGS84Point(89, 0)) / 1000.0
  results to 111.69386491426172

which is apparently incorrect.

Note that for a point that is off the pole just a fraction of a degree, the algorithm seems working correctly (or at least plausible):

VincentyGeodesy.distanceInMeters(new WGS84Point(89.9999, 0), new WGS84Point(89, 0)) / 1000.0
  results to 111.6826955163081

VincentyGeodesy.distanceInMeters(new WGS84Point(-89.9999, 0), new WGS84Point(89, 0)) / 1000.0
  results to 19892.22642424046

How to get Hash String with GeoHash

After I get GeoHash[], I need to get Hash String ex: "ez6".
How can I convert GeoHash to Hash? It's like the reverse of an already function called "fromGeohashString".

Request: sample code on how to use this library to get restaurants closest to user with a NoSQL database like DynamoDB

Is there any sample code for how one might use this library with a NoSQL database such as DynamoDB?

I have a large number of NoSQL objects containing the geohash of the lat & long of restaurants that looks like this:

id | geohash     | restaurant_name
1  | 9q8ymjj56343  | pizza hut
2  | 9q8ymjj29365  | maxies
3  | 9q8ymjj29334  | ralphs
4  | 9q8ymjj98552  | pizza zen
5  | 9q8ymjj294m5  | 5 guys
6  | 9q8ymjj34543  | in-n-out

I want to be able to create a query where I can pass in the user's location as a geohash and return all restaurants in a 100 meter radius. I would like the results to be sorted so that the restaurants closest to the user appear at the top of the list.

So far, I understand that I can take the lat/long of a restaurant and turn it into a geohash like this:

GeoHash geoHash = GeoHash.withCharacterPrecision(lat, long, 12);

Then, I can use the same code to get the current position of the user:

GeoHash usersPosition = GeoHash.withCharacterPrecision(lat, long, 12);
I know that after this I need to make a query on my restaurants where I filter results based on the user's geohash. I know how to make such queries with my database code, but I am not sure how to use this library to help me.

Could someone provide sample code on how to use this library to create my query? (pseudo code for the part where you query the NoSQL database is fine). I know there may be false-positives I need to account for as well, and throw them out. And that there may be multiple queries I need to make to my database.

This seems like a really good library, and I want to make sure I am using it right.

GeoHashCircleQuery bug?

I'm very new to Geohashing and maybe I don't understand this correctly. I live in Vancouver and my code has Trout Lake Park at center. Then the next point is QE Park which is 6km away. I was expecting the query to say QE Park is NOT within a 1.6km radius of Trout Lake. Is this a bug?

@Test
public void testRadius() {
	//Trout Lake Park
	WGS84Point center = new WGS84Point(49.255322162733535, -123.0620549806324);

	GeoHashCircleQuery query = new GeoHashCircleQuery(center, 1600);

	//Queen Elizabeth Park (about 6km from Trout Lake)
	WGS84Point test1 = new WGS84Point(49.241578198431874, -123.11217797222398);
	
	assertFalse(query.contains(test1));
}

GeoHashBoundingBoxQuery returns binary that matches points outside box

Hi there, I'm using geohash dependency 1.3.0 and it's being working out of the box so far! Great library!

I'm implementing a BoundingBox search and even though it seems to be really straight forward implementation, I notice that some points outside the box are matched when using the binary geohash prefix. Actually, I'm doing like this:

WGS84Point point1 = new WGS84Point(10.557597041722232, -35.52832642341309);
WGS84Point point2 = new WGS84Point(-41.76269104573268, -68.00914348298193);
GeoHashBoundingBoxQuery boundingBoxQuery = new GeoHashBoundingBoxQuery(new BoundingBox(point1, point2));
	
List<GeoHash> containedGeohashes = boundingBoxQuery.getSearchHashes();
Set<String> containedStringGeohashes = new HashSet<>();

for(GeoHash hash : containedGeohashes){
    containedStringGeohashes.add(hash.toBinaryString());
}

for(String significantHash : containedStringGeohashes){
    FunctionUtil.log(TAG, "Searching for " + significantHash);
    Query<MXChallenge> queryChallenges = ofy().load().type(MXChallenge.class).filter("binaryGeohash >=", significantHash).filter("binaryGeohash <", significantHash + "\uFFFD").limit(ConstUtil.limit.MAP_CHALLENGES_PER_BOUNDING_BOX);
    ....
}

The problem is, the points above creates a boundingbox pretty much in Brazil, but when I iterate between the Geohash objects returned from boundingBoxQuery.getSearchHashes() and retrieved their binary representations, these are the strings I get:

001
011

And then when I search for those binaries on my database it matches points in for example Ireland:

0111101011000011001001110001100110101010010101100101111001011001 (51.473854, -9.388135)
0111101011000001101101000011100111011100110011000001100100010101 (51.608044, -10.158703)
0111101011000001110001000000001101100011000110010100111110111111 (51.771931, -10.539617)

Those binaries from my database are also generated using geohash-java 1.3.0 like this:

GeoHash.withBitPrecision(latitude, longitude, 64).toBinaryString();

Please can you advise what I am doing wrong?
Thanks again for the awesome library!

Base16 Support?

I have a datasource that uses geohashes encoded with Base16 and I'm trying to figure out how to use your library with that data. I have tried to decode using GeoHash.fromGeohashString() but it appears it's assuming the GeoHash is Base32, so it's incorrect. Is there a way to use your library with Base16 geohashes?

Bounding Box cannot specify a Box which goes over the -180/+180 degree Meridian (v1.3.0)

Expected Behaviour

A constructed bounding box takes the provided points as is and will not reorder them to specifiy a different box.

Actual Behaviour

The provided points are reorderd to always represent a box fully contained in the longitude ranges -180/+180 and latitude ranges -90/+90.
As this is a solution to work with always well defined min/max values after the initialization, it is not what I would expect to happen when I define a BoundingBox.

This is the constructor of the BoundingBox class:

public BoundingBox(double y1, double y2, double x1, double x2) {
		minLon = Math.min(x1, x2);
		maxLon = Math.max(x1, x2);
		minLat = Math.min(y1, y2);
		maxLat = Math.max(y1, y2);
	}

Also this method to expand an existing BoundingBox:

public void expandToInclude(BoundingBox other) {
		if (other.minLon < minLon) {
			minLon = other.minLon;
		}
		if (other.maxLon > maxLon) {
			maxLon = other.maxLon;
		}
		if (other.minLat < minLat) {
			minLat = other.minLat;
		}
		if (other.maxLat > maxLat) {
			maxLat = other.maxLat;
		}
	}

In both cases the points are reorderd to not go over the -180/+180 Meridian.

Also in the class TwoGeoHashBoundingBox:

public TwoGeoHashBoundingBox(GeoHash bottomLeft, GeoHash topRight) {
		if (bottomLeft.significantBits() != topRight.significantBits()) {
			throw new IllegalArgumentException(
					"Does it make sense to iterate between hashes that have different precisions?");
		}
		this.bottomLeft = GeoHash.fromLongValue(bottomLeft.longValue(), bottomLeft.significantBits());
		this.topRight = GeoHash.fromLongValue(topRight.longValue(), topRight.significantBits());
		this.boundingBox = this.bottomLeft.getBoundingBox();
		this.boundingBox.expandToInclude(this.topRight.getBoundingBox());
	}

The constructor suggests that the Box will be saved as it is defined. With bottomLeft and topRight. But since at the bottom of the constructor expandToInclude is called, the points will be reorder again and maybe the defined box will change.

This whole thing may be related to issue #31 but I'm not sure about this.

Proposed Solution

  1. I'm not familiar with the classes and goehashing itself so I'd like to ask if it is save to not do the reordering and take the box as it is defined.
  2. Split the defined box into 2 boxes. Splitted at the -180/+180 meridian and to perform 2 seach queries. But I personally don't like that approach, cause it leaves the handling of this case to the user.
  3. In extend to 2., throw an Exception when such a box is defined. Atleast the user knows what went wrong and is not tapping in the dark.

In the meanwhile I'll fork and try to work on a fix but idk how much effort I can put into it.

I personally would like solution 1 and maybe I'll come up with a PR. What's your opinion?

Publish Maven Central

Wondering if this could be published to Maven Central by the author vs. going the route of 3rd-party artifact-bundles?

LGPL in Android application

Hello Silvio

First of all thank you for handy library.
But I have one licensing question - LGPL seems do not work well with android application without shipping code.
Is there any chance to switch license to more Android friendly Apache Software License 2.0

Thank you.

GeoHash recombineLatLonBitsToHash miss reset class member point?

If one instance of GeoHash invoke getAdjacent, and then invoke getPoint on the element of the array returned from getAdjacent, it will occur null pointer exception. check the code, find all the element in the array returned from "recombineLatLonBitsToHash" this method, I'm not so sure if it miss the reset to the "point" in the instance of GeoHash.

GeoHash is not divisible by 5

Hi, first of all, there is a little grammatical error in GeoHash class:
" IllegalStateException("precision of GeoHash is not divisble by 5: " + this); ".

But my question is: if i have a bounding box and i want to know all geohashes, in base32, where it fits how should i do? Because i always get GeoHash : Cannot convert a geohash to base32 if the precision is not a multiple of 5.
Is there a workaround?
Thanks

a bug in CircleQuery

line 34 in GeoHashCircleQuery.java is wrong:
double halfRadius = radius / 2.0;

the "halfRadius" is meaningLess.
you should use the radius to create the BoundingBox, which will exactly overlap the circle.

stepsBetween function

I was wondering if the stepsBetween function could be related to actual distance between two geohashes? Also, "steps" makes it sound like the difference in geohash "rectangles", but it is not. Would there be a way of implementing this based on the stepsBetween function?

Neighbouring NESW GeoHashes

Would be useful to make "getNorthern/Southern..." methods public BUT unlike other forms of object construction GeoHash neighbour objects aren't initialized with boundingBox pre-calculated so get null for calls to getBoundingBoxPoints()

GeoHashCircleQuery fails near the poles

The following code:

WGS84Point target = new WGS84Point(new LatLonPair(89.99904405694745,-60.02364542228088));
GeoHashCircleQuery query = new GeoHashCircleQuery(target, 250);
List<GeoHash> boundingHashes = query.getSearchHashes();
for(GeoHash hash : boundingHashes ){
  System.out.println(hash);
}

produces the following output:

0 -> (90.0,-180.0) -> (-90.0,180.0) ->

Reducing the radius in the query to 100m gives:

100000000000000000000000000000000000000000000000000000000000000 -> (90.0,-180.0) -> (0.0,0.0), bits: 2
1100000000000000000000000000000000000000000000000000000000000000 -> (90.0,0.0) -> (0.0,180.0), bits: 2

Neither of these results looks correct. I would expect to see some results grouped around the hash 'czurvpx2uxj9'.

Also, for the case of the poles themselves, there can be upto 32 nearby hashes. Should there be a special case to handle the poles?

How can I set the precision so it's a multiple of 5?

Hey, I'm so sorry for asking this stupid question but I'm very new to Java and use Android studio for the first time. I spent the last few hours writing the following query for Firebase but I always get the error "Cannot convert a geohash to base32 if the precision is not a multiple of 5." I have already looked at all the questions about this on here but I can't figure out what to do about it. Like how can I change the precision to a value that is a multiple of 5? I'm really stuck :(

`
private void search(){

    CollectionReference locCollectionRef = fStore.collection("users");

    WGS84Point firstPos = new WGS84Point(firstLatLng.getLatitude(), firstLatLng.getLongitude());
    WGS84Point secondPos = new WGS84Point(secLatLng.getLatitude(), secLatLng.getLongitude());


    GeoHashBoundingBoxQuery search = new GeoHashBoundingBoxQuery(new BoundingBox(firstPos, secondPos));

    List<GeoHash> hashes = search.getSearchHashes();


    for (GeoHash hash : hashes){
        String start = hash.toBase32();
        String end = start + "~";

        Query locQuery = locCollectionRef
                .orderBy("Hash")
                .startAt(start)
                .endAt(end);
        locQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
            @Override
            public void onComplete(@NonNull Task<QuerySnapshot> task) {
                if(task.isSuccessful()){
                    for(QueryDocumentSnapshot doc : task.getResult()){
                        String usr = doc.get("name").toString();
                        printUsr(usr);
                    }
                }

            }
        });
    }
}

`
PS: if it's not clear, I have a database with lots of documents and each of the documents has a field with a hash code. now I want to get every document that is inside of a rectangle.

Edit: I got it working doing this. I think the code is very ugly and bad but I'll post it anyway. (improvements are appreciated)

`
private void search(){

    CollectionReference locCollectionRef = fStore.collection("users");

    WGS84Point firstPos = new WGS84Point(firstLatLng.getLatitude(), firstLatLng.getLongitude());
    WGS84Point secondPos = new WGS84Point(secLatLng.getLatitude(), secLatLng.getLongitude());

    GeoHashBoundingBoxQuery search = new GeoHashBoundingBoxQuery(new BoundingBox(firstPos, secondPos));

    List<GeoHash> hashes = search.getSearchHashes();

    List<String> starts = new ArrayList<>();

    for (GeoHash hash : hashes){
        int bits = (int)(hash.significantBits() / 5) * 5;
        String start = GeoHash.fromLongValue(hash.longValue(), bits).toBase32();
        if(starts.contains(start)){

        }
        else {
            starts.add(start);
        }
    };

    for (String start : starts){

        String end = start + "~";
        printUsr(start);

        Query locQuery = locCollectionRef
                .orderBy("Hash")
                .startAt(start)
                .endAt(end);
        locQuery.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
            @Override
            public void onComplete(@NonNull Task<QuerySnapshot> task) {
                if(task.isSuccessful()){
                    for(QueryDocumentSnapshot doc : task.getResult()){
                        String usr = doc.get("name").toString();
                        printUsr(usr);
                    }
                }

            }
        });
    }
}

`

I need a function

would you like to provide a function that convert a 'bits' and 'signifciantbits' to a GeoHash ? for example:

public static GeoHash fromGeoHashBits(long bits, byte significantBits);

VincentGeodesy.moveInDirection - issue when close to the international date line

If I use:

WGS84Point point = new WGS84Point(0.0, 179.999);
point = VincentyGeodesy.moveInDirection(point , 90.0, 1000);

I get

Exception in thread "main" java.lang.IllegalArgumentException: The supplied coordinates (-3.4624406578435726E-18,180.00798315284126) are out of range.
at ch.hsr.geohash.WGS84Point.(WGS84Point.java:26)
at ch.hsr.geohash.util.VincentyGeodesy.moveInDirection(VincentyGeodesy.java:81)
at WeatherLocationTest.(WeatherLocationTest.java:52)
at WeatherLocationTest.main(WeatherLocationTest.java:28)

I believe a quick fix is:

public static WGS84Point moveInDirection(WGS84Point point, double bearingInDegrees, double distanceInMeters) {

if (bearingInDegrees < 0 || bearingInDegrees > 360) {
  throw new IllegalArgumentException("direction must be in (0,360)");
}

double a = 6378137, b = 6356752.3142, f = 1 / 298.257223563; // WGS-84
// ellipsiod
double alpha1 = bearingInDegrees * degToRad;
double sinAlpha1 = Math.sin(alpha1), cosAlpha1 = Math.cos(alpha1);

double tanU1 = (1 - f) * Math.tan(point.getLatitude() * degToRad);
double cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1;
double sigma1 = Math.atan2(tanU1, cosAlpha1);
double sinAlpha = cosU1 * sinAlpha1;
double cosSqAlpha = 1 - sinAlpha * sinAlpha;
double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
double A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));

double sinSigma = 0, cosSigma = 0, cos2SigmaM = 0;
double sigma = distanceInMeters / (b * A), sigmaP = 2 * Math.PI;
while (Math.abs(sigma - sigmaP) > 1e-12) {
  cos2SigmaM = Math.cos(2 * sigma1 + sigma);
  sinSigma = Math.sin(sigma);
  cosSigma = Math.cos(sigma);
  double deltaSigma = B
  * sinSigma
  * (cos2SigmaM + B
          / 4
          * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM
                  * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
  sigmaP = sigma;
  sigma = distanceInMeters / (b * A) + deltaSigma;
}

double tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
double lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1, (1 - f)
                         * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp));
double lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1);
double C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
double L = lambda - (1 - C) * f * sinAlpha
* (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));

double newLat = lat2 / degToRad;
double newLon = point.getLongitude() + L / degToRad;

newLon = (newLon > 180.0 ? 360.0 - newLon : newLon);
newLon = (newLon < -180.0 ? 360.0 + newLon : newLon);

return new WGS84Point(newLat, newLon);

}

GeoHash.withBitPrecision accepts negative values for bit precision

Example:

GeoHash.withBitPrecision(12.345, 67.89, -1)

results to

0000000000000000000000000000000000000000000000000000000000000000 -> (90.0,-180.0) -> (-90.0,180.0) ->

I.e. any negative value is treated just as zero.

Expected behavior: an IllegalArgumentException is thrown for all values that are not within the [0..64] (inclusive) range.

Version affected: 1.4.0

Feature - BoundingBox.expandToInclude(WSG84Point)

Background

At the moment a BoundingBox is only expandable by defining another BoundingBox: public void expandToInclude(BoundingBox other).
I recently got a use case where it would be nice to just specify WSG84Points to be included.

Example

Exp. given you have an array of Points that you want to include into a BoundingBox. Just call BoundingBox.expandToInclude(WSG84Point) for each point and you are done.

I would like to do this with the BoundingBox class since it already has the logic to expand the box into the direction with the minimal distance. (since v1.4.0)

Proposal

I'd go for the implemtation by myself and would do a PR for review. Ofc testing will also be included. What do you think?

BoundingBoxGeoHashIterator (Version 1.3.0) hasNext() never returns false with some specific coordinates

Please see the following test class:

import ch.hsr.geohash.BoundingBox;
import ch.hsr.geohash.GeoHash;
import ch.hsr.geohash.util.BoundingBoxGeoHashIterator;
import ch.hsr.geohash.util.TwoGeoHashBoundingBox;
import org.junit.Test;

import java.util.HashSet;
import java.util.Set;

import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;

/**
 * Created by liptak on 2016.10.21..
 */
public class GeoHashBug {
    @Test
    public void endlessIterator() {
        BoundingBox box = new BoundingBox(72.28907f, 88.62655f, -50.976562f, 170.50781f);
        BoundingBoxGeoHashIterator iterator = new BoundingBoxGeoHashIterator(
                TwoGeoHashBoundingBox.withCharacterPrecision(box, 2)
        );
        Set<GeoHash> hashes = new HashSet<>();
        while( iterator.hasNext() ) {
            GeoHash hash = iterator.next();
            assertThat("Hash has been already produced by the iterator once: " + hash, hashes, not(hasItem(hash)));
            hashes.add(hash);
        }
    }
}

I would expect, that each hash comes only once, and the iterator exits. Output:

java.lang.AssertionError: Hash has been already produced by the iterator once: 111011010000000000000000000000000000000000000000000000000000000 -> (73.125,-56.25) -> (67.5,-45.0) -> fu
Expected: not a collection containing <111011010000000000000000000000000000000000000000000000000000000 -> (73.125,-56.25) -> (67.5,-45.0) -> fu>
but: was <[1101110111000000000000000000000000000000000000000000000000000000 -> (90.0,56.25) -> (84.375,67.5) -> vr, 111111011000000000000000000000000000000000000000000000000000000 -> (78.75,-11.25) -> (73.125,0.0) -> gv, 111111001000000000000000000000000000000000000000000000000000000 -> (78.75,-22.5) -> (73.125,-11.25) -> gt, 111110111000000000000000000000000000000000000000000000000000000 -> (90.0,-33.75) -> (84.375,-22.5) -> gr, 111011011000000000000000000000000000000000000000000000000000000 -> (78.75,-56.25) -> (73.125,-45.0) -> fv, 111111111000000000000000000000000000000000000000000000000000000 -> (90.0,-11.25) -> (84.375,0.0) -> gz, 111111101000000000000000000000000000000000000000000000000000000 -> (90.0,-22.5) -> (84.375,-11.25) -> gx, 111011111000000000000000000000000000000000000000000000000000000 -> (90.0,-56.25) -> (84.375,-45.0) -> fz, 1111110001000000000000000000000000000000000000000000000000000000 -> (78.75,135.0) -> (73.125,146.25) -> zj, 1111010001000000000000000000000000000000000000000000000000000000 -> (78.75,90.0) -> (73.125,101.25) -> yj, 1111010011000000000000000000000000000000000000000000000000000000 -> (78.75,101.25) -> (73.125,112.5) -> ym, 1111110101000000000000000000000000000000000000000000000000000000 -> (90.0,135.0) -> (84.375,146.25) -> zp, 1111110011000000000000000000000000000000000000000000000000000000 -> (78.75,146.25) -> (73.125,157.5) -> zm, 1101110001000000000000000000000000000000000000000000000000000000 -> (78.75,45.0) -> (73.125,56.25) -> vj, 1101010001000000000000000000000000000000000000000000000000000000 -> (78.75,0.0) -> (73.125,11.25) -> uj, 1111110111000000000000000000000000000000000000000000000000000000 -> (90.0,146.25) -> (84.375,157.5) -> zr, 1111010101000000000000000000000000000000000000000000000000000000 -> (90.0,90.0) -> (84.375,101.25) -> yp, 1101110011000000000000000000000000000000000000000000000000000000 -> (78.75,56.25) -> (73.125,67.5) -> vm, 1101010011000000000000000000000000000000000000000000000000000000 -> (78.75,11.25) -> (73.125,22.5) -> um, 1111111001000000000000000000000000000000000000000000000000000000 -> (78.75,157.5) -> (73.125,168.75) -> zt, 1111010111000000000000000000000000000000000000000000000000000000 -> (90.0,101.25) -> (84.375,112.5) -> yr, 1101110101000000000000000000000000000000000000000000000000000000 -> (90.0,45.0) -> (84.375,56.25) -> vp, 1101010101000000000000000000000000000000000000000000000000000000 -> (90.0,0.0) -> (84.375,11.25) -> up, 1111111011000000000000000000000000000000000000000000000000000000 -> (78.75,168.75) -> (73.125,180.0) -> zv, 1111011001000000000000000000000000000000000000000000000000000000 -> (78.75,112.5) -> (73.125,123.75) -> yt, 1101010111000000000000000000000000000000000000000000000000000000 -> (90.0,11.25) -> (84.375,22.5) -> ur, 1111111101000000000000000000000000000000000000000000000000000000 -> (90.0,157.5) -> (84.375,168.75) -> zx, 1111011011000000000000000000000000000000000000000000000000000000 -> (78.75,123.75) -> (73.125,135.0) -> yv, 1101111001000000000000000000000000000000000000000000000000000000 -> (78.75,67.5) -> (73.125,78.75) -> vt, 1101011001000000000000000000000000000000000000000000000000000000 -> (78.75,22.5) -> (73.125,33.75) -> ut, 1111111111000000000000000000000000000000000000000000000000000000 -> (90.0,168.75) -> (84.375,180.0) -> zz, 1111011101000000000000000000000000000000000000000000000000000000 -> (90.0,112.5) -> (84.375,123.75) -> yx, 1101111011000000000000000000000000000000000000000000000000000000 -> (78.75,78.75) -> (73.125,90.0) -> vv, 1101011011000000000000000000000000000000000000000000000000000000 -> (78.75,33.75) -> (73.125,45.0) -> uv, 1111011111000000000000000000000000000000000000000000000000000000 -> (90.0,123.75) -> (84.375,135.0) -> yz, 1101111101000000000000000000000000000000000000000000000000000000 -> (90.0,67.5) -> (84.375,78.75) -> vx, 111110001000000000000000000000000000000000000000000000000000000 -> (78.75,-45.0) -> (73.125,-33.75) -> gj, 1101011101000000000000000000000000000000000000000000000000000000 -> (90.0,22.5) -> (84.375,33.75) -> ux, 1101111111000000000000000000000000000000000000000000000000000000 -> (90.0,78.75) -> (84.375,90.0) -> vz, 111110011000000000000000000000000000000000000000000000000000000 -> (78.75,-33.75) -> (73.125,-22.5) -> gm, 1101011111000000000000000000000000000000000000000000000000000000 -> (90.0,33.75) -> (84.375,45.0) -> uz, 111110101000000000000000000000000000000000000000000000000000000 -> (90.0,-45.0) -> (84.375,-33.75) -> gp, 1101010110000000000000000000000000000000000000000000000000000000 -> (84.375,11.25) -> (78.75,22.5) -> uq, 111110110000000000000000000000000000000000000000000000000000000 -> (84.375,-33.75) -> (78.75,-22.5) -> gq, 111110010000000000000000000000000000000000000000000000000000000 -> (73.125,-33.75) -> (67.5,-22.5) -> gk, 111011010000000000000000000000000000000000000000000000000000000 -> (90.0,-56.25) -> (67.5,180.0) -> fu, 111111100000000000000000000000000000000000000000000000000000000 -> (84.375,-22.5) -> (78.75,-11.25) -> gw, 111111010000000000000000000000000000000000000000000000000000000 -> (73.125,-11.25) -> (67.5,0.0) -> gu, 111111000000000000000000000000000000000000000000000000000000000 -> (73.125,-22.5) -> (67.5,-11.25) -> gs, 111011110000000000000000000000000000000000000000000000000000000 -> (84.375,-56.25) -> (78.75,-45.0) -> fy, 111111110000000000000000000000000000000000000000000000000000000 -> (84.375,-11.25) -> (78.75,0.0) -> gy, 1101110000000000000000000000000000000000000000000000000000000000 -> (73.125,45.0) -> (67.5,56.25) -> vh, 1111110100000000000000000000000000000000000000000000000000000000 -> (84.375,135.0) -> (78.75,146.25) -> zn, 1111010100000000000000000000000000000000000000000000000000000000 -> (84.375,90.0) -> (78.75,101.25) -> yn, 1111010000000000000000000000000000000000000000000000000000000000 -> (73.125,90.0) -> (67.5,101.25) -> yh, 1111110010000000000000000000000000000000000000000000000000000000 -> (73.125,146.25) -> (67.5,157.5) -> zk, 1111110000000000000000000000000000000000000000000000000000000000 -> (73.125,135.0) -> (67.5,146.25) -> zh, 1111010010000000000000000000000000000000000000000000000000000000 -> (73.125,101.25) -> (67.5,112.5) -> yk, 1101010000000000000000000000000000000000000000000000000000000000 -> (73.125,0.0) -> (67.5,11.25) -> uh, 1101110010000000000000000000000000000000000000000000000000000000 -> (73.125,56.25) -> (67.5,67.5) -> vk, 1111110110000000000000000000000000000000000000000000000000000000 -> (84.375,146.25) -> (78.75,157.5) -> zq, 1111010110000000000000000000000000000000000000000000000000000000 -> (84.375,101.25) -> (78.75,112.5) -> yq, 1101010010000000000000000000000000000000000000000000000000000000 -> (73.125,11.25) -> (67.5,22.5) -> uk, 1101110100000000000000000000000000000000000000000000000000000000 -> (84.375,45.0) -> (78.75,56.25) -> vn, 1111111000000000000000000000000000000000000000000000000000000000 -> (73.125,157.5) -> (67.5,168.75) -> zs, 1111011000000000000000000000000000000000000000000000000000000000 -> (73.125,112.5) -> (67.5,123.75) -> ys, 1101010100000000000000000000000000000000000000000000000000000000 -> (84.375,0.0) -> (78.75,11.25) -> un, 1101110110000000000000000000000000000000000000000000000000000000 -> (84.375,56.25) -> (78.75,67.5) -> vq, 1111111010000000000000000000000000000000000000000000000000000000 -> (73.125,168.75) -> (67.5,180.0) -> zu, 1111011010000000000000000000000000000000000000000000000000000000 -> (73.125,123.75) -> (67.5,135.0) -> yu, 1101111000000000000000000000000000000000000000000000000000000000 -> (73.125,67.5) -> (67.5,78.75) -> vs, 1111111100000000000000000000000000000000000000000000000000000000 -> (84.375,157.5) -> (78.75,168.75) -> zw, 1111011100000000000000000000000000000000000000000000000000000000 -> (84.375,112.5) -> (78.75,123.75) -> yw, 1101011000000000000000000000000000000000000000000000000000000000 -> (73.125,22.5) -> (67.5,33.75) -> us, 1101111010000000000000000000000000000000000000000000000000000000 -> (73.125,78.75) -> (67.5,90.0) -> vu, 1111111110000000000000000000000000000000000000000000000000000000 -> (84.375,168.75) -> (78.75,180.0) -> zy, 1111011110000000000000000000000000000000000000000000000000000000 -> (84.375,123.75) -> (78.75,135.0) -> yy, 1101011010000000000000000000000000000000000000000000000000000000 -> (73.125,33.75) -> (67.5,45.0) -> uu, 1101111100000000000000000000000000000000000000000000000000000000 -> (84.375,67.5) -> (78.75,78.75) -> vw, 111110000000000000000000000000000000000000000000000000000000000 -> (73.125,-45.0) -> (67.5,-33.75) -> gh, 1101011100000000000000000000000000000000000000000000000000000000 -> (84.375,22.5) -> (78.75,33.75) -> uw, 1101111110000000000000000000000000000000000000000000000000000000 -> (84.375,78.75) -> (78.75,90.0) -> vy, 1101011110000000000000000000000000000000000000000000000000000000 -> (84.375,33.75) -> (78.75,45.0) -> uy, 111110100000000000000000000000000000000000000000000000000000000 -> (84.375,-45.0) -> (78.75,-33.75) -> gn]>

at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:956)
at xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.GeoHashBug.endlessIterator(GeoHashBug.java:29)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

Process finished with exit code 255

Terminology explanation and getting started with geohash

I am interested to know more about geohash but I tried to learn from the test classes it is quite hard to understand for me. Could you tell me how can I get start with geohash?

Also could you tell me what does adjacent and precision mean? I found 22 bit precision and true adjacent but can't work out what does it mean?

toBinaryString() does not always work.

While creating a unit test for toBinaryString() and fromBinaryString() I just found, that the current implementation of toBinaryString() does not work in some corner cases.
For instance:

The Geohash:

(-89.9945068359375,-180.0) -> (-90.0,-179.989013671875) -> 000000

causes it to throw an StringIndexOutOfBoundsException.

I will have to reimplement toBinaryString().

Traverse geohashes in a spiral so I can have a cursor

I want to query for a given geohash in a box and then go around it in a spiral, box by box, and query for geohashes in those one by one. The reason I want to do this is so I can save the geohash of a given box and use it as a cursor to continue to look for results from where I left off.

My goal would be to keep track of how many geohashes I found and once I found a certain amount, I would stop and send those results as well as the geohash of the current box as a cursor to the end client.

This is what it would look like visually:

geo1

Is there an established way to do this with this library? I was thinking of using the geoHash.get[northern, southern, etc.]Neighbor() methods to go in a circle one by one.

Any tips would be greatly appreciated. Thank you.

Cannot convert a geohash to base32 if the precision is not a multiple of 5

    Double lat = new Double("39.941800");
    Double lon = new Double("116.352380");
    double radius = 500;
    WGS84Point center = new WGS84Point(lat, lon);
    GeoHashCircleQuery query = new GeoHashCircleQuery(center, radius);
    List<GeoHash> searchHashes = query.getSearchHashes();

    for (GeoHash searchHash : searchHashes) {
        System.out.println(searchHash.toBase32());
    }

How can I resolve it??

Difference with geohash.org

In testing encoding of all use zip codes against another Geohash impl linked from Wikipedia I found coords 40.390943 -75.937500 with a 12 char precision had a different geohash of dr1vzbzyrcxb rather than dr4jb0bn2180.
The site Geohash.org also gives hash dr4jb0bn2180 for these coords so I'm guessing your impl is at fault?
I'd like to use your implementation as it has more features (bbox etc)

Code Example or API for Simple Nearby Search

Hi, I've been searching everywhere for a clear example of how to use your lib to search for nearby locations. I mean, I just need to implement the following situation:

  • 1 - I have a Long latitude and Long longitude.
  • 2 - Convert this latitude/longitude to hash. (I believe it's done by using "GeoHash.withBitPrecision(latitude, longitude, 64)" but I'm not sure what precision is here. I suppose 64bits would be to have the same precision as long
  • 3 - Retrieve a list of latitudes/longitudes within 100km distance (I have no idea how to even start it using geohash)
  • 4 - Query objectify objects using the latitudes/longitudes result list.

Please could someone advice me or give me any start point to search about it?
Thanks a lot!

GeoHash.fromGeohashString() throws NPE for Invalid GeoHash

First of all, a big thanks to the contributors and the maintainers of this great library.

Now coming to the actual issue... The method GeoHash.fromGeohashString() throws NullPointerException if the argument is not a valid GeoHash (e.g. aaaaaa, sggfha).

Exception in thread "main" java.lang.NullPointerException
	at ch.hsr.geohash.GeoHash.fromGeohashString(GeoHash.java:103)
	at ch.hsr.geohash.Main.main(Main.java:6)

Background:
Actually we faced the crash in our app because somehow an invalid geohash made into our server's DB and the app started crashing due to NPE.

Suggestion/request:
Can a throws NullPointerException be added to the method signature so that the developers are aware that passing an invalid argument will throw an exception?

Since NPE is not a checked exception, the library will stay backwards compatible.

some conflict

if (significantBits % 5 != 0) {
            throw new IllegalStateException("Cannot convert a geohash to base32 if the precision is not a multiple of 5.");
}

the precision must be the multiple of 5, but because in GeoHash constructor,

desiredPrecision = Math.min(desiredPrecision, 64);

so that when i use

GeoHash hash = GeoHash.withBitPrecision(10.0,10.0, 70);

it will cause IllegalStateException.

Match intersection

Is is possible to check whether the tile, corresponding to a GeoHash of certain precision, partly intersects with a given bounding box? I think GeoHash::within() will only match when the other GeoHash is completely contained in the one, which the method is called on, right?

Example:

image

VincentyGeodesy.distanceInMeters can result in NaN for valid input coordinates

Sometimes VincentyGeodesy.distanceInMeters produces NaN for pretty valid input points.
There are examples (not a complete list!) of arguments for distanceInMeters below that result it to generate NaN:

foo=(0.0,0.0), bar=(0.0,-180.0)
foo=(1.0,180.0), bar=(0.0,1.0)
foo=(0.0,0.0), bar=(0.0,180.0)
foo=(-1.0,1.0), bar=(0.0,-180.0)
foo=(-1.0,-103.0), bar=(0.0,78.0)
foo=(-1.0,-106.0), bar=(0.0,75.0)
foo=(-1.0,-142.0), bar=(0.0,39.0)
foo=(-1.0,-155.0), bar=(0.0,26.0)
foo=(-1.0,-161.0), bar=(0.0,18.0)
foo=(-1.0,-164.0), bar=(0.0,17.0)
foo=(-1.0,-176.0), bar=(1.0,4.0)
foo=(-1.0,-178.0), bar=(0.0,1.0)
foo=(-1.0,-179.0), bar=(0.0,0.0)
foo=(-1.0,-30.0), bar=(0.0,149.0)
foo=(-1.0,-33.0), bar=(0.0,148.0)
foo=(-1.0,-50.0), bar=(1.0,130.0)
foo=(-1.0,-74.0), bar=(0.0,105.0)
foo=(-1.0,-79.0), bar=(1.0,101.0)
foo=(-1.0,-93.0), bar=(0.0,86.0)
foo=(-1.0,0.0), bar=(0.0,-179.0)
foo=(-1.0,0.0), bar=(0.0,179.0)
foo=(-1.0,1.0), bar=(0.0,-178.0)
foo=(-1.0,1.0), bar=(1.0,-179.0)
foo=(-1.0,110.0), bar=(0.0,-69.0)
foo=(-1.0,144.0), bar=(0.0,-35.0)
foo=(-1.0,158.0), bar=(1.0,-22.0)
foo=(-1.0,167.0), bar=(1.0,-13.0)
foo=(-1.0,174.0), bar=(0.0,-5.0)
foo=(-1.0,176.0), bar=(0.0,-3.0)
foo=(-1.0,179.0), bar=(0.0,0.0)
foo=(-1.0,33.0), bar=(0.0,-148.0)
foo=(-1.0,46.0), bar=(0.0,-135.0)
foo=(-1.0,47.0), bar=(0.0,-134.0)
foo=(-1.0,5.0), bar=(1.0,-175.0)

The list above is generated with help of ScalaCheck.

Version: 1.4.0

VincentyGeodesy

Line 80
newLon = (newLon > 180.0 ? 360.0 - newLon : newLon);

should be
newLon = (newLon > 180.0 ? newLon - 360 : newLon);

Expose validation-related constants in public API?

Hi,

I'd like to use GeoHash.MAX_CHARACTER_PRECISION to validate my inputs ahead of calling withCharacterPrecision (rather than catching an exception and parsing the error message from there). Although I can copy the value into my own code, it would be clearer to use the library constant directly. Would it make sense for this (and MAX_BIT_PRECISION or any similar constant) to be public?

(Happy to submit a PR, but it's a trivial change).

Many thanks,
Mickey

feature proposal: GeoHash.withSizePrecision

It would be a nice touch to be able to initialize the hash based on a bounding box precision measurement, which will just wrap the bit precision call (e.g withSizePrecision(GeoHash.PRECISION_1KM) or similar). I see there is some size table utility but I can't figure out how to use it in this context.

List of GeoHashes within corner points of map

Hi!

Just wondering if this library is able to form a list of geohashes of some precision (precision as a parameter as well as corners coordinates) which are within 2 corner points of map (ne and sw).
Or this feature is probably going to be released? I mean "true" geohashes with valid 32Base string value.

For now I found this function only in ClickHouse geohashesInBox method

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.