PostGIS  3.4.0dev-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 LWPROJ *
50 lwproj_from_str(const char* str_in, const char* str_out)
51 {
52  uint8_t source_is_latlong = LW_FALSE;
53  double semi_major_metre = DBL_MAX, semi_minor_metre = DBL_MAX;
54 
55  /* Usable inputs? */
56  if (! (str_in && str_out))
57  return NULL;
58 
59  PJ* pj = proj_create_crs_to_crs(PJ_DEFAULT_CTX, str_in, str_out, NULL);
60  if (!pj)
61  return NULL;
62 
63  /* Fill in geodetic parameter information when a null-transform */
64  /* is passed, because that's how we signal we want to store */
65  /* that info in the cache */
66  if (strcmp(str_in, str_out) == 0)
67  {
68  PJ *pj_source_crs = proj_get_source_crs(PJ_DEFAULT_CTX, pj);
69  PJ *pj_ellps;
70  PJ_TYPE pj_type = proj_get_type(pj_source_crs);
71  if (pj_type == PJ_TYPE_UNKNOWN)
72  {
73  proj_destroy(pj);
74  lwerror("%s: unable to access source crs type", __func__);
75  return NULL;
76  }
77  source_is_latlong = (pj_type == PJ_TYPE_GEOGRAPHIC_2D_CRS) || (pj_type == PJ_TYPE_GEOGRAPHIC_3D_CRS);
78 
79  pj_ellps = proj_get_ellipsoid(PJ_DEFAULT_CTX, pj_source_crs);
80  proj_destroy(pj_source_crs);
81  if (!pj_ellps)
82  {
83  proj_destroy(pj);
84  lwerror("%s: unable to access source crs ellipsoid", __func__);
85  return NULL;
86  }
87  if (!proj_ellipsoid_get_parameters(PJ_DEFAULT_CTX,
88  pj_ellps,
89  &semi_major_metre,
90  &semi_minor_metre,
91  NULL,
92  NULL))
93  {
94  proj_destroy(pj_ellps);
95  proj_destroy(pj);
96  lwerror("%s: unable to access source crs ellipsoid parameters", __func__);
97  return NULL;
98  }
99  proj_destroy(pj_ellps);
100  }
101 
102  /* Add in an axis swap if necessary */
103  PJ* pj_norm = proj_normalize_for_visualization(PJ_DEFAULT_CTX, pj);
104  /* Swap failed for some reason? Fall back to coordinate operation */
105  if (!pj_norm)
106  pj_norm = pj;
107  /* Swap is not a copy of input? Clean up input */
108  else if (pj != pj_norm)
109  proj_destroy(pj);
110 
111  /* Allocate and populate return value */
112  LWPROJ *lp = lwalloc(sizeof(LWPROJ));
113  lp->pj = pj_norm; /* Caller is going to have to explicitly proj_destroy this */
114  lp->pipeline_is_forward = true;
115  lp->source_is_latlong = source_is_latlong;
116  lp->source_semi_major_metre = semi_major_metre;
117  lp->source_semi_minor_metre = semi_minor_metre;
118  return lp;
119 }
120 
121 LWPROJ *
122 lwproj_from_str_pipeline(const char* str_pipeline, bool is_forward)
123 {
124  /* Usable inputs? */
125  if (!str_pipeline)
126  return NULL;
127 
128  PJ* pj = proj_create(PJ_DEFAULT_CTX, str_pipeline);
129  if (!pj)
130  return NULL;
131 
132  /* check we have a transform, not a crs */
133  if (proj_is_crs(pj))
134  return NULL;
135 
136  /* Add in an axis swap if necessary */
137  PJ* pj_norm = proj_normalize_for_visualization(PJ_DEFAULT_CTX, pj);
138  if (!pj_norm)
139  pj_norm = pj;
140  /* Swap is not a copy of input? Clean up input */
141  else if (pj != pj_norm)
142  proj_destroy(pj);
143 
144  /* Allocate and populate return value */
145  LWPROJ *lp = lwalloc(sizeof(LWPROJ));
146  lp->pj = pj_norm; /* Caller is going to have to explicitly proj_destroy this */
147  lp->pipeline_is_forward = is_forward;
148 
149  /* this is stuff for geography calculations; doesn't matter here */
151  lp->source_semi_major_metre = DBL_MAX;
152  lp->source_semi_minor_metre = DBL_MAX;
153  return lp;
154 }
155 
156 int
157 lwgeom_transform_from_str(LWGEOM *geom, const char* instr, const char* outstr)
158 {
159  LWPROJ *lp = lwproj_from_str(instr, outstr);
160  if (!lp)
161  {
162  PJ *pj_in = proj_create(PJ_DEFAULT_CTX, instr);
163  if (!pj_in)
164  {
165  proj_errno_reset(NULL);
166  lwerror("could not parse proj string '%s'", instr);
167  }
168  proj_destroy(pj_in);
169 
170  PJ *pj_out = proj_create(PJ_DEFAULT_CTX, outstr);
171  if (!pj_out)
172  {
173  proj_errno_reset(NULL);
174  lwerror("could not parse proj string '%s'", outstr);
175  }
176  proj_destroy(pj_out);
177  lwerror("%s: Failed to transform", __func__);
178  return LW_FAILURE;
179  }
180  int ret = lwgeom_transform(geom, lp);
181  proj_destroy(lp->pj);
182  lwfree(lp);
183  return ret;
184 }
185 
186 int
187 lwgeom_transform_pipeline(LWGEOM *geom, const char* pipelinestr, bool is_forward)
188 {
189  LWPROJ *lp = lwproj_from_str_pipeline(pipelinestr, is_forward);
190  if (!lp)
191  {
192  PJ *pj_in = proj_create(PJ_DEFAULT_CTX, pipelinestr);
193  if (!pj_in)
194  {
195  proj_errno_reset(NULL);
196  lwerror("could not parse coordinate operation '%s'", pipelinestr);
197  }
198  proj_destroy(pj_in);
199  lwerror("%s: Failed to transform", __func__);
200  return LW_FAILURE;
201  }
202  int ret = lwgeom_transform(geom, lp);
203  proj_destroy(lp->pj);
204  lwfree(lp);
205  return ret;
206 }
207 
208 int
210 {
211  POINT4D pt;
212  POINTARRAY *pa = ptarray_construct(0, 0, 4);
213  pt = (POINT4D){gbox->xmin, gbox->ymin, 0, 0};
214  ptarray_set_point4d(pa, 0, &pt);
215 
216  pt = (POINT4D){gbox->xmax, gbox->ymin, 0, 0};
217  ptarray_set_point4d(pa, 1, &pt);
218 
219  pt = (POINT4D){gbox->xmax, gbox->ymax, 0, 0};
220  ptarray_set_point4d(pa, 2, &pt);
221 
222  pt = (POINT4D){gbox->xmin, gbox->ymax, 0, 0};
223  ptarray_set_point4d(pa, 3, &pt);
224 
225  ptarray_transform(pa, pj);
226  return ptarray_calculate_gbox_cartesian(pa, gbox);
227 }
228 
229 int
231 {
232  uint32_t i;
233  POINT4D p;
234  size_t n_converted;
235  size_t n_points = pa->npoints;
236  size_t point_size = ptarray_point_size(pa);
237  int has_z = ptarray_has_z(pa);
238  double *pa_double = (double*)(pa->serialized_pointlist);
239 
240  PJ_DIRECTION direction = pj->pipeline_is_forward ? PJ_FWD : PJ_INV;
241 
242  /* Convert to radians if necessary */
243  if (proj_angular_input(pj->pj, direction))
244  {
245  for (i = 0; i < pa->npoints; i++)
246  {
247  getPoint4d_p(pa, i, &p);
248  to_rad(&p);
249  ptarray_set_point4d(pa, i, &p);
250  }
251  }
252 
253  if (n_points == 1)
254  {
255  /* For single points it's faster to call proj_trans */
256  PJ_XYZT v = {pa_double[0], pa_double[1], has_z ? pa_double[2] : 0.0, 0.0};
257  PJ_COORD c;
258  c.xyzt = v;
259  PJ_COORD t = proj_trans(pj->pj, direction, c);
260 
261  int pj_errno_val = proj_errno_reset(pj->pj);
262  if (pj_errno_val)
263  {
264  lwerror("transform: %s (%d)", proj_errno_string(pj_errno_val), pj_errno_val);
265  return LW_FAILURE;
266  }
267  pa_double[0] = (t.xyzt).x;
268  pa_double[1] = (t.xyzt).y;
269  if (has_z)
270  pa_double[2] = (t.xyzt).z;
271  }
272  else
273  {
274  /*
275  * size_t proj_trans_generic(PJ *P, PJ_DIRECTION direction,
276  * double *x, size_t sx, size_t nx,
277  * double *y, size_t sy, size_t ny,
278  * double *z, size_t sz, size_t nz,
279  * double *t, size_t st, size_t nt)
280  */
281 
282  n_converted = proj_trans_generic(pj->pj,
283  direction,
284  pa_double,
285  point_size,
286  n_points, /* X */
287  pa_double + 1,
288  point_size,
289  n_points, /* Y */
290  has_z ? pa_double + 2 : NULL,
291  has_z ? point_size : 0,
292  has_z ? n_points : 0, /* Z */
293  NULL,
294  0,
295  0 /* M */
296  );
297 
298  if (n_converted != n_points)
299  {
300  lwerror("ptarray_transform: converted (%d) != input (%d)", n_converted, n_points);
301  return LW_FAILURE;
302  }
303 
304  int pj_errno_val = proj_errno_reset(pj->pj);
305  if (pj_errno_val)
306  {
307  lwerror("transform: %s (%d)", proj_errno_string(pj_errno_val), pj_errno_val);
308  return LW_FAILURE;
309  }
310  }
311 
312  /* Convert radians to degrees if necessary */
313  if (proj_angular_output(pj->pj, direction))
314  {
315  for (i = 0; i < pa->npoints; i++)
316  {
317  getPoint4d_p(pa, i, &p);
318  to_dec(&p);
319  ptarray_set_point4d(pa, i, &p);
320  }
321  }
322 
323  return LW_SUCCESS;
324 }
325 
330 int
332 {
333  uint32_t i;
334 
335  /* No points to transform in an empty! */
336  if ( lwgeom_is_empty(geom) )
337  return LW_SUCCESS;
338 
339  switch(geom->type)
340  {
341  case POINTTYPE:
342  case LINETYPE:
343  case CIRCSTRINGTYPE:
344  case TRIANGLETYPE:
345  {
346  LWLINE *g = (LWLINE*)geom;
347  if ( ! ptarray_transform(g->points, pj) ) return LW_FAILURE;
348  break;
349  }
350  case POLYGONTYPE:
351  {
352  LWPOLY *g = (LWPOLY*)geom;
353  for ( i = 0; i < g->nrings; i++ )
354  {
355  if ( ! ptarray_transform(g->rings[i], pj) ) return LW_FAILURE;
356  }
357  break;
358  }
359  case MULTIPOINTTYPE:
360  case MULTILINETYPE:
361  case MULTIPOLYGONTYPE:
362  case COLLECTIONTYPE:
363  case COMPOUNDTYPE:
364  case CURVEPOLYTYPE:
365  case MULTICURVETYPE:
366  case MULTISURFACETYPE:
368  case TINTYPE:
369  {
370  LWCOLLECTION *g = (LWCOLLECTION*)geom;
371  for ( i = 0; i < g->ngeoms; i++ )
372  {
373  if ( ! lwgeom_transform(g->geoms[i], pj) ) return LW_FAILURE;
374  }
375  break;
376  }
377  default:
378  {
379  lwerror("lwgeom_transform: Cannot handle type '%s'",
380  lwtype_name(geom->type));
381  return LW_FAILURE;
382  }
383  }
384  return LW_SUCCESS;
385 }
int ptarray_calculate_gbox_cartesian(const POINTARRAY *pa, GBOX *gbox)
Calculate box (x/y) and add values to gbox.
Definition: gbox.c:601
int lwgeom_transform_pipeline(LWGEOM *geom, const char *pipelinestr, bool is_forward)
Transform (reproject) a geometry in-place using a PROJ pipeline.
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_pipeline(const char *str_pipeline, bool is_forward)
Allocate a new LWPROJ containing the reference to the PROJ's PJ using a PROJ pipeline definition:
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)
int box3d_transform(GBOX *gbox, LWPROJ *pj)
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:94
#define COLLECTIONTYPE
Definition: liblwgeom.h:108
#define COMPOUNDTYPE
Definition: liblwgeom.h:110
#define LW_FAILURE
Definition: liblwgeom.h:96
#define CURVEPOLYTYPE
Definition: liblwgeom.h:111
#define MULTILINETYPE
Definition: liblwgeom.h:106
#define MULTISURFACETYPE
Definition: liblwgeom.h:113
#define LINETYPE
Definition: liblwgeom.h:103
#define LW_SUCCESS
Definition: liblwgeom.h:97
#define MULTIPOINTTYPE
Definition: liblwgeom.h:105
POINTARRAY * ptarray_construct(char hasz, char hasm, uint32_t npoints)
Construct an empty pointarray, allocating storage and setting the npoints, but not filling in any inf...
Definition: ptarray.c:51
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
#define TINTYPE
Definition: liblwgeom.h:116
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
void lwfree(void *mem)
Definition: lwutil.c:242
#define POLYGONTYPE
Definition: liblwgeom.h:104
#define POLYHEDRALSURFACETYPE
Definition: liblwgeom.h:114
#define CIRCSTRINGTYPE
Definition: liblwgeom.h:109
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:125
#define MULTICURVETYPE
Definition: liblwgeom.h:112
#define TRIANGLETYPE
Definition: liblwgeom.h:115
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:369
int ptarray_has_z(const POINTARRAY *pa)
Definition: ptarray.c:37
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition: lwutil.c:190
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
double ymax
Definition: liblwgeom.h:357
double xmax
Definition: liblwgeom.h:355
double ymin
Definition: liblwgeom.h:356
double xmin
Definition: liblwgeom.h:354
uint32_t ngeoms
Definition: liblwgeom.h:580
LWGEOM ** geoms
Definition: liblwgeom.h:575
uint8_t type
Definition: liblwgeom.h:462
POINTARRAY * points
Definition: liblwgeom.h:483
POINTARRAY ** rings
Definition: liblwgeom.h:519
uint32_t nrings
Definition: liblwgeom.h:524
bool pipeline_is_forward
Definition: liblwgeom.h:49
uint8_t source_is_latlong
Definition: liblwgeom.h:52
double source_semi_major_metre
Definition: liblwgeom.h:54
double source_semi_minor_metre
Definition: liblwgeom.h:55
PJ * pj
Definition: liblwgeom.h:46
double x
Definition: liblwgeom.h:414
double y
Definition: liblwgeom.h:414
uint32_t npoints
Definition: liblwgeom.h:427
uint8_t * serialized_pointlist
Definition: liblwgeom.h:434