PostGIS 3.7.0dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
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
32static void
34{
35 pt->x *= M_PI/180.0;
36 pt->y *= M_PI/180.0;
37}
38
40static void
42{
43 pt->x *= 180.0/M_PI;
44 pt->y *= 180.0/M_PI;
45}
46
47/***************************************************************************/
48
49LWPROJ *
50lwproj_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
121LWPROJ *
122lwproj_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
156int
157lwgeom_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
186int
187lwgeom_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
208int
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
229int
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 (%zu) != input (%zu)", 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
330int
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:613
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.
int lwgeom_transform_from_str(LWGEOM *geom, const char *instr, const char *outstr)
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:
int box3d_transform(GBOX *gbox, LWPROJ *pj)
static void to_rad(POINT4D *pt)
convert decimal degrees to radians
static void to_dec(POINT4D *pt)
convert radians to decimal degrees
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,...
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition lwutil.c:216
#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
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition liblwgeom.h:102
void * lwalloc(size_t size)
Definition lwutil.c:227
#define TINTYPE
Definition liblwgeom.h:116
#define MULTIPOLYGONTYPE
Definition liblwgeom.h:107
void lwfree(void *mem)
Definition lwutil.c:248
#define POLYGONTYPE
Definition liblwgeom.h:104
#define POLYHEDRALSURFACETYPE
Definition liblwgeom.h:114
#define CIRCSTRINGTYPE
Definition liblwgeom.h:109
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 ptarray_set_point4d(POINTARRAY *pa, uint32_t n, const POINT4D *p4d)
Definition lwgeom_api.c:369
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
int ptarray_has_z(const POINTARRAY *pa)
Definition ptarray.c:37
void void lwerror(const char *fmt,...) __attribute__((format(printf
Write a notice out to the error handler.
static size_t ptarray_point_size(const POINTARRAY *pa)
Definition lwinline.h:56
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:199
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