package geodb; public class GeoString { static byte[] toBits(double f) { return toBits(f,32); } static byte[] toBits(double f, int depth) { f *= (1L << depth); byte[] bits = new byte[depth]; for ( int i = 1; i < depth+1; i++) { bits[i-1] = (byte) (((long)f >> (depth-i)) & 1); } return bits; } static String bitstring(double x, double y, Box b, int depth) { byte[] xbits = toBits( (x - b.l) / ( b.r - b.l ), depth ); byte[] ybits = toBits( (y - b.b) / ( b.t - b.b ), depth ); StringBuffer bits = new StringBuffer(); for ( int i = 0; i < xbits.length; i++ ) { bits.append(xbits[i]).append(ybits[i]); } return bits.toString(); } static double round( double d, int n ) { double scale = Math.pow( 10, n ); double r = d*scale + 0.5; return ((int)r) / scale; } Box bound; int depth; double originx,originy; double sizex,sizey; String hash; public GeoString( double x, double y) { this(x,y,32); } public GeoString( double x, double y, Box bound) { this(x,y,bound,32); } public GeoString( double x, double y, int depth) { this(x,y,Box.WGS84,depth); } public GeoString( double x, double y, Box bound, int depth ) { this.bound = bound; this.depth = depth; originx = bound.l; originy = bound.b; sizex = bound.r - bound.l; sizey = bound.t - bound.b; hash = bitstring(x, y, bound, depth); } public GeoString( String hash ) { this(hash,32); } public GeoString( String hash, Box bound) { this(hash,bound,32); } public GeoString( String hash, int depth) { this(hash,Box.WGS84,depth); } public GeoString( String hash, Box bound, int depth ) { this.bound = bound; this.depth = depth; this.hash = hash; originx = bound.l; originy = bound.b; sizex = bound.r - bound.l; sizey = bound.t - bound.b; } public Box bbox() { return bbox(hash.length()); } public Box bbox(int prefix) { String bits = this.hash.substring(0,prefix); int depth = bits.length()/2; double minx = 0, miny = 0; double maxx = 1, maxy = 1; for ( int i = 0; i < depth+1; i++ ) { try { minx += Double.parseDouble( String.valueOf(bits.charAt(i*2)) ) / (2L<<i); miny += Double.parseDouble( String.valueOf(bits.charAt(i*2+1)) ) / (2L<<i); } catch( StringIndexOutOfBoundsException e ) { //ok } } if ( depth > 0 ) { maxx = minx + 1.0/((double)(2L<<(depth-1))); maxy = miny + 1.0/((double)(2L<<(depth-1))); } else if ( bits.length() == 1 ) { maxx = Math.min(minx + 0.5, 1.0); } minx = round(originx + minx*sizex, 6); maxx = round(originx + maxx*sizex, 6); miny = round(originy + miny*sizey, 6); maxy = round(originy + maxy*sizey, 6); return new Box(minx,miny,maxx,maxy); } public GeoString union(GeoString string) { String other = string.hash; int n = Math.min(hash.length(),other.length()); int i = 0; for ( ; i < n; i++ ) { if ( hash.charAt(i) != other.charAt(i) ) { break; } } String union = hash.substring(0,i); return new GeoString(union,this.bound,this.depth); } @Override public String toString() { return hash; } public static void main(String[] args) { GeoString gs = new GeoString(-0.25,51.5); System.out.println(gs); System.out.println(gs.bbox()); } }