kungfoo / geohash-java Goto Github PK
View Code? Open in Web Editor NEWImplementation of GeoHashes in java. We try to be/stay compliant to the spec, as far as possible.
License: Other
Implementation of GeoHashes in java. We try to be/stay compliant to the spec, as far as possible.
License: Other
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));
}
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?
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??
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);
}
}
}
});
}
}
`
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);
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.
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.
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)
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?
Wondering if this could be published to Maven Central by the author vs. going the route of 3rd-party artifact-bundles?
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.
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.
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:
Please could someone advice me or give me any start point to search about it?
Thanks a lot!
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);
}
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
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
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.
Line 80
newLon = (newLon > 180.0 ? 360.0 - newLon : newLon);
should be
newLon = (newLon > 180.0 ? newLon - 360 : newLon);
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
this probably happens because containslongtitude uses <=/>= instead of </>.
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!
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?
A constructed bounding box takes the provided points as is and will not reorder them to specifiy a different box.
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.
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?
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?
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
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.
Where can I find instructions on how to use it?
I'm completely lost.
Can you support to gradle? It is used more and more frequently in Android project.
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?
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:
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.
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)
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()
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.
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".
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.
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
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
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()
.
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
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.