PostGIS  3.7.0dev-r@@SVN_REVISION@@
postgis/lwgeom_transform.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * PostGIS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * PostGIS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18  *
19  **********************************************************************
20  *
21  * Copyright (C) 2001-2003 Refractions Research Inc.
22  *
23  **********************************************************************/
24 
25 
26 #include "postgres.h"
27 #include "fmgr.h"
28 #include "funcapi.h"
29 #include "utils/builtins.h"
30 
31 #include "../postgis_config.h"
32 #include "liblwgeom.h"
33 #include "lwgeodetic.h"
34 #include "stringbuffer.h"
35 #include "lwgeom_transform.h"
36 
37 
38 Datum transform(PG_FUNCTION_ARGS);
39 Datum transform_geom(PG_FUNCTION_ARGS);
40 Datum transform_pipeline_geom(PG_FUNCTION_ARGS);
41 Datum postgis_proj_version(PG_FUNCTION_ARGS);
42 Datum postgis_proj_compiled_version(PG_FUNCTION_ARGS);
43 Datum LWGEOM_asKML(PG_FUNCTION_ARGS);
44 
52 Datum transform(PG_FUNCTION_ARGS)
53 {
54  GSERIALIZED* geom;
55  GSERIALIZED* result=NULL;
56  LWGEOM* lwgeom;
57  LWPROJ *pj;
58  int32 srid_to, srid_from;
59 
60  srid_to = PG_GETARG_INT32(1);
61  if (srid_to == SRID_UNKNOWN)
62  {
63  elog(ERROR, "ST_Transform: %d is an invalid target SRID", SRID_UNKNOWN);
64  PG_RETURN_NULL();
65  }
66 
67  geom = PG_GETARG_GSERIALIZED_P_COPY(0);
68  srid_from = gserialized_get_srid(geom);
69 
70  if ( srid_from == SRID_UNKNOWN )
71  {
72  PG_FREE_IF_COPY(geom, 0);
73  elog(ERROR, "ST_Transform: Input geometry has unknown (%d) SRID", SRID_UNKNOWN);
74  PG_RETURN_NULL();
75  }
76 
77  /* Input SRID and output SRID are equal, noop */
78  if ( srid_from == srid_to )
79  PG_RETURN_POINTER(geom);
80 
81  postgis_initialize_cache();
82  if ( lwproj_lookup(srid_from, srid_to, &pj) == LW_FAILURE )
83  {
84  PG_FREE_IF_COPY(geom, 0);
85  elog(ERROR, "ST_Transform: Failure reading projections from spatial_ref_sys.");
86  PG_RETURN_NULL();
87  }
88 
89  /* now we have a geometry, and input/output PJ structs. */
90  lwgeom = lwgeom_from_gserialized(geom);
91  lwgeom_transform(lwgeom, pj);
92  lwgeom->srid = srid_to;
93 
94  /* Re-compute bbox if input had one (COMPUTE_BBOX TAINTING) */
95  if ( lwgeom->bbox )
96  {
97  lwgeom_refresh_bbox(lwgeom);
98  }
99 
100  result = geometry_serialize(lwgeom);
101  lwgeom_free(lwgeom);
102  PG_FREE_IF_COPY(geom, 0);
103 
104  PG_RETURN_POINTER(result); /* new geometry */
105 }
106 
116 Datum transform_geom(PG_FUNCTION_ARGS)
117 {
118  GSERIALIZED *gser, *gser_result=NULL;
119  LWGEOM *geom;
120  char *input_srs, *output_srs;
121  int32 result_srid;
122  int rv;
123 
124  /* Take a copy, since we will be altering the coordinates */
125  gser = PG_GETARG_GSERIALIZED_P_COPY(0);
126 
127  /* Convert from text to cstring for libproj */
128  input_srs = text_to_cstring(PG_GETARG_TEXT_P(1));
129  output_srs = text_to_cstring(PG_GETARG_TEXT_P(2));
130  result_srid = PG_GETARG_INT32(3);
131 
132  /* now we have a geometry, and input/output PJ structs. */
133  geom = lwgeom_from_gserialized(gser);
134  rv = lwgeom_transform_from_str(geom, input_srs, output_srs);
135  pfree(input_srs);
136  pfree(output_srs);
137 
138  if (rv == LW_FAILURE)
139  {
140  elog(ERROR, "coordinate transformation failed");
141  PG_RETURN_NULL();
142  }
143 
144  /* Re-compute bbox if input had one (COMPUTE_BBOX TAINTING) */
145  geom->srid = result_srid;
146  if (geom->bbox)
147  lwgeom_refresh_bbox(geom);
148 
149  gser_result = geometry_serialize(geom);
150  lwgeom_free(geom);
151  PG_FREE_IF_COPY(gser, 0);
152 
153  PG_RETURN_POINTER(gser_result); /* new geometry */
154 }
155 
156 
162 Datum transform_pipeline_geom(PG_FUNCTION_ARGS)
163 {
164  GSERIALIZED *gser, *gser_result=NULL;
165  LWGEOM *geom;
166  char *input_pipeline;
167  bool is_forward;
168  int32 result_srid;
169  int rv;
170 
171  /* Take a copy, since we will be altering the coordinates */
172  gser = PG_GETARG_GSERIALIZED_P_COPY(0);
173 
174  /* Convert from text to cstring for libproj */
175  input_pipeline = text_to_cstring(PG_GETARG_TEXT_P(1));
176  is_forward = PG_GETARG_BOOL(2);
177  result_srid = PG_GETARG_INT32(3);
178 
179  geom = lwgeom_from_gserialized(gser);
180  rv = lwgeom_transform_pipeline(geom, input_pipeline, is_forward);
181  pfree(input_pipeline);
182 
183  if (rv == LW_FAILURE)
184  {
185  elog(ERROR, "coordinate transformation failed");
186  PG_RETURN_NULL();
187  }
188 
189  /* Re-compute bbox if input had one (COMPUTE_BBOX TAINTING) */
190  geom->srid = result_srid;
191  if (geom->bbox)
192  lwgeom_refresh_bbox(geom);
193 
194  gser_result = geometry_serialize(geom);
195  lwgeom_free(geom);
196  PG_FREE_IF_COPY(gser, 0);
197 
198  PG_RETURN_POINTER(gser_result); /* new geometry */
199 }
200 
201 
203 Datum postgis_proj_version(PG_FUNCTION_ARGS)
204 {
205  stringbuffer_t sb;
206 
207  PJ_INFO pji = proj_info();
208  stringbuffer_init(&sb);
209  stringbuffer_append(&sb, pji.version);
210 
211 #if POSTGIS_PROJ_VERSION >= 70100
212 
214  " NETWORK_ENABLED=%s",
215  proj_context_is_network_enabled(NULL) ? "ON" : "OFF");
216 
217  if (proj_context_get_url_endpoint(NULL))
218  stringbuffer_aprintf(&sb, " URL_ENDPOINT=%s", proj_context_get_url_endpoint(NULL));
219 
220  if (proj_context_get_user_writable_directory(NULL, 0))
221  stringbuffer_aprintf(&sb, " USER_WRITABLE_DIRECTORY=%s", proj_context_get_user_writable_directory(NULL, 0));
222 
223  if (proj_context_get_database_path(NULL))
224  stringbuffer_aprintf(&sb, " DATABASE_PATH=%s", proj_context_get_database_path(NULL));
225 
226 #endif
227 
228  PG_RETURN_POINTER(cstring_to_text(stringbuffer_getstring(&sb)));
229 }
230 
232 Datum postgis_proj_compiled_version(PG_FUNCTION_ARGS)
233 {
234  static char ver[64];
235  text *result;
236  sprintf(
237  ver,
238  "%d.%d.%d",
239  (POSTGIS_PROJ_VERSION/10000),
240  ((POSTGIS_PROJ_VERSION%10000)/100),
241  ((POSTGIS_PROJ_VERSION)%100)
242  );
243 
244  result = cstring_to_text(ver);
245  PG_RETURN_POINTER(result);
246 }
247 
252 Datum LWGEOM_asKML(PG_FUNCTION_ARGS)
253 {
254  LWGEOM *lwgeom;
255  lwvarlena_t *kml;
256  const char *default_prefix = ""; /* default prefix */
257  char *prefixbuf;
258  const char *prefix = default_prefix;
259  int32_t srid_from;
260  const int32_t srid_to = 4326;
261 
262  /* Get the geometry */
263  GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P_COPY(0);
264  int precision = PG_GETARG_INT32(1);
265  text *prefix_text = PG_GETARG_TEXT_P(2);
266  srid_from = gserialized_get_srid(geom);
267 
268  if ( srid_from == SRID_UNKNOWN )
269  {
270  PG_FREE_IF_COPY(geom, 0);
271  elog(ERROR, "ST_AsKML: Input geometry has unknown (%d) SRID", SRID_UNKNOWN);
272  PG_RETURN_NULL();
273  }
274 
275  /* Condition precision */
276  if (precision < 0)
277  precision = 0;
278 
279  if (VARSIZE_ANY_EXHDR(prefix_text) > 0)
280  {
281  /* +2 is one for the ':' and one for term null */
282  prefixbuf = palloc(VARSIZE_ANY_EXHDR(prefix_text)+2);
283  memcpy(prefixbuf, VARDATA(prefix_text),
284  VARSIZE_ANY_EXHDR(prefix_text));
285  /* add colon and null terminate */
286  prefixbuf[VARSIZE_ANY_EXHDR(prefix_text)] = ':';
287  prefixbuf[VARSIZE_ANY_EXHDR(prefix_text)+1] = '\0';
288  prefix = prefixbuf;
289  }
290 
291  lwgeom = lwgeom_from_gserialized(geom);
292 
293  if (srid_from != srid_to)
294  {
295  LWPROJ *pj;
296  if (lwproj_lookup(srid_from, srid_to, &pj) == LW_FAILURE)
297  {
298  PG_FREE_IF_COPY(geom, 0);
299  elog(ERROR, "ST_AsKML: Failure reading projections from spatial_ref_sys.");
300  PG_RETURN_NULL();
301  }
302  lwgeom_transform(lwgeom, pj);
303  }
304 
305  kml = lwgeom_to_kml2(lwgeom, precision, prefix);
306  if (kml)
307  PG_RETURN_TEXT_P(kml);
308  PG_RETURN_NULL();
309 }
310 
311 /********************************************************************************
312  * PROJ database reading functions
313  */
314 
315 struct srs_entry {
316  text* auth_name;
317  text* auth_code;
318  double sort;
319 };
320 
321 struct srs_data {
323  uint32_t num_entries;
324  uint32_t capacity;
325  uint32_t current_entry;
326 };
327 
328 static int
329 srs_entry_cmp (const void *a, const void *b)
330 {
331  const struct srs_entry *entry_a = (const struct srs_entry*)(a);
332  const struct srs_entry *entry_b = (const struct srs_entry*)(b);
333  if (entry_a->sort < entry_b->sort) return -1;
334  else if (entry_a->sort > entry_b->sort) return 1;
335  else return 0;
336 }
337 
338 static Datum
339 srs_tuple_from_entry(const struct srs_entry* entry, TupleDesc tuple_desc)
340 {
341  HeapTuple tuple;
342  Datum tuple_data[7] = {0, 0, 0, 0, 0, 0, 0};
343  bool tuple_null[7] = {true, true, true, true, true, true, true};
344  PJ_CONTEXT * ctx = NULL;
345  const char * const empty_options[2] = {NULL};
346  const char * const wkt_options[2] = {"MULTILINE=NO", NULL};
347  const char * srtext;
348  const char * proj4text;
349  const char * srname;
350  double w_lon, s_lat, e_lon, n_lat;
351  int ok;
352 
353  PJ *obj = proj_create_from_database(ctx,
354  text_to_cstring(entry->auth_name),
355  text_to_cstring(entry->auth_code),
356  PJ_CATEGORY_CRS, 0, empty_options);
357 
358  if (!obj)
359  return (Datum) 0;
360 
361  srtext = proj_as_wkt(ctx, obj, PJ_WKT1_GDAL, wkt_options);
362  proj4text = proj_as_proj_string(ctx, obj, PJ_PROJ_4, empty_options);
363  srname = proj_get_name(obj);
364  ok = proj_get_area_of_use(ctx, obj, &w_lon, &s_lat, &e_lon, &n_lat, NULL);
365 
366  if (entry->auth_name) {
367  tuple_data[0] = PointerGetDatum(entry->auth_name);
368  tuple_null[0] = false;
369  }
370 
371  if (entry->auth_code) {
372  tuple_data[1] = PointerGetDatum(entry->auth_code);
373  tuple_null[1] = false;
374  }
375 
376  if (srname) {
377  tuple_data[2] = PointerGetDatum(cstring_to_text(srname));
378  tuple_null[2] = false;
379  }
380 
381  if (srtext) {
382  tuple_data[3] = PointerGetDatum(cstring_to_text(srtext));
383  tuple_null[3] = false;
384  }
385 
386  if (proj4text) {
387  tuple_data[4] = PointerGetDatum(cstring_to_text(proj4text));
388  tuple_null[4] = false;
389  }
390 
391  if (ok) {
392  LWPOINT *p_sw = lwpoint_make2d(4326, w_lon, s_lat);
393  LWPOINT *p_ne = lwpoint_make2d(4326, e_lon, n_lat);
394  GSERIALIZED *g_sw = geometry_serialize((LWGEOM*)p_sw);
395  GSERIALIZED *g_ne = geometry_serialize((LWGEOM*)p_ne);
396  tuple_data[5] = PointerGetDatum(g_sw);
397  tuple_null[5] = false;
398  tuple_data[6] = PointerGetDatum(g_ne);
399  tuple_null[6] = false;
400  }
401 
402  tuple = heap_form_tuple(tuple_desc, tuple_data, tuple_null);
403  proj_destroy(obj);
404 
405  return HeapTupleGetDatum(tuple);
406 }
407 
408 static struct srs_data *
410 {
411  struct srs_data *state = palloc0(sizeof(*state));
412  state->capacity = 8192;
413  state->num_entries = 0;
414  state->entries = palloc0(state->capacity * sizeof(*(state->entries)));
415  return state;
416 }
417 
418 static void
420 {
421  if (state->num_entries == state->capacity)
422  {
423  state->capacity *= 2;
424  state->entries = repalloc(state->entries, state->capacity * sizeof(*(state->entries)));
425  }
426  return;
427 }
428 
429 static void
430 srs_state_codes(const char* auth_name, struct srs_data *state)
431 {
432  /*
433  * Only a subset of supported proj types actually
434  * show up in spatial_ref_sys
435  */
436  #define ntypes 3
437  PJ_TYPE types[ntypes] = {PJ_TYPE_PROJECTED_CRS, PJ_TYPE_GEOGRAPHIC_CRS, PJ_TYPE_COMPOUND_CRS};
438  uint32_t j;
439 
440  for (j = 0; j < ntypes; j++)
441  {
442  PJ_CONTEXT *ctx = NULL;
443  int allow_deprecated = 0;
444  PJ_TYPE type = types[j];
445  PROJ_STRING_LIST codes_ptr = proj_get_codes_from_database(ctx, auth_name, type, allow_deprecated);
446  PROJ_STRING_LIST codes = codes_ptr;
447  const char *code;
448  while(codes && *codes)
449  {
450  /* Read current code and move forward one entry */
451  code = *codes++;
452  /* Ensure there is space in the entry list */
453  srs_state_memcheck(state);
454 
455  /* Write the entry into the entry list and increment */
456  state->entries[state->num_entries].auth_name = cstring_to_text(auth_name);
457  state->entries[state->num_entries].auth_code = cstring_to_text(code);
458  state->num_entries++;
459  }
460  /* Clean up system allocated memory */
461  proj_string_list_destroy(codes_ptr);
462  }
463 }
464 
465 static void
466 srs_find_planar(const char *auth_name, const LWGEOM *bounds, struct srs_data *state)
467 {
468  int32_t srid_from = lwgeom_get_srid(bounds);
469  const int32_t srid_to = 4326;
470  GBOX gbox = *(lwgeom_get_bbox(bounds));
471  PJ_TYPE types[1] = {PJ_TYPE_PROJECTED_CRS};
472  PROJ_CRS_INFO **crs_list_ptr, **crs_list;
473  int crs_count;
474  PJ_CONTEXT *ctx = NULL;
475 
476  PROJ_CRS_LIST_PARAMETERS *params = proj_get_crs_list_parameters_create();
477  params->types = types;
478  params->typesCount = 1;
479  params->crs_area_of_use_contains_bbox = true;
480  params->bbox_valid = true;
481  params->allow_deprecated = false;
482 
483 #if POSTGIS_PROJ_VERSION >= 80100
484  params->celestial_body_name = "Earth";
485 #endif
486 
487  if (srid_from != srid_to)
488  {
489  LWPROJ *pj;
490  if (lwproj_lookup(srid_from, srid_to, &pj) == LW_FAILURE)
491  elog(ERROR, "%s: Lookup of SRID %u => %u transform failed",
492  __func__, srid_from, srid_to);
493 
494  box3d_transform(&gbox, pj);
495  }
496 
497  params->west_lon_degree = gbox.xmin;
498  params->south_lat_degree = gbox.ymin;
499  params->east_lon_degree = gbox.xmax;
500  params->north_lat_degree = gbox.ymax;
501 
502  crs_list = crs_list_ptr = proj_get_crs_info_list_from_database(
503  ctx, auth_name, params, &crs_count);
504 
505  /* TODO, throw out really huge / dumb areas? */
506 
507  while (crs_list && *crs_list)
508  {
509  /* Read current crs and move forward one entry */
510  PROJ_CRS_INFO *crs = *crs_list++;
511 
512  /* Read the corners of the CRS area of use */
513  double area;
514  double height = crs->north_lat_degree - crs->south_lat_degree;
515  double width = crs->east_lon_degree - crs->west_lon_degree;
516  if (width < 0.0)
517  width = 360 - (crs->west_lon_degree - crs->east_lon_degree);
518  area = width * height;
519 
520  /* Ensure there is space in the entry list */
521  srs_state_memcheck(state);
522 
523  /* Write the entry into the entry list and increment */
524  state->entries[state->num_entries].auth_name = cstring_to_text(crs->auth_name);
525  state->entries[state->num_entries].auth_code = cstring_to_text(crs->code);
526  state->entries[state->num_entries].sort = area;
527  state->num_entries++;
528  }
529 
530  /* Put the list of entries into order of area size, smallest to largest */
531  qsort(state->entries, state->num_entries, sizeof(struct srs_data), srs_entry_cmp);
532 
533  proj_crs_info_list_destroy(crs_list_ptr);
534  proj_get_crs_list_parameters_destroy(params);
535 }
536 
541 Datum postgis_srs_entry(PG_FUNCTION_ARGS);
543 Datum postgis_srs_entry(PG_FUNCTION_ARGS)
544 {
545  Datum result;
546  struct srs_entry entry;
547  text* auth_name = PG_GETARG_TEXT_P(0);
548  text* auth_code = PG_GETARG_TEXT_P(1);
549  TupleDesc tuple_desc;
550 
551  if (get_call_result_type(fcinfo, 0, &tuple_desc) != TYPEFUNC_COMPOSITE)
552  {
553  ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
554  errmsg("%s called with incompatible return type", __func__)));
555  }
556  BlessTupleDesc(tuple_desc);
557 
558  entry.auth_name = auth_name;
559  entry.auth_code = auth_code;
560  result = srs_tuple_from_entry(&entry, tuple_desc);
561 
562  if (result)
563  PG_RETURN_DATUM(srs_tuple_from_entry(&entry, tuple_desc));
564  else
565  PG_RETURN_NULL();
566 }
567 
568 
569 Datum postgis_srs_entry_all(PG_FUNCTION_ARGS);
571 Datum postgis_srs_entry_all(PG_FUNCTION_ARGS)
572 {
573  FuncCallContext *funcctx;
574  MemoryContext oldcontext;
575  struct srs_data *state;
576  Datum result;
577 
578  /*
579  * On the first call, fill in the state with all
580  * of the auth_name/auth_srid pairings in the
581  * proj database. Then the per-call routine is just
582  * one isolated call per pair.
583  */
584  if (SRF_IS_FIRSTCALL())
585  {
586  funcctx = SRF_FIRSTCALL_INIT();
587  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
588 
589  /*
590  * Could read all authorities from database, but includes
591  * authorities (IGN, OGC) that use non-integral values in
592  * auth_srid. So hand-coded list for now.
593  */
594  state = srs_state_init();
595  srs_state_codes("EPSG", state);
596  srs_state_codes("ESRI", state);
597  srs_state_codes("IAU_2015", state);
598 
599  /*
600  * Read the TupleDesc from the FunctionCallInfo. The SQL definition
601  * of the function must have the right number of fields and types
602  * to match up to this C code.
603  */
604  if (get_call_result_type(fcinfo, 0, &funcctx->tuple_desc) != TYPEFUNC_COMPOSITE)
605  {
606  ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
607  errmsg("%s called with incompatible return type", __func__)));
608  }
609 
610  BlessTupleDesc(funcctx->tuple_desc);
611  funcctx->user_fctx = state;
612  MemoryContextSwitchTo(oldcontext);
613  }
614 
615  /* Stuff done on every call of the function */
616  funcctx = SRF_PERCALL_SETUP();
617  state = funcctx->user_fctx;
618 
619  /* Exit when we've read all entries */
620  if (!state->num_entries || state->current_entry == state->num_entries)
621  {
622  SRF_RETURN_DONE(funcctx);
623  }
624 
625  /* Lookup the srtext/proj4text for this entry */
627  state->entries + state->current_entry++,
628  funcctx->tuple_desc);
629 
630  if (result)
631  SRF_RETURN_NEXT(funcctx, result);
632 
633  /* Stop if lookup fails drastically */
634  SRF_RETURN_DONE(funcctx);
635 }
636 
637 
638 Datum postgis_srs_codes(PG_FUNCTION_ARGS);
640 Datum postgis_srs_codes(PG_FUNCTION_ARGS)
641 {
642  FuncCallContext *funcctx;
643  MemoryContext oldcontext;
644  struct srs_data *state;
645  Datum result;
646  text* auth_name = PG_GETARG_TEXT_P(0);
647  text* auth_code;
648 
649  /*
650  * On the first call, fill in the state with all
651  * of the auth_name/auth_srid pairings in the
652  * proj database. Then the per-call routine is just
653  * one isolated call per pair.
654  */
655  if (SRF_IS_FIRSTCALL())
656  {
657  /*
658  * Only a subset of supported proj types actually
659  * show up in spatial_ref_sys
660  */
661  funcctx = SRF_FIRSTCALL_INIT();
662  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
663  state = srs_state_init();
664  srs_state_codes(text_to_cstring(auth_name), state);
665  funcctx->user_fctx = state;
666  MemoryContextSwitchTo(oldcontext);
667  }
668 
669  /* Stuff done on every call of the function */
670  funcctx = SRF_PERCALL_SETUP();
671  state = funcctx->user_fctx;
672 
673  /* Exit when we've read all entries */
674  if (!state->num_entries || state->current_entry == state->num_entries)
675  {
676  SRF_RETURN_DONE(funcctx);
677  }
678 
679  /* Read the code for this entry */
680  auth_code = state->entries[state->current_entry++].auth_code;
681  result = PointerGetDatum(auth_code);
682 
683  /* We are returning setof(text) */
684  if (result)
685  SRF_RETURN_NEXT(funcctx, result);
686 
687  /* Stop if lookup fails drastically */
688  SRF_RETURN_DONE(funcctx);
689  SRF_RETURN_DONE(funcctx);
690 }
691 
692 
697 Datum postgis_srs_search(PG_FUNCTION_ARGS);
699 Datum postgis_srs_search(PG_FUNCTION_ARGS)
700 {
701  FuncCallContext *funcctx;
702  MemoryContext oldcontext;
703  struct srs_data *state;
704  Datum result;
705 
706  /*
707  * On the first call, fill in the state with all
708  * of the auth_name/auth_srid pairings in the
709  * proj database. Then the per-call routine is just
710  * one isolated call per pair.
711  */
712  if (SRF_IS_FIRSTCALL())
713  {
714  GSERIALIZED *gbounds = PG_GETARG_GSERIALIZED_P(0);
715  LWGEOM *bounds = lwgeom_from_gserialized(gbounds);
716  text *auth_name = PG_GETARG_TEXT_P(1);
717 
718  funcctx = SRF_FIRSTCALL_INIT();
719  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
720 
721  /*
722  * Could read all authorities from database, but includes
723  * authorities (IGN, OGC) that use non-integral values in
724  * auth_srid. So hand-coded list for now.
725  */
726  state = srs_state_init();
727 
728  /* Run the Proj query */
729  srs_find_planar(text_to_cstring(auth_name), bounds, state);
730 
731  /*
732  * Read the TupleDesc from the FunctionCallInfo. The SQL definition
733  * of the function must have the right number of fields and types
734  * to match up to this C code.
735  */
736  if (get_call_result_type(fcinfo, 0, &funcctx->tuple_desc) != TYPEFUNC_COMPOSITE)
737  {
738  ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
739  errmsg("%s called with incompatible return type", __func__)));
740  }
741 
742  BlessTupleDesc(funcctx->tuple_desc);
743  funcctx->user_fctx = state;
744  MemoryContextSwitchTo(oldcontext);
745  }
746 
747  /* Stuff done on every call of the function */
748  funcctx = SRF_PERCALL_SETUP();
749  state = funcctx->user_fctx;
750 
751  /* Exit when we've read all entries */
752  if (!state->num_entries ||
753  state->current_entry == state->num_entries)
754  {
755  SRF_RETURN_DONE(funcctx);
756  }
757 
758  /* Lookup the srtext/proj4text for this entry */
760  state->entries + state->current_entry++,
761  funcctx->tuple_desc);
762 
763  if (result)
764  SRF_RETURN_NEXT(funcctx, result);
765 
766  /* Stop if lookup fails drastically */
767  SRF_RETURN_DONE(funcctx);
768 }
769 
770 
static uint8_t precision
Definition: cu_in_twkb.c:25
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:267
int32_t gserialized_get_srid(const GSERIALIZED *g)
Extract the SRID from the serialized form (it is packed into three bytes so this is a handy function)...
Definition: gserialized.c:155
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
Definition: gserialized.c:268
void lwgeom_refresh_bbox(LWGEOM *lwgeom)
Drop current bbox and calculate a fresh one.
Definition: lwgeom.c:707
lwvarlena_t * lwgeom_to_kml2(const LWGEOM *geom, int precision, const char *prefix)
Definition: lwout_kml.c:44
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition: lwgeom.c:927
LWPOINT * lwpoint_make2d(int32_t srid, double x, double y)
Definition: lwpoint.c:163
#define LW_FAILURE
Definition: liblwgeom.h:96
int lwgeom_transform(LWGEOM *geom, LWPROJ *pj)
Transform (reproject) a geometry in-place.
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1218
int box3d_transform(GBOX *box, LWPROJ *pj)
int lwgeom_transform_from_str(LWGEOM *geom, const char *instr, const char *outstr)
const GBOX * lwgeom_get_bbox(const LWGEOM *lwgeom)
Get a non-empty geometry bounding box, computing and caching it if not already there.
Definition: lwgeom.c:743
int lwgeom_transform_pipeline(LWGEOM *geom, const char *pipeline, bool is_forward)
Transform (reproject) a geometry in-place using a PROJ pipeline.
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:215
This library is the generic geometry handling section of PostGIS.
type
Definition: ovdump.py:42
static struct srs_data * srs_state_init()
static void srs_state_memcheck(struct srs_data *state)
Datum postgis_proj_compiled_version(PG_FUNCTION_ARGS)
Datum postgis_srs_entry(PG_FUNCTION_ARGS)
Search for srtext and proj4text given auth_name and auth_srid, returns TABLE(auth_name text,...
static void srs_state_codes(const char *auth_name, struct srs_data *state)
Datum transform_geom(PG_FUNCTION_ARGS)
#define ntypes
Datum postgis_srs_search(PG_FUNCTION_ARGS)
Search for projections given extent and (optional) auth_name returns TABLE(auth_name,...
Datum transform(PG_FUNCTION_ARGS)
Datum postgis_proj_version(PG_FUNCTION_ARGS)
Datum postgis_srs_entry_all(PG_FUNCTION_ARGS)
static Datum srs_tuple_from_entry(const struct srs_entry *entry, TupleDesc tuple_desc)
static int srs_entry_cmp(const void *a, const void *b)
Datum transform_pipeline_geom(PG_FUNCTION_ARGS)
Datum LWGEOM_asKML(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(transform)
transform( GEOMETRY, INT (output srid) ) tmpPts - if there is a nadgrid error (-38),...
static void srs_find_planar(const char *auth_name, const LWGEOM *bounds, struct srs_data *state)
Datum postgis_srs_codes(PG_FUNCTION_ARGS)
unsigned int int32
Definition: shpopen.c:54
#define POSTGIS_PROJ_VERSION
Definition: sqldefines.h:12
int stringbuffer_aprintf(stringbuffer_t *s, const char *fmt,...)
Appends a formatted string to the current string buffer, using the format and argument list provided.
Definition: stringbuffer.c:254
void stringbuffer_init(stringbuffer_t *s)
Definition: stringbuffer.c:54
const char * stringbuffer_getstring(stringbuffer_t *s)
Returns a reference to the internal string being managed by the stringbuffer.
Definition: stringbuffer.c:125
static void stringbuffer_append(stringbuffer_t *s, const char *a)
Append the specified string to the stringbuffer_t.
Definition: stringbuffer.h:105
double ymax
Definition: liblwgeom.h:357
double xmax
Definition: liblwgeom.h:355
double ymin
Definition: liblwgeom.h:356
double xmin
Definition: liblwgeom.h:354
GBOX * bbox
Definition: liblwgeom.h:458
int32_t srid
Definition: liblwgeom.h:460
struct srs_entry * entries
text * auth_name
text * auth_code
double sort