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