source: trunk/postgis/lwgeom_functions_basic.c

Last change on this file was 17876, checked in by Raul Marin, 5 years ago

Address compiler warnings

Closes #4527
Closes https://github.com/postgis/postgis/pull/490

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
  • Property svn:mime-type set to text/plain
File size: 77.0 KB
Line 
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 2001-2006 Refractions Research Inc.
22 * Copyright 2017-2018 Daniel Baston <dbaston@gmail.com>
23 *
24 **********************************************************************/
25
26#include "postgres.h"
27#include "fmgr.h"
28#include "utils/array.h"
29#include "utils/builtins.h"
30#include "utils/elog.h"
31#include "utils/geo_decls.h"
32
33#include "../postgis_config.h"
34#include "liblwgeom.h"
35#include "lwgeom_pg.h"
36
37#include <math.h>
38#include <float.h>
39#include <string.h>
40#include <stdio.h>
41
42Datum LWGEOM_mem_size(PG_FUNCTION_ARGS);
43Datum LWGEOM_summary(PG_FUNCTION_ARGS);
44Datum LWGEOM_npoints(PG_FUNCTION_ARGS);
45Datum LWGEOM_nrings(PG_FUNCTION_ARGS);
46Datum ST_Area(PG_FUNCTION_ARGS);
47Datum postgis_scripts_released(PG_FUNCTION_ARGS);
48Datum postgis_version(PG_FUNCTION_ARGS);
49Datum postgis_liblwgeom_version(PG_FUNCTION_ARGS);
50Datum postgis_lib_version(PG_FUNCTION_ARGS);
51Datum postgis_svn_version(PG_FUNCTION_ARGS);
52Datum postgis_libxml_version(PG_FUNCTION_ARGS);
53Datum postgis_lib_build_date(PG_FUNCTION_ARGS);
54Datum LWGEOM_length2d_linestring(PG_FUNCTION_ARGS);
55Datum LWGEOM_length_linestring(PG_FUNCTION_ARGS);
56Datum LWGEOM_perimeter2d_poly(PG_FUNCTION_ARGS);
57Datum LWGEOM_perimeter_poly(PG_FUNCTION_ARGS);
58
59Datum LWGEOM_maxdistance2d_linestring(PG_FUNCTION_ARGS);
60Datum ST_Distance(PG_FUNCTION_ARGS);
61Datum LWGEOM_closestpoint(PG_FUNCTION_ARGS);
62Datum LWGEOM_shortestline2d(PG_FUNCTION_ARGS);
63Datum LWGEOM_longestline2d(PG_FUNCTION_ARGS);
64Datum LWGEOM_dwithin(PG_FUNCTION_ARGS);
65Datum LWGEOM_dfullywithin(PG_FUNCTION_ARGS);
66
67Datum LWGEOM_maxdistance3d(PG_FUNCTION_ARGS);
68Datum LWGEOM_mindistance3d(PG_FUNCTION_ARGS);
69Datum LWGEOM_closestpoint3d(PG_FUNCTION_ARGS);
70Datum LWGEOM_shortestline3d(PG_FUNCTION_ARGS);
71Datum LWGEOM_longestline3d(PG_FUNCTION_ARGS);
72Datum LWGEOM_dwithin3d(PG_FUNCTION_ARGS);
73Datum LWGEOM_dfullywithin3d(PG_FUNCTION_ARGS);
74
75Datum LWGEOM_inside_circle_point(PG_FUNCTION_ARGS);
76Datum LWGEOM_collect(PG_FUNCTION_ARGS);
77Datum LWGEOM_collect_garray(PG_FUNCTION_ARGS);
78Datum LWGEOM_expand(PG_FUNCTION_ARGS);
79Datum LWGEOM_to_BOX(PG_FUNCTION_ARGS);
80Datum LWGEOM_envelope(PG_FUNCTION_ARGS);
81Datum LWGEOM_isempty(PG_FUNCTION_ARGS);
82Datum LWGEOM_segmentize2d(PG_FUNCTION_ARGS);
83Datum LWGEOM_reverse(PG_FUNCTION_ARGS);
84Datum LWGEOM_force_clockwise_poly(PG_FUNCTION_ARGS);
85Datum LWGEOM_force_sfs(PG_FUNCTION_ARGS);
86Datum LWGEOM_noop(PG_FUNCTION_ARGS);
87Datum LWGEOM_zmflag(PG_FUNCTION_ARGS);
88Datum LWGEOM_hasz(PG_FUNCTION_ARGS);
89Datum LWGEOM_hasm(PG_FUNCTION_ARGS);
90Datum LWGEOM_ndims(PG_FUNCTION_ARGS);
91Datum LWGEOM_makepoint(PG_FUNCTION_ARGS);
92Datum LWGEOM_makepoint3dm(PG_FUNCTION_ARGS);
93Datum LWGEOM_makeline_garray(PG_FUNCTION_ARGS);
94Datum LWGEOM_makeline(PG_FUNCTION_ARGS);
95Datum LWGEOM_makepoly(PG_FUNCTION_ARGS);
96Datum LWGEOM_line_from_mpoint(PG_FUNCTION_ARGS);
97Datum LWGEOM_addpoint(PG_FUNCTION_ARGS);
98Datum LWGEOM_removepoint(PG_FUNCTION_ARGS);
99Datum LWGEOM_setpoint_linestring(PG_FUNCTION_ARGS);
100Datum LWGEOM_asEWKT(PG_FUNCTION_ARGS);
101Datum LWGEOM_hasBBOX(PG_FUNCTION_ARGS);
102Datum LWGEOM_azimuth(PG_FUNCTION_ARGS);
103Datum LWGEOM_angle(PG_FUNCTION_ARGS);
104Datum LWGEOM_affine(PG_FUNCTION_ARGS);
105Datum LWGEOM_longitude_shift(PG_FUNCTION_ARGS);
106Datum optimistic_overlap(PG_FUNCTION_ARGS);
107Datum ST_GeoHash(PG_FUNCTION_ARGS);
108Datum ST_MakeEnvelope(PG_FUNCTION_ARGS);
109Datum ST_TileEnvelope(PG_FUNCTION_ARGS);
110Datum ST_CollectionExtract(PG_FUNCTION_ARGS);
111Datum ST_CollectionHomogenize(PG_FUNCTION_ARGS);
112Datum ST_IsCollection(PG_FUNCTION_ARGS);
113Datum ST_QuantizeCoordinates(PG_FUNCTION_ARGS);
114Datum ST_WrapX(PG_FUNCTION_ARGS);
115Datum LWGEOM_FilterByM(PG_FUNCTION_ARGS);
116
117/*------------------------------------------------------------------*/
118
119/** find the size of geometry */
120PG_FUNCTION_INFO_V1(LWGEOM_mem_size);
121Datum LWGEOM_mem_size(PG_FUNCTION_ARGS)
122{
123 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
124 size_t size = VARSIZE(geom);
125 PG_FREE_IF_COPY(geom, 0);
126 PG_RETURN_INT32(size);
127}
128
129/** get summary info on a GEOMETRY */
130PG_FUNCTION_INFO_V1(LWGEOM_summary);
131Datum LWGEOM_summary(PG_FUNCTION_ARGS)
132{
133 text *summary;
134 GSERIALIZED *g = PG_GETARG_GSERIALIZED_P(0);
135 LWGEOM *lwg = lwgeom_from_gserialized(g);
136 char *lwresult = lwgeom_summary(lwg, 0);
137 uint32_t gver = gserialized_get_version(g);
138 size_t result_sz = strlen(lwresult) + 8;
139 char *result;
140 if (gver == 0)
141 {
142 result = lwalloc(result_sz + 2);
143 snprintf(result, result_sz, "0:%s", lwresult);
144 }
145 else
146 {
147 result = lwalloc(result_sz);
148 snprintf(result, result_sz, "%s", lwresult);
149 }
150 lwgeom_free(lwg);
151 lwfree(lwresult);
152
153 /* create a text obj to return */
154 summary = cstring_to_text(result);
155 lwfree(result);
156
157 PG_FREE_IF_COPY(g, 0);
158 PG_RETURN_TEXT_P(summary);
159}
160
161PG_FUNCTION_INFO_V1(postgis_version);
162Datum postgis_version(PG_FUNCTION_ARGS)
163{
164 char *ver = POSTGIS_VERSION;
165 text *result = cstring_to_text(ver);
166 PG_RETURN_TEXT_P(result);
167}
168
169PG_FUNCTION_INFO_V1(postgis_liblwgeom_version);
170Datum postgis_liblwgeom_version(PG_FUNCTION_ARGS)
171{
172 const char *ver = lwgeom_version();
173 text *result = cstring_to_text(ver);
174 PG_RETURN_TEXT_P(result);
175}
176
177PG_FUNCTION_INFO_V1(postgis_lib_version);
178Datum postgis_lib_version(PG_FUNCTION_ARGS)
179{
180 char *ver = POSTGIS_LIB_VERSION;
181 text *result = cstring_to_text(ver);
182 PG_RETURN_TEXT_P(result);
183}
184
185PG_FUNCTION_INFO_V1(postgis_svn_version);
186Datum postgis_svn_version(PG_FUNCTION_ARGS)
187{
188 static int rev = POSTGIS_SVN_REVISION;
189 char ver[32];
190 if (rev > 0)
191 {
192 snprintf(ver, 32, "%d", rev);
193 PG_RETURN_TEXT_P(cstring_to_text(ver));
194 }
195 else
196 PG_RETURN_NULL();
197}
198
199PG_FUNCTION_INFO_V1(postgis_lib_build_date);
200Datum postgis_lib_build_date(PG_FUNCTION_ARGS)
201{
202 char *ver = POSTGIS_BUILD_DATE;
203 text *result = cstring_to_text(ver);
204 PG_RETURN_TEXT_P(result);
205}
206
207PG_FUNCTION_INFO_V1(postgis_scripts_released);
208Datum postgis_scripts_released(PG_FUNCTION_ARGS)
209{
210 char ver[64];
211 text *result;
212
213 snprintf(ver, 64, "%s r%d", POSTGIS_LIB_VERSION, POSTGIS_SVN_REVISION);
214 ver[63] = '\0';
215
216 result = cstring_to_text(ver);
217 PG_RETURN_TEXT_P(result);
218}
219
220PG_FUNCTION_INFO_V1(postgis_libxml_version);
221Datum postgis_libxml_version(PG_FUNCTION_ARGS)
222{
223 char *ver = POSTGIS_LIBXML2_VERSION;
224 text *result = cstring_to_text(ver);
225 PG_RETURN_TEXT_P(result);
226}
227
228/** number of points in an object */
229PG_FUNCTION_INFO_V1(LWGEOM_npoints);
230Datum LWGEOM_npoints(PG_FUNCTION_ARGS)
231{
232 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
233 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
234 int npoints = 0;
235
236 npoints = lwgeom_count_vertices(lwgeom);
237 lwgeom_free(lwgeom);
238
239 PG_FREE_IF_COPY(geom, 0);
240 PG_RETURN_INT32(npoints);
241}
242
243/** number of rings in an object */
244PG_FUNCTION_INFO_V1(LWGEOM_nrings);
245Datum LWGEOM_nrings(PG_FUNCTION_ARGS)
246{
247 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
248 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
249 int nrings = 0;
250
251 nrings = lwgeom_count_rings(lwgeom);
252 lwgeom_free(lwgeom);
253
254 PG_FREE_IF_COPY(geom, 0);
255 PG_RETURN_INT32(nrings);
256}
257
258/**
259 * @brief Calculate the area of all the subobj in a polygon
260 * area(point) = 0
261 * area (line) = 0
262 * area(polygon) = find its 2d area
263 */
264PG_FUNCTION_INFO_V1(ST_Area);
265Datum ST_Area(PG_FUNCTION_ARGS)
266{
267 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
268 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
269 double area = 0.0;
270
271 area = lwgeom_area(lwgeom);
272
273 lwgeom_free(lwgeom);
274 PG_FREE_IF_COPY(geom, 0);
275
276 PG_RETURN_FLOAT8(area);
277}
278
279/**
280 * @brief find the "length of a geometry"
281 * length2d(point) = 0
282 * length2d(line) = length of line
283 * length2d(polygon) = 0 -- could make sense to return sum(ring perimeter)
284 * uses euclidian 2d length (even if input is 3d)
285 */
286PG_FUNCTION_INFO_V1(LWGEOM_length2d_linestring);
287Datum LWGEOM_length2d_linestring(PG_FUNCTION_ARGS)
288{
289 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
290 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
291 double dist = lwgeom_length_2d(lwgeom);
292 lwgeom_free(lwgeom);
293 PG_FREE_IF_COPY(geom, 0);
294 PG_RETURN_FLOAT8(dist);
295}
296
297/**
298 * @brief find the "length of a geometry"
299 * length(point) = 0
300 * length(line) = length of line
301 * length(polygon) = 0 -- could make sense to return sum(ring perimeter)
302 * uses euclidian 3d/2d length depending on input dimensions.
303 */
304PG_FUNCTION_INFO_V1(LWGEOM_length_linestring);
305Datum LWGEOM_length_linestring(PG_FUNCTION_ARGS)
306{
307 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
308 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
309 double dist = lwgeom_length(lwgeom);
310 lwgeom_free(lwgeom);
311 PG_FREE_IF_COPY(geom, 0);
312 PG_RETURN_FLOAT8(dist);
313}
314
315/**
316 * @brief find the "perimeter of a geometry"
317 * perimeter(point) = 0
318 * perimeter(line) = 0
319 * perimeter(polygon) = sum of ring perimeters
320 * uses euclidian 3d/2d computation depending on input dimension.
321 */
322PG_FUNCTION_INFO_V1(LWGEOM_perimeter_poly);
323Datum LWGEOM_perimeter_poly(PG_FUNCTION_ARGS)
324{
325 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
326 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
327 double perimeter = 0.0;
328
329 perimeter = lwgeom_perimeter(lwgeom);
330 PG_FREE_IF_COPY(geom, 0);
331 PG_RETURN_FLOAT8(perimeter);
332}
333
334/**
335 * @brief find the "perimeter of a geometry"
336 * perimeter(point) = 0
337 * perimeter(line) = 0
338 * perimeter(polygon) = sum of ring perimeters
339 * uses euclidian 2d computation even if input is 3d
340 */
341PG_FUNCTION_INFO_V1(LWGEOM_perimeter2d_poly);
342Datum LWGEOM_perimeter2d_poly(PG_FUNCTION_ARGS)
343{
344 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
345 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
346 double perimeter = 0.0;
347
348 perimeter = lwgeom_perimeter_2d(lwgeom);
349 PG_FREE_IF_COPY(geom, 0);
350 PG_RETURN_FLOAT8(perimeter);
351}
352
353/* transform input geometry to 2d if not 2d already */
354PG_FUNCTION_INFO_V1(LWGEOM_force_2d);
355Datum LWGEOM_force_2d(PG_FUNCTION_ARGS)
356{
357 GSERIALIZED *pg_geom_in = PG_GETARG_GSERIALIZED_P(0);
358 GSERIALIZED *pg_geom_out;
359 LWGEOM *lwg_in, *lwg_out;
360
361 /* already 2d */
362 if (gserialized_ndims(pg_geom_in) == 2)
363 PG_RETURN_POINTER(pg_geom_in);
364
365 lwg_in = lwgeom_from_gserialized(pg_geom_in);
366 lwg_out = lwgeom_force_2d(lwg_in);
367 pg_geom_out = geometry_serialize(lwg_out);
368 lwgeom_free(lwg_out);
369 lwgeom_free(lwg_in);
370
371 PG_FREE_IF_COPY(pg_geom_in, 0);
372 PG_RETURN_POINTER(pg_geom_out);
373}
374
375/* transform input geometry to 3dz if not 3dz already */
376PG_FUNCTION_INFO_V1(LWGEOM_force_3dz);
377Datum LWGEOM_force_3dz(PG_FUNCTION_ARGS)
378{
379 GSERIALIZED *pg_geom_in = PG_GETARG_GSERIALIZED_P(0);
380 GSERIALIZED *pg_geom_out;
381 LWGEOM *lwg_in, *lwg_out;
382
383 /* already 3d */
384 if (gserialized_ndims(pg_geom_in) == 3 && gserialized_has_z(pg_geom_in))
385 PG_RETURN_POINTER(pg_geom_in);
386
387 lwg_in = lwgeom_from_gserialized(pg_geom_in);
388 lwg_out = lwgeom_force_3dz(lwg_in);
389 pg_geom_out = geometry_serialize(lwg_out);
390 lwgeom_free(lwg_out);
391 lwgeom_free(lwg_in);
392
393 PG_FREE_IF_COPY(pg_geom_in, 0);
394 PG_RETURN_POINTER(pg_geom_out);
395}
396
397/** transform input geometry to 3dm if not 3dm already */
398PG_FUNCTION_INFO_V1(LWGEOM_force_3dm);
399Datum LWGEOM_force_3dm(PG_FUNCTION_ARGS)
400{
401 GSERIALIZED *pg_geom_in = PG_GETARG_GSERIALIZED_P(0);
402 GSERIALIZED *pg_geom_out;
403 LWGEOM *lwg_in, *lwg_out;
404
405 /* already 3d */
406 if (gserialized_ndims(pg_geom_in) == 3 && gserialized_has_m(pg_geom_in))
407 PG_RETURN_POINTER(pg_geom_in);
408
409 lwg_in = lwgeom_from_gserialized(pg_geom_in);
410 lwg_out = lwgeom_force_3dm(lwg_in);
411 pg_geom_out = geometry_serialize(lwg_out);
412 lwgeom_free(lwg_out);
413 lwgeom_free(lwg_in);
414
415 PG_FREE_IF_COPY(pg_geom_in, 0);
416 PG_RETURN_POINTER(pg_geom_out);
417}
418
419/* transform input geometry to 4d if not 4d already */
420PG_FUNCTION_INFO_V1(LWGEOM_force_4d);
421Datum LWGEOM_force_4d(PG_FUNCTION_ARGS)
422{
423 GSERIALIZED *pg_geom_in = PG_GETARG_GSERIALIZED_P(0);
424 GSERIALIZED *pg_geom_out;
425 LWGEOM *lwg_in, *lwg_out;
426
427 /* already 4d */
428 if (gserialized_ndims(pg_geom_in) == 4)
429 PG_RETURN_POINTER(pg_geom_in);
430
431 lwg_in = lwgeom_from_gserialized(pg_geom_in);
432 lwg_out = lwgeom_force_4d(lwg_in);
433 pg_geom_out = geometry_serialize(lwg_out);
434 lwgeom_free(lwg_out);
435 lwgeom_free(lwg_in);
436
437 PG_FREE_IF_COPY(pg_geom_in, 0);
438 PG_RETURN_POINTER(pg_geom_out);
439}
440
441/** transform input geometry to a collection type */
442PG_FUNCTION_INFO_V1(LWGEOM_force_collection);
443Datum LWGEOM_force_collection(PG_FUNCTION_ARGS)
444{
445 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
446 GSERIALIZED *result;
447 LWGEOM **lwgeoms;
448 LWGEOM *lwgeom;
449 int32_t srid;
450 GBOX *bbox;
451
452 POSTGIS_DEBUG(2, "LWGEOM_force_collection called");
453
454 /*
455 * This funx is a no-op only if a bbox cache is already present
456 * in input. If bbox cache is not there we'll need to handle
457 * automatic bbox addition FOR_COMPLEX_GEOMS.
458 */
459 if (gserialized_get_type(geom) == COLLECTIONTYPE && gserialized_has_bbox(geom))
460 {
461 PG_RETURN_POINTER(geom);
462 }
463
464 /* deserialize into lwgeoms[0] */
465 lwgeom = lwgeom_from_gserialized(geom);
466
467 /* alread a multi*, just make it a collection */
468 if (lwgeom_is_collection(lwgeom))
469 {
470 lwgeom->type = COLLECTIONTYPE;
471 }
472
473 /* single geom, make it a collection */
474 else
475 {
476 srid = lwgeom->srid;
477 /* We transfer bbox ownership from input to output */
478 bbox = lwgeom->bbox;
479 lwgeom->srid = SRID_UNKNOWN;
480 lwgeom->bbox = NULL;
481 lwgeoms = palloc(sizeof(LWGEOM *));
482 lwgeoms[0] = lwgeom;
483 lwgeom = (LWGEOM *)lwcollection_construct(COLLECTIONTYPE, srid, bbox, 1, lwgeoms);
484 }
485
486 result = geometry_serialize(lwgeom);
487 lwgeom_free(lwgeom);
488
489 PG_FREE_IF_COPY(geom, 0);
490 PG_RETURN_POINTER(result);
491}
492
493/** transform input geometry to a multi* type */
494PG_FUNCTION_INFO_V1(LWGEOM_force_multi);
495Datum LWGEOM_force_multi(PG_FUNCTION_ARGS)
496{
497 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
498 GSERIALIZED *result;
499 LWGEOM *lwgeom;
500 LWGEOM *ogeom;
501
502 POSTGIS_DEBUG(2, "LWGEOM_force_multi called");
503
504 /*
505 ** This funx is a no-op only if a bbox cache is already present
506 ** in input. If bbox cache is not there we'll need to handle
507 ** automatic bbox addition FOR_COMPLEX_GEOMS.
508 */
509 if (gserialized_has_bbox(geom))
510 {
511 switch (gserialized_get_type(geom))
512 {
513 case MULTIPOINTTYPE:
514 case MULTILINETYPE:
515 case MULTIPOLYGONTYPE:
516 case COLLECTIONTYPE:
517 case MULTICURVETYPE:
518 case MULTISURFACETYPE:
519 case TINTYPE:
520 PG_RETURN_POINTER(geom);
521 default:
522 break;
523 }
524 }
525
526 /* deserialize into lwgeoms[0] */
527 lwgeom = lwgeom_from_gserialized(geom);
528 ogeom = lwgeom_as_multi(lwgeom);
529
530 result = geometry_serialize(ogeom);
531
532 PG_FREE_IF_COPY(geom, 0);
533
534 PG_RETURN_POINTER(result);
535}
536
537/** transform input geometry to a curved type */
538PG_FUNCTION_INFO_V1(LWGEOM_force_curve);
539Datum LWGEOM_force_curve(PG_FUNCTION_ARGS)
540{
541 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
542 GSERIALIZED *result;
543 LWGEOM *lwgeom;
544 LWGEOM *ogeom;
545
546 POSTGIS_DEBUG(2, "LWGEOM_force_curve called");
547
548 /* TODO: early out if input is already a curve */
549
550 lwgeom = lwgeom_from_gserialized(geom);
551 ogeom = lwgeom_as_curve(lwgeom);
552
553 result = geometry_serialize(ogeom);
554
555 PG_FREE_IF_COPY(geom, 0);
556
557 PG_RETURN_POINTER(result);
558}
559
560/** transform input geometry to a SFS 1.1 geometry type compliant */
561PG_FUNCTION_INFO_V1(LWGEOM_force_sfs);
562Datum LWGEOM_force_sfs(PG_FUNCTION_ARGS)
563{
564 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
565 GSERIALIZED *result;
566 LWGEOM *lwgeom;
567 LWGEOM *ogeom;
568 text *ver;
569 int version = 110; /* default version is SFS 1.1 */
570
571 POSTGIS_DEBUG(2, "LWGEOM_force_sfs called");
572
573 /* If user specified version, respect it */
574 if ((PG_NARGS() > 1) && (!PG_ARGISNULL(1)))
575 {
576 ver = PG_GETARG_TEXT_P(1);
577
578 if (!strncmp(VARDATA(ver), "1.2", 3))
579 {
580 version = 120;
581 }
582 }
583
584 lwgeom = lwgeom_from_gserialized(geom);
585 ogeom = lwgeom_force_sfs(lwgeom, version);
586
587 result = geometry_serialize(ogeom);
588
589 PG_FREE_IF_COPY(geom, 0);
590
591 PG_RETURN_POINTER(result);
592}
593
594/**
595Returns the point in first input geometry that is closest to the second input geometry in 2d
596*/
597
598PG_FUNCTION_INFO_V1(LWGEOM_closestpoint);
599Datum LWGEOM_closestpoint(PG_FUNCTION_ARGS)
600{
601 GSERIALIZED *result;
602 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
603 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
604 LWGEOM *point;
605 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
606 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
607 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
608
609 point = lwgeom_closest_point(lwgeom1, lwgeom2);
610
611 if (lwgeom_is_empty(point))
612 PG_RETURN_NULL();
613
614 result = geometry_serialize(point);
615 lwgeom_free(point);
616 lwgeom_free(lwgeom1);
617 lwgeom_free(lwgeom2);
618
619 PG_FREE_IF_COPY(geom1, 0);
620 PG_FREE_IF_COPY(geom2, 1);
621 PG_RETURN_POINTER(result);
622}
623
624/**
625Returns the shortest 2d line between two geometries
626*/
627PG_FUNCTION_INFO_V1(LWGEOM_shortestline2d);
628Datum LWGEOM_shortestline2d(PG_FUNCTION_ARGS)
629{
630 GSERIALIZED *result;
631 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
632 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
633 LWGEOM *theline;
634 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
635 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
636 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
637
638 theline = lwgeom_closest_line(lwgeom1, lwgeom2);
639
640 if (lwgeom_is_empty(theline))
641 PG_RETURN_NULL();
642
643 result = geometry_serialize(theline);
644 lwgeom_free(theline);
645 lwgeom_free(lwgeom1);
646 lwgeom_free(lwgeom2);
647
648 PG_FREE_IF_COPY(geom1, 0);
649 PG_FREE_IF_COPY(geom2, 1);
650 PG_RETURN_POINTER(result);
651}
652
653/**
654Returns the longest 2d line between two geometries
655*/
656PG_FUNCTION_INFO_V1(LWGEOM_longestline2d);
657Datum LWGEOM_longestline2d(PG_FUNCTION_ARGS)
658{
659 GSERIALIZED *result;
660 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
661 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
662 LWGEOM *theline;
663 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
664 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
665 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
666
667 theline = lwgeom_furthest_line(lwgeom1, lwgeom2);
668
669 if (lwgeom_is_empty(theline))
670 PG_RETURN_NULL();
671
672 result = geometry_serialize(theline);
673 lwgeom_free(theline);
674 lwgeom_free(lwgeom1);
675 lwgeom_free(lwgeom2);
676
677 PG_FREE_IF_COPY(geom1, 0);
678 PG_FREE_IF_COPY(geom2, 1);
679 PG_RETURN_POINTER(result);
680}
681/**
682 Minimum 2d distance between objects in geom1 and geom2.
683 */
684PG_FUNCTION_INFO_V1(ST_Distance);
685Datum ST_Distance(PG_FUNCTION_ARGS)
686{
687 double mindist;
688 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
689 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
690 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
691 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
692 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
693
694 mindist = lwgeom_mindistance2d(lwgeom1, lwgeom2);
695
696 lwgeom_free(lwgeom1);
697 lwgeom_free(lwgeom2);
698
699 PG_FREE_IF_COPY(geom1, 0);
700 PG_FREE_IF_COPY(geom2, 1);
701
702 /* if called with empty geometries the ingoing mindistance is untouched, and makes us return NULL*/
703 if (mindist < FLT_MAX)
704 PG_RETURN_FLOAT8(mindist);
705
706 PG_RETURN_NULL();
707}
708
709/**
710Returns boolean describing if
711mininimum 2d distance between objects in
712geom1 and geom2 is shorter than tolerance
713*/
714PG_FUNCTION_INFO_V1(LWGEOM_dwithin);
715Datum LWGEOM_dwithin(PG_FUNCTION_ARGS)
716{
717 double mindist;
718 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
719 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
720 double tolerance = PG_GETARG_FLOAT8(2);
721 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
722 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
723
724 if (tolerance < 0)
725 {
726 elog(ERROR, "Tolerance cannot be less than zero\n");
727 PG_RETURN_NULL();
728 }
729
730 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
731
732 mindist = lwgeom_mindistance2d_tolerance(lwgeom1, lwgeom2, tolerance);
733
734 PG_FREE_IF_COPY(geom1, 0);
735 PG_FREE_IF_COPY(geom2, 1);
736 /*empty geometries cases should be right handled since return from underlying
737 functions should be FLT_MAX which causes false as answer*/
738 PG_RETURN_BOOL(tolerance >= mindist);
739}
740
741/**
742Returns boolean describing if
743maximum 2d distance between objects in
744geom1 and geom2 is shorter than tolerance
745*/
746PG_FUNCTION_INFO_V1(LWGEOM_dfullywithin);
747Datum LWGEOM_dfullywithin(PG_FUNCTION_ARGS)
748{
749 double maxdist;
750 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
751 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
752 double tolerance = PG_GETARG_FLOAT8(2);
753 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
754 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
755
756 if (tolerance < 0)
757 {
758 elog(ERROR, "Tolerance cannot be less than zero\n");
759 PG_RETURN_NULL();
760 }
761
762 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
763
764 maxdist = lwgeom_maxdistance2d_tolerance(lwgeom1, lwgeom2, tolerance);
765
766 PG_FREE_IF_COPY(geom1, 0);
767 PG_FREE_IF_COPY(geom2, 1);
768
769 /*If function is feed with empty geometries we should return false*/
770 if (maxdist > -1)
771 PG_RETURN_BOOL(tolerance >= maxdist);
772
773 PG_RETURN_BOOL(LW_FALSE);
774}
775
776/**
777 Maximum 2d distance between objects in geom1 and geom2.
778 */
779PG_FUNCTION_INFO_V1(LWGEOM_maxdistance2d_linestring);
780Datum LWGEOM_maxdistance2d_linestring(PG_FUNCTION_ARGS)
781{
782 double maxdist;
783 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
784 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
785 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
786 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
787 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
788
789 maxdist = lwgeom_maxdistance2d(lwgeom1, lwgeom2);
790
791 PG_FREE_IF_COPY(geom1, 0);
792 PG_FREE_IF_COPY(geom2, 1);
793
794 /*if called with empty geometries the ingoing mindistance is untouched, and makes us return NULL*/
795 if (maxdist > -1)
796 PG_RETURN_FLOAT8(maxdist);
797
798 PG_RETURN_NULL();
799}
800
801/**
802Returns the point in first input geometry that is closest to the second input geometry in 3D
803*/
804
805PG_FUNCTION_INFO_V1(LWGEOM_closestpoint3d);
806Datum LWGEOM_closestpoint3d(PG_FUNCTION_ARGS)
807{
808 GSERIALIZED *result;
809 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
810 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
811 LWGEOM *point;
812 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
813 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
814 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
815
816 point = lwgeom_closest_point_3d(lwgeom1, lwgeom2);
817 // point = lw_dist3d_distancepoint(lwgeom1, lwgeom2, lwgeom1->srid, DIST_MIN);
818
819 if (lwgeom_is_empty(point))
820 PG_RETURN_NULL();
821
822 result = geometry_serialize(point);
823
824 lwgeom_free(point);
825 lwgeom_free(lwgeom1);
826 lwgeom_free(lwgeom2);
827
828 PG_FREE_IF_COPY(geom1, 0);
829 PG_FREE_IF_COPY(geom2, 1);
830 PG_RETURN_POINTER(result);
831}
832
833/**
834Returns the shortest line between two geometries in 3D
835*/
836PG_FUNCTION_INFO_V1(LWGEOM_shortestline3d);
837Datum LWGEOM_shortestline3d(PG_FUNCTION_ARGS)
838{
839 GSERIALIZED *result;
840 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
841 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
842 LWGEOM *theline;
843 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
844 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
845 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
846
847 theline = lwgeom_closest_line_3d(lwgeom1, lwgeom2);
848 // theline = lw_dist3d_distanceline(lwgeom1, lwgeom2, lwgeom1->srid, DIST_MIN);
849
850 if (lwgeom_is_empty(theline))
851 PG_RETURN_NULL();
852
853 result = geometry_serialize(theline);
854
855 lwgeom_free(theline);
856 lwgeom_free(lwgeom1);
857 lwgeom_free(lwgeom2);
858
859 PG_FREE_IF_COPY(geom1, 0);
860 PG_FREE_IF_COPY(geom2, 1);
861 PG_RETURN_POINTER(result);
862}
863
864/**
865Returns the longest line between two geometries in 3D
866*/
867PG_FUNCTION_INFO_V1(LWGEOM_longestline3d);
868Datum LWGEOM_longestline3d(PG_FUNCTION_ARGS)
869{
870 GSERIALIZED *result;
871 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
872 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
873 LWGEOM *theline;
874 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
875 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
876 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
877
878 theline = lwgeom_furthest_line_3d(lwgeom1, lwgeom2);
879 // theline = lw_dist3d_distanceline(lwgeom1, lwgeom2, lwgeom1->srid, DIST_MAX);
880
881 if (lwgeom_is_empty(theline))
882 PG_RETURN_NULL();
883
884 result = geometry_serialize(theline);
885
886 lwgeom_free(theline);
887 lwgeom_free(lwgeom1);
888 lwgeom_free(lwgeom2);
889
890 PG_FREE_IF_COPY(geom1, 0);
891 PG_FREE_IF_COPY(geom2, 1);
892 PG_RETURN_POINTER(result);
893}
894/**
895 Minimum 2d distance between objects in geom1 and geom2 in 3D
896 */
897PG_FUNCTION_INFO_V1(ST_3DDistance);
898Datum ST_3DDistance(PG_FUNCTION_ARGS)
899{
900 double mindist;
901 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
902 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
903 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
904 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
905 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
906
907 mindist = lwgeom_mindistance3d(lwgeom1, lwgeom2);
908
909 PG_FREE_IF_COPY(geom1, 0);
910 PG_FREE_IF_COPY(geom2, 1);
911
912 /*if called with empty geometries the ingoing mindistance is untouched, and makes us return NULL*/
913 if (mindist < FLT_MAX)
914 PG_RETURN_FLOAT8(mindist);
915
916 PG_RETURN_NULL();
917}
918
919/* intersects3d through dwithin */
920PG_FUNCTION_INFO_V1(ST_3DIntersects);
921Datum ST_3DIntersects(PG_FUNCTION_ARGS)
922{
923 double mindist;
924 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
925 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
926 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
927 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
928 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
929
930 mindist = lwgeom_mindistance3d_tolerance(lwgeom1, lwgeom2, 0.0);
931
932 PG_FREE_IF_COPY(geom1, 0);
933 PG_FREE_IF_COPY(geom2, 1);
934 /*empty geometries cases should be right handled since return from underlying
935 functions should be FLT_MAX which causes false as answer*/
936 PG_RETURN_BOOL(0.0 == mindist);
937}
938
939
940/**
941Returns boolean describing if
942mininimum 3d distance between objects in
943geom1 and geom2 is shorter than tolerance
944*/
945PG_FUNCTION_INFO_V1(LWGEOM_dwithin3d);
946Datum LWGEOM_dwithin3d(PG_FUNCTION_ARGS)
947{
948 double mindist;
949 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
950 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
951 double tolerance = PG_GETARG_FLOAT8(2);
952 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
953 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
954
955 if (tolerance < 0)
956 {
957 elog(ERROR, "Tolerance cannot be less than zero\n");
958 PG_RETURN_NULL();
959 }
960
961 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
962
963 mindist = lwgeom_mindistance3d_tolerance(lwgeom1, lwgeom2, tolerance);
964
965 PG_FREE_IF_COPY(geom1, 0);
966 PG_FREE_IF_COPY(geom2, 1);
967
968 /*empty geometries cases should be right handled since return from underlying
969 functions should be FLT_MAX which causes false as answer*/
970 PG_RETURN_BOOL(tolerance >= mindist);
971}
972
973/**
974Returns boolean describing if
975maximum 3d distance between objects in
976geom1 and geom2 is shorter than tolerance
977*/
978PG_FUNCTION_INFO_V1(LWGEOM_dfullywithin3d);
979Datum LWGEOM_dfullywithin3d(PG_FUNCTION_ARGS)
980{
981 double maxdist;
982 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
983 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
984 double tolerance = PG_GETARG_FLOAT8(2);
985 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
986 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
987
988 if (tolerance < 0)
989 {
990 elog(ERROR, "Tolerance cannot be less than zero\n");
991 PG_RETURN_NULL();
992 }
993
994 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
995 maxdist = lwgeom_maxdistance3d_tolerance(lwgeom1, lwgeom2, tolerance);
996
997 PG_FREE_IF_COPY(geom1, 0);
998 PG_FREE_IF_COPY(geom2, 1);
999
1000 /*If function is feed with empty geometries we should return false*/
1001 if (maxdist > -1)
1002 PG_RETURN_BOOL(tolerance >= maxdist);
1003
1004 PG_RETURN_BOOL(LW_FALSE);
1005}
1006
1007/**
1008 Maximum 3d distance between objects in geom1 and geom2.
1009 */
1010PG_FUNCTION_INFO_V1(LWGEOM_maxdistance3d);
1011Datum LWGEOM_maxdistance3d(PG_FUNCTION_ARGS)
1012{
1013 double maxdist;
1014 GSERIALIZED *geom1 = PG_GETARG_GSERIALIZED_P(0);
1015 GSERIALIZED *geom2 = PG_GETARG_GSERIALIZED_P(1);
1016 LWGEOM *lwgeom1 = lwgeom_from_gserialized(geom1);
1017 LWGEOM *lwgeom2 = lwgeom_from_gserialized(geom2);
1018
1019 gserialized_error_if_srid_mismatch(geom1, geom2, __func__);
1020
1021 maxdist = lwgeom_maxdistance3d(lwgeom1, lwgeom2);
1022
1023 PG_FREE_IF_COPY(geom1, 0);
1024 PG_FREE_IF_COPY(geom2, 1);
1025
1026 /*if called with empty geometries the ingoing mindistance is untouched, and makes us return NULL*/
1027 if (maxdist > -1)
1028 PG_RETURN_FLOAT8(maxdist);
1029
1030 PG_RETURN_NULL();
1031}
1032
1033PG_FUNCTION_INFO_V1(LWGEOM_longitude_shift);
1034Datum LWGEOM_longitude_shift(PG_FUNCTION_ARGS)
1035{
1036 GSERIALIZED *geom;
1037 LWGEOM *lwgeom;
1038 GSERIALIZED *ret;
1039
1040 POSTGIS_DEBUG(2, "LWGEOM_longitude_shift called.");
1041
1042 geom = PG_GETARG_GSERIALIZED_P_COPY(0);
1043 lwgeom = lwgeom_from_gserialized(geom);
1044
1045 /* Drop bbox, will be recomputed */
1046 lwgeom_drop_bbox(lwgeom);
1047
1048 /* Modify geometry */
1049 lwgeom_longitude_shift(lwgeom);
1050
1051 /* Construct GSERIALIZED */
1052 ret = geometry_serialize(lwgeom);
1053
1054 /* Release deserialized geometry */
1055 lwgeom_free(lwgeom);
1056
1057 /* Release detoasted geometry */
1058 pfree(geom);
1059
1060 PG_RETURN_POINTER(ret);
1061}
1062
1063PG_FUNCTION_INFO_V1(ST_WrapX);
1064Datum ST_WrapX(PG_FUNCTION_ARGS)
1065{
1066 Datum gdatum;
1067 GSERIALIZED *geom_in;
1068 LWGEOM *lwgeom_in, *lwgeom_out;
1069 GSERIALIZED *geom_out;
1070 double cutx;
1071 double amount;
1072
1073 POSTGIS_DEBUG(2, "ST_WrapX called.");
1074
1075 gdatum = PG_GETARG_DATUM(0);
1076 cutx = PG_GETARG_FLOAT8(1);
1077 amount = PG_GETARG_FLOAT8(2);
1078
1079 // if ( ! amount ) PG_RETURN_DATUM(gdatum);
1080
1081 geom_in = ((GSERIALIZED *)PG_DETOAST_DATUM(gdatum));
1082 lwgeom_in = lwgeom_from_gserialized(geom_in);
1083
1084 lwgeom_out = lwgeom_wrapx(lwgeom_in, cutx, amount);
1085 geom_out = geometry_serialize(lwgeom_out);
1086
1087 lwgeom_free(lwgeom_in);
1088 lwgeom_free(lwgeom_out);
1089 PG_FREE_IF_COPY(geom_in, 0);
1090
1091 PG_RETURN_POINTER(geom_out);
1092}
1093
1094PG_FUNCTION_INFO_V1(LWGEOM_inside_circle_point);
1095Datum LWGEOM_inside_circle_point(PG_FUNCTION_ARGS)
1096{
1097 GSERIALIZED *geom;
1098 double cx = PG_GETARG_FLOAT8(1);
1099 double cy = PG_GETARG_FLOAT8(2);
1100 double rr = PG_GETARG_FLOAT8(3);
1101 LWPOINT *lwpoint;
1102 LWGEOM *lwgeom;
1103 int inside;
1104
1105 geom = PG_GETARG_GSERIALIZED_P(0);
1106 lwgeom = lwgeom_from_gserialized(geom);
1107 lwpoint = lwgeom_as_lwpoint(lwgeom);
1108 if (lwpoint == NULL || lwgeom_is_empty(lwgeom))
1109 {
1110 PG_FREE_IF_COPY(geom, 0);
1111 PG_RETURN_NULL(); /* not a point */
1112 }
1113
1114 inside = lwpoint_inside_circle(lwpoint, cx, cy, rr);
1115 lwpoint_free(lwpoint);
1116
1117 PG_FREE_IF_COPY(geom, 0);
1118 PG_RETURN_BOOL(inside);
1119}
1120
1121/**
1122 * @brief collect( geom, geom ) returns a geometry which contains
1123 * all the sub_objects from both of the argument geometries
1124 * @return geometry is the simplest possible, based on the types
1125 * of the collected objects
1126 * ie. if all are of either X or multiX, then a multiX is returned.
1127 */
1128PG_FUNCTION_INFO_V1(LWGEOM_collect);
1129Datum LWGEOM_collect(PG_FUNCTION_ARGS)
1130{
1131 GSERIALIZED *gser1, *gser2, *result;
1132 LWGEOM *lwgeoms[2], *outlwg;
1133 uint32 type1, type2;
1134 uint8_t outtype;
1135 int32_t srid;
1136
1137 POSTGIS_DEBUG(2, "LWGEOM_collect called.");
1138
1139 /* return null if both geoms are null */
1140 if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
1141 PG_RETURN_NULL();
1142
1143 /* Return the second geom if the first geom is null */
1144 if (PG_ARGISNULL(0))
1145 PG_RETURN_DATUM(PG_GETARG_DATUM(1));
1146
1147 /* Return the first geom if the second geom is null */
1148 if (PG_ARGISNULL(1))
1149 PG_RETURN_DATUM(PG_GETARG_DATUM(0));
1150
1151 gser1 = PG_GETARG_GSERIALIZED_P(0);
1152 gser2 = PG_GETARG_GSERIALIZED_P(1);
1153 gserialized_error_if_srid_mismatch(gser1, gser2, __func__);
1154
1155 POSTGIS_DEBUGF(3,
1156 "LWGEOM_collect(%s, %s): call",
1157 lwtype_name(gserialized_get_type(gser1)),
1158 lwtype_name(gserialized_get_type(gser2)));
1159
1160 if ((gserialized_has_z(gser1) != gserialized_has_z(gser2)) ||
1161 (gserialized_has_m(gser1) != gserialized_has_m(gser2)))
1162 {
1163 elog(ERROR, "Cannot ST_Collect geometries with differing dimensionality.");
1164 PG_RETURN_NULL();
1165 }
1166
1167 srid = gserialized_get_srid(gser1);
1168
1169 lwgeoms[0] = lwgeom_from_gserialized(gser1);
1170 lwgeoms[1] = lwgeom_from_gserialized(gser2);
1171
1172 type1 = lwgeoms[0]->type;
1173 type2 = lwgeoms[1]->type;
1174
1175 if ((type1 == type2) && (!lwgeom_is_collection(lwgeoms[0])))
1176 outtype = lwtype_get_collectiontype(type1);
1177 else
1178 outtype = COLLECTIONTYPE;
1179
1180 POSTGIS_DEBUGF(3, " outtype = %d", outtype);
1181
1182 /* Drop input geometries bbox and SRID */
1183 lwgeom_drop_bbox(lwgeoms[0]);
1184 lwgeom_drop_srid(lwgeoms[0]);
1185 lwgeom_drop_bbox(lwgeoms[1]);
1186 lwgeom_drop_srid(lwgeoms[1]);
1187
1188 outlwg = (LWGEOM *)lwcollection_construct(outtype, srid, NULL, 2, lwgeoms);
1189 result = geometry_serialize(outlwg);
1190
1191 lwgeom_free(lwgeoms[0]);
1192 lwgeom_free(lwgeoms[1]);
1193
1194 PG_FREE_IF_COPY(gser1, 0);
1195 PG_FREE_IF_COPY(gser2, 1);
1196
1197 PG_RETURN_POINTER(result);
1198}
1199
1200/**
1201 * @brief collect_garray ( GEOMETRY[] ) returns a geometry which contains
1202 * all the sub_objects from all of the geometries in given array.
1203 *
1204 * @return geometry is the simplest possible, based on the types
1205 * of the collected objects
1206 * ie. if all are of either X or multiX, then a multiX is returned
1207 * bboxonly types are treated as null geometries (no sub_objects)
1208 */
1209PG_FUNCTION_INFO_V1(LWGEOM_collect_garray);
1210Datum LWGEOM_collect_garray(PG_FUNCTION_ARGS)
1211{
1212 ArrayType *array;
1213 int nelems;
1214 /*GSERIALIZED **geoms; */
1215 GSERIALIZED *result = NULL;
1216 LWGEOM **lwgeoms, *outlwg;
1217 uint32 outtype;
1218 int count;
1219 int32_t srid = SRID_UNKNOWN;
1220 GBOX *box = NULL;
1221
1222 ArrayIterator iterator;
1223 Datum value;
1224 bool isnull;
1225
1226 POSTGIS_DEBUG(2, "LWGEOM_collect_garray called.");
1227
1228 if (PG_ARGISNULL(0))
1229 PG_RETURN_NULL();
1230
1231 /* Get actual ArrayType */
1232 array = PG_GETARG_ARRAYTYPE_P(0);
1233 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
1234
1235 POSTGIS_DEBUGF(3,
1236 " array is %d-bytes in size, %ld w/out header",
1237 ARR_SIZE(array),
1238 ARR_SIZE(array) - ARR_OVERHEAD_NONULLS(ARR_NDIM(array)));
1239
1240 POSTGIS_DEBUGF(3, "LWGEOM_collect_garray: array has %d elements", nelems);
1241
1242 /* Return null on 0-elements input array */
1243 if (nelems == 0)
1244 PG_RETURN_NULL();
1245
1246 /*
1247 * Deserialize all geometries in array into the lwgeoms pointers
1248 * array. Check input types to form output type.
1249 */
1250 lwgeoms = palloc(sizeof(LWGEOM *) * nelems);
1251 count = 0;
1252 outtype = 0;
1253
1254 iterator = array_create_iterator(array, 0, NULL);
1255
1256 while (array_iterate(iterator, &value, &isnull))
1257 {
1258 GSERIALIZED *geom;
1259 uint8_t intype;
1260
1261 /* Don't do anything for NULL values */
1262 if (isnull)
1263 continue;
1264
1265 geom = (GSERIALIZED *)DatumGetPointer(value);
1266 intype = gserialized_get_type(geom);
1267
1268 lwgeoms[count] = lwgeom_from_gserialized(geom);
1269
1270 POSTGIS_DEBUGF(3, "%s: geom %d deserialized", __func__, count);
1271
1272 if (!count)
1273 {
1274 /* Get first geometry SRID */
1275 srid = lwgeoms[count]->srid;
1276
1277 /* COMPUTE_BBOX WHEN_SIMPLE */
1278 if (lwgeoms[count]->bbox)
1279 box = gbox_copy(lwgeoms[count]->bbox);
1280 }
1281 else
1282 {
1283 /* Check SRID homogeneity */
1284 gserialized_error_if_srid_mismatch_reference(geom, srid, __func__);
1285
1286 /* COMPUTE_BBOX WHEN_SIMPLE */
1287 if (box)
1288 {
1289 if (lwgeoms[count]->bbox)
1290 gbox_merge(lwgeoms[count]->bbox, box);
1291 else
1292 {
1293 pfree(box);
1294 box = NULL;
1295 }
1296 }
1297 }
1298
1299 lwgeom_drop_srid(lwgeoms[count]);
1300 lwgeom_drop_bbox(lwgeoms[count]);
1301
1302 /* Output type not initialized */
1303 if (!outtype)
1304 {
1305 outtype = lwtype_get_collectiontype(intype);
1306 }
1307 /* Input type not compatible with output */
1308 /* make output type a collection */
1309 else if (outtype != COLLECTIONTYPE && lwtype_get_collectiontype(intype) != outtype)
1310 {
1311 outtype = COLLECTIONTYPE;
1312 }
1313
1314 count++;
1315 }
1316 array_free_iterator(iterator);
1317
1318 POSTGIS_DEBUGF(3, "LWGEOM_collect_garray: outtype = %d", outtype);
1319
1320 /* If we have been passed a complete set of NULLs then return NULL */
1321 if (!outtype)
1322 {
1323 PG_RETURN_NULL();
1324 }
1325 else
1326 {
1327 outlwg = (LWGEOM *)lwcollection_construct(outtype, srid, box, count, lwgeoms);
1328
1329 result = geometry_serialize(outlwg);
1330
1331 PG_RETURN_POINTER(result);
1332 }
1333}
1334
1335/**
1336 * LineFromMultiPoint ( GEOMETRY ) returns a LINE formed by
1337 * all the points in the in given multipoint.
1338 */
1339PG_FUNCTION_INFO_V1(LWGEOM_line_from_mpoint);
1340Datum LWGEOM_line_from_mpoint(PG_FUNCTION_ARGS)
1341{
1342 GSERIALIZED *ingeom, *result;
1343 LWLINE *lwline;
1344 LWMPOINT *mpoint;
1345
1346 POSTGIS_DEBUG(2, "LWGEOM_line_from_mpoint called");
1347
1348 /* Get input GSERIALIZED and deserialize it */
1349 ingeom = PG_GETARG_GSERIALIZED_P(0);
1350
1351 if (gserialized_get_type(ingeom) != MULTIPOINTTYPE)
1352 {
1353 elog(ERROR, "makeline: input must be a multipoint");
1354 PG_RETURN_NULL(); /* input is not a multipoint */
1355 }
1356
1357 mpoint = lwgeom_as_lwmpoint(lwgeom_from_gserialized(ingeom));
1358 lwline = lwline_from_lwmpoint(mpoint->srid, mpoint);
1359 if (!lwline)
1360 {
1361 PG_FREE_IF_COPY(ingeom, 0);
1362 elog(ERROR, "makeline: lwline_from_lwmpoint returned NULL");
1363 PG_RETURN_NULL();
1364 }
1365
1366 result = geometry_serialize(lwline_as_lwgeom(lwline));
1367
1368 PG_FREE_IF_COPY(ingeom, 0);
1369 lwline_free(lwline);
1370
1371 PG_RETURN_POINTER(result);
1372}
1373
1374/**
1375 * @brief makeline_garray ( GEOMETRY[] ) returns a LINE formed by
1376 * all the point geometries in given array.
1377 * array elements that are NOT points are discarded..
1378 */
1379PG_FUNCTION_INFO_V1(LWGEOM_makeline_garray);
1380Datum LWGEOM_makeline_garray(PG_FUNCTION_ARGS)
1381{
1382 ArrayType *array;
1383 int nelems;
1384 GSERIALIZED *result = NULL;
1385 LWGEOM **geoms;
1386 LWGEOM *outlwg;
1387 uint32 ngeoms;
1388 int32_t srid = SRID_UNKNOWN;
1389
1390 ArrayIterator iterator;
1391 Datum value;
1392 bool isnull;
1393
1394 POSTGIS_DEBUGF(2, "%s called", __func__);
1395
1396 /* Return null on null input */
1397 if (PG_ARGISNULL(0))
1398 PG_RETURN_NULL();
1399
1400 /* Get actual ArrayType */
1401 array = PG_GETARG_ARRAYTYPE_P(0);
1402
1403 /* Get number of geometries in array */
1404 nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
1405
1406 POSTGIS_DEBUGF(3, "%s: array has %d elements", __func__, nelems);
1407
1408 /* Return null on 0-elements input array */
1409 if (nelems == 0)
1410 PG_RETURN_NULL();
1411
1412 /*
1413 * Deserialize all point geometries in array into the
1414 * geoms pointers array.
1415 * Count actual number of points.
1416 */
1417
1418 /* possibly more then required */
1419 geoms = palloc(sizeof(LWGEOM *) * nelems);
1420 ngeoms = 0;
1421
1422 iterator = array_create_iterator(array, 0, NULL);
1423
1424 while (array_iterate(iterator, &value, &isnull))
1425 {
1426 GSERIALIZED *geom;
1427
1428 if (isnull)
1429 continue;
1430
1431 geom = (GSERIALIZED *)DatumGetPointer(value);
1432
1433 if (gserialized_get_type(geom) != POINTTYPE && gserialized_get_type(geom) != LINETYPE &&
1434 gserialized_get_type(geom) != MULTIPOINTTYPE)
1435 {
1436 continue;
1437 }
1438
1439 geoms[ngeoms++] = lwgeom_from_gserialized(geom);
1440
1441 /* Check SRID homogeneity */
1442 if (ngeoms == 1)
1443 {
1444 /* Get first geometry SRID */
1445 srid = geoms[ngeoms - 1]->srid;
1446 /* TODO: also get ZMflags */
1447 }
1448 else
1449 gserialized_error_if_srid_mismatch_reference(geom, srid, __func__);
1450
1451 POSTGIS_DEBUGF(3, "%s: element %d deserialized", __func__, ngeoms);
1452 }
1453 array_free_iterator(iterator);
1454
1455 /* Return null on 0-points input array */
1456 if (ngeoms == 0)
1457 {
1458 /* TODO: should we return LINESTRING EMPTY here ? */
1459 elog(NOTICE, "No points or linestrings in input array");
1460 PG_RETURN_NULL();
1461 }
1462
1463 POSTGIS_DEBUGF(3, "LWGEOM_makeline_garray: elements: %d", ngeoms);
1464
1465 outlwg = (LWGEOM *)lwline_from_lwgeom_array(srid, ngeoms, geoms);
1466
1467 result = geometry_serialize(outlwg);
1468
1469 PG_RETURN_POINTER(result);
1470}
1471
1472/**
1473 * makeline ( GEOMETRY, GEOMETRY ) returns a LINESTRIN segment
1474 * formed by the given point geometries.
1475 */
1476PG_FUNCTION_INFO_V1(LWGEOM_makeline);
1477Datum LWGEOM_makeline(PG_FUNCTION_ARGS)
1478{
1479 GSERIALIZED *pglwg1, *pglwg2;
1480 GSERIALIZED *result = NULL;
1481 LWGEOM *lwgeoms[2];
1482 LWLINE *outline;
1483
1484 POSTGIS_DEBUG(2, "LWGEOM_makeline called.");
1485
1486 /* Get input datum */
1487 pglwg1 = PG_GETARG_GSERIALIZED_P(0);
1488 pglwg2 = PG_GETARG_GSERIALIZED_P(1);
1489
1490 if ((gserialized_get_type(pglwg1) != POINTTYPE && gserialized_get_type(pglwg1) != LINETYPE) ||
1491 (gserialized_get_type(pglwg2) != POINTTYPE && gserialized_get_type(pglwg2) != LINETYPE))
1492 {
1493 elog(ERROR, "Input geometries must be points or lines");
1494 PG_RETURN_NULL();
1495 }
1496
1497 gserialized_error_if_srid_mismatch(pglwg1, pglwg2, __func__);
1498
1499 lwgeoms[0] = lwgeom_from_gserialized(pglwg1);
1500 lwgeoms[1] = lwgeom_from_gserialized(pglwg2);
1501
1502 outline = lwline_from_lwgeom_array(lwgeoms[0]->srid, 2, lwgeoms);
1503
1504 result = geometry_serialize((LWGEOM *)outline);
1505
1506 PG_FREE_IF_COPY(pglwg1, 0);
1507 PG_FREE_IF_COPY(pglwg2, 1);
1508 lwgeom_free(lwgeoms[0]);
1509 lwgeom_free(lwgeoms[1]);
1510
1511 PG_RETURN_POINTER(result);
1512}
1513
1514/**
1515 * makepoly( GEOMETRY, GEOMETRY[] ) returns a POLYGON
1516 * formed by the given shell and holes geometries.
1517 */
1518PG_FUNCTION_INFO_V1(LWGEOM_makepoly);
1519Datum LWGEOM_makepoly(PG_FUNCTION_ARGS)
1520{
1521 GSERIALIZED *pglwg1;
1522 ArrayType *array = NULL;
1523 GSERIALIZED *result = NULL;
1524 const LWLINE *shell = NULL;
1525 const LWLINE **holes = NULL;
1526 LWPOLY *outpoly;
1527 uint32 nholes = 0;
1528 uint32 i;
1529 size_t offset = 0;
1530
1531 POSTGIS_DEBUG(2, "LWGEOM_makepoly called.");
1532
1533 /* Get input shell */
1534 pglwg1 = PG_GETARG_GSERIALIZED_P(0);
1535 if (gserialized_get_type(pglwg1) != LINETYPE)
1536 {
1537 lwpgerror("Shell is not a line");
1538 }
1539 shell = lwgeom_as_lwline(lwgeom_from_gserialized(pglwg1));
1540
1541 /* Get input holes if any */
1542 if (PG_NARGS() > 1)
1543 {
1544 array = PG_GETARG_ARRAYTYPE_P(1);
1545 nholes = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
1546 holes = lwalloc(sizeof(LWLINE *) * nholes);
1547 for (i = 0; i < nholes; i++)
1548 {
1549#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
1550#pragma GCC diagnostic push
1551#pragma GCC diagnostic ignored "-Wsign-compare"
1552#endif
1553 GSERIALIZED *g = (GSERIALIZED *)(ARR_DATA_PTR(array) + offset);
1554#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
1555#pragma GCC diagnostic pop
1556#endif
1557 LWLINE *hole;
1558 offset += INTALIGN(VARSIZE(g));
1559 if (gserialized_get_type(g) != LINETYPE)
1560 {
1561 lwpgerror("Hole %d is not a line", i);
1562 }
1563 hole = lwgeom_as_lwline(lwgeom_from_gserialized(g));
1564 holes[i] = hole;
1565 }
1566 }
1567
1568 outpoly = lwpoly_from_lwlines(shell, nholes, holes);
1569 POSTGIS_DEBUGF(3, "%s", lwgeom_summary((LWGEOM *)outpoly, 0));
1570 result = geometry_serialize((LWGEOM *)outpoly);
1571
1572 lwline_free((LWLINE *)shell);
1573 PG_FREE_IF_COPY(pglwg1, 0);
1574
1575 for (i = 0; i < nholes; i++)
1576 {
1577 lwline_free((LWLINE *)holes[i]);
1578 }
1579
1580 PG_RETURN_POINTER(result);
1581}
1582
1583/**
1584 * makes a polygon of the expanded features bvol - 1st point = LL 3rd=UR
1585 * 2d only. (3d might be worth adding).
1586 * create new geometry of type polygon, 1 ring, 5 points
1587 */
1588PG_FUNCTION_INFO_V1(LWGEOM_expand);
1589Datum LWGEOM_expand(PG_FUNCTION_ARGS)
1590{
1591 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
1592 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
1593 int32_t srid = lwgeom_get_srid(lwgeom);
1594 LWPOLY *poly;
1595 GSERIALIZED *result;
1596 GBOX gbox;
1597
1598 POSTGIS_DEBUG(2, "LWGEOM_expand called.");
1599
1600 /* Can't expand an empty */
1601 if (lwgeom_is_empty(lwgeom))
1602 {
1603 lwgeom_free(lwgeom);
1604 PG_RETURN_POINTER(geom);
1605 }
1606
1607 /* Can't expand something with no gbox! */
1608 if (LW_FAILURE == lwgeom_calculate_gbox(lwgeom, &gbox))
1609 {
1610 lwgeom_free(lwgeom);
1611 PG_RETURN_POINTER(geom);
1612 }
1613
1614 if (PG_NARGS() == 2)
1615 {
1616 /* Expand the box the same amount in all directions */
1617 double d = PG_GETARG_FLOAT8(1);
1618 gbox_expand(&gbox, d);
1619 }
1620 else
1621 {
1622 double dx = PG_GETARG_FLOAT8(1);
1623 double dy = PG_GETARG_FLOAT8(2);
1624 double dz = PG_GETARG_FLOAT8(3);
1625 double dm = PG_GETARG_FLOAT8(4);
1626
1627 gbox_expand_xyzm(&gbox, dx, dy, dz, dm);
1628 }
1629
1630 {
1631 POINT4D p1 = {gbox.xmin, gbox.ymin, gbox.zmin, gbox.mmin};
1632 POINT4D p2 = {gbox.xmin, gbox.ymax, gbox.zmin, gbox.mmin};
1633 POINT4D p3 = {gbox.xmax, gbox.ymax, gbox.zmax, gbox.mmax};
1634 POINT4D p4 = {gbox.xmax, gbox.ymin, gbox.zmax, gbox.mmax};
1635
1636 poly = lwpoly_construct_rectangle(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), &p1, &p2, &p3, &p4);
1637 }
1638
1639 lwgeom_add_bbox(lwpoly_as_lwgeom(poly));
1640 lwgeom_set_srid(lwpoly_as_lwgeom(poly), srid);
1641
1642 /* Construct GSERIALIZED */
1643 result = geometry_serialize(lwpoly_as_lwgeom(poly));
1644
1645 lwgeom_free(lwpoly_as_lwgeom(poly));
1646 lwgeom_free(lwgeom);
1647 PG_FREE_IF_COPY(geom, 0);
1648
1649 PG_RETURN_POINTER(result);
1650}
1651
1652/** Convert geometry to BOX (internal postgres type) */
1653PG_FUNCTION_INFO_V1(LWGEOM_to_BOX);
1654Datum LWGEOM_to_BOX(PG_FUNCTION_ARGS)
1655{
1656 GSERIALIZED *pg_lwgeom = PG_GETARG_GSERIALIZED_P(0);
1657 LWGEOM *lwgeom = lwgeom_from_gserialized(pg_lwgeom);
1658 GBOX gbox;
1659 int result;
1660 BOX *out = NULL;
1661
1662 /* Zero out flags */
1663 gbox_init(&gbox);
1664
1665 /* Calculate the GBOX of the geometry */
1666 result = lwgeom_calculate_gbox(lwgeom, &gbox);
1667
1668 /* Clean up memory */
1669 lwfree(lwgeom);
1670 PG_FREE_IF_COPY(pg_lwgeom, 0);
1671
1672 /* Null on failure */
1673 if (!result)
1674 PG_RETURN_NULL();
1675
1676 out = lwalloc(sizeof(BOX));
1677 out->low.x = gbox.xmin;
1678 out->low.y = gbox.ymin;
1679 out->high.x = gbox.xmax;
1680 out->high.y = gbox.ymax;
1681 PG_RETURN_POINTER(out);
1682}
1683
1684/**
1685 * makes a polygon of the features bvol - 1st point = LL 3rd=UR
1686 * 2d only. (3d might be worth adding).
1687 * create new geometry of type polygon, 1 ring, 5 points
1688 */
1689PG_FUNCTION_INFO_V1(LWGEOM_envelope);
1690Datum LWGEOM_envelope(PG_FUNCTION_ARGS)
1691{
1692 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
1693 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
1694 int32_t srid = lwgeom->srid;
1695 POINT4D pt;
1696 GBOX box;
1697 POINTARRAY *pa;
1698 GSERIALIZED *result;
1699
1700 if (lwgeom_is_empty(lwgeom))
1701 {
1702 /* must be the EMPTY geometry */
1703 PG_RETURN_POINTER(geom);
1704 }
1705
1706 if (lwgeom_calculate_gbox(lwgeom, &box) == LW_FAILURE)
1707 {
1708 /* must be the EMPTY geometry */
1709 PG_RETURN_POINTER(geom);
1710 }
1711
1712 /*
1713 * Alter envelope type so that a valid geometry is always
1714 * returned depending upon the size of the geometry. The
1715 * code makes the following assumptions:
1716 * - If the bounding box is a single point then return a
1717 * POINT geometry
1718 * - If the bounding box represents either a horizontal or
1719 * vertical line, return a LINESTRING geometry
1720 * - Otherwise return a POLYGON
1721 */
1722
1723 if ((box.xmin == box.xmax) && (box.ymin == box.ymax))
1724 {
1725 /* Construct and serialize point */
1726 LWPOINT *point = lwpoint_make2d(srid, box.xmin, box.ymin);
1727 result = geometry_serialize(lwpoint_as_lwgeom(point));
1728 lwpoint_free(point);
1729 }
1730 else if ((box.xmin == box.xmax) || (box.ymin == box.ymax))
1731 {
1732 LWLINE *line;
1733 /* Construct point array */
1734 pa = ptarray_construct_empty(0, 0, 2);
1735
1736 /* Assign coordinates to POINT2D array */
1737 pt.x = box.xmin;
1738 pt.y = box.ymin;
1739 ptarray_append_point(pa, &pt, LW_TRUE);
1740 pt.x = box.xmax;
1741 pt.y = box.ymax;
1742 ptarray_append_point(pa, &pt, LW_TRUE);
1743
1744 /* Construct and serialize linestring */
1745 line = lwline_construct(srid, NULL, pa);
1746 result = geometry_serialize(lwline_as_lwgeom(line));
1747 lwline_free(line);
1748 }
1749 else
1750 {
1751 LWPOLY *poly;
1752 POINTARRAY **ppa = lwalloc(sizeof(POINTARRAY *));
1753 pa = ptarray_construct_empty(0, 0, 5);
1754 ppa[0] = pa;
1755
1756 /* Assign coordinates to POINT2D array */
1757 pt.x = box.xmin;
1758 pt.y = box.ymin;
1759 ptarray_append_point(pa, &pt, LW_TRUE);
1760 pt.x = box.xmin;
1761 pt.y = box.ymax;
1762 ptarray_append_point(pa, &pt, LW_TRUE);
1763 pt.x = box.xmax;
1764 pt.y = box.ymax;
1765 ptarray_append_point(pa, &pt, LW_TRUE);
1766 pt.x = box.xmax;
1767 pt.y = box.ymin;
1768 ptarray_append_point(pa, &pt, LW_TRUE);
1769 pt.x = box.xmin;
1770 pt.y = box.ymin;
1771 ptarray_append_point(pa, &pt, LW_TRUE);
1772
1773 /* Construct polygon */
1774 poly = lwpoly_construct(srid, NULL, 1, ppa);
1775 result = geometry_serialize(lwpoly_as_lwgeom(poly));
1776 lwpoly_free(poly);
1777 }
1778
1779 PG_FREE_IF_COPY(geom, 0);
1780
1781 PG_RETURN_POINTER(result);
1782}
1783
1784PG_FUNCTION_INFO_V1(LWGEOM_isempty);
1785Datum LWGEOM_isempty(PG_FUNCTION_ARGS)
1786{
1787 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
1788 PG_RETURN_BOOL(gserialized_is_empty(geom));
1789}
1790
1791/**
1792 * @brief Returns a modified geometry so that no segment is
1793 * longer then the given distance (computed using 2d).
1794 * Every input point is kept.
1795 * Z and M values for added points (if needed) are set to 0.
1796 */
1797PG_FUNCTION_INFO_V1(LWGEOM_segmentize2d);
1798Datum LWGEOM_segmentize2d(PG_FUNCTION_ARGS)
1799{
1800 GSERIALIZED *outgeom, *ingeom;
1801 double dist;
1802 LWGEOM *inlwgeom, *outlwgeom;
1803 int type;
1804
1805 POSTGIS_DEBUG(2, "LWGEOM_segmentize2d called");
1806
1807 ingeom = PG_GETARG_GSERIALIZED_P(0);
1808 dist = PG_GETARG_FLOAT8(1);
1809 type = gserialized_get_type(ingeom);
1810
1811 /* Avoid types we cannot segmentize. */
1812 if ((type == POINTTYPE) || (type == MULTIPOINTTYPE) || (type == TRIANGLETYPE) || (type == TINTYPE) ||
1813 (type == POLYHEDRALSURFACETYPE))
1814 {
1815 PG_RETURN_POINTER(ingeom);
1816 }
1817
1818 if (dist <= 0)
1819 {
1820 /* Protect from knowingly infinite loops, see #1799 */
1821 /* Note that we'll end out of memory anyway for other small distances */
1822 elog(ERROR, "ST_Segmentize: invalid max_distance %g (must be >= 0)", dist);
1823 PG_RETURN_NULL();
1824 }
1825
1826 LWGEOM_INIT();
1827
1828 inlwgeom = lwgeom_from_gserialized(ingeom);
1829 if (lwgeom_is_empty(inlwgeom))
1830 {
1831 /* Should only happen on interruption */
1832 lwgeom_free(inlwgeom);
1833 PG_RETURN_POINTER(ingeom);
1834 }
1835
1836 outlwgeom = lwgeom_segmentize2d(inlwgeom, dist);
1837 if (!outlwgeom)
1838 {
1839 /* Should only happen on interruption */
1840 PG_FREE_IF_COPY(ingeom, 0);
1841 PG_RETURN_NULL();
1842 }
1843
1844 /* Copy input bounding box if any */
1845 if (inlwgeom->bbox)
1846 outlwgeom->bbox = gbox_copy(inlwgeom->bbox);
1847
1848 outgeom = geometry_serialize(outlwgeom);
1849
1850 // lwgeom_free(outlwgeom); /* TODO fix lwgeom_clone / ptarray_clone_deep for consistent semantics */
1851 lwgeom_free(inlwgeom);
1852
1853 PG_FREE_IF_COPY(ingeom, 0);
1854
1855 PG_RETURN_POINTER(outgeom);
1856}
1857
1858/** Reverse vertex order of geometry */
1859PG_FUNCTION_INFO_V1(LWGEOM_reverse);
1860Datum LWGEOM_reverse(PG_FUNCTION_ARGS)
1861{
1862 GSERIALIZED *geom;
1863 LWGEOM *lwgeom;
1864
1865 POSTGIS_DEBUG(2, "LWGEOM_reverse called");
1866
1867 geom = PG_GETARG_GSERIALIZED_P_COPY(0);
1868
1869 lwgeom = lwgeom_from_gserialized(geom);
1870 lwgeom_reverse_in_place(lwgeom);
1871
1872 geom = geometry_serialize(lwgeom);
1873
1874 PG_RETURN_POINTER(geom);
1875}
1876
1877/** Force polygons of the collection to obey Right-Hand-Rule */
1878PG_FUNCTION_INFO_V1(LWGEOM_force_clockwise_poly);
1879Datum LWGEOM_force_clockwise_poly(PG_FUNCTION_ARGS)
1880{
1881 GSERIALIZED *ingeom, *outgeom;
1882 LWGEOM *lwgeom;
1883
1884 POSTGIS_DEBUG(2, "LWGEOM_force_clockwise_poly called");
1885
1886 ingeom = PG_GETARG_GSERIALIZED_P_COPY(0);
1887
1888 lwgeom = lwgeom_from_gserialized(ingeom);
1889 lwgeom_force_clockwise(lwgeom);
1890
1891 outgeom = geometry_serialize(lwgeom);
1892
1893 lwgeom_free(lwgeom);
1894 PG_FREE_IF_COPY(ingeom, 0);
1895 PG_RETURN_POINTER(outgeom);
1896}
1897
1898/** Test deserialize/serialize operations */
1899PG_FUNCTION_INFO_V1(LWGEOM_noop);
1900Datum LWGEOM_noop(PG_FUNCTION_ARGS)
1901{
1902 GSERIALIZED *in = PG_GETARG_GSERIALIZED_P(0);
1903 LWGEOM *lwgeom = lwgeom_from_gserialized(in);
1904 GSERIALIZED *out = geometry_serialize(lwgeom);
1905 lwgeom_free(lwgeom);
1906 PG_FREE_IF_COPY(in, 0);
1907 PG_RETURN_POINTER(out);
1908}
1909
1910Datum ST_Normalize(PG_FUNCTION_ARGS);
1911PG_FUNCTION_INFO_V1(ST_Normalize);
1912Datum ST_Normalize(PG_FUNCTION_ARGS)
1913{
1914 GSERIALIZED *in, *out;
1915 LWGEOM *lwgeom_in, *lwgeom_out;
1916
1917 POSTGIS_DEBUG(2, "ST_Normalize called");
1918
1919 in = PG_GETARG_GSERIALIZED_P_COPY(0);
1920
1921 lwgeom_in = lwgeom_from_gserialized(in);
1922 POSTGIS_DEBUGF(3, "Deserialized: %s", lwgeom_summary(lwgeom_in, 0));
1923
1924 lwgeom_out = lwgeom_normalize(lwgeom_in);
1925 POSTGIS_DEBUGF(3, "Normalized: %s", lwgeom_summary(lwgeom_out, 0));
1926
1927 out = geometry_serialize(lwgeom_out);
1928 lwgeom_free(lwgeom_in);
1929 lwgeom_free(lwgeom_out);
1930
1931 PG_FREE_IF_COPY(in, 0);
1932
1933 PG_RETURN_POINTER(out);
1934}
1935
1936/**
1937 * @return:
1938 * 0==2d
1939 * 1==3dm
1940 * 2==3dz
1941 * 3==4d
1942 */
1943PG_FUNCTION_INFO_V1(LWGEOM_zmflag);
1944Datum LWGEOM_zmflag(PG_FUNCTION_ARGS)
1945{
1946 GSERIALIZED *in = PG_GETARG_GSERIALIZED_P(0);
1947 int ret = 0;
1948
1949 if (gserialized_has_z(in))
1950 ret += 2;
1951 if (gserialized_has_m(in))
1952 ret += 1;
1953 PG_FREE_IF_COPY(in, 0);
1954 PG_RETURN_INT16(ret);
1955}
1956
1957PG_FUNCTION_INFO_V1(LWGEOM_hasz);
1958Datum LWGEOM_hasz(PG_FUNCTION_ARGS)
1959{
1960 GSERIALIZED *in = PG_GETARG_GSERIALIZED_P(0);
1961 PG_RETURN_BOOL(gserialized_has_z(in));
1962}
1963
1964PG_FUNCTION_INFO_V1(LWGEOM_hasm);
1965Datum LWGEOM_hasm(PG_FUNCTION_ARGS)
1966{
1967 GSERIALIZED *in = PG_GETARG_GSERIALIZED_P(0);
1968 PG_RETURN_BOOL(gserialized_has_m(in));
1969}
1970
1971PG_FUNCTION_INFO_V1(LWGEOM_hasBBOX);
1972Datum LWGEOM_hasBBOX(PG_FUNCTION_ARGS)
1973{
1974 GSERIALIZED *in = PG_GETARG_GSERIALIZED_P(0);
1975 char res = gserialized_has_bbox(in);
1976 PG_FREE_IF_COPY(in, 0);
1977 PG_RETURN_BOOL(res);
1978}
1979
1980/** Return: 2,3 or 4 */
1981PG_FUNCTION_INFO_V1(LWGEOM_ndims);
1982Datum LWGEOM_ndims(PG_FUNCTION_ARGS)
1983{
1984 GSERIALIZED *in = PG_GETARG_GSERIALIZED_P(0);
1985 int ret = gserialized_ndims(in);
1986 PG_FREE_IF_COPY(in, 0);
1987 PG_RETURN_INT16(ret);
1988}
1989
1990/** lwgeom_same(lwgeom1, lwgeom2) */
1991PG_FUNCTION_INFO_V1(LWGEOM_same);
1992Datum LWGEOM_same(PG_FUNCTION_ARGS)
1993{
1994 GSERIALIZED *g1 = PG_GETARG_GSERIALIZED_P(0);
1995 GSERIALIZED *g2 = PG_GETARG_GSERIALIZED_P(1);
1996
1997 PG_RETURN_BOOL(gserialized_cmp(g1, g2) == 0);
1998}
1999
2000PG_FUNCTION_INFO_V1(ST_MakeEnvelope);
2001Datum ST_MakeEnvelope(PG_FUNCTION_ARGS)
2002{
2003 LWPOLY *poly;
2004 GSERIALIZED *result;
2005 double x1, y1, x2, y2;
2006 int32_t srid = SRID_UNKNOWN;
2007
2008 POSTGIS_DEBUG(2, "ST_MakeEnvelope called");
2009
2010 x1 = PG_GETARG_FLOAT8(0);
2011 y1 = PG_GETARG_FLOAT8(1);
2012 x2 = PG_GETARG_FLOAT8(2);
2013 y2 = PG_GETARG_FLOAT8(3);
2014 if (PG_NARGS() > 4)
2015 {
2016 srid = PG_GETARG_INT32(4);
2017 }
2018
2019 poly = lwpoly_construct_envelope(srid, x1, y1, x2, y2);
2020
2021 result = geometry_serialize(lwpoly_as_lwgeom(poly));
2022 lwpoly_free(poly);
2023
2024 PG_RETURN_POINTER(result);
2025}
2026
2027
2028PG_FUNCTION_INFO_V1(ST_TileEnvelope);
2029Datum ST_TileEnvelope(PG_FUNCTION_ARGS)
2030{
2031 GSERIALIZED *bounds;
2032 uint32_t zoomu;
2033 int32_t x, y, zoom;
2034 uint32_t worldTileSize;
2035 double tileGeoSizeX, tileGeoSizeY;
2036 double boundsWidth, boundsHeight;
2037 double x1, y1, x2, y2;
2038 /* This is broken, since 3857 doesn't mean "web mercator", it means
2039 the contents of the row in spatial_ref_sys with srid = 3857.
2040 For practical purposes this will work, but in good implementation
2041 we should de-reference in spatial ref sys to confirm that the
2042 srid of the object is EPSG:3857. */
2043 int32_t srid;
2044 GBOX bbox;
2045
2046 POSTGIS_DEBUG(2, "ST_TileEnvelope called");
2047
2048 zoom = PG_GETARG_INT32(0);
2049 x = PG_GETARG_INT32(1);
2050 y = PG_GETARG_INT32(2);
2051
2052 bounds = PG_GETARG_GSERIALIZED_P(3);
2053 if(gserialized_get_gbox_p(bounds, &bbox) != LW_SUCCESS)
2054 elog(ERROR, "%s: Empty bounds", __func__);
2055 srid = gserialized_get_srid(bounds);
2056
2057 boundsWidth = bbox.xmax - bbox.xmin;
2058 boundsHeight = bbox.ymax - bbox.ymin;
2059 if (boundsWidth <= 0 || boundsHeight <= 0)
2060 elog(ERROR, "%s: Geometric bounds are too small", __func__);
2061
2062 if (zoom < 0 || zoom >= 32)
2063 elog(ERROR, "%s: Invalid tile zoom value, %d", __func__, zoom);
2064
2065 zoomu = (uint32_t)zoom;
2066 worldTileSize = 0x01u << (zoomu > 31 ? 31 : zoomu);
2067
2068 if (x < 0 || (uint32_t)x >= worldTileSize)
2069 elog(ERROR, "%s: Invalid tile x value, %d", __func__, x);
2070 if (y < 0 || (uint32_t)y >= worldTileSize)
2071 elog(ERROR, "%s: Invalid tile y value, %d", __func__, y);
2072
2073 tileGeoSizeX = boundsWidth / worldTileSize;
2074 tileGeoSizeY = boundsHeight / worldTileSize;
2075 x1 = bbox.xmin + tileGeoSizeX * (x);
2076 x2 = bbox.xmin + tileGeoSizeX * (x+1);
2077 y1 = bbox.ymax - tileGeoSizeY * (y+1);
2078 y2 = bbox.ymax - tileGeoSizeY * (y);
2079
2080 PG_RETURN_POINTER(
2081 geometry_serialize(
2082 lwpoly_as_lwgeom(
2083 lwpoly_construct_envelope(
2084 srid, x1, y1, x2, y2))));
2085}
2086
2087
2088PG_FUNCTION_INFO_V1(ST_IsCollection);
2089Datum ST_IsCollection(PG_FUNCTION_ARGS)
2090{
2091 GSERIALIZED *geom;
2092 int type;
2093 size_t size;
2094
2095 /* Pull only a small amount of the tuple, enough to get the type. */
2096 /* header + srid/flags + bbox? + type number */
2097 size = VARHDRSZ + 8 + 32 + 4;
2098
2099 geom = PG_GETARG_GSERIALIZED_P_SLICE(0, 0, size);
2100
2101 type = gserialized_get_type(geom);
2102 PG_RETURN_BOOL(lwtype_is_collection(type));
2103}
2104
2105PG_FUNCTION_INFO_V1(LWGEOM_makepoint);
2106Datum LWGEOM_makepoint(PG_FUNCTION_ARGS)
2107{
2108 double x, y, z, m;
2109 LWPOINT *point;
2110 GSERIALIZED *result;
2111
2112 POSTGIS_DEBUG(2, "LWGEOM_makepoint called");
2113
2114 x = PG_GETARG_FLOAT8(0);
2115 y = PG_GETARG_FLOAT8(1);
2116
2117 if (PG_NARGS() == 2)
2118 point = lwpoint_make2d(SRID_UNKNOWN, x, y);
2119 else if (PG_NARGS() == 3)
2120 {
2121 z = PG_GETARG_FLOAT8(2);
2122 point = lwpoint_make3dz(SRID_UNKNOWN, x, y, z);
2123 }
2124 else if (PG_NARGS() == 4)
2125 {
2126 z = PG_GETARG_FLOAT8(2);
2127 m = PG_GETARG_FLOAT8(3);
2128 point = lwpoint_make4d(SRID_UNKNOWN, x, y, z, m);
2129 }
2130 else
2131 {
2132 elog(ERROR, "LWGEOM_makepoint: unsupported number of args: %d", PG_NARGS());
2133 PG_RETURN_NULL();
2134 }
2135
2136 result = geometry_serialize((LWGEOM *)point);
2137
2138 PG_RETURN_POINTER(result);
2139}
2140
2141PG_FUNCTION_INFO_V1(LWGEOM_makepoint3dm);
2142Datum LWGEOM_makepoint3dm(PG_FUNCTION_ARGS)
2143{
2144 double x, y, m;
2145 LWPOINT *point;
2146 GSERIALIZED *result;
2147
2148 POSTGIS_DEBUG(2, "LWGEOM_makepoint3dm called.");
2149
2150 x = PG_GETARG_FLOAT8(0);
2151 y = PG_GETARG_FLOAT8(1);
2152 m = PG_GETARG_FLOAT8(2);
2153
2154 point = lwpoint_make3dm(SRID_UNKNOWN, x, y, m);
2155 result = geometry_serialize((LWGEOM *)point);
2156
2157 PG_RETURN_POINTER(result);
2158}
2159
2160PG_FUNCTION_INFO_V1(LWGEOM_addpoint);
2161Datum LWGEOM_addpoint(PG_FUNCTION_ARGS)
2162{
2163 GSERIALIZED *pglwg1, *pglwg2, *result;
2164 LWPOINT *point;
2165 LWLINE *line, *linecopy;
2166 int32 where = -1;
2167
2168 POSTGIS_DEBUGF(2, "%s called.", __func__);
2169
2170 pglwg1 = PG_GETARG_GSERIALIZED_P(0);
2171 pglwg2 = PG_GETARG_GSERIALIZED_P(1);
2172
2173 if (gserialized_get_type(pglwg1) != LINETYPE)
2174 {
2175 elog(ERROR, "First argument must be a LINESTRING");
2176 PG_RETURN_NULL();
2177 }
2178
2179 if (gserialized_get_type(pglwg2) != POINTTYPE)
2180 {
2181 elog(ERROR, "Second argument must be a POINT");
2182 PG_RETURN_NULL();
2183 }
2184
2185 line = lwgeom_as_lwline(lwgeom_from_gserialized(pglwg1));
2186
2187 if (PG_NARGS() > 2)
2188 {
2189 where = PG_GETARG_INT32(2);
2190 }
2191 else
2192 {
2193 where = line->points->npoints;
2194 }
2195
2196 if (where < 0 || where > (int32)line->points->npoints)
2197 {
2198 elog(ERROR, "Invalid offset");
2199 PG_RETURN_NULL();
2200 }
2201
2202 point = lwgeom_as_lwpoint(lwgeom_from_gserialized(pglwg2));
2203 linecopy = lwgeom_as_lwline(lwgeom_clone_deep(lwline_as_lwgeom(line)));
2204 lwline_free(line);
2205
2206 if (lwline_add_lwpoint(linecopy, point, (uint32_t)where) == LW_FAILURE)
2207 {
2208 elog(ERROR, "Point insert failed");
2209 PG_RETURN_NULL();
2210 }
2211
2212 result = geometry_serialize(lwline_as_lwgeom(linecopy));
2213
2214 /* Release memory */
2215 PG_FREE_IF_COPY(pglwg1, 0);
2216 PG_FREE_IF_COPY(pglwg2, 1);
2217 lwpoint_free(point);
2218
2219 PG_RETURN_POINTER(result);
2220}
2221
2222PG_FUNCTION_INFO_V1(LWGEOM_removepoint);
2223Datum LWGEOM_removepoint(PG_FUNCTION_ARGS)
2224{
2225 GSERIALIZED *pglwg1, *result;
2226 LWLINE *line, *outline;
2227 int32 which;
2228
2229 POSTGIS_DEBUG(2, "LWGEOM_removepoint called.");
2230
2231 pglwg1 = PG_GETARG_GSERIALIZED_P(0);
2232 which = PG_GETARG_INT32(1);
2233
2234 if (gserialized_get_type(pglwg1) != LINETYPE)
2235 {
2236 elog(ERROR, "First argument must be a LINESTRING");
2237 PG_RETURN_NULL();
2238 }
2239
2240 line = lwgeom_as_lwline(lwgeom_from_gserialized(pglwg1));
2241
2242 if (which < 0 || (uint32_t)which > line->points->npoints - 1)
2243 {
2244 elog(ERROR, "Point index out of range (%u..%u)", 0, line->points->npoints - 1);
2245 PG_RETURN_NULL();
2246 }
2247
2248 if (line->points->npoints < 3)
2249 {
2250 elog(ERROR, "Can't remove points from a single segment line");
2251 PG_RETURN_NULL();
2252 }
2253
2254 outline = lwline_removepoint(line, (uint32_t)which);
2255 /* Release memory */
2256 lwline_free(line);
2257
2258 result = geometry_serialize((LWGEOM *)outline);
2259 lwline_free(outline);
2260
2261 PG_FREE_IF_COPY(pglwg1, 0);
2262 PG_RETURN_POINTER(result);
2263}
2264
2265PG_FUNCTION_INFO_V1(LWGEOM_setpoint_linestring);
2266Datum LWGEOM_setpoint_linestring(PG_FUNCTION_ARGS)
2267{
2268 GSERIALIZED *pglwg1, *pglwg2, *result;
2269 LWGEOM *lwg;
2270 LWLINE *line;
2271 LWPOINT *lwpoint;
2272 POINT4D newpoint;
2273 int64_t which;
2274
2275 POSTGIS_DEBUG(2, "LWGEOM_setpoint_linestring called.");
2276
2277 /* we copy input as we're going to modify it */
2278 pglwg1 = PG_GETARG_GSERIALIZED_P_COPY(0);
2279
2280 which = PG_GETARG_INT32(1);
2281 pglwg2 = PG_GETARG_GSERIALIZED_P(2);
2282
2283 /* Extract a POINT4D from the point */
2284 lwg = lwgeom_from_gserialized(pglwg2);
2285 lwpoint = lwgeom_as_lwpoint(lwg);
2286 if (!lwpoint)
2287 {
2288 elog(ERROR, "Third argument must be a POINT");
2289 PG_RETURN_NULL();
2290 }
2291 getPoint4d_p(lwpoint->point, 0, &newpoint);
2292 lwpoint_free(lwpoint);
2293 PG_FREE_IF_COPY(pglwg2, 2);
2294
2295 lwg = lwgeom_from_gserialized(pglwg1);
2296 line = lwgeom_as_lwline(lwg);
2297 if (!line)
2298 {
2299 elog(ERROR, "First argument must be a LINESTRING");
2300 PG_RETURN_NULL();
2301 }
2302 if (which < 0)
2303 {
2304 /* Use backward indexing for negative values */
2305 which += (int64_t)line->points->npoints;
2306 }
2307 if ((uint32_t)which > line->points->npoints - 1)
2308 {
2309 elog(ERROR, "abs(Point index) out of range (-)(%u..%u)", 0, line->points->npoints - 1);
2310 PG_RETURN_NULL();
2311 }
2312
2313 /*
2314 * This will change pointarray of the serialized pglwg1,
2315 */
2316 lwline_setPoint4d(line, (uint32_t)which, &newpoint);
2317 result = geometry_serialize((LWGEOM *)line);
2318
2319 /* Release memory */
2320 lwline_free(line);
2321 pfree(pglwg1); /* we forced copy, POINARRAY is released now */
2322
2323 PG_RETURN_POINTER(result);
2324}
2325
2326/* convert LWGEOM to ewkt (in TEXT format) */
2327PG_FUNCTION_INFO_V1(LWGEOM_asEWKT);
2328Datum LWGEOM_asEWKT(PG_FUNCTION_ARGS)
2329{
2330 GSERIALIZED *geom;
2331 LWGEOM *lwgeom;
2332 char *wkt;
2333 size_t wkt_size;
2334 text *result;
2335
2336 POSTGIS_DEBUG(2, "LWGEOM_asEWKT called.");
2337
2338 geom = PG_GETARG_GSERIALIZED_P(0);
2339 lwgeom = lwgeom_from_gserialized(geom);
2340
2341 /* Write to WKT and free the geometry */
2342 wkt = lwgeom_to_wkt(lwgeom, WKT_EXTENDED, DBL_DIG, &wkt_size);
2343 lwgeom_free(lwgeom);
2344
2345 /* Write to text and free the WKT */
2346 result = cstring_to_text(wkt);
2347 lwfree(wkt);
2348
2349 /* Return the text */
2350 PG_FREE_IF_COPY(geom, 0);
2351 PG_RETURN_TEXT_P(result);
2352}
2353
2354/**
2355 * Compute the azimuth of segment defined by the two
2356 * given Point geometries.
2357 * @return NULL on exception (same point).
2358 * Return radians otherwise.
2359 */
2360PG_FUNCTION_INFO_V1(LWGEOM_azimuth);
2361Datum LWGEOM_azimuth(PG_FUNCTION_ARGS)
2362{
2363 GSERIALIZED *geom;
2364 LWPOINT *lwpoint;
2365 POINT2D p1, p2;
2366 double result;
2367 int32_t srid;
2368
2369 /* Extract first point */
2370 geom = PG_GETARG_GSERIALIZED_P(0);
2371 lwpoint = lwgeom_as_lwpoint(lwgeom_from_gserialized(geom));
2372 if (!lwpoint)
2373 {
2374 PG_FREE_IF_COPY(geom, 0);
2375 lwpgerror("Argument must be POINT geometries");
2376 PG_RETURN_NULL();
2377 }
2378 srid = lwpoint->srid;
2379 if (!getPoint2d_p(lwpoint->point, 0, &p1))
2380 {
2381 PG_FREE_IF_COPY(geom, 0);
2382 lwpgerror("Error extracting point");
2383 PG_RETURN_NULL();
2384 }
2385 lwpoint_free(lwpoint);
2386 PG_FREE_IF_COPY(geom, 0);
2387
2388 /* Extract second point */
2389 geom = PG_GETARG_GSERIALIZED_P(1);
2390 lwpoint = lwgeom_as_lwpoint(lwgeom_from_gserialized(geom));
2391 if (!lwpoint)
2392 {
2393 PG_FREE_IF_COPY(geom, 1);
2394 lwpgerror("Argument must be POINT geometries");
2395 PG_RETURN_NULL();
2396 }
2397 if (lwpoint->srid != srid)
2398 {
2399 PG_FREE_IF_COPY(geom, 1);
2400 lwpgerror("Operation on mixed SRID geometries");
2401 PG_RETURN_NULL();
2402 }
2403 if (!getPoint2d_p(lwpoint->point, 0, &p2))
2404 {
2405 PG_FREE_IF_COPY(geom, 1);
2406 lwpgerror("Error extracting point");
2407 PG_RETURN_NULL();
2408 }
2409 lwpoint_free(lwpoint);
2410 PG_FREE_IF_COPY(geom, 1);
2411
2412 /* Standard return value for equality case */
2413 if ((p1.x == p2.x) && (p1.y == p2.y))
2414 {
2415 PG_RETURN_NULL();
2416 }
2417
2418 /* Compute azimuth */
2419 if (!azimuth_pt_pt(&p1, &p2, &result))
2420 {
2421 PG_RETURN_NULL();
2422 }
2423
2424 PG_RETURN_FLOAT8(result);
2425}
2426
2427/**
2428 * Compute the angle defined by 3 points or the angle between 2 vectors
2429 * defined by 4 points
2430 * given Point geometries.
2431 * @return NULL on exception (same point).
2432 * Return radians otherwise (always positive).
2433 */
2434PG_FUNCTION_INFO_V1(LWGEOM_angle);
2435Datum LWGEOM_angle(PG_FUNCTION_ARGS)
2436{
2437 GSERIALIZED *seri_geoms[4];
2438 LWGEOM *geom_unser;
2439 LWPOINT *lwpoint;
2440 POINT2D points[4];
2441 double az1, az2;
2442 double result;
2443 int32_t srids[4];
2444 int i = 0;
2445 int j = 0;
2446 int err_code = 0;
2447 int n_args = PG_NARGS();
2448
2449 /* no deserialize, checking for common error first*/
2450 for (i = 0; i < n_args; i++)
2451 {
2452 seri_geoms[i] = PG_GETARG_GSERIALIZED_P(i);
2453 if (gserialized_is_empty(seri_geoms[i]))
2454 { /* empty geom */
2455 if (i == 3)
2456 {
2457 n_args = 3;
2458 }
2459 else
2460 {
2461 err_code = 1;
2462 break;
2463 }
2464 }
2465 else
2466 {
2467 if (gserialized_get_type(seri_geoms[i]) != POINTTYPE)
2468 { /* geom type */
2469 err_code = 2;
2470 break;
2471 }
2472 else
2473 {
2474 srids[i] = gserialized_get_srid(seri_geoms[i]);
2475 if (srids[0] != srids[i])
2476 { /* error on srid*/
2477 err_code = 3;
2478 break;
2479 }
2480 }
2481 }
2482 }
2483 if (err_code > 0)
2484 switch (err_code)
2485 {
2486 default: /*always executed*/
2487 for (j = 0; j <= i; j++)
2488 PG_FREE_IF_COPY(seri_geoms[j], j);
2489 /*FALLTHROUGH*/
2490 case 1:
2491 lwpgerror("Empty geometry");
2492 PG_RETURN_NULL();
2493 break;
2494
2495 case 2:
2496 lwpgerror("Argument must be POINT geometries");
2497 PG_RETURN_NULL();
2498 break;
2499
2500 case 3:
2501 lwpgerror("Operation on mixed SRID geometries");
2502 PG_RETURN_NULL();
2503 break;
2504 }
2505 /* extract points */
2506 for (i = 0; i < n_args; i++)
2507 {
2508 geom_unser = lwgeom_from_gserialized(seri_geoms[i]);
2509 lwpoint = lwgeom_as_lwpoint(geom_unser);
2510 if (!lwpoint)
2511 {
2512 for (j = 0; j < n_args; j++)
2513 PG_FREE_IF_COPY(seri_geoms[j], j);
2514 lwpgerror("Error unserializing geometry");
2515 PG_RETURN_NULL();
2516 }
2517
2518 if (!getPoint2d_p(lwpoint->point, 0, &points[i]))
2519 {
2520 /* // can't free serialized geom, it might be needed by lw
2521 for (j=0;j<n_args;j++)
2522 PG_FREE_IF_COPY(seri_geoms[j], j); */
2523 lwpgerror("Error extracting point");
2524 PG_RETURN_NULL();
2525 }
2526 /* lwfree(geom_unser);don't do, lw may rely on this memory
2527 lwpoint_free(lwpoint); dont do , this memory is needed ! */
2528 }
2529 /* // can't free serialized geom, it might be needed by lw
2530 for (j=0;j<n_args;j++)
2531 PG_FREE_IF_COPY(seri_geoms[j], j); */
2532
2533 /* compute azimuth for the 2 pairs of points
2534 * note that angle is not defined identically for 3 points or 4 points*/
2535 if (n_args == 3)
2536 { /* we rely on azimuth to complain if points are identical */
2537 if (!azimuth_pt_pt(&points[0], &points[1], &az1))
2538 PG_RETURN_NULL();
2539 if (!azimuth_pt_pt(&points[2], &points[1], &az2))
2540 PG_RETURN_NULL();
2541 }
2542 else
2543 {
2544 if (!azimuth_pt_pt(&points[0], &points[1], &az1))
2545 PG_RETURN_NULL();
2546 if (!azimuth_pt_pt(&points[2], &points[3], &az2))
2547 PG_RETURN_NULL();
2548 }
2549 result = az2 - az1;
2550 result += (result < 0) * 2 * M_PI; /* we dont want negative angle*/
2551 PG_RETURN_FLOAT8(result);
2552}
2553
2554/*
2555 * optimistic_overlap(Polygon P1, Multipolygon MP2, double dist)
2556 * returns true if P1 overlaps MP2
2557 * method: bbox check -
2558 * is separation < dist? no - return false (quick)
2559 * yes - return distance(P1,MP2) < dist
2560 */
2561PG_FUNCTION_INFO_V1(optimistic_overlap);
2562Datum optimistic_overlap(PG_FUNCTION_ARGS)
2563{
2564 GSERIALIZED *pg_geom1 = PG_GETARG_GSERIALIZED_P(0);
2565 GSERIALIZED *pg_geom2 = PG_GETARG_GSERIALIZED_P(1);
2566 double dist = PG_GETARG_FLOAT8(2);
2567 GBOX g1_bvol;
2568 double calc_dist;
2569 LWGEOM *geom1 = lwgeom_from_gserialized(pg_geom1);
2570 LWGEOM *geom2 = lwgeom_from_gserialized(pg_geom2);
2571 gserialized_error_if_srid_mismatch(pg_geom1, pg_geom2, __func__);
2572
2573 if (geom1->type != POLYGONTYPE)
2574 {
2575 elog(ERROR, "optimistic_overlap: first arg isn't a polygon\n");
2576 PG_RETURN_NULL();
2577 }
2578
2579 if (geom2->type != POLYGONTYPE && geom2->type != MULTIPOLYGONTYPE)
2580 {
2581 elog(ERROR, "optimistic_overlap: 2nd arg isn't a [multi-]polygon\n");
2582 PG_RETURN_NULL();
2583 }
2584
2585 /*bbox check */
2586 gserialized_get_gbox_p(pg_geom1, &g1_bvol);
2587
2588 g1_bvol.xmin = g1_bvol.xmin - dist;
2589 g1_bvol.ymin = g1_bvol.ymin - dist;
2590 g1_bvol.xmax = g1_bvol.xmax + dist;
2591 g1_bvol.ymax = g1_bvol.ymax + dist;
2592
2593 if ((g1_bvol.xmin > geom2->bbox->xmax) || (g1_bvol.xmax < geom2->bbox->xmin) ||
2594 (g1_bvol.ymin > geom2->bbox->ymax) || (g1_bvol.ymax < geom2->bbox->ymin))
2595 {
2596 PG_RETURN_BOOL(false); /*bbox not overlap */
2597 }
2598
2599 /*
2600 * compute distances
2601 * should be a fast calc if they actually do intersect
2602 */
2603 calc_dist =
2604 DatumGetFloat8(DirectFunctionCall2(ST_Distance, PointerGetDatum(pg_geom1), PointerGetDatum(pg_geom2)));
2605
2606 PG_RETURN_BOOL(calc_dist < dist);
2607}
2608
2609/*affine transform geometry */
2610PG_FUNCTION_INFO_V1(LWGEOM_affine);
2611Datum LWGEOM_affine(PG_FUNCTION_ARGS)
2612{
2613 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P_COPY(0);
2614 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
2615 GSERIALIZED *ret;
2616 AFFINE affine;
2617
2618 affine.afac = PG_GETARG_FLOAT8(1);
2619 affine.bfac = PG_GETARG_FLOAT8(2);
2620 affine.cfac = PG_GETARG_FLOAT8(3);
2621 affine.dfac = PG_GETARG_FLOAT8(4);
2622 affine.efac = PG_GETARG_FLOAT8(5);
2623 affine.ffac = PG_GETARG_FLOAT8(6);
2624 affine.gfac = PG_GETARG_FLOAT8(7);
2625 affine.hfac = PG_GETARG_FLOAT8(8);
2626 affine.ifac = PG_GETARG_FLOAT8(9);
2627 affine.xoff = PG_GETARG_FLOAT8(10);
2628 affine.yoff = PG_GETARG_FLOAT8(11);
2629 affine.zoff = PG_GETARG_FLOAT8(12);
2630
2631 POSTGIS_DEBUG(2, "LWGEOM_affine called.");
2632
2633 lwgeom_affine(lwgeom, &affine);
2634
2635 /* COMPUTE_BBOX TAINTING */
2636 if (lwgeom->bbox)
2637 {
2638 lwgeom_refresh_bbox(lwgeom);
2639 }
2640 ret = geometry_serialize(lwgeom);
2641
2642 /* Release memory */
2643 lwgeom_free(lwgeom);
2644 PG_FREE_IF_COPY(geom, 0);
2645
2646 PG_RETURN_POINTER(ret);
2647}
2648
2649PG_FUNCTION_INFO_V1(ST_GeoHash);
2650Datum ST_GeoHash(PG_FUNCTION_ARGS)
2651{
2652
2653 GSERIALIZED *geom = NULL;
2654 int precision = 0;
2655 char *geohash = NULL;
2656 text *result = NULL;
2657
2658 if (PG_ARGISNULL(0))
2659 {
2660 PG_RETURN_NULL();
2661 }
2662
2663 geom = PG_GETARG_GSERIALIZED_P(0);
2664
2665 if (!PG_ARGISNULL(1))
2666 {
2667 precision = PG_GETARG_INT32(1);
2668 }
2669
2670 geohash = lwgeom_geohash((LWGEOM *)(lwgeom_from_gserialized(geom)), precision);
2671
2672 if (!geohash)
2673 PG_RETURN_NULL();
2674
2675 result = cstring_to_text(geohash);
2676 pfree(geohash);
2677
2678 PG_RETURN_TEXT_P(result);
2679}
2680
2681PG_FUNCTION_INFO_V1(ST_CollectionExtract);
2682Datum ST_CollectionExtract(PG_FUNCTION_ARGS)
2683{
2684 GSERIALIZED *input = PG_GETARG_GSERIALIZED_P(0);
2685 GSERIALIZED *output;
2686 LWGEOM *lwgeom = lwgeom_from_gserialized(input);
2687 LWGEOM *lwcol = NULL;
2688 int type = PG_GETARG_INT32(1);
2689 int lwgeom_type = lwgeom->type;
2690
2691 /* Ensure the right type was input */
2692 if (!(type == POINTTYPE || type == LINETYPE || type == POLYGONTYPE))
2693 {
2694 lwgeom_free(lwgeom);
2695 elog(ERROR, "ST_CollectionExtract: only point, linestring and polygon may be extracted");
2696 PG_RETURN_NULL();
2697 }
2698
2699 /* Mirror non-collections right back */
2700 if (!lwgeom_is_collection(lwgeom))
2701 {
2702 /* Non-collections of the matching type go back */
2703 if (lwgeom_type == type)
2704 {
2705 lwgeom_free(lwgeom);
2706 PG_RETURN_POINTER(input);
2707 }
2708 /* Others go back as EMPTY */
2709 else
2710 {
2711 lwcol = lwgeom_construct_empty(
2712 type, lwgeom->srid, lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom));
2713 }
2714 }
2715 else
2716 {
2717 lwcol = lwcollection_as_lwgeom(lwcollection_extract((LWCOLLECTION *)lwgeom, type));
2718 }
2719
2720 output = geometry_serialize((LWGEOM *)lwcol);
2721 lwgeom_free(lwgeom);
2722 lwgeom_free(lwcol);
2723
2724 PG_RETURN_POINTER(output);
2725}
2726
2727PG_FUNCTION_INFO_V1(ST_CollectionHomogenize);
2728Datum ST_CollectionHomogenize(PG_FUNCTION_ARGS)
2729{
2730 GSERIALIZED *input = PG_GETARG_GSERIALIZED_P(0);
2731 GSERIALIZED *output;
2732 LWGEOM *lwgeom = lwgeom_from_gserialized(input);
2733 LWGEOM *lwoutput = NULL;
2734
2735 lwoutput = lwgeom_homogenize(lwgeom);
2736 lwgeom_free(lwgeom);
2737
2738 if (!lwoutput)
2739 {
2740 PG_FREE_IF_COPY(input, 0);
2741 PG_RETURN_NULL();
2742 }
2743
2744 output = geometry_serialize(lwoutput);
2745 lwgeom_free(lwoutput);
2746
2747 PG_FREE_IF_COPY(input, 0);
2748 PG_RETURN_POINTER(output);
2749}
2750
2751Datum ST_RemoveRepeatedPoints(PG_FUNCTION_ARGS);
2752PG_FUNCTION_INFO_V1(ST_RemoveRepeatedPoints);
2753Datum ST_RemoveRepeatedPoints(PG_FUNCTION_ARGS)
2754{
2755 GSERIALIZED *g_in = PG_GETARG_GSERIALIZED_P_COPY(0);
2756 uint32_t type = gserialized_get_type(g_in);
2757 GSERIALIZED *g_out;
2758 LWGEOM *lwgeom_in = NULL;
2759 double tolerance = 0.0;
2760 int modified = LW_FALSE;
2761
2762 /* Don't even start to think about points */
2763 if (type == POINTTYPE)
2764 PG_RETURN_POINTER(g_in);
2765
2766 if (PG_NARGS() > 1 && !PG_ARGISNULL(1))
2767 tolerance = PG_GETARG_FLOAT8(1);
2768
2769 lwgeom_in = lwgeom_from_gserialized(g_in);
2770 modified = lwgeom_remove_repeated_points_in_place(lwgeom_in, tolerance);
2771 if (!modified)
2772 {
2773 /* Since there were no changes, we can return the input to avoid the serialization */
2774 PG_RETURN_POINTER(g_in);
2775 }
2776
2777 g_out = geometry_serialize(lwgeom_in);
2778
2779 pfree(g_in);
2780 PG_RETURN_POINTER(g_out);
2781}
2782
2783Datum ST_FlipCoordinates(PG_FUNCTION_ARGS);
2784PG_FUNCTION_INFO_V1(ST_FlipCoordinates);
2785Datum ST_FlipCoordinates(PG_FUNCTION_ARGS)
2786{
2787 GSERIALIZED *in = PG_GETARG_GSERIALIZED_P_COPY(0);
2788 GSERIALIZED *out;
2789 LWGEOM *lwgeom = lwgeom_from_gserialized(in);
2790
2791 lwgeom_swap_ordinates(lwgeom, LWORD_X, LWORD_Y);
2792 out = geometry_serialize(lwgeom);
2793
2794 lwgeom_free(lwgeom);
2795 PG_FREE_IF_COPY(in, 0);
2796
2797 PG_RETURN_POINTER(out);
2798}
2799
2800static LWORD
2801ordname2ordval(char n)
2802{
2803 if (n == 'x' || n == 'X')
2804 return LWORD_X;
2805 if (n == 'y' || n == 'Y')
2806 return LWORD_Y;
2807 if (n == 'z' || n == 'Z')
2808 return LWORD_Z;
2809 if (n == 'm' || n == 'M')
2810 return LWORD_M;
2811 lwpgerror("Invalid ordinate name '%c'. Expected x,y,z or m", n);
2812 return (LWORD)-1;
2813}
2814
2815Datum ST_SwapOrdinates(PG_FUNCTION_ARGS);
2816PG_FUNCTION_INFO_V1(ST_SwapOrdinates);
2817Datum ST_SwapOrdinates(PG_FUNCTION_ARGS)
2818{
2819 GSERIALIZED *in;
2820 GSERIALIZED *out;
2821 LWGEOM *lwgeom;
2822 const char *ospec;
2823 LWORD o1, o2;
2824
2825 ospec = PG_GETARG_CSTRING(1);
2826 if (strlen(ospec) != 2)
2827 {
2828 lwpgerror(
2829 "Invalid ordinate specification. "
2830 "Need two letters from the set (x,y,z,m). "
2831 "Got '%s'",
2832 ospec);
2833 PG_RETURN_NULL();
2834 }
2835 o1 = ordname2ordval(ospec[0]);
2836 o2 = ordname2ordval(ospec[1]);
2837
2838 in = PG_GETARG_GSERIALIZED_P_COPY(0);
2839
2840 /* Check presence of given ordinates */
2841 if ((o1 == LWORD_M || o2 == LWORD_M) && !gserialized_has_m(in))
2842 {
2843 lwpgerror("Geometry does not have an M ordinate");
2844 PG_RETURN_NULL();
2845 }
2846 if ((o1 == LWORD_Z || o2 == LWORD_Z) && !gserialized_has_z(in))
2847 {
2848 lwpgerror("Geometry does not have a Z ordinate");
2849 PG_RETURN_NULL();
2850 }
2851
2852 /* Nothing to do if swapping the same ordinate, pity for the copy... */
2853 if (o1 == o2)
2854 PG_RETURN_POINTER(in);
2855
2856 lwgeom = lwgeom_from_gserialized(in);
2857 lwgeom_swap_ordinates(lwgeom, o1, o2);
2858 out = geometry_serialize(lwgeom);
2859 lwgeom_free(lwgeom);
2860 PG_FREE_IF_COPY(in, 0);
2861 PG_RETURN_POINTER(out);
2862}
2863
2864/*
2865 * ST_BoundingDiagonal(inp geometry, fits boolean)
2866 */
2867Datum ST_BoundingDiagonal(PG_FUNCTION_ARGS);
2868PG_FUNCTION_INFO_V1(ST_BoundingDiagonal);
2869Datum ST_BoundingDiagonal(PG_FUNCTION_ARGS)
2870{
2871 GSERIALIZED *geom_in = PG_GETARG_GSERIALIZED_P(0);
2872 GSERIALIZED *geom_out;
2873 bool fits = PG_GETARG_BOOL(1);
2874 LWGEOM *lwgeom_in = lwgeom_from_gserialized(geom_in);
2875 LWGEOM *lwgeom_out;
2876 const GBOX *gbox;
2877 int hasz = FLAGS_GET_Z(lwgeom_in->flags);
2878 int hasm = FLAGS_GET_M(lwgeom_in->flags);
2879 int32_t srid = lwgeom_in->srid;
2880 POINT4D pt;
2881 POINTARRAY *pa;
2882
2883 if (fits)
2884 {
2885 /* unregister any cached bbox to ensure it's recomputed */
2886 lwgeom_in->bbox = NULL;
2887 }
2888
2889 gbox = lwgeom_get_bbox(lwgeom_in);
2890
2891 if (!gbox)
2892 {
2893 lwgeom_out = lwgeom_construct_empty(LINETYPE, srid, hasz, hasm);
2894 }
2895 else
2896 {
2897 pa = ptarray_construct_empty(hasz, hasm, 2);
2898 pt.x = gbox->xmin;
2899 pt.y = gbox->ymin;
2900 pt.z = gbox->zmin;
2901 pt.m = gbox->mmin;
2902 ptarray_append_point(pa, &pt, LW_TRUE);
2903 pt.x = gbox->xmax;
2904 pt.y = gbox->ymax;
2905 pt.z = gbox->zmax;
2906 pt.m = gbox->mmax;
2907 ptarray_append_point(pa, &pt, LW_TRUE);
2908 lwgeom_out = lwline_as_lwgeom(lwline_construct(srid, NULL, pa));
2909 }
2910
2911 lwgeom_free(lwgeom_in);
2912 PG_FREE_IF_COPY(geom_in, 0);
2913
2914 geom_out = geometry_serialize(lwgeom_out);
2915 lwgeom_free(lwgeom_out);
2916
2917 PG_RETURN_POINTER(geom_out);
2918}
2919
2920Datum ST_Scale(PG_FUNCTION_ARGS);
2921PG_FUNCTION_INFO_V1(ST_Scale);
2922Datum ST_Scale(PG_FUNCTION_ARGS)
2923{
2924 GSERIALIZED *geom;
2925 GSERIALIZED *geom_scale = PG_GETARG_GSERIALIZED_P(1);
2926 GSERIALIZED *geom_origin = NULL;
2927 LWGEOM *lwg, *lwg_scale, *lwg_origin;
2928 LWPOINT *lwpt_scale, *lwpt_origin;
2929 POINT4D origin;
2930 POINT4D factors;
2931 bool translate = false;
2932 GSERIALIZED *ret;
2933 AFFINE aff;
2934
2935 /* Make sure we have a valid scale input */
2936 lwg_scale = lwgeom_from_gserialized(geom_scale);
2937 lwpt_scale = lwgeom_as_lwpoint(lwg_scale);
2938 if (!lwpt_scale)
2939 {
2940 lwgeom_free(lwg_scale);
2941 PG_FREE_IF_COPY(geom_scale, 1);
2942 lwpgerror("Scale factor geometry parameter must be a point");
2943 PG_RETURN_NULL();
2944 }
2945
2946 /* Geom Will be modified in place, so take a copy */
2947 geom = PG_GETARG_GSERIALIZED_P_COPY(0);
2948 lwg = lwgeom_from_gserialized(geom);
2949
2950 /* Empty point, return input untouched */
2951 if (lwgeom_is_empty(lwg))
2952 {
2953 lwgeom_free(lwg_scale);
2954 lwgeom_free(lwg);
2955 PG_FREE_IF_COPY(geom_scale, 1);
2956 PG_RETURN_POINTER(geom);
2957 }
2958
2959 /* Once we read the scale data into local static point, we can */
2960 /* free the lwgeom */
2961 lwpoint_getPoint4d_p(lwpt_scale, &factors);
2962 if (!lwgeom_has_z(lwg_scale))
2963 factors.z = 1.0;
2964 if (!lwgeom_has_m(lwg_scale))
2965 factors.m = 1.0;
2966 lwgeom_free(lwg_scale);
2967
2968 /* Do we have the optional false origin? */
2969 if (PG_NARGS() > 2 && !PG_ARGISNULL(2))
2970 {
2971 geom_origin = PG_GETARG_GSERIALIZED_P(2);
2972 lwg_origin = lwgeom_from_gserialized(geom_origin);
2973 lwpt_origin = lwgeom_as_lwpoint(lwg_origin);
2974 if (lwpt_origin)
2975 {
2976 lwpoint_getPoint4d_p(lwpt_origin, &origin);
2977 translate = true;
2978 }
2979 /* Free the false origin inputs */
2980 lwgeom_free(lwg_origin);
2981 PG_FREE_IF_COPY(geom_origin, 2);
2982 }
2983
2984 /* If we have false origin, translate to it before scaling */
2985 if (translate)
2986 {
2987 /* Initialize affine */
2988 memset(&aff, 0, sizeof(AFFINE));
2989 /* Set rotation/scale/sheer matrix to no-op */
2990 aff.afac = aff.efac = aff.ifac = 1.0;
2991 /* Strip false origin from all coordinates */
2992 aff.xoff = -1 * origin.x;
2993 aff.yoff = -1 * origin.y;
2994 aff.zoff = -1 * origin.z;
2995 lwgeom_affine(lwg, &aff);
2996 }
2997
2998 lwgeom_scale(lwg, &factors);
2999
3000 /* Return to original origin after scaling */
3001 if (translate)
3002 {
3003 aff.xoff *= -1;
3004 aff.yoff *= -1;
3005 aff.zoff *= -1;
3006 lwgeom_affine(lwg, &aff);
3007 }
3008
3009 /* Cleanup and return */
3010 ret = geometry_serialize(lwg);
3011 lwgeom_free(lwg);
3012 PG_FREE_IF_COPY(geom, 0);
3013 PG_FREE_IF_COPY(geom_scale, 1);
3014 PG_RETURN_POINTER(ret);
3015}
3016
3017Datum ST_Points(PG_FUNCTION_ARGS);
3018PG_FUNCTION_INFO_V1(ST_Points);
3019Datum ST_Points(PG_FUNCTION_ARGS)
3020{
3021 if (PG_ARGISNULL(0))
3022 {
3023 PG_RETURN_NULL();
3024 }
3025 else
3026 {
3027 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
3028 GSERIALIZED *ret;
3029 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
3030 LWMPOINT *result = lwmpoint_from_lwgeom(lwgeom);
3031
3032 lwgeom_free(lwgeom);
3033
3034 ret = geometry_serialize(lwmpoint_as_lwgeom(result));
3035 lwmpoint_free(result);
3036 PG_RETURN_POINTER(ret);
3037 }
3038}
3039
3040PG_FUNCTION_INFO_V1(ST_QuantizeCoordinates);
3041Datum ST_QuantizeCoordinates(PG_FUNCTION_ARGS)
3042{
3043 GSERIALIZED *input;
3044 GSERIALIZED *result;
3045 LWGEOM *g;
3046 int32_t prec_x;
3047 int32_t prec_y;
3048 int32_t prec_z;
3049 int32_t prec_m;
3050
3051 if (PG_ARGISNULL(0))
3052 PG_RETURN_NULL();
3053 if (PG_ARGISNULL(1))
3054 {
3055 lwpgerror("Must specify precision");
3056 PG_RETURN_NULL();
3057 }
3058 else
3059 {
3060 prec_x = PG_GETARG_INT32(1);
3061 }
3062 prec_y = PG_ARGISNULL(2) ? prec_x : PG_GETARG_INT32(2);
3063 prec_z = PG_ARGISNULL(3) ? prec_x : PG_GETARG_INT32(3);
3064 prec_m = PG_ARGISNULL(4) ? prec_x : PG_GETARG_INT32(4);
3065
3066 input = PG_GETARG_GSERIALIZED_P_COPY(0);
3067
3068 g = lwgeom_from_gserialized(input);
3069
3070 lwgeom_trim_bits_in_place(g, prec_x, prec_y, prec_z, prec_m);
3071
3072 result = geometry_serialize(g);
3073 lwgeom_free(g);
3074 PG_FREE_IF_COPY(input, 0);
3075 PG_RETURN_POINTER(result);
3076}
3077
3078/*
3079 * ST_FilterByM(in geometry, val double precision)
3080 */
3081PG_FUNCTION_INFO_V1(LWGEOM_FilterByM);
3082Datum LWGEOM_FilterByM(PG_FUNCTION_ARGS)
3083{
3084 GSERIALIZED *geom_in;
3085 GSERIALIZED *geom_out;
3086 LWGEOM *lwgeom_in;
3087 LWGEOM *lwgeom_out;
3088 double min, max;
3089 int returnm;
3090 int hasm;
3091
3092 if (PG_NARGS() > 0 && !PG_ARGISNULL(0))
3093 {
3094 geom_in = PG_GETARG_GSERIALIZED_P(0);
3095 }
3096 else
3097 {
3098 PG_RETURN_NULL();
3099 }
3100
3101 if (PG_NARGS() > 1 && !PG_ARGISNULL(1))
3102 min = PG_GETARG_FLOAT8(1);
3103 else
3104 {
3105 min = DBL_MIN;
3106 }
3107 if (PG_NARGS() > 2 && !PG_ARGISNULL(2))
3108 max = PG_GETARG_FLOAT8(2);
3109 else
3110 {
3111 max = DBL_MAX;
3112 }
3113 if (PG_NARGS() > 3 && !PG_ARGISNULL(3) && PG_GETARG_BOOL(3))
3114 returnm = 1;
3115 else
3116 {
3117 returnm = 0;
3118 }
3119
3120 if (min > max)
3121 {
3122 elog(ERROR, "Min-value cannot be larger than Max value\n");
3123 PG_RETURN_NULL();
3124 }
3125
3126 lwgeom_in = lwgeom_from_gserialized(geom_in);
3127
3128 hasm = lwgeom_has_m(lwgeom_in);
3129
3130 if (!hasm)
3131 {
3132 elog(NOTICE, "No M-value, No vertex removed\n");
3133 PG_RETURN_POINTER(geom_in);
3134 }
3135
3136 lwgeom_out = lwgeom_filter_m(lwgeom_in, min, max, returnm);
3137
3138 geom_out = geometry_serialize(lwgeom_out);
3139 lwgeom_free(lwgeom_out);
3140 PG_RETURN_POINTER(geom_out);
3141}
Note: See TracBrowser for help on using the repository browser.