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