diff -crN postgis.vanilla/lwgeom/lwgeom_transform.c postgis/lwgeom/lwgeom_transform.c *** postgis.vanilla/lwgeom/lwgeom_transform.c 2005-11-05 15:31:18.597177032 +0000 --- postgis/lwgeom/lwgeom_transform.c 2005-11-05 15:54:35.522812056 +0000 *************** *** 1,3 **** --- 1,14 ---- + /********************************************************************** + * $Id: lwgeom_transform.c,v 1.29.2.5 2005/09/08 19:26:10 strk Exp $ + * + * PostGIS - Spatial Types for PostgreSQL + * http://postgis.refractions.net + * Copyright 2001-2003 Refractions Research Inc. + * + * This is free software; you can redistribute and/or modify it under + * the terms of the GNU General Public Licence. See the COPYING file. + * + **********************************************************************/ #include #include *************** *** 11,27 **** #include "liblwgeom.h" #include "lwgeom_pg.h" Datum transform_geom(PG_FUNCTION_ARGS); Datum postgis_proj_version(PG_FUNCTION_ARGS); // if USE_PROJECTION undefined, we get a do-nothing transform() function #ifndef USE_PROJ PG_FUNCTION_INFO_V1(transform_geom); Datum transform_geom(PG_FUNCTION_ARGS) { ! elog(ERROR,"PostGIS transform() called, but support not compiled in. Modify your makefile to add proj support, remake and re-install"); PG_RETURN_NULL(); } --- 22,47 ---- #include "liblwgeom.h" #include "lwgeom_pg.h" + Datum transform(PG_FUNCTION_ARGS); Datum transform_geom(PG_FUNCTION_ARGS); Datum postgis_proj_version(PG_FUNCTION_ARGS); // if USE_PROJECTION undefined, we get a do-nothing transform() function #ifndef USE_PROJ + PG_FUNCTION_INFO_V1(transform); + Datum transform(PG_FUNCTION_ARGS) + { + + elog(ERROR,"PostGIS transform() called, but support not compiled in. Modify your makefile to add proj support, remake and re-install"); + PG_RETURN_NULL(); + } + PG_FUNCTION_INFO_V1(transform_geom); Datum transform_geom(PG_FUNCTION_ARGS) { ! elog(ERROR,"PostGIS transform_geom() called, but support not compiled in. Modify your makefile to add proj support, remake and re-install"); PG_RETURN_NULL(); } *************** *** 34,39 **** --- 54,61 ---- #else // defined USE_PROJ #include "projects.h" + #include "utils/memutils.h" + #include "executor/spi.h" PJ *make_project(char *str1); void to_rad(POINT2D *pt); *************** *** 42,47 **** --- 64,311 ---- int transform_point(POINT2D *pt, PJ *srcdefn, PJ *dstdefn); int lwgeom_transform_recursive(char *geom, PJ *inpj, PJ *outpj); + + // PROJ4 SRS Cache debugging + //#define PROJ4_CACHE_DEBUG 1 + + // PROJ 4 lookup transaction cache methods + #define PROJ4_CACHE_ITEMS 5 + + // An entry in the PROJ4 SRS cache + typedef struct struct_PROJ4SRSCacheItem + { + int srid; + PJ *projection; + } PROJ4SRSCacheItem; + + // The PROJ4 SRS cache itself + PROJ4SRSCacheItem PROJ4SRSCache[PROJ4_CACHE_ITEMS]; + int PROJ4SRSCacheCount = 0; + + // Cache API + bool IsInPROJ4SRSCache(int srid); + void AddToPROJ4SRSCache(int srid); + PJ *GetProjectionFromPROJ4SRSCache(int srid); + + // Cache memory context + MemoryContext PROJ4SRSCacheContext = NULL; + + static void PROJ4SRSCacheInit(MemoryContext context); + static void PROJ4SRSCacheDelete(MemoryContext context); + + + // Memory context definition must match the current version of PostgreSQL + #if USE_VERSION == 72 + static MemoryContextMethods PROJ4SRSCacheContextMethods = { + NULL, + NULL, + NULL, + PROJ4SRSCacheInit, + NULL, + PROJ4SRSCacheDelete + #ifdef MEMORY_CONTEXT_CHECKING + ,NULL + #endif + }; + #endif + + #if USE_VERSION == 73 || USE_VERSION == 74 + static MemoryContextMethods PROJ4SRSCacheContextMethods = { + NULL, + NULL, + NULL, + PROJ4SRSCacheInit, + NULL, + PROJ4SRSCacheDelete, + NULL, + NULL + #ifdef MEMORY_CONTEXT_CHECKING + ,NULL + #endif + }; + #endif + + #if USE_VERSION >= 80 + static MemoryContextMethods PROJ4SRSCacheContextMethods = { + NULL, + NULL, + NULL, + PROJ4SRSCacheInit, + NULL, + PROJ4SRSCacheDelete, + NULL, + NULL, + NULL + #ifdef MEMORY_CONTEXT_CHECKING + ,NULL + #endif + }; + #endif + + + static void + PROJ4SRSCacheInit(MemoryContext context) + { + int i; + + #if PROJ4_CACHE_DEBUG + elog(NOTICE, "Initialising query PROJ4 SRS cache"); + #endif + + // Store a srid of -1 and a pointer of NULL to show the slot is empty + for (i = 0; i < PROJ4_CACHE_ITEMS; i++) + { + PROJ4SRSCache[i].srid = -1; + PROJ4SRSCache[i].projection = NULL; + } + } + + static void + PROJ4SRSCacheDelete(MemoryContext context) + { + int i; + + #if PROJ4_CACHE_DEBUG + elog(NOTICE, "Destroying query PROJ4 SRS cache"); + #endif + + // Free all items that have been used + for (i = 0; i < PROJ4_CACHE_ITEMS; i++) + { + if (PROJ4SRSCache[i].projection != NULL) + pj_free(PROJ4SRSCache[i].projection); + + PROJ4SRSCache[i].srid = -1; + } + + // Reset the cache count back to zero + PROJ4SRSCacheCount = 0; + + // Remove the cache context so we rebuild it next time we execute transform + PROJ4SRSCacheContext = NULL; + } + + // Buffer for building up SPI query + char proj4_spi_buffer[256]; + + + // + // Cache management functions + // + + bool IsInPROJ4SRSCache(int srid) + { + // + // Return true/false depending upon whether the item is in the SRS cache + // + + int i; + + for (i = 0; i < PROJ4_CACHE_ITEMS; i++) + { + if (PROJ4SRSCache[i].srid == srid) + return true; + } + + // Otherwise not found + return false; + } + + + PJ *GetProjectionFromPROJ4SRSCache(int srid) + { + // + // Return the projection object from the cache (we should already have checked it exists + // using IsInPROJ4SRSCache first) + // + + int i; + + for (i = 0; i < PROJ4_CACHE_ITEMS; i++) + { + if (PROJ4SRSCache[i].srid == srid) + return PROJ4SRSCache[i].projection; + } + + return NULL; + } + + + void AddToPROJ4SRSCache(int srid) + { + // + // Add an entry to the local PROJ4 SRS cache + // + + int spi_result; + PJ *projection; + char *proj_str; + + // Connect + spi_result = SPI_connect(); + if (spi_result != SPI_OK_CONNECT) + { + elog(ERROR, "AddToPROJ4SRSCache: Could not connect to database using SPI"); + } + + // Execute the lookup query + snprintf(proj4_spi_buffer, 255, "SELECT proj4text FROM spatial_ref_sys WHERE srid = %d LIMIT 1", srid); + spi_result = SPI_execute(proj4_spi_buffer, false, 1); + + // Read back the PROJ4 text + if (spi_result == SPI_OK_SELECT && SPI_processed > 0) + { + // Select the first (and only tuple) + TupleDesc tupdesc = SPI_tuptable->tupdesc; + SPITupleTable *tuptable = SPI_tuptable; + HeapTuple tuple = tuptable->vals[0]; + + // Make a projection object out of it + proj_str = palloc(strlen(SPI_getvalue(tuple, tupdesc, 1))); + strcpy(proj_str, SPI_getvalue(tuple, tupdesc, 1)); + projection = make_project(proj_str); + + if ( (projection == NULL) || pj_errno) + { + //pfree(projection); // we need this for error reporting + elog(ERROR, "AddToPROJ4SRSCache: couldn't parse proj4 string: '%s': %s", proj_str, pj_strerrno(pj_errno)); + } + + // If the cache is already full then we replace the first entry + if (PROJ4SRSCacheCount == PROJ4_CACHE_ITEMS) + { + if (PROJ4SRSCache[0].projection != NULL) + pj_free(PROJ4SRSCache[0].projection); + + PROJ4SRSCacheCount = 0; + } + + // Store in the cache + #if PROJ4_CACHE_DEBUG + elog(NOTICE, "AddToPROJ4SRSCache: adding SRID %d with proj4text \"%s\" to query cache at index %d", srid, proj_str, PROJ4SRSCacheCount); + #endif + PROJ4SRSCache[PROJ4SRSCacheCount].srid = srid; + PROJ4SRSCache[PROJ4SRSCacheCount].projection = projection; + PROJ4SRSCacheCount++; + + // Free the projection string + pfree(proj_str); + } + else + { + elog(ERROR, "AddToPROJ4SRSCache: Cannot find SRID (%d) in spatial_ref_sys", srid); + } + + // Close the connection + spi_result = SPI_finish(); + if (spi_result != SPI_OK_FINISH) + { + elog(ERROR, "AddToPROJ4SRSCache: Could not disconnect from database using SPI"); + } + + } + + //this is *exactly* the same as PROJ.4's pj_transform(), but it doesnt do the // datum shift. int *************** *** 246,251 **** --- 510,598 ---- } + //tranform( GEOMETRY, INT (output srid) ) + // tmpPts - if there is a nadgrid error (-38), we re-try the transform on a copy of points. The transformed points + // are in an indeterminate state after the -38 error is thrown. + PG_FUNCTION_INFO_V1(transform); + Datum transform(PG_FUNCTION_ARGS) + { + PG_LWGEOM *geom; + PG_LWGEOM *result=NULL; + LWGEOM *lwgeom; + PJ *input_pj,*output_pj; + int32 result_srid ; + char *srl; + + + result_srid = PG_GETARG_INT32(1); + if (result_srid == -1) + { + elog(ERROR,"-1 is an invalid target SRID"); + PG_RETURN_NULL(); + } + + geom = (PG_LWGEOM *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)); + if (pglwgeom_getSRID(geom) == -1) + { + pfree(geom); + elog(ERROR,"Input geometry has unknown (-1) SRID"); + PG_RETURN_NULL(); + } + + // If no cache is present, create one as a child context for the current command (MessageContext) + if (PROJ4SRSCacheContext == NULL) + { + #if PROJ4_CACHE_DEBUG + elog(NOTICE, "Generating PROJ4 cache memory context"); + #endif + PROJ4SRSCacheContext = MemoryContextCreate(T_MemoryContext, 8192, + &PROJ4SRSCacheContextMethods, + QueryContext, + "PROJ4SRSCacheContext"); + } + + // Add the output srid to the cache if it's not already there + if (!IsInPROJ4SRSCache(result_srid)) + AddToPROJ4SRSCache(result_srid); + + // Get the output projection + output_pj = GetProjectionFromPROJ4SRSCache(result_srid); + + // Add the input srid to the cache if it's not already there + if (!IsInPROJ4SRSCache(pglwgeom_getSRID(geom))) + AddToPROJ4SRSCache(pglwgeom_getSRID(geom)); + + // Get the input projection + input_pj = GetProjectionFromPROJ4SRSCache(pglwgeom_getSRID(geom)); + + /* now we have a geometry, and input/output PJ structs. */ + lwgeom_transform_recursive(SERIALIZED_FORM(geom), + input_pj, output_pj); + + srl = SERIALIZED_FORM(geom); + + /* Re-compute bbox if input had one (COMPUTE_BBOX TAINTING) */ + if ( TYPE_HASBBOX(geom->type) ) + { + lwgeom = lwgeom_deserialize(srl); + lwgeom_dropBBOX(lwgeom); + lwgeom->bbox = lwgeom_compute_box2d(lwgeom); + lwgeom->SRID = result_srid; + result = pglwgeom_serialize(lwgeom); + if ( lwgeom->bbox ) lwfree(lwgeom->bbox); + lwgeom_release(lwgeom); + } + else + { + result = PG_LWGEOM_construct(srl, result_srid, 0); + } + + pfree(geom); + + PG_RETURN_POINTER(result); // new geometry + } + + //tranform_geom( GEOMETRY, TEXT (input proj4), TEXT (output proj4), INT (output srid) // tmpPts - if there is a nadgrid error (-38), we re-try the transform on a copy of points. The transformed points // are in an indeterminate state after the -38 error is thrown. *************** *** 344,349 **** --- 691,697 ---- PG_RETURN_POINTER(result); // new geometry } + PG_FUNCTION_INFO_V1(postgis_proj_version); Datum postgis_proj_version(PG_FUNCTION_ARGS) { diff -crN postgis.vanilla/lwgeom/lwpostgis.sql.in postgis/lwgeom/lwpostgis.sql.in *** postgis.vanilla/lwgeom/lwpostgis.sql.in 2005-11-05 15:31:18.604175968 +0000 --- postgis/lwgeom/lwpostgis.sql.in 2005-11-05 16:02:29.436766184 +0000 *************** *** 3059,3112 **** AS '@MODULE_FILENAME@','transform_geom' LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable); ! CREATEFUNCTION transform(geometry,integer) RETURNS geometry AS ! ' ! DECLARE ! iproj text := NULL; ! oproj text := NULL; ! isrid integer; ! osrid alias for $2; ! ret geometry; ! rec RECORD; ! BEGIN ! ! SELECT INTO isrid SRID($1); ! ! IF osrid = isrid THEN ! RETURN $1; ! END IF; ! ! IF osrid = -1 THEN ! RAISE EXCEPTION ''-1 is an invalid target SRID''; ! END IF; ! ! IF isrid = -1 THEN ! RAISE EXCEPTION ''Input geometry has unknown (-1) SRID''; ! END IF; ! ! FOR rec IN SELECT srid, proj4text FROM spatial_ref_sys ! WHERE srid = isrid OR srid = osrid ! LOOP ! IF rec.srid = isrid THEN ! iproj = rec.proj4text; ! ELSE ! oproj = rec.proj4text; ! END IF; ! END LOOP; ! ! IF iproj IS NULL THEN ! RAISE EXCEPTION ''Cannot find input SRID (%) in spatial_ref_sys'', isrid; ! END IF; ! ! IF oproj IS NULL THEN ! RAISE EXCEPTION ''Cannot find target SRID (%) in spatial_ref_sys'', osrid; ! END IF; ! ! RETURN transform_geometry($1, iproj, oproj, osrid); - END; - ' - LANGUAGE 'plpgsql' _IMMUTABLE_STRICT; -- WITH (iscachable,isstrict); ----------------------------------------------------------------------- -- POSTGIS_VERSION() --- 3059,3069 ---- AS '@MODULE_FILENAME@','transform_geom' LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable); ! CREATEFUNCTION transform(geometry,integer) ! RETURNS geometry ! AS '@MODULE_FILENAME@','transform' ! LANGUAGE 'C' _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable); ----------------------------------------------------------------------- -- POSTGIS_VERSION()