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