PostGIS  3.3.9dev-r@@SVN_REVISION@@
liblwgeom/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 "../postgis_config.h"
27 #include "liblwgeom_internal.h"
28 #include "lwgeom_log.h"
29 #include <string.h>
30 
32 static void
34 {
35  pt->x *= M_PI/180.0;
36  pt->y *= M_PI/180.0;
37 }
38 
40 static void
42 {
43  pt->x *= 180.0/M_PI;
44  pt->y *= 180.0/M_PI;
45 }
46 
47 /***************************************************************************/
48 
49 #if POSTGIS_PROJ_VERSION < 61
50 
51 static int
52 point4d_transform(POINT4D *pt, LWPROJ *pj)
53 {
54  POINT3D orig_pt = {pt->x, pt->y, pt->z}; /* Copy for error report*/
55 
56  if (pj_is_latlong(pj->pj_from)) to_rad(pt) ;
57 
58  LWDEBUGF(4, "transforming POINT(%f %f) from '%s' to '%s'",
59  orig_pt.x, orig_pt.y, pj_get_def(pj->pj_from,0), pj_get_def(pj->pj_to,0));
60 
61  if (pj_transform(pj->pj_from, pj->pj_to, 1, 0, &(pt->x), &(pt->y), &(pt->z)) != 0)
62  {
63  int pj_errno_val = *pj_get_errno_ref();
64  if (pj_errno_val == -38)
65  {
66  lwnotice("PostGIS was unable to transform the point because either no grid"
67  " shift files were found, or the point does not lie within the "
68  "range for which the grid shift is defined. Refer to the "
69  "ST_Transform() section of the PostGIS manual for details on how "
70  "to configure PostGIS to alter this behaviour.");
71  lwerror("transform: couldn't project point (%g %g %g): %s (%d)",
72  orig_pt.x, orig_pt.y, orig_pt.z,
73  pj_strerrno(pj_errno_val), pj_errno_val);
74  }
75  else
76  {
77  lwerror("transform: %s (%d)",
78  pj_strerrno(pj_errno_val), pj_errno_val);
79  }
80  return LW_FAILURE;
81  }
82 
83  if (pj_is_latlong(pj->pj_to)) to_dec(pt);
84  return LW_SUCCESS;
85 }
86 
91 int
93 {
94  uint32_t i;
95  POINT4D p;
96 
97  for ( i = 0; i < pa->npoints; i++ )
98  {
99  getPoint4d_p(pa, i, &p);
100  if ( ! point4d_transform(&p, pj) ) return LW_FAILURE;
101  ptarray_set_point4d(pa, i, &p);
102  }
103 
104  return LW_SUCCESS;
105 }
106 
107 int
108 lwgeom_transform_from_str(LWGEOM *geom, const char* instr, const char* outstr)
109 {
110  char *pj_errstr;
111  int rv;
112  LWPROJ pj;
113 
114  pj.pj_from = projpj_from_string(instr);
115  if (!pj.pj_from)
116  {
117  pj_errstr = pj_strerrno(*pj_get_errno_ref());
118  if (!pj_errstr) pj_errstr = "";
119  lwerror("could not parse proj string '%s'", instr);
120  return LW_FAILURE;
121  }
122 
123  pj.pj_to = projpj_from_string(outstr);
124  if (!pj.pj_to)
125  {
126  pj_free(pj.pj_from);
127  pj_errstr = pj_strerrno(*pj_get_errno_ref());
128  if (!pj_errstr) pj_errstr = "";
129  lwerror("could not parse proj string '%s'", outstr);
130  return LW_FAILURE;
131  }
132 
133  rv = lwgeom_transform(geom, &pj);
134  pj_free(pj.pj_from);
135  pj_free(pj.pj_to);
136  return rv;
137 }
138 
139 
140 
141 projPJ
142 projpj_from_string(const char *str1)
143 {
144  if (!str1 || str1[0] == '\0')
145  {
146  return NULL;
147  }
148  return pj_init_plus(str1);
149 }
150 
151 /***************************************************************************/
152 
153 #else /* POSTGIS_PROJ_VERION >= 61 */
154 
155 LWPROJ *
156 lwproj_from_str(const char* str_in, const char* str_out)
157 {
158  uint8_t source_is_latlong = LW_FALSE;
159  double semi_major_metre = DBL_MAX, semi_minor_metre = DBL_MAX;
160 
161  /* Usable inputs? */
162  if (! (str_in && str_out))
163  return NULL;
164 
165  PJ* pj = proj_create_crs_to_crs(PJ_DEFAULT_CTX, str_in, str_out, NULL);
166  if (!pj)
167  return NULL;
168 
169  /* Fill in geodetic parameter information when a null-transform */
170  /* is passed, because that's how we signal we want to store */
171  /* that info in the cache */
172  if (strcmp(str_in, str_out) == 0)
173  {
174  PJ *pj_source_crs = proj_get_source_crs(PJ_DEFAULT_CTX, pj);
175  PJ *pj_ellps;
176  PJ_TYPE pj_type = proj_get_type(pj_source_crs);
177  if (pj_type == PJ_TYPE_UNKNOWN)
178  {
179  proj_destroy(pj);
180  lwerror("%s: unable to access source crs type", __func__);
181  return NULL;
182  }
183  source_is_latlong = (pj_type == PJ_TYPE_GEOGRAPHIC_2D_CRS) || (pj_type == PJ_TYPE_GEOGRAPHIC_3D_CRS);
184 
185  pj_ellps = proj_get_ellipsoid(PJ_DEFAULT_CTX, pj_source_crs);
186  proj_destroy(pj_source_crs);
187  if (!pj_ellps)
188  {
189  proj_destroy(pj);
190  lwerror("%s: unable to access source crs ellipsoid", __func__);
191  return NULL;
192  }
193  if (!proj_ellipsoid_get_parameters(PJ_DEFAULT_CTX,
194  pj_ellps,
195  &semi_major_metre,
196  &semi_minor_metre,
197  NULL,
198  NULL))
199  {
200  proj_destroy(pj_ellps);
201  proj_destroy(pj);
202  lwerror("%s: unable to access source crs ellipsoid parameters", __func__);
203  return NULL;
204  }
205  proj_destroy(pj_ellps);
206  }
207 
208  /* Add in an axis swap if necessary */
209  PJ* pj_norm = proj_normalize_for_visualization(PJ_DEFAULT_CTX, pj);
210  /* Swap failed for some reason? Fall back to coordinate operation */
211  if (!pj_norm)
212  pj_norm = pj;
213  /* Swap is not a copy of input? Clean up input */
214  else if (pj != pj_norm)
215  proj_destroy(pj);
216 
217  /* Allocate and populate return value */
218  LWPROJ *lp = lwalloc(sizeof(LWPROJ));
219  lp->pj = pj_norm; /* Caller is going to have to explicitly proj_destroy this */
220  lp->source_is_latlong = source_is_latlong;
221  lp->source_semi_major_metre = semi_major_metre;
222  lp->source_semi_minor_metre = semi_minor_metre;
223  return lp;
224 }
225 
226 int
227 lwgeom_transform_from_str(LWGEOM *geom, const char* instr, const char* outstr)
228 {
229  LWPROJ *lp = lwproj_from_str(instr, outstr);
230  if (!lp)
231  {
232  PJ *pj_in = proj_create(PJ_DEFAULT_CTX, instr);
233  if (!pj_in)
234  {
235  proj_errno_reset(NULL);
236  lwerror("could not parse proj string '%s'", instr);
237  }
238  proj_destroy(pj_in);
239 
240  PJ *pj_out = proj_create(PJ_DEFAULT_CTX, outstr);
241  if (!pj_out)
242  {
243  proj_errno_reset(NULL);
244  lwerror("could not parse proj string '%s'", outstr);
245  }
246  proj_destroy(pj_out);
247  lwerror("%s: Failed to transform", __func__);
248  return LW_FAILURE;
249  }
250  int ret = lwgeom_transform(geom, lp);
251  proj_destroy(lp->pj);
252  lwfree(lp);
253  return ret;
254 }
255 
256 int
258 {
259  uint32_t i;
260  POINT4D p;
261  size_t n_converted;
262  size_t n_points = pa->npoints;
263  size_t point_size = ptarray_point_size(pa);
264  int has_z = ptarray_has_z(pa);
265  double *pa_double = (double*)(pa->serialized_pointlist);
266 
267  /* Convert to radians if necessary */
268  if (proj_angular_input(pj->pj, PJ_FWD))
269  {
270  for (i = 0; i < pa->npoints; i++)
271  {
272  getPoint4d_p(pa, i, &p);
273  to_rad(&p);
274  }
275  }
276 
277  if (n_points == 1)
278  {
279  /* For single points it's faster to call proj_trans */
280  PJ_XYZT v = {pa_double[0], pa_double[1], has_z ? pa_double[2] : 0.0, 0.0};
281  PJ_COORD c;
282  c.xyzt = v;
283  PJ_COORD t = proj_trans(pj->pj, PJ_FWD, c);
284 
285  int pj_errno_val = proj_errno_reset(pj->pj);
286  if (pj_errno_val)
287  {
288  lwerror("transform: %s (%d)", proj_errno_string(pj_errno_val), pj_errno_val);
289  return LW_FAILURE;
290  }
291  pa_double[0] = (t.xyzt).x;
292  pa_double[1] = (t.xyzt).y;
293  if (has_z)
294  pa_double[2] = (t.xyzt).z;
295  }
296  else
297  {
298  /*
299  * size_t proj_trans_generic(PJ *P, PJ_DIRECTION direction,
300  * double *x, size_t sx, size_t nx,
301  * double *y, size_t sy, size_t ny,
302  * double *z, size_t sz, size_t nz,
303  * double *t, size_t st, size_t nt)
304  */
305 
306  n_converted = proj_trans_generic(pj->pj,
307  PJ_FWD,
308  pa_double,
309  point_size,
310  n_points, /* X */
311  pa_double + 1,
312  point_size,
313  n_points, /* Y */
314  has_z ? pa_double + 2 : NULL,
315  has_z ? point_size : 0,
316  has_z ? n_points : 0, /* Z */
317  NULL,
318  0,
319  0 /* M */
320  );
321 
322  if (n_converted != n_points)
323  {
324  lwerror("ptarray_transform: converted (%d) != input (%d)", n_converted, n_points);
325  return LW_FAILURE;
326  }
327 
328  int pj_errno_val = proj_errno_reset(pj->pj);
329  if (pj_errno_val)
330  {
331  lwerror("transform: %s (%d)", proj_errno_string(pj_errno_val), pj_errno_val);
332  return LW_FAILURE;
333  }
334  }
335 
336  /* Convert radians to degrees if necessary */
337  if (proj_angular_output(pj->pj, PJ_FWD))
338  {
339  for (i = 0; i < pa->npoints; i++)
340  {
341  getPoint4d_p(pa, i, &p);
342  to_dec(&p);
343  }
344  }
345 
346  return LW_SUCCESS;
347 }
348 
349 #endif
350 
355 int
357 {
358  uint32_t i;
359 
360  /* No points to transform in an empty! */
361  if ( lwgeom_is_empty(geom) )
362  return LW_SUCCESS;
363 
364  switch(geom->type)
365  {
366  case POINTTYPE:
367  case LINETYPE:
368  case CIRCSTRINGTYPE:
369  case TRIANGLETYPE:
370  {
371  LWLINE *g = (LWLINE*)geom;
372  if ( ! ptarray_transform(g->points, pj) ) return LW_FAILURE;
373  break;
374  }
375  case POLYGONTYPE:
376  {
377  LWPOLY *g = (LWPOLY*)geom;
378  for ( i = 0; i < g->nrings; i++ )
379  {
380  if ( ! ptarray_transform(g->rings[i], pj) ) return LW_FAILURE;
381  }
382  break;
383  }
384  case MULTIPOINTTYPE:
385  case MULTILINETYPE:
386  case MULTIPOLYGONTYPE:
387  case COLLECTIONTYPE:
388  case COMPOUNDTYPE:
389  case CURVEPOLYTYPE:
390  case MULTICURVETYPE:
391  case MULTISURFACETYPE:
393  case TINTYPE:
394  {
395  LWCOLLECTION *g = (LWCOLLECTION*)geom;
396  for ( i = 0; i < g->ngeoms; i++ )
397  {
398  if ( ! lwgeom_transform(g->geoms[i], pj) ) return LW_FAILURE;
399  }
400  break;
401  }
402  default:
403  {
404  lwerror("lwgeom_transform: Cannot handle type '%s'",
405  lwtype_name(geom->type));
406  return LW_FAILURE;
407  }
408  }
409  return LW_SUCCESS;
410 }
int ptarray_transform(POINTARRAY *pa, LWPROJ *pj)
int lwgeom_transform(LWGEOM *geom, LWPROJ *pj)
Transform given LWGEOM geometry from inpj projection to outpj projection.
LWPROJ * lwproj_from_str(const char *str_in, const char *str_out)
Allocate a new LWPROJ containing the reference to the PROJ's PJ If extra_geography_data is true,...
int lwgeom_transform_from_str(LWGEOM *geom, const char *instr, const char *outstr)
static void to_rad(POINT4D *pt)
convert decimal degress to radians
static void to_dec(POINT4D *pt)
convert radians to decimal degress
#define LW_FALSE
Definition: liblwgeom.h:109
#define COLLECTIONTYPE
Definition: liblwgeom.h:123
#define COMPOUNDTYPE
Definition: liblwgeom.h:125
#define LW_FAILURE
Definition: liblwgeom.h:111
#define CURVEPOLYTYPE
Definition: liblwgeom.h:126
#define MULTILINETYPE
Definition: liblwgeom.h:121
#define MULTISURFACETYPE
Definition: liblwgeom.h:128
#define LINETYPE
Definition: liblwgeom.h:118
#define LW_SUCCESS
Definition: liblwgeom.h:112
#define MULTIPOINTTYPE
Definition: liblwgeom.h:120
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:117
#define TINTYPE
Definition: liblwgeom.h:131
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:122
void lwfree(void *mem)
Definition: lwutil.c:242
#define POLYGONTYPE
Definition: liblwgeom.h:119
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:129
#define CIRCSTRINGTYPE
Definition: liblwgeom.h:124
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
int getPoint4d_p(const POINTARRAY *pa, uint32_t n, POINT4D *point)
Definition: lwgeom_api.c:126
#define MULTICURVETYPE
Definition: liblwgeom.h:127
#define TRIANGLETYPE
Definition: liblwgeom.h:130
void * lwalloc(size_t size)
Definition: lwutil.c:227
void ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition: lwgeom_api.c:370
int ptarray_has_z(const POINTARRAY *pa)
Definition: ptarray.c:37
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:88
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:190
void lwnotice(const char *fmt,...)
Write a notice out to the notice handler.
Definition: lwutil.c:177
static size_t ptarray_point_size(const POINTARRAY *pa)
Definition: lwinline.h:58
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition: lwinline.h:203
uint32_t ngeoms
Definition: liblwgeom.h:595
LWGEOM ** geoms
Definition: liblwgeom.h:590
uint8_t type
Definition: liblwgeom.h:477
POINTARRAY * points
Definition: liblwgeom.h:498
POINTARRAY ** rings
Definition: liblwgeom.h:534
uint32_t nrings
Definition: liblwgeom.h:539
uint8_t source_is_latlong
Definition: liblwgeom.h:63
double source_semi_major_metre
Definition: liblwgeom.h:65
double source_semi_minor_metre
Definition: liblwgeom.h:66
PJ * pj
Definition: liblwgeom.h:61
double z
Definition: liblwgeom.h:417
double x
Definition: liblwgeom.h:417
double y
Definition: liblwgeom.h:417
double x
Definition: liblwgeom.h:429
double z
Definition: liblwgeom.h:429
double y
Definition: liblwgeom.h:429
uint32_t npoints
Definition: liblwgeom.h:442
uint8_t * serialized_pointlist
Definition: liblwgeom.h:449