[postgis-commits] svn - r3688 - in trunk: doc liblwgeom liblwgeom/cunit postgis

postgis-commits at postgis.refractions.net postgis-commits at postgis.refractions.net
Wed Feb 11 13:48:14 PST 2009


Author: pramsey
Date: 2009-02-11 13:48:13 -0800 (Wed, 11 Feb 2009)
New Revision: 3688

Modified:
   trunk/doc/reference_new.xml
   trunk/liblwgeom/cunit/cu_algorithm.c
   trunk/liblwgeom/cunit/cu_algorithm.h
   trunk/liblwgeom/lwalgorithm.c
   trunk/liblwgeom/lwalgorithm.h
   trunk/postgis/lwgeom_functions_basic.c
   trunk/postgis/postgis.sql.in.c
Log:
Bind ST_GeoHash into SQL.


Modified: trunk/doc/reference_new.xml
===================================================================
--- trunk/doc/reference_new.xml	2009-02-11 18:28:53 UTC (rev 3687)
+++ trunk/doc/reference_new.xml	2009-02-11 21:48:13 UTC (rev 3688)
@@ -7521,7 +7521,7 @@
 	 <refsection>
 		<title>See Also</title>
 
-		<para><xref linkend="PostGIS_Full_Version" />, <xref linkend="ST_SetSRID" />, <xref linkend="ST_SRID" /></para>
+		<para><xref linkend="ST_AsSVG" />, <xref linkend="ST_AsGML" /></para>
 	  </refsection>
 	</refentry>
 	<refentry id="ST_AsSVG">
@@ -7577,6 +7577,72 @@
 		M 0 0 0 -1 1 -1 1 0 Z</programlisting>
 	  </refsection>
 	</refentry>
+
+
+	<refentry id="ST_GeoHash">
+	  <refnamediv>
+		<refname>ST_GeoHash</refname>
+
+		<refpurpose>Return a GeoHash representation (geohash.org) of the geometry.</refpurpose>
+	  </refnamediv>
+
+	  <refsynopsisdiv>
+		<funcsynopsis>
+			<funcprototype>
+				<funcdef>text <function>ST_GeoHash</function></funcdef>
+				<paramdef><type>geometry </type> <parameter>g1</parameter></paramdef>
+			</funcprototype>
+			<funcprototype>
+				<funcdef>text <function>ST_GeoHash</function></funcdef>
+				<paramdef><type>geometry </type> <parameter>g1</parameter></paramdef>
+				<paramdef><type>integer </type> <parameter>precision</parameter></paramdef>
+			</funcprototype>
+		</funcsynopsis>
+	  </refsynopsisdiv>
+
+	  <refsection>
+		<title>Description</title>
+
+		<para>Return a GeoHash representation (geohash.org) of the geometry. A GeoHash encodes a point into a text form that is sortable and searchable based on prefixing.  A shorter GeoHash is a less precise representation of a point.  It can also be thought of as a box, that contains the actual point.</para>
+		
+		<para>The one-parameter variant of ST_GeoHash returns a GeoHash based on the input geometry type. Points return a GeoHash with 20 characters of precision (about enough to hold the full double precision of the input). Other types return a GeoHash with a variable amount of precision, based on the size of the feature. Larger features are represented with less precision, smaller features with more precision. The idea is that the box implied by the GeoHash will always contain the input feature.</para>
+		
+		<para>The two-parameter variant of ST_GeoHash returns a GeoHash with a requested precision. For non-points, the starting point of the calculation is the center of the bounding box of the geometry.</para>
+
+		<note>
+		  <para>Availability: 1.4.0</para>
+		</note>
+
+		<note>
+			<para>ST_GeoHash will not work with geometries that are not in geographic (lon/lat) coordinates.</para>
+		</note>
+
+	  </refsection>
+
+	  <refsection>
+		<title>Examples</title>
+		<programlisting><![CDATA[SELECT ST_GeoHash(ST_SetSRID(ST_MakePoint(-126,48),4326));
+
+     st_geohash      
+----------------------
+ c0w3hf1s70w3hf1s70w3
+
+SELECT ST_GeoHash(ST_SetSRID(ST_MakePoint(-126,48),4326),5);
+
+ st_geohash 
+------------
+ c0w3h
+		]]>
+		</programlisting>
+	  </refsection>
+	 <refsection>
+		<title>See Also</title>
+
+		<para></para>
+	  </refsection>
+	</refentry>
+
+
 	<refentry id="ST_AsText">
 		  <refnamediv>
 			<refname>ST_AsText</refname>

Modified: trunk/liblwgeom/cunit/cu_algorithm.c
===================================================================
--- trunk/liblwgeom/cunit/cu_algorithm.c	2009-02-11 18:28:53 UTC (rev 3687)
+++ trunk/liblwgeom/cunit/cu_algorithm.c	2009-02-11 21:48:13 UTC (rev 3688)
@@ -36,6 +36,7 @@
 	    (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_geohash_point()", test_geohash_point)) ||
 	    (NULL == CU_add_test(pSuite, "test_geohash_precision()", test_geohash_precision)) ||
 	    (NULL == CU_add_test(pSuite, "test_geohash()", test_geohash))
 	)
@@ -706,34 +707,56 @@
 void test_geohash_precision(void)
 {
 	BOX3D bbox;
+    BOX3D bounds;
 	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);
+	precision = lwgeom_geohash_precision(bbox, &bounds);
+	printf("\nprecision %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);
+	precision = lwgeom_geohash_precision(bbox, &bounds);
+	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);
+	precision = lwgeom_geohash_precision(bbox, &bounds);
+	printf("precision %d\n",precision);
 	CU_ASSERT_EQUAL(precision, 6);
 
 }
 
+void test_geohash_point(void)
+{
+    char *geohash;
+	
+    geohash = geohash_point(0, 0, 16);
+	//printf("\ngeohash %s\n",geohash);
+	CU_ASSERT_STRING_EQUAL(geohash, "7zzzzzzzzzzzzzzz");
+	lwfree(geohash);
+
+    geohash = geohash_point(90, 0, 16);
+	//printf("\ngeohash %s\n",geohash);
+	CU_ASSERT_STRING_EQUAL(geohash, "gzzzzzzzzzzzzzzz");
+	lwfree(geohash);
+
+    geohash = geohash_point(20.012345, -20.012345, 15);
+	//printf("\ngeohash %s\n",geohash);
+	CU_ASSERT_STRING_EQUAL(geohash, "ee9cbe5kqe6pbku");
+	lwfree(geohash);
+
+}
+
 void test_geohash(void)
 {
 	LWPOINT *lwpoint = NULL;
@@ -743,7 +766,7 @@
 	
 	lwpoint = (LWPOINT*)lwgeom_from_ewkt("POINT(23.0 25.2)", PARSER_CHECK_NONE);
 	geohash = lwgeom_geohash((LWGEOM*)lwpoint);
-	printf("geohash %s\n",geohash);
+	printf("\ngeohash %s\n",geohash);
 	CU_ASSERT_STRING_EQUAL(geohash, "20");
 	lwfree(lwpoint);
 	lwfree(geohash);

Modified: trunk/liblwgeom/cunit/cu_algorithm.h
===================================================================
--- trunk/liblwgeom/cunit/cu_algorithm.h	2009-02-11 18:28:53 UTC (rev 3687)
+++ trunk/liblwgeom/cunit/cu_algorithm.h	2009-02-11 21:48:13 UTC (rev 3688)
@@ -38,4 +38,5 @@
 void test_lwline_clip_big(void);
 void test_lwmline_clip(void);
 void test_geohash_precision(void);
+void test_geohash_point(void);
 void test_geohash(void);

Modified: trunk/liblwgeom/lwalgorithm.c
===================================================================
--- trunk/liblwgeom/lwalgorithm.c	2009-02-11 18:28:53 UTC (rev 3687)
+++ trunk/liblwgeom/lwalgorithm.c	2009-02-11 21:48:13 UTC (rev 3688)
@@ -735,7 +735,72 @@
 
 }
 
-int lwgeom_geohash_precision(BOX3D bbox)
+static char *base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
+
+/*
+** Calculate the geohash, iterating downwards and gaining precision.
+** From geohash-native.c, (c) 2008 David Troy <dave at roundhousetech.com>
+** Released under the MIT License.
+*/
+char *geohash_point(double longitude, double latitude, 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;
+}
+
+int lwgeom_geohash_precision(BOX3D bbox, BOX3D *bounds)
 {
 	double minx, miny, maxx, maxy;
 	double latmax, latmin, lonmax, lonmin;
@@ -785,36 +850,51 @@
 		{
 			latmaxadjust = -1 * latwidth / 2.0;
 		}
+		/* Only adjust if adjustments are legal (we haven't crossed any edges). */
 		if ( (lonminadjust || lonmaxadjust) && (latminadjust || latmaxadjust ) )
 		{
 			latmin += latminadjust;
 			lonmin += lonminadjust;
 			latmax += latmaxadjust;
 			lonmax += lonmaxadjust;
-			precision++;
+            /* Each adjustment cycle corresponds to 2 bits of storage in the 
+	        ** geohash.	*/
+	        precision += 2;
 		}
 		else 
 		{
 			break;
 		}
 	}
+	
+	/* Save the edges of our bounds, in case someone cares later. */
+    bounds->xmin = lonmin;
+    bounds->xmax = lonmax;
+    bounds->ymin = latmin;
+    bounds->ymax = latmax;
 
-	/* 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 );
+	/* Each geohash character (base32) can contain 5 bits of information. 
+	** We are returning the precision in characters, so here we divide. */
+	return precision / 5;
 }
 
-char *lwgeom_geohash(const LWGEOM *lwgeom) 
+
+/*
+** Return a geohash string for the geometry. <http://geohash.org>
+** Where the precision is non-positive, calculate a precision based on the 
+** bounds of the feature. Big features have loose precision.
+** Small features have tight precision.
+*/
+char *lwgeom_geohash(const LWGEOM *lwgeom, int precision) 
 {
 	BOX3D *bbox = NULL;
-	int precision = 0;
+    BOX3D precision_bounds;
 	double lat, lon;
 	
 	bbox = lwgeom_compute_box3d(lwgeom);
 	if( ! bbox ) return NULL;
 	
+	/* Return error if we are being fed something outside our working bounds */
 	if ( bbox->xmin < -180 || bbox->ymin < -90 || bbox->xmax > 180 || bbox->ymax > 90 )
 	{
 		lwerror("Geohash requires inputs in decimal degrees.");
@@ -822,78 +902,31 @@
 		return NULL;
 	}
 
-	/* What is the center of our geometry? */
+	/* What is the center of our geometry bounds? We'll use that to 
+	** approximate location. */
 	lon = bbox->xmin + (bbox->xmax - bbox->xmin) / 2;
 	lat = bbox->ymin + (bbox->ymax - bbox->ymin) / 2;
+
+	if ( precision <= 0 ) 
+	{
+	    precision = lwgeom_geohash_precision(*bbox, &precision_bounds);
+    }
 	
-	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);
+	/* 
+	** Return the geohash of the center, with a precision determined by the
+	** extent of the bounds. 
+	** Possible change: return the point at the center of the precision bounds?
+	*/
+	return geohash_point(lon, lat, 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;
-}
 
 
 
@@ -911,8 +944,3 @@
 
 
 
-
-
-
-
-

Modified: trunk/liblwgeom/lwalgorithm.h
===================================================================
--- trunk/liblwgeom/lwalgorithm.h	2009-02-11 18:28:53 UTC (rev 3687)
+++ trunk/liblwgeom/lwalgorithm.h	2009-02-11 21:48:13 UTC (rev 3688)
@@ -46,7 +46,7 @@
 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);
+int lwgeom_geohash_precision(BOX3D bbox, BOX3D *bounds);
+char *lwgeom_geohash(const LWGEOM *lwgeom, int precision);
+char *geohash_point(double longitude, double latitude, int precision);
 

Modified: trunk/postgis/lwgeom_functions_basic.c
===================================================================
--- trunk/postgis/lwgeom_functions_basic.c	2009-02-11 18:28:53 UTC (rev 3687)
+++ trunk/postgis/lwgeom_functions_basic.c	2009-02-11 21:48:13 UTC (rev 3688)
@@ -23,6 +23,7 @@
 #include "utils/geo_decls.h"
 
 #include "liblwgeom.h"
+#include "lwalgorithm.h"
 #include "lwgeom_pg.h"
 #include "profile.h"
 
@@ -73,6 +74,7 @@
 Datum LWGEOM_affine(PG_FUNCTION_ARGS);
 Datum LWGEOM_longitude_shift(PG_FUNCTION_ARGS);
 Datum optimistic_overlap(PG_FUNCTION_ARGS);
+Datum ST_GeoHash(PG_FUNCTION_ARGS);
 
 void lwgeom_affine_ptarray(POINTARRAY *pa, double afac, double bfac, double cfac,
                            double dfac, double efac, double ffac, double gfac, double hfac, double ifac, double xoff, double yoff, double zoff);
@@ -3299,3 +3301,42 @@
 
 	PG_RETURN_POINTER(ret);
 }
+
+PG_FUNCTION_INFO_V1(ST_GeoHash);
+Datum ST_GeoHash(PG_FUNCTION_ARGS)
+{
+
+    PG_LWGEOM *geom = NULL;
+    int precision = 0;
+    int len = 0;
+    char *geohash = NULL;
+    char *result = NULL;
+
+    if( PG_ARGISNULL(0) )
+    {
+		PG_RETURN_NULL();
+    }
+
+    geom = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
+
+    if( ! PG_ARGISNULL(1) ) 
+    {
+        precision = PG_GETARG_INT32(1);
+    }
+
+    geohash = lwgeom_geohash((LWGEOM*)(pglwgeom_deserialize(geom)), precision);
+
+    if( ! geohash ) 
+    {
+		elog(ERROR,"ST_GeoHash: lwgeom_geohash returned NULL.\n");
+		PG_RETURN_NULL();
+    }
+
+	len = strlen(geohash) + VARHDRSZ;
+    result = palloc(len);
+	SET_VARSIZE(result, len);
+	memcpy(VARDATA(result), geohash, len-VARHDRSZ);
+	pfree(geohash);
+	PG_RETURN_POINTER(result);
+    
+}

Modified: trunk/postgis/postgis.sql.in.c
===================================================================
--- trunk/postgis/postgis.sql.in.c	2009-02-11 18:28:53 UTC (rev 3687)
+++ trunk/postgis/postgis.sql.in.c	2009-02-11 21:48:13 UTC (rev 3688)
@@ -4534,21 +4534,21 @@
 	AS 'SELECT _ST_AsGML(2, $1, 15)'
 	LANGUAGE 'SQL' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
 
--- Availabiltiy: 1.2.2
+-- Availability: 1.2.2
 CREATEFUNCTION ST_AsGML(geometry)
 	RETURNS TEXT
 	AS 'SELECT _ST_AsGML(2, $1, 15)'
 	LANGUAGE 'SQL' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
 
 -- ST_AsGML(version, geom) / precision=15 version=2
--- Availabiltiy: 1.3.2
+-- Availability: 1.3.2
 CREATEFUNCTION ST_AsGML(int4, geometry)
 	RETURNS TEXT
 	AS 'SELECT _ST_AsGML($1, $2, 15)'
 	LANGUAGE 'SQL' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
 
 -- ST_AsGML(version, geom, precision)
--- Availabiltiy: 1.3.2
+-- Availability: 1.3.2
 CREATEFUNCTION ST_AsGML(int4, geometry, int4)
 	RETURNS TEXT
 	AS 'SELECT _ST_AsGML($1, $2, $3)'
@@ -4590,21 +4590,21 @@
 	AS 'SELECT _ST_AsKML($1, transform($2,4326), $3)' 
 	LANGUAGE 'SQL' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
 
--- Availabiltiy: 1.2.2
+-- Availability: 1.2.2
 CREATEFUNCTION ST_AsKML(geometry)
 	RETURNS TEXT
 	AS 'SELECT _ST_AsKML(2, ST_Transform($1,4326), 15)'
 	LANGUAGE 'SQL' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
 
 -- ST_AsKML(version, geom) / precision=15 version=2
--- Availabiltiy: 1.3.2
+-- Availability: 1.3.2
 CREATEFUNCTION ST_AsKML(int4, geometry)
 	RETURNS TEXT
 	AS 'SELECT _ST_AsKML($1, ST_Transform($2,4326), 15)'
 	LANGUAGE 'SQL' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
 
 -- ST_AsKML(version, geom, precision)
--- Availabiltiy: 1.3.2
+-- Availability: 1.3.2
 CREATEFUNCTION ST_AsKML(int4, geometry, int4) 
 	RETURNS TEXT
 	AS 'SELECT _ST_AsKML($1, ST_Transform($2,4326), $3)' 
@@ -4612,7 +4612,7 @@
 
 -----------------------------------------------------------------------
 -- GEOJSON OUTPUT
--- Availabiltiy: 1.3.4
+-- Availability: 1.3.4
 -----------------------------------------------------------------------
 -- _ST_AsGeoJson(version, geom, precision, options)
 CREATEFUNCTION _ST_AsGeoJson(int4, geometry, int4, int4)
@@ -4656,7 +4656,22 @@
         AS 'SELECT _ST_AsGeoJson($1, $2, $3, $4)'
         LANGUAGE 'SQL' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
 
+------------------------------------------------------------------------
+-- GeoHash (geohash.org)
+------------------------------------------------------------------------
 
+-- Availability 1.4.0
+CREATEFUNCTION ST_GeoHash(geometry, int4)
+        RETURNS TEXT
+	    AS 'MODULE_PATHNAME', 'ST_GeoHash'
+        LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrictl,iscachable);
+
+-- Availability 1.4.0
+CREATEFUNCTION ST_GeoHash(geometry)
+        RETURNS TEXT
+        AS 'SELECT ST_GeoHash($1, 0)'
+        LANGUAGE 'SQL' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable);
+
 ------------------------------------------------------------------------
 -- OGC defined
 ------------------------------------------------------------------------



More information about the postgis-commits mailing list