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