]> granicus.if.org Git - postgis/commitdiff
GeoHash implementation first cut.
authorPaul Ramsey <pramsey@cleverelephant.ca>
Wed, 11 Feb 2009 02:11:24 +0000 (02:11 +0000)
committerPaul Ramsey <pramsey@cleverelephant.ca>
Wed, 11 Feb 2009 02:11:24 +0000 (02:11 +0000)
git-svn-id: http://svn.osgeo.org/postgis/trunk@3685 b70326c6-7e19-0410-871a-916f4a2858ee

liblwgeom/cunit/cu_algorithm.c
liblwgeom/cunit/cu_algorithm.h
liblwgeom/liblwgeom.h
liblwgeom/lwalgorithm.c
liblwgeom/lwalgorithm.h
liblwgeom/lwcollection.c
liblwgeom/lwgeom.c

index 80a0389b523853b511d077745b43a3db63c5714d..9c5a014471de481622c6821c4dc3e57c9af4afc9 100644 (file)
@@ -35,7 +35,9 @@ CU_pSuite register_cg_suite(void)
            (NULL == CU_add_test(pSuite, "test_lwpoint_interpolate()", test_lwpoint_interpolate)) ||
            (NULL == CU_add_test(pSuite, "test_lwline_clip()", test_lwline_clip)) ||
            (NULL == CU_add_test(pSuite, "test_lwline_clip_big()", test_lwline_clip_big)) ||
-           (NULL == CU_add_test(pSuite, "test_lwmline_clip()", test_lwmline_clip))
+           (NULL == CU_add_test(pSuite, "test_lwmline_clip()", test_lwmline_clip)) ||
+           (NULL == CU_add_test(pSuite, "test_geohash_precision()", test_geohash_precision)) ||
+           (NULL == CU_add_test(pSuite, "test_geohash()", test_geohash))
        )
        {
                CU_cleanup_registry();
@@ -701,3 +703,71 @@ void test_lwline_clip_big(void)
        lwline_free(line);
 }
 
+void test_geohash_precision(void)
+{
+       BOX3D bbox;
+       int precision = 0;
+       
+       bbox.xmin = 23.0;
+       bbox.xmax = 23.0;
+       bbox.ymin = 25.2;
+       bbox.ymax = 25.2;
+       precision = lwgeom_geohash_precision(bbox);
+       //printf("precision %d\n",precision);
+       CU_ASSERT_EQUAL(precision, 20);
+
+       bbox.xmin = 23.0;
+       bbox.ymin = 23.0;
+       bbox.xmax = 23.1;
+       bbox.ymax = 23.1;
+       precision = lwgeom_geohash_precision(bbox);
+       //printf("precision %d\n",precision);
+       CU_ASSERT_EQUAL(precision, 2);
+
+       bbox.xmin = 23.0;
+       bbox.ymin = 23.0;
+       bbox.xmax = 23.0001;
+       bbox.ymax = 23.0001;
+       precision = lwgeom_geohash_precision(bbox);
+       //printf("precision %d\n",precision);
+       CU_ASSERT_EQUAL(precision, 6);
+
+}
+
+void test_geohash(void)
+{
+       LWPOINT *lwpoint = NULL;
+       LWLINE *lwline = NULL;
+       LWMLINE *lwmline = NULL;
+       char *geohash = NULL;
+       
+       lwpoint = (LWPOINT*)lwgeom_from_ewkt("POINT(23.0 25.2)", PARSER_CHECK_NONE);
+       geohash = lwgeom_geohash((LWGEOM*)lwpoint);
+       printf("geohash %s\n",geohash);
+       CU_ASSERT_STRING_EQUAL(geohash, "20");
+       lwfree(lwpoint);
+       lwfree(geohash);
+
+       lwline = (LWLINE*)lwgeom_from_ewkt("LINESTRING(23.0 23.0,23.1 23.1)", PARSER_CHECK_NONE);
+       geohash = lwgeom_geohash((LWGEOM*)lwline);
+       printf("geohash %s\n",geohash);
+       CU_ASSERT_STRING_EQUAL(geohash, "20");
+       lwfree(lwline);
+       lwfree(geohash);
+
+       lwline = (LWLINE*)lwgeom_from_ewkt("LINESTRING(23.0 23.0,23.001 23.001)", PARSER_CHECK_NONE);
+       geohash = lwgeom_geohash((LWGEOM*)lwline);
+       printf("geohash %s\n",geohash);
+       CU_ASSERT_STRING_EQUAL(geohash, "20");
+       lwfree(lwline);
+       lwfree(geohash);
+
+       lwmline = (LWMLINE*)lwgeom_from_ewkt("MULTILINESTRING((23.0 23.0,23.1 23.1),(23.0 23.0,23.1 23.1))", PARSER_CHECK_NONE);
+       geohash = lwgeom_geohash((LWGEOM*)lwmline);
+       printf("geohash %s\n",geohash);
+       CU_ASSERT_STRING_EQUAL(geohash, "20");
+       lwfree(lwmline);
+       lwfree(geohash);
+}
+
+
index 2fc17ac692a237b56b5313a4793e655f4298da4d..b53be42cd87836a9fc840e97772b5cfca36f6727 100644 (file)
@@ -37,4 +37,5 @@ void test_lwpoint_interpolate(void);
 void test_lwline_clip(void);
 void test_lwline_clip_big(void);
 void test_lwmline_clip(void);
-
+void test_geohash_precision(void);
+void test_geohash(void);
index 1a2930cdef6c2e160e5fc932fe07dae4c3ec04e6..7b742d441c8856dfd058dbbe8dce1cae3e69602d 100644 (file)
@@ -843,7 +843,7 @@ extern int lwcollection_ngeoms(const LWCOLLECTION *col);
  * pointers to the serialized form (POINTARRAYs).
  */
 LWGEOM *lwgeom_deserialize(uchar *serializedform);
-
+BOX3D *lwgeom_compute_box3d(const LWGEOM *geom);
 
 
 /******************************************************************
@@ -859,8 +859,8 @@ LWCURVEPOLY *lwcurvepoly_deserialize(uchar *serialized_form);
 LWMCURVE *lwmcurve_deserialize(uchar *serialized_form);
 LWMSURFACE *lwmsurface_deserialize(uchar *serialized_form);
 
-LWGEOM *lwcollection_getsubgeom(LWCOLLECTION *, int);
-
+LWGEOM *lwcollection_getsubgeom(LWCOLLECTION *col, int gnum);
+BOX3D *lwcollection_compute_box3d(LWCOLLECTION *col);
 
 
 /******************************************************************
index 76f7cae94f716c7c5fcc8854bf8051b6922b846d..04b2bdb71560458daddd2d4b3d86b74935a63a95 100644 (file)
@@ -734,3 +734,185 @@ LWCOLLECTION *lwline_clip_to_ordinate_range(LWLINE *line, int ordinate, double f
        return NULL;
 
 }
+
+int lwgeom_geohash_precision(BOX3D bbox)
+{
+       double minx, miny, maxx, maxy;
+       double latmax, latmin, lonmax, lonmin;
+       double lonwidth, latwidth;
+       double latmaxadjust, lonmaxadjust, latminadjust, lonminadjust;
+       int precision = 0;
+       
+       /* Get the bounding box, return error if things don't work out. */
+       minx = bbox.xmin;
+       miny = bbox.ymin;
+       maxx = bbox.xmax;
+       maxy = bbox.ymax;
+
+       if( minx == maxx && miny == maxy )
+       {
+               /* It's a point. Doubles have 51 bits of precision. 
+               ** 2 * 51 / 5 == 20 */
+               return 20;
+       }
+
+       lonmin = -180.0;
+       latmin = -90.0;
+       lonmax = 180.0;
+       latmax = 90.0;
+
+       /* Shrink a world bounding box until one of the edges interferes with the
+       ** bounds of our rectangle. */
+       while( 1 ) 
+       {
+               lonwidth = lonmax - lonmin;
+               latwidth = latmax - latmin;
+               latmaxadjust = lonmaxadjust = latminadjust = lonminadjust = 0.0;
+
+               if( minx > lonmin + lonwidth / 2.0 ) 
+               {
+                       lonminadjust = lonwidth / 2.0;
+               }
+               else if ( maxx < lonmax - lonwidth / 2.0 ) 
+               {
+                       lonmaxadjust = -1 * lonwidth / 2.0;
+               }
+               if( miny > latmin + latwidth / 2.0 ) 
+               {
+                       latminadjust = latwidth / 2.0;
+               }
+               else if (maxy < latmax - latwidth / 2.0 ) 
+               {
+                       latmaxadjust = -1 * latwidth / 2.0;
+               }
+               if ( (lonminadjust || lonmaxadjust) && (latminadjust || latmaxadjust ) )
+               {
+                       latmin += latminadjust;
+                       lonmin += lonminadjust;
+                       latmax += latmaxadjust;
+                       lonmax += lonmaxadjust;
+                       precision++;
+               }
+               else 
+               {
+                       break;
+               }
+       }
+
+       /* Each adjustment above corresponds to 2 bits of storage in the 
+       ** geohash. Each geohash character can contain 5 bits of information.
+       ** So geohashes have even lengths. 2 characters corresponds to 10 bits
+       ** of storage, which is 5 adjustments. */
+       return 2 * ( precision / 5 );
+}
+
+char *lwgeom_geohash(const LWGEOM *lwgeom) 
+{
+       BOX3D *bbox = NULL;
+       int precision = 0;
+       double lat, lon;
+       
+       bbox = lwgeom_compute_box3d(lwgeom);
+       if( ! bbox ) return NULL;
+       
+       if ( bbox->xmin < -180 || bbox->ymin < -90 || bbox->xmax > 180 || bbox->ymax > 90 )
+       {
+               lwerror("Geohash requires inputs in decimal degrees.");
+               lwfree(bbox);
+               return NULL;
+       }
+
+       /* What is the center of our geometry? */
+       lon = bbox->xmin + (bbox->xmax - bbox->xmin) / 2;
+       lat = bbox->ymin + (bbox->ymax - bbox->ymin) / 2;
+       
+       precision = lwgeom_geohash_precision(*bbox);
+       
+       lwfree(bbox);
+
+       /* Return the geohash of the center, with a precision determined by the
+       ** extent of the bounds. */
+       return geohash_point(lat, lon, precision);
+}
+
+static char *base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
+
+char *geohash_point(double latitude, double longitude, int precision)
+{   
+       int is_even=1, i=0;
+       double lat[2], lon[2], mid;
+       char bits[] = {16,8,4,2,1};
+       int bit=0, ch=0;
+       char *geohash = NULL;
+
+       geohash = lwalloc(precision + 1);
+
+       lat[0] = -90.0;  lat[1] = 90.0;
+       lon[0] = -180.0; lon[1] = 180.0;
+
+       while (i < precision) 
+       {
+               if (is_even) 
+               {
+                       mid = (lon[0] + lon[1]) / 2;
+                       if (longitude > mid) 
+                       {
+                               ch |= bits[bit];
+                               lon[0] = mid;
+                       } 
+                       else
+                       {
+                               lon[1] = mid;
+                       }
+               } 
+               else 
+               {
+                       mid = (lat[0] + lat[1]) / 2;
+                       if (latitude > mid) 
+                       {
+                               ch |= bits[bit];
+                               lat[0] = mid;
+                       } 
+                       else
+                       {
+                               lat[1] = mid;
+                       }
+               }
+
+               is_even = !is_even;
+               if (bit < 4)
+               {
+                       bit++;
+               }
+               else 
+               {
+                       geohash[i++] = base32[ch];
+                       bit = 0;
+                       ch = 0;
+               }
+       }
+       geohash[i] = 0;
+       return geohash;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
index 4f5fdfb1dd7c9d2ab92c72e2934279a0d3fcf848..2cdf32173a3a9a6785ba76d9d59f21c47f93399b 100644 (file)
@@ -45,3 +45,8 @@ void lwpoint_set_ordinate(POINT4D *p, int ordinate, double value);
 int lwpoint_interpolate(const POINT4D *p1, const POINT4D *p2, POINT4D *p, int ndims, int ordinate, double interpolation_value);
 LWCOLLECTION *lwline_clip_to_ordinate_range(LWLINE *line, int ordinate, double from, double to);
 LWCOLLECTION *lwmline_clip_to_ordinate_range(LWMLINE *mline, int ordinate, double from, double to);
+
+int lwgeom_geohash_precision(BOX3D bbox);
+char *lwgeom_geohash(const LWGEOM *lwgeom);
+char *geohash_point(double latitude, double longitude, int precision);
+
index 250d91fab713e7ad795fbd9e21278199e14f6a51..0ab9dfe7d01f93b90e612cae513da1fe8e1d9bce 100644 (file)
@@ -487,3 +487,54 @@ void lwcollection_free(LWCOLLECTION *col)
        
 };
 
+BOX3D *lwcollection_compute_box3d(LWCOLLECTION *col)
+{
+       int i;
+    BOX3D *boxfinal = NULL;
+    BOX3D *boxtmp1 = NULL;
+    BOX3D *boxtmp2 = NULL;
+       for ( i = 0; i < col->ngeoms; i++ ) 
+       {
+               if( col->geoms[i] ) {
+                       switch( TYPE_GETTYPE(col->geoms[i]->type) )
+                       {
+                               case POINTTYPE:
+                    boxtmp1 = lwpoint_compute_box3d((LWPOINT*)(col->geoms[i]));
+                    break;
+                               case LINETYPE:
+                    boxtmp1 = lwline_compute_box3d((LWLINE*)(col->geoms[i]));
+                                       break;
+                               case POLYGONTYPE:
+                    boxtmp1 = lwpoly_compute_box3d((LWPOLY*)(col->geoms[i]));
+                                       break;
+                               case CIRCSTRINGTYPE:
+                                       boxtmp1 = lwcircstring_compute_box3d((LWCIRCSTRING *)(col->geoms[i]));
+                                       break;
+                               case COMPOUNDTYPE:
+                               case CURVEPOLYTYPE:
+                               case MULTIPOINTTYPE:
+                               case MULTILINETYPE:
+                               case MULTIPOLYGONTYPE:
+                               case MULTICURVETYPE:
+                               case MULTISURFACETYPE:
+                               case COLLECTIONTYPE:
+                    boxtmp1 = lwcollection_compute_box3d((LWCOLLECTION*)(col->geoms[i]));
+                    boxfinal = box3d_union(boxtmp1, boxtmp2);
+                                       break;
+                       }
+            boxtmp2 = boxfinal;
+            boxfinal = box3d_union(boxtmp1, boxtmp2);
+                       if( boxtmp1 && boxtmp1 != boxfinal ) 
+                       {
+                lwfree(boxtmp1);
+                boxtmp1 = NULL;
+                   }
+                       if( boxtmp2 && boxtmp2 != boxfinal ) 
+                       {
+                lwfree(boxtmp2);
+                boxtmp2 = NULL;
+                   }
+               }
+       }
+       return boxfinal;
+}
index 89ca19e7d164d040365097cbeed24641cc08389f..81932dd2dd49cce58ab2ecd6051fc963ab6ee6b4 100644 (file)
@@ -199,6 +199,34 @@ lwgeom_reverse(LWGEOM *lwgeom)
        }
 }
 
+BOX3D *lwgeom_compute_box3d(const LWGEOM *lwgeom)
+{
+    if( ! lwgeom ) return NULL;
+    
+    switch(TYPE_GETTYPE(lwgeom->type))
+       {
+               case POINTTYPE:
+                       return lwpoint_compute_box3d((LWPOINT *)lwgeom);
+               case LINETYPE:
+                       return lwline_compute_box3d((LWLINE *)lwgeom);
+        case CIRCSTRINGTYPE:
+            return lwcircstring_compute_box3d((LWCIRCSTRING *)lwgeom);
+               case POLYGONTYPE:
+                       return lwpoly_compute_box3d((LWPOLY *)lwgeom);
+        case COMPOUNDTYPE:
+        case CURVEPOLYTYPE:
+               case MULTIPOINTTYPE:
+               case MULTILINETYPE:
+        case MULTICURVETYPE:
+               case MULTIPOLYGONTYPE:
+        case MULTISURFACETYPE:
+               case COLLECTIONTYPE:
+                       return lwcollection_compute_box3d((LWCOLLECTION *)lwgeom);
+       }
+       /* Never get here, please. */
+       return NULL;
+}
+
 int
 lwgeom_compute_box2d_p(LWGEOM *lwgeom, BOX2DFLOAT4 *buf)
 {