Datum transform_geom(PG_FUNCTION_ARGS) { GSERIALIZED *geom; GSERIALIZED *result=NULL; LWGEOM *lwgeom; projPJ input_pj, output_pj; char *input_proj4, *output_proj4; text *input_proj4_text; text *output_proj4_text; int32 result_srid ; char *pj_errstr; result_srid = PG_GETARG_INT32(3); if (result_srid == SRID_UNKNOWN) { elog(ERROR,"tranform: destination SRID = %d",SRID_UNKNOWN); PG_RETURN_NULL(); } geom = (GSERIALIZED *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0)); if (gserialized_get_srid(geom) == SRID_UNKNOWN) { pfree(geom); elog(ERROR,"transform_geom: source SRID = %d",SRID_UNKNOWN); PG_RETURN_NULL(); } /* Set the search path if we haven't already */ SetPROJ4LibPath(); /* Read the arguments */ input_proj4_text = (PG_GETARG_TEXT_P(1)); output_proj4_text = (PG_GETARG_TEXT_P(2)); /* Convert from text to cstring for libproj */ input_proj4 = text2cstring(input_proj4_text); output_proj4 = text2cstring(output_proj4_text); /* make input and output projection objects */ input_pj = lwproj_from_string(input_proj4); if ( input_pj == NULL ) { pj_errstr = pj_strerrno(*pj_get_errno_ref()); if ( ! pj_errstr ) pj_errstr = ""; /* we need this for error reporting */ /* pfree(input_proj4); */ pfree(output_proj4); pfree(geom); elog(ERROR, "transform_geom: could not parse proj4 string '%s' %s", input_proj4, pj_errstr); PG_RETURN_NULL(); } pfree(input_proj4); output_pj = lwproj_from_string(output_proj4); if ( output_pj == NULL ) { pj_errstr = pj_strerrno(*pj_get_errno_ref()); if ( ! pj_errstr ) pj_errstr = ""; /* we need this for error reporting */ /* pfree(output_proj4); */ pj_free(input_pj); pfree(geom); elog(ERROR, "transform_geom: couldn't parse proj4 output string: '%s': %s", output_proj4, pj_errstr); PG_RETURN_NULL(); } pfree(output_proj4); /* now we have a geometry, and input/output PJ structs. */ lwgeom = lwgeom_from_gserialized(geom); lwgeom_transform(lwgeom, input_pj, output_pj); lwgeom->srid = result_srid; /* clean up */ pj_free(input_pj); pj_free(output_pj); /* Re-compute bbox if input had one (COMPUTE_BBOX TAINTING) */ if ( lwgeom->bbox ) { lwgeom_drop_bbox(lwgeom); lwgeom_add_bbox(lwgeom); } result = geometry_serialize(lwgeom); lwgeom_free(lwgeom); PG_FREE_IF_COPY(geom, 0); PG_RETURN_POINTER(result); /* new geometry */ }
/** * Add an entry to the local PROJ4 SRS cache. If we need to wrap around then * we must make sure the entry we choose to delete does not contain other_srid * which is the definition for the other half of the transformation. */ static void AddToPROJ4SRSCache(PROJ4PortalCache *PROJ4Cache, int srid, int other_srid) { MemoryContext PJMemoryContext; projPJ projection = NULL; char *proj_str; int* pj_errno_ref; /* ** Turn the SRID number into a proj4 string, by reading from spatial_ref_sys ** or instantiating a magical value from a negative srid. */ proj_str = GetProj4String(srid); if ( ! proj_str ) { elog(ERROR, "GetProj4String returned NULL for SRID (%d)", srid); } projection = lwproj_from_string(proj_str); pj_errno_ref = pj_get_errno_ref(); if ( (projection == NULL) || (*pj_errno_ref)) { /* we need this for error reporting */ /*pfree(projection); */ elog(ERROR, "AddToPROJ4SRSCache: couldn't parse proj4 string: '%s': %s", proj_str, pj_strerrno(*pj_errno_ref)); } /* * If the cache is already full then find the first entry * that doesn't contain other_srid and use this as the * subsequent value of PROJ4SRSCacheCount */ if (PROJ4Cache->PROJ4SRSCacheCount == PROJ4_CACHE_ITEMS) { bool found = false; int i; for (i = 0; i < PROJ4_CACHE_ITEMS; i++) { if (PROJ4Cache->PROJ4SRSCache[i].srid != other_srid && found == false) { POSTGIS_DEBUGF(3, "choosing to remove item from query cache with SRID %d and index %d", PROJ4Cache->PROJ4SRSCache[i].srid, i); DeleteFromPROJ4SRSCache(PROJ4Cache, PROJ4Cache->PROJ4SRSCache[i].srid); PROJ4Cache->PROJ4SRSCacheCount = i; found = true; } } } /* * Now create a memory context for this projection and * store it in the backend hash */ POSTGIS_DEBUGF(3, "adding SRID %d with proj4text \"%s\" to query cache at index %d", srid, proj_str, PROJ4Cache->PROJ4SRSCacheCount); PJMemoryContext = MemoryContextCreate(T_AllocSetContext, 8192, &PROJ4SRSCacheContextMethods, PROJ4Cache->PROJ4SRSCacheContext, "PostGIS PROJ4 PJ Memory Context"); /* Create the backend hash if it doesn't already exist */ if (!PJHash) PJHash = CreatePJHash(); /* * Add the MemoryContext to the backend hash so we can * clean up upon portal shutdown */ POSTGIS_DEBUGF(3, "adding projection object (%p) to hash table with MemoryContext key (%p)", projection, PJMemoryContext); AddPJHashEntry(PJMemoryContext, projection); PROJ4Cache->PROJ4SRSCache[PROJ4Cache->PROJ4SRSCacheCount].srid = srid; PROJ4Cache->PROJ4SRSCache[PROJ4Cache->PROJ4SRSCacheCount].projection = projection; PROJ4Cache->PROJ4SRSCache[PROJ4Cache->PROJ4SRSCacheCount].projection_mcxt = PJMemoryContext; PROJ4Cache->PROJ4SRSCacheCount++; /* Free the projection string */ pfree(proj_str); }