1 /*
2 
3  gg_relations.c -- Gaia spatial relations
4 
5  version 5.0, 2020 August 1
6 
7  Author: Sandro Furieri a.furieri@lqt.it
8 
9  ------------------------------------------------------------------------------
10 
11  Version: MPL 1.1/GPL 2.0/LGPL 2.1
12 
13  The contents of this file are subject to the Mozilla Public License Version
14  1.1 (the "License"); you may not use this file except in compliance with
15  the License. You may obtain a copy of the License at
16  http://www.mozilla.org/MPL/
17 
18 Software distributed under the License is distributed on an "AS IS" basis,
19 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
20 for the specific language governing rights and limitations under the
21 License.
22 
23 The Original Code is the SpatiaLite library
24 
25 The Initial Developer of the Original Code is Alessandro Furieri
26 
27 Portions created by the Initial Developer are Copyright (C) 2008-2021
28 the Initial Developer. All Rights Reserved.
29 
30 Contributor(s):
31 
32 Alternatively, the contents of this file may be used under the terms of
33 either the GNU General Public License Version 2 or later (the "GPL"), or
34 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
35 in which case the provisions of the GPL or the LGPL are applicable instead
36 of those above. If you wish to allow use of your version of this file only
37 under the terms of either the GPL or the LGPL, and not to allow others to
38 use your version of this file under the terms of the MPL, indicate your
39 decision by deleting the provisions above and replace them with the notice
40 and other provisions required by the GPL or the LGPL. If you do not delete
41 the provisions above, a recipient may use your version of this file under
42 the terms of any one of the MPL, the GPL or the LGPL.
43 
44 */
45 
46 #include <sys/types.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <float.h>
51 
52 #if defined(_WIN32) && !defined(__MINGW32__)
53 #include "config-msvc.h"
54 #else
55 #include "config.h"
56 #endif
57 
58 #ifndef OMIT_GEOS		/* including GEOS */
59 #ifdef GEOS_REENTRANT
60 #ifdef GEOS_ONLY_REENTRANT
61 #define GEOS_USE_ONLY_R_API	/* only fully thread-safe GEOS API */
62 #endif
63 #endif
64 #include <geos_c.h>
65 #endif
66 
67 #include <spatialite_private.h>
68 #include <spatialite/sqlite.h>
69 
70 #include <spatialite/gaiageo.h>
71 
72 /* GLOBAL variables */
73 char *gaia_geos_error_msg = NULL;
74 char *gaia_geos_warning_msg = NULL;
75 char *gaia_geosaux_error_msg = NULL;
76 
77 SPATIALITE_PRIVATE void
splite_free_geos_cache_item(struct splite_geos_cache_item * p)78 splite_free_geos_cache_item (struct splite_geos_cache_item *p)
79 {
80 #ifndef OMIT_GEOS		/* including GEOS */
81 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
82     if (p->preparedGeosGeom)
83 	GEOSPreparedGeom_destroy (p->preparedGeosGeom);
84     if (p->geosGeom)
85 	GEOSGeom_destroy (p->geosGeom);
86 #endif
87 #endif
88     p->geosGeom = NULL;
89     p->preparedGeosGeom = NULL;
90 }
91 
92 SPATIALITE_PRIVATE void
splite_free_geos_cache_item_r(const void * p_cache,struct splite_geos_cache_item * p)93 splite_free_geos_cache_item_r (const void *p_cache,
94 			       struct splite_geos_cache_item *p)
95 {
96 #ifndef OMIT_GEOS		/* including GEOS */
97     struct splite_internal_cache *cache =
98 	(struct splite_internal_cache *) p_cache;
99     GEOSContextHandle_t handle = NULL;
100     if (cache == NULL)
101       {
102 	  splite_free_geos_cache_item (p);
103 	  return;
104       }
105     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
106 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
107       {
108 	  splite_free_geos_cache_item (p);
109 	  return;
110       }
111     handle = cache->GEOS_handle;
112     if (handle == NULL)
113       {
114 	  splite_free_geos_cache_item (p);
115 	  return;
116       }
117     if (p->preparedGeosGeom)
118 	GEOSPreparedGeom_destroy_r (handle, p->preparedGeosGeom);
119     if (p->geosGeom)
120 	GEOSGeom_destroy_r (handle, p->geosGeom);
121 #endif
122     p->geosGeom = NULL;
123     p->preparedGeosGeom = NULL;
124 }
125 
126 GAIAGEO_DECLARE void
gaiaResetGeosMsg()127 gaiaResetGeosMsg ()
128 {
129 /* resets the GEOS error and warning messages */
130     if (gaia_geos_error_msg != NULL)
131 	free (gaia_geos_error_msg);
132     if (gaia_geos_warning_msg != NULL)
133 	free (gaia_geos_warning_msg);
134     if (gaia_geosaux_error_msg != NULL)
135 	free (gaia_geosaux_error_msg);
136     gaia_geos_error_msg = NULL;
137     gaia_geos_warning_msg = NULL;
138     gaia_geosaux_error_msg = NULL;
139 }
140 
141 GAIAGEO_DECLARE const char *
gaiaGetGeosErrorMsg()142 gaiaGetGeosErrorMsg ()
143 {
144 /* return the latest GEOS error message */
145     return gaia_geos_error_msg;
146 }
147 
148 GAIAGEO_DECLARE const char *
gaiaGetGeosWarningMsg()149 gaiaGetGeosWarningMsg ()
150 {
151 /* return the latest GEOS error message */
152     return gaia_geos_warning_msg;
153 }
154 
155 GAIAGEO_DECLARE const char *
gaiaGetGeosAuxErrorMsg()156 gaiaGetGeosAuxErrorMsg ()
157 {
158 /* return the latest GEOS (auxialiary) error message */
159     return gaia_geosaux_error_msg;
160 }
161 
162 GAIAGEO_DECLARE void
gaiaSetGeosErrorMsg(const char * msg)163 gaiaSetGeosErrorMsg (const char *msg)
164 {
165 /* setting the latest GEOS error message */
166     int len;
167     if (gaia_geos_error_msg != NULL)
168 	free (gaia_geos_error_msg);
169     gaia_geos_error_msg = NULL;
170     if (msg == NULL)
171 	return;
172     len = strlen (msg);
173     gaia_geos_error_msg = malloc (len + 1);
174     strcpy (gaia_geos_error_msg, msg);
175 }
176 
177 GAIAGEO_DECLARE void
gaiaSetGeosWarningMsg(const char * msg)178 gaiaSetGeosWarningMsg (const char *msg)
179 {
180 /* setting the latest GEOS error message */
181     int len;
182     if (gaia_geos_warning_msg != NULL)
183 	free (gaia_geos_warning_msg);
184     gaia_geos_warning_msg = NULL;
185     if (msg == NULL)
186 	return;
187     len = strlen (msg);
188     gaia_geos_warning_msg = malloc (len + 1);
189     strcpy (gaia_geos_warning_msg, msg);
190 }
191 
192 GAIAGEO_DECLARE void
gaiaSetGeosAuxErrorMsg(const char * msg)193 gaiaSetGeosAuxErrorMsg (const char *msg)
194 {
195 /* setting the latest GEOS (auxiliary) error message */
196     int len;
197     if (gaia_geosaux_error_msg != NULL)
198 	free (gaia_geosaux_error_msg);
199     gaia_geosaux_error_msg = NULL;
200     if (msg == NULL)
201 	return;
202     len = strlen (msg);
203     gaia_geosaux_error_msg = malloc (len + 1);
204     strcpy (gaia_geosaux_error_msg, msg);
205 }
206 
207 static int
check_point(double * coords,int points,double x,double y)208 check_point (double *coords, int points, double x, double y)
209 {
210 /* checks if [X,Y] point is defined into this coordinate array [Linestring or Ring] */
211     int iv;
212     double xx;
213     double yy;
214     for (iv = 0; iv < points; iv++)
215       {
216 	  gaiaGetPoint (coords, iv, &xx, &yy);
217 	  if (xx == x && yy == y)
218 	      return 1;
219       }
220     return 0;
221 }
222 
223 GAIAGEO_DECLARE int
gaiaLinestringEquals(gaiaLinestringPtr line1,gaiaLinestringPtr line2)224 gaiaLinestringEquals (gaiaLinestringPtr line1, gaiaLinestringPtr line2)
225 {
226 /* checks if two Linestrings are "spatially equal" */
227     int iv;
228     double x;
229     double y;
230     if (line1->Points != line2->Points)
231 	return 0;
232     for (iv = 0; iv < line1->Points; iv++)
233       {
234 	  gaiaGetPoint (line1->Coords, iv, &x, &y);
235 	  if (!check_point (line2->Coords, line2->Points, x, y))
236 	      return 0;
237       }
238     return 1;
239 }
240 
241 GAIAGEO_DECLARE int
gaiaPolygonEquals(gaiaPolygonPtr polyg1,gaiaPolygonPtr polyg2)242 gaiaPolygonEquals (gaiaPolygonPtr polyg1, gaiaPolygonPtr polyg2)
243 {
244 /* checks if two Polygons are "spatially equal" */
245     int ib;
246     int ib2;
247     int iv;
248     int ok2;
249     double x;
250     double y;
251     gaiaRingPtr ring1;
252     gaiaRingPtr ring2;
253     if (polyg1->NumInteriors != polyg2->NumInteriors)
254 	return 0;
255 /* checking the EXTERIOR RINGs */
256     ring1 = polyg1->Exterior;
257     ring2 = polyg2->Exterior;
258     if (ring1->Points != ring2->Points)
259 	return 0;
260     for (iv = 0; iv < ring1->Points; iv++)
261       {
262 	  gaiaGetPoint (ring1->Coords, iv, &x, &y);
263 	  if (!check_point (ring2->Coords, ring2->Points, x, y))
264 	      return 0;
265       }
266     for (ib = 0; ib < polyg1->NumInteriors; ib++)
267       {
268 	  /* checking the INTERIOR RINGS */
269 	  int ok = 0;
270 	  ring1 = polyg1->Interiors + ib;
271 	  for (ib2 = 0; ib2 < polyg2->NumInteriors; ib2++)
272 	    {
273 		ok2 = 1;
274 		ring2 = polyg2->Interiors + ib2;
275 		for (iv = 0; iv < ring1->Points; iv++)
276 		  {
277 		      gaiaGetPoint (ring1->Coords, iv, &x, &y);
278 		      if (!check_point (ring2->Coords, ring2->Points, x, y))
279 			{
280 			    ok2 = 0;
281 			    break;
282 			}
283 		  }
284 		if (ok2)
285 		  {
286 		      ok = 1;
287 		      break;
288 		  }
289 	    }
290 	  if (!ok)
291 	      return 0;
292       }
293     return 1;
294 }
295 
296 #ifndef OMIT_GEOS		/* including GEOS */
297 
298 static int
splite_mbr_overlaps(gaiaGeomCollPtr g1,gaiaGeomCollPtr g2)299 splite_mbr_overlaps (gaiaGeomCollPtr g1, gaiaGeomCollPtr g2)
300 {
301 /* checks if two MBRs do overlap */
302     if (g1->MaxX < g2->MinX)
303 	return 0;
304     if (g1->MinX > g2->MaxX)
305 	return 0;
306     if (g1->MaxY < g2->MinY)
307 	return 0;
308     if (g1->MinY > g2->MaxY)
309 	return 0;
310     return 1;
311 }
312 
313 static int
splite_mbr_contains(gaiaGeomCollPtr g1,gaiaGeomCollPtr g2)314 splite_mbr_contains (gaiaGeomCollPtr g1, gaiaGeomCollPtr g2)
315 {
316 /* checks if MBR#1 fully contains MBR#2 */
317     if (g2->MinX < g1->MinX)
318 	return 0;
319     if (g2->MaxX > g1->MaxX)
320 	return 0;
321     if (g2->MinY < g1->MinY)
322 	return 0;
323     if (g2->MaxY > g1->MaxY)
324 	return 0;
325     return 1;
326 }
327 
328 static int
splite_mbr_within(gaiaGeomCollPtr g1,gaiaGeomCollPtr g2)329 splite_mbr_within (gaiaGeomCollPtr g1, gaiaGeomCollPtr g2)
330 {
331 /* checks if MBR#1 is fully contained within MBR#2 */
332     if (g1->MinX < g2->MinX)
333 	return 0;
334     if (g1->MaxX > g2->MaxX)
335 	return 0;
336     if (g1->MinY < g2->MinY)
337 	return 0;
338     if (g1->MaxY > g2->MaxY)
339 	return 0;
340     return 1;
341 }
342 
343 static int
splite_mbr_equals(gaiaGeomCollPtr g1,gaiaGeomCollPtr g2)344 splite_mbr_equals (gaiaGeomCollPtr g1, gaiaGeomCollPtr g2)
345 {
346 /* checks if MBR#1 equals MBR#2 */
347     if (g1->MinX != g2->MinX)
348 	return 0;
349     if (g1->MaxX != g2->MaxX)
350 	return 0;
351     if (g1->MinY != g2->MinY)
352 	return 0;
353     if (g1->MaxY != g2->MaxY)
354 	return 0;
355     return 1;
356 }
357 
358 static int
evalGeosCacheItem(unsigned char * blob,int blob_size,uLong crc,struct splite_geos_cache_item * p)359 evalGeosCacheItem (unsigned char *blob, int blob_size, uLong crc,
360 		   struct splite_geos_cache_item *p)
361 {
362 /* evaluting if this one could be a valid cache hit */
363     if (blob_size != p->gaiaBlobSize)
364       {
365 	  /* surely not a match; different size */
366 	  return 0;
367       }
368     if (crc != p->crc32)
369       {
370 	  /* surely not a match: different CRC32 */
371 	  return 0;
372       }
373 
374 /* the first 46 bytes of the BLOB contain the MBR,
375    the SRID and the Type; so are assumed to represent
376    a valid signature */
377     if (memcmp (blob, p->gaiaBlob, 46) == 0)
378 	return 1;
379     return 0;
380 }
381 
382 static int
sniffTinyPointBlob(const unsigned char * blob,const int size)383 sniffTinyPointBlob (const unsigned char *blob, const int size)
384 {
385 /* sniffing for a possible TinyPoint BLOB */
386     if (size == 24 || size == 32 || size == 40)
387 	;
388     else
389 	return 0;
390     if (*(blob + 0) != GAIA_MARK_START)
391 	return 0;
392     if (*(blob + 1) == GAIA_TINYPOINT_LITTLE_ENDIAN
393 	|| *(blob + 1) == GAIA_TINYPOINT_BIG_ENDIAN)
394 	;
395     else
396 	return 0;
397     if (*(blob + (size - 1)) != GAIA_MARK_END)
398 	return 0;
399     return 1;
400 }
401 
402 static void
tinyPoint2Geom(const unsigned char * tiny,unsigned char ** geom,int * geom_sz)403 tinyPoint2Geom (const unsigned char *tiny, unsigned char **geom, int *geom_sz)
404 {
405 /* quickly converting from BLOB-TinyPoint to BLOB-Geometry */
406     int little_endian;
407     int endian_arch = gaiaEndianArch ();
408     int srid;
409     int type;
410     double x;
411     double y;
412     double z;
413     double m;
414     unsigned char *blob;
415     int blob_sz;
416 
417 /* parsing the BLOB-TinyPoint */
418     if (*(tiny + 1) == GAIA_TINYPOINT_LITTLE_ENDIAN)
419 	little_endian = 1;
420     else
421 	little_endian = 0;
422     srid = gaiaImport32 (tiny + 2, little_endian, endian_arch);
423     if (*(tiny + 6) == GAIA_TINYPOINT_XYZ)
424 	type = GAIA_POINTZ;
425     else if (*(tiny + 6) == GAIA_TINYPOINT_XYM)
426 	type = GAIA_POINTM;
427     else if (*(tiny + 6) == GAIA_TINYPOINT_XYZM)
428 	type = GAIA_POINTZM;
429     else
430 	type = GAIA_POINT;
431     x = gaiaImport64 (tiny + 7, little_endian, endian_arch);
432     y = gaiaImport64 (tiny + 15, little_endian, endian_arch);
433     if (type == GAIA_POINTZ)
434 	z = gaiaImport64 (tiny + 23, little_endian, endian_arch);
435     if (type == GAIA_POINTM)
436 	m = gaiaImport64 (tiny + 23, little_endian, endian_arch);
437     if (type == GAIA_POINTZM)
438       {
439 	  z = gaiaImport64 (tiny + 23, little_endian, endian_arch);
440 	  m = gaiaImport64 (tiny + 31, little_endian, endian_arch);
441       }
442 
443 /* allocating and initializing the BLOB-Geometry */
444     switch (type)
445       {
446       case GAIA_POINT:
447 	  blob_sz = 60;
448 	  break;
449       case GAIA_POINTZ:
450       case GAIA_POINTM:
451 	  blob_sz = 68;
452 	  break;
453       case GAIA_POINTZM:
454 	  blob_sz = 76;
455 	  break;
456       };
457     blob = malloc (blob_sz);
458 
459     *geom = blob;
460     *geom_sz = blob_sz;
461     switch (type)
462       {
463       case GAIA_POINT:
464 	  *blob = GAIA_MARK_START;	/* START signature */
465 	  *(blob + 1) = GAIA_LITTLE_ENDIAN;	/* byte ordering */
466 	  gaiaExport32 (blob + 2, srid, 1, endian_arch);	/* the SRID */
467 	  gaiaExport64 (blob + 6, x, 1, endian_arch);	/* MBR - minimum X */
468 	  gaiaExport64 (blob + 14, y, 1, endian_arch);	/* MBR - minimum Y */
469 	  gaiaExport64 (blob + 22, x, 1, endian_arch);	/* MBR - maximum X */
470 	  gaiaExport64 (blob + 30, y, 1, endian_arch);	/* MBR - maximum Y */
471 	  *(blob + 38) = GAIA_MARK_MBR;	/* MBR signature */
472 	  gaiaExport32 (blob + 39, GAIA_POINT, 1, endian_arch);	/* class POINT */
473 	  gaiaExport64 (blob + 43, x, 1, endian_arch);	/* X */
474 	  gaiaExport64 (blob + 51, y, 1, endian_arch);	/* Y */
475 	  *(blob + 59) = GAIA_MARK_END;	/* END signature */
476 	  break;
477       case GAIA_POINTZ:
478 	  *blob = GAIA_MARK_START;	/* START signature */
479 	  *(blob + 1) = GAIA_LITTLE_ENDIAN;	/* byte ordering */
480 	  gaiaExport32 (blob + 2, srid, 1, endian_arch);	/* the SRID */
481 	  gaiaExport64 (blob + 6, x, 1, endian_arch);	/* MBR - minimum X */
482 	  gaiaExport64 (blob + 14, y, 1, endian_arch);	/* MBR - minimum Y */
483 	  gaiaExport64 (blob + 22, x, 1, endian_arch);	/* MBR - maximum X */
484 	  gaiaExport64 (blob + 30, y, 1, endian_arch);	/* MBR - maximum Y */
485 	  *(blob + 38) = GAIA_MARK_MBR;	/* MBR signature */
486 	  gaiaExport32 (blob + 39, GAIA_POINTZ, 1, endian_arch);	/* class POINT XYZ */
487 	  gaiaExport64 (blob + 43, x, 1, endian_arch);	/* X */
488 	  gaiaExport64 (blob + 51, y, 1, endian_arch);	/* Y */
489 	  gaiaExport64 (blob + 59, z, 1, endian_arch);	/* Z */
490 	  *(blob + 67) = GAIA_MARK_END;	/* END signature */
491 	  break;
492       case GAIA_POINTM:
493 	  *blob = GAIA_MARK_START;	/* START signature */
494 	  *(blob + 1) = GAIA_LITTLE_ENDIAN;	/* byte ordering */
495 	  gaiaExport32 (blob + 2, srid, 1, endian_arch);	/* the SRID */
496 	  gaiaExport64 (blob + 6, x, 1, endian_arch);	/* MBR - minimum X */
497 	  gaiaExport64 (blob + 14, y, 1, endian_arch);	/* MBR - minimum Y */
498 	  gaiaExport64 (blob + 22, x, 1, endian_arch);	/* MBR - maximum X */
499 	  gaiaExport64 (blob + 30, y, 1, endian_arch);	/* MBR - maximum Y */
500 	  *(blob + 38) = GAIA_MARK_MBR;	/* MBR signature */
501 	  gaiaExport32 (blob + 39, GAIA_POINTM, 1, endian_arch);	/* class POINT XYM */
502 	  gaiaExport64 (blob + 43, x, 1, endian_arch);	/* X */
503 	  gaiaExport64 (blob + 51, y, 1, endian_arch);	/* Y */
504 	  gaiaExport64 (blob + 59, m, 1, endian_arch);	/* M */
505 	  *(blob + 67) = GAIA_MARK_END;	/* END signature */
506 	  break;
507       case GAIA_POINTZM:
508 	  *blob = GAIA_MARK_START;	/* START signature */
509 	  *(blob + 1) = GAIA_LITTLE_ENDIAN;	/* byte ordering */
510 	  gaiaExport32 (blob + 2, srid, 1, endian_arch);	/* the SRID */
511 	  gaiaExport64 (blob + 6, x, 1, endian_arch);	/* MBR - minimum X */
512 	  gaiaExport64 (blob + 14, y, 1, endian_arch);	/* MBR - minimum Y */
513 	  gaiaExport64 (blob + 22, x, 1, endian_arch);	/* MBR - maximum X */
514 	  gaiaExport64 (blob + 30, y, 1, endian_arch);	/* MBR - maximum Y */
515 	  *(blob + 38) = GAIA_MARK_MBR;	/* MBR signature */
516 	  gaiaExport32 (blob + 39, GAIA_POINTZM, 1, endian_arch);	/* class POINT XYZM */
517 	  gaiaExport64 (blob + 43, x, 1, endian_arch);	/* X */
518 	  gaiaExport64 (blob + 51, y, 1, endian_arch);	/* Y */
519 	  gaiaExport64 (blob + 59, z, 1, endian_arch);	/* M */
520 	  gaiaExport64 (blob + 67, m, 1, endian_arch);	/* Z */
521 	  *(blob + 75) = GAIA_MARK_END;	/* END signature */
522 	  break;
523       };
524 }
525 
526 static int
evalGeosCache(struct splite_internal_cache * cache,gaiaGeomCollPtr geom1,const unsigned char * blob1,const int size1,gaiaGeomCollPtr geom2,const unsigned char * blob2,const int size2,GEOSPreparedGeometry ** gPrep,gaiaGeomCollPtr * geom)527 evalGeosCache (struct splite_internal_cache *cache, gaiaGeomCollPtr geom1,
528 	       const unsigned char *blob1, const int size1,
529 	       gaiaGeomCollPtr geom2, const unsigned char *blob2,
530 	       const int size2, GEOSPreparedGeometry ** gPrep,
531 	       gaiaGeomCollPtr * geom)
532 {
533 /* handling the internal GEOS cache */
534     struct splite_geos_cache_item *p1 = &(cache->cacheItem1);
535     struct splite_geos_cache_item *p2 = &(cache->cacheItem2);
536     uLong crc1;
537     uLong crc2;
538     unsigned char *tiny1 = NULL;
539     unsigned char *tiny2 = NULL;
540     unsigned char *p_blob1;
541     unsigned char *p_blob2;
542     int sz1;
543     int sz2;
544     int tiny_sz;
545     int retcode;
546     GEOSContextHandle_t handle = NULL;
547     if (cache == NULL)
548 	return 0;
549     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
550 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
551 	return 0;
552     handle = cache->GEOS_handle;
553     if (handle == NULL)
554 	return 0;
555 
556     if (sniffTinyPointBlob (blob1, size1))
557       {
558 	  tinyPoint2Geom (blob1, &tiny1, &tiny_sz);
559 	  p_blob1 = tiny1;
560 	  sz1 = tiny_sz;
561       }
562     else
563       {
564 	  p_blob1 = (unsigned char *) blob1;
565 	  sz1 = size1;
566       }
567     if (sniffTinyPointBlob (blob2, size2))
568       {
569 	  tinyPoint2Geom (blob2, &tiny2, &tiny_sz);
570 	  p_blob2 = tiny2;
571 	  sz2 = tiny_sz;
572       }
573     else
574       {
575 	  p_blob2 = (unsigned char *) blob2;
576 	  sz2 = size2;
577       }
578     crc1 = crc32 (0L, p_blob1, sz1);
579     crc2 = crc32 (0L, p_blob2, sz2);
580 
581 /* checking the first cache item */
582     if (evalGeosCacheItem (p_blob1, sz1, crc1, p1))
583       {
584 	  /* found a matching item */
585 	  if (p1->preparedGeosGeom == NULL)
586 	    {
587 		/* preparing the GeosGeometries */
588 		p1->geosGeom = gaiaToGeos_r (cache, geom1);
589 		if (p1->geosGeom)
590 		  {
591 		      p1->preparedGeosGeom =
592 			  (void *) GEOSPrepare_r (handle, p1->geosGeom);
593 		      if (p1->preparedGeosGeom == NULL)
594 			{
595 			    /* unexpected failure */
596 			    GEOSGeom_destroy_r (handle, p1->geosGeom);
597 			    p1->geosGeom = NULL;
598 			}
599 		  }
600 	    }
601 	  if (p1->preparedGeosGeom)
602 	    {
603 		/* returning the corresponding GeosPreparedGeometry */
604 		*gPrep = p1->preparedGeosGeom;
605 		*geom = geom2;
606 		retcode = 1;
607 		goto end;
608 	    }
609 	  retcode = 0;
610 	  goto end;
611       }
612 
613 /* checking the second cache item */
614     if (evalGeosCacheItem (p_blob2, sz2, crc2, p2))
615       {
616 	  /* found a matching item */
617 	  if (p2->preparedGeosGeom == NULL)
618 	    {
619 		/* preparing the GeosGeometries */
620 		p2->geosGeom = gaiaToGeos_r (cache, geom2);
621 		if (p2->geosGeom)
622 		  {
623 		      p2->preparedGeosGeom =
624 			  (void *) GEOSPrepare_r (handle, p2->geosGeom);
625 		      if (p2->preparedGeosGeom == NULL)
626 			{
627 			    /* unexpected failure */
628 			    GEOSGeom_destroy_r (handle, p2->geosGeom);
629 			    p2->geosGeom = NULL;
630 			}
631 		  }
632 	    }
633 	  if (p2->preparedGeosGeom)
634 	    {
635 		/* returning the corresponding GeosPreparedGeometry */
636 		*gPrep = p2->preparedGeosGeom;
637 		*geom = geom1;
638 		retcode = 1;
639 		goto end;
640 	    }
641 	  retcode = 0;
642 	  goto end;
643       }
644 
645 /* updating the GEOS cache item#1 */
646     memcpy (p1->gaiaBlob, p_blob1, 46);
647     p1->gaiaBlobSize = sz1;
648     p1->crc32 = crc1;
649     if (p1->preparedGeosGeom)
650 	GEOSPreparedGeom_destroy_r (handle, p1->preparedGeosGeom);
651     if (p1->geosGeom)
652 	GEOSGeom_destroy_r (handle, p1->geosGeom);
653     p1->geosGeom = NULL;
654     p1->preparedGeosGeom = NULL;
655 
656 /* updating the GEOS cache item#2 */
657     memcpy (p2->gaiaBlob, p_blob2, 46);
658     p2->gaiaBlobSize = sz2;
659     p2->crc32 = crc2;
660     if (p2->preparedGeosGeom)
661 	GEOSPreparedGeom_destroy_r (handle, p2->preparedGeosGeom);
662     if (p2->geosGeom)
663 	GEOSGeom_destroy_r (handle, p2->geosGeom);
664     p2->geosGeom = NULL;
665     p2->preparedGeosGeom = NULL;
666     retcode = 0;
667 
668   end:
669     if (tiny1 != NULL)
670 	free (tiny1);
671     if (tiny2 != NULL)
672 	free (tiny2);
673     return retcode;
674 }
675 
676 GAIAGEO_DECLARE int
gaiaGeomCollEquals(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)677 gaiaGeomCollEquals (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
678 {
679 /* checks if two Geometries are "spatially equal" */
680     int ret = -1;
681 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
682     GEOSGeometry *g1;
683     GEOSGeometry *g2;
684     gaiaResetGeosMsg ();
685     if (!geom1 || !geom2)
686 	return -1;
687     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
688 	return -1;
689 
690 /* quick check based on MBRs comparison */
691     if (!splite_mbr_equals (geom1, geom2))
692 	return 0;
693 
694     g1 = gaiaToGeos (geom1);
695     g2 = gaiaToGeos (geom2);
696     ret = GEOSEquals (g1, g2);
697     GEOSGeom_destroy (g1);
698     GEOSGeom_destroy (g2);
699 #else
700     if (geom1 == NULL || geom2 == NULL)
701 	geom1 = NULL;		/* silencing stupid compiler warnings */
702 #endif
703     return ret;
704 }
705 
706 GAIAGEO_DECLARE int
gaiaGeomCollEquals_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)707 gaiaGeomCollEquals_r (const void *p_cache, gaiaGeomCollPtr geom1,
708 		      gaiaGeomCollPtr geom2)
709 {
710 /* checks if two Geometries are "spatially equal" */
711     int ret;
712     GEOSGeometry *g1;
713     GEOSGeometry *g2;
714     struct splite_internal_cache *cache =
715 	(struct splite_internal_cache *) p_cache;
716     GEOSContextHandle_t handle = NULL;
717     if (cache == NULL)
718 	return -1;
719     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
720 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
721 	return -1;
722     handle = cache->GEOS_handle;
723     if (handle == NULL)
724 	return -1;
725     gaiaResetGeosMsg_r (cache);
726     if (!geom1 || !geom2)
727 	return -1;
728     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
729 	return -1;
730 
731 /* quick check based on MBRs comparison */
732     if (!splite_mbr_equals (geom1, geom2))
733 	return 0;
734 
735     g1 = gaiaToGeos_r (cache, geom1);
736     g2 = gaiaToGeos_r (cache, geom2);
737     ret = GEOSEquals_r (handle, g1, g2);
738     GEOSGeom_destroy_r (handle, g1);
739     GEOSGeom_destroy_r (handle, g2);
740     return ret;
741 }
742 
743 GAIAGEO_DECLARE int
gaiaGeomCollIntersects(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)744 gaiaGeomCollIntersects (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
745 {
746 /* checks if two Geometries do "spatially intersects" */
747     int ret = -1;
748 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
749     GEOSGeometry *g1;
750     GEOSGeometry *g2;
751     gaiaResetGeosMsg ();
752     if (!geom1 || !geom2)
753 	return -1;
754     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
755 	return -1;
756 
757 /* quick check based on MBRs comparison */
758     if (!splite_mbr_overlaps (geom1, geom2))
759 	return 0;
760 
761     g1 = gaiaToGeos (geom1);
762     g2 = gaiaToGeos (geom2);
763     ret = GEOSIntersects (g1, g2);
764     GEOSGeom_destroy (g1);
765     GEOSGeom_destroy (g2);
766 #else
767     if (geom1 == NULL || geom2 == NULL)
768 	geom1 = NULL;		/* silencing stupid compiler warnings */
769 #endif
770     return ret;
771 }
772 
773 GAIAGEO_DECLARE int
gaiaGeomCollIntersects_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)774 gaiaGeomCollIntersects_r (const void *p_cache, gaiaGeomCollPtr geom1,
775 			  gaiaGeomCollPtr geom2)
776 {
777 /* checks if two Geometries do "spatially intersects" */
778     int ret;
779     GEOSGeometry *g1;
780     GEOSGeometry *g2;
781     struct splite_internal_cache *cache =
782 	(struct splite_internal_cache *) p_cache;
783     GEOSContextHandle_t handle = NULL;
784     if (cache == NULL)
785 	return -1;
786     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
787 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
788 	return -1;
789     handle = cache->GEOS_handle;
790     if (handle == NULL)
791 	return -1;
792     gaiaResetGeosMsg_r (cache);
793     if (!geom1 || !geom2)
794 	return -1;
795     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
796 	return -1;
797 
798 /* quick check based on MBRs comparison */
799     if (!splite_mbr_overlaps (geom1, geom2))
800 	return 0;
801 
802     g1 = gaiaToGeos_r (cache, geom1);
803     g2 = gaiaToGeos_r (cache, geom2);
804     ret = GEOSIntersects_r (handle, g1, g2);
805     GEOSGeom_destroy_r (handle, g1);
806     GEOSGeom_destroy_r (handle, g2);
807     return ret;
808 }
809 
810 GAIAGEO_DECLARE int
gaiaGeomCollPreparedIntersects(const void * p_cache,gaiaGeomCollPtr geom1,unsigned char * blob1,int size1,gaiaGeomCollPtr geom2,unsigned char * blob2,int size2)811 gaiaGeomCollPreparedIntersects (const void *p_cache, gaiaGeomCollPtr geom1,
812 				unsigned char *blob1, int size1,
813 				gaiaGeomCollPtr geom2, unsigned char *blob2,
814 				int size2)
815 {
816 /* checks if two Geometries do "spatially intersects" */
817     int ret;
818     struct splite_internal_cache *cache =
819 	(struct splite_internal_cache *) p_cache;
820     GEOSPreparedGeometry *gPrep;
821     GEOSGeometry *g1;
822     GEOSGeometry *g2;
823     gaiaGeomCollPtr geom;
824     GEOSContextHandle_t handle = NULL;
825     if (cache == NULL)
826 	return -1;
827     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
828 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
829 	return -1;
830     handle = cache->GEOS_handle;
831     if (handle == NULL)
832 	return -1;
833     gaiaResetGeosMsg_r (cache);
834     if (!geom1 || !geom2)
835 	return -1;
836     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
837 	return -1;
838 
839 /* quick check based on MBRs comparison */
840     if (!splite_mbr_overlaps (geom1, geom2))
841 	return 0;
842 
843 /* handling the internal GEOS cache */
844     if (evalGeosCache
845 	(cache, geom1, blob1, size1, geom2, blob2, size2, &gPrep, &geom))
846       {
847 	  g2 = gaiaToGeos_r (cache, geom);
848 	  ret = GEOSPreparedIntersects_r (handle, gPrep, g2);
849 	  GEOSGeom_destroy_r (handle, g2);
850 	  return ret;
851       }
852     g1 = gaiaToGeos_r (cache, geom1);
853     g2 = gaiaToGeos_r (cache, geom2);
854     ret = GEOSIntersects_r (handle, g1, g2);
855     GEOSGeom_destroy_r (handle, g1);
856     GEOSGeom_destroy_r (handle, g2);
857     return ret;
858 }
859 
860 GAIAGEO_DECLARE int
gaiaGeomCollDisjoint(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)861 gaiaGeomCollDisjoint (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
862 {
863 /* checks if two Geometries are "spatially disjoint" */
864     int ret = -1;
865 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
866     GEOSGeometry *g1;
867     GEOSGeometry *g2;
868     gaiaResetGeosMsg ();
869     if (!geom1 || !geom2)
870 	return -1;
871     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
872 	return -1;
873 
874 /* quick check based on MBRs comparison */
875     if (!splite_mbr_overlaps (geom1, geom2))
876 	return 1;
877 
878     g1 = gaiaToGeos (geom1);
879     g2 = gaiaToGeos (geom2);
880     ret = GEOSDisjoint (g1, g2);
881     GEOSGeom_destroy (g1);
882     GEOSGeom_destroy (g2);
883 #else
884     if (geom1 == NULL || geom2 == NULL)
885 	geom1 = NULL;		/* silencing stupid compiler warnings */
886 #endif
887     return ret;
888 }
889 
890 GAIAGEO_DECLARE int
gaiaGeomCollDisjoint_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)891 gaiaGeomCollDisjoint_r (const void *p_cache, gaiaGeomCollPtr geom1,
892 			gaiaGeomCollPtr geom2)
893 {
894 /* checks if two Geometries are "spatially disjoint" */
895     int ret;
896     GEOSGeometry *g1;
897     GEOSGeometry *g2;
898     struct splite_internal_cache *cache =
899 	(struct splite_internal_cache *) p_cache;
900     GEOSContextHandle_t handle = NULL;
901     if (cache == NULL)
902 	return -1;
903     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
904 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
905 	return -1;
906     handle = cache->GEOS_handle;
907     if (handle == NULL)
908 	return -1;
909     gaiaResetGeosMsg_r (cache);
910     if (!geom1 || !geom2)
911 	return -1;
912     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
913 	return -1;
914 
915 /* quick check based on MBRs comparison */
916     if (!splite_mbr_overlaps (geom1, geom2))
917 	return 1;
918 
919     g1 = gaiaToGeos_r (cache, geom1);
920     g2 = gaiaToGeos_r (cache, geom2);
921     ret = GEOSDisjoint_r (handle, g1, g2);
922     GEOSGeom_destroy_r (handle, g1);
923     GEOSGeom_destroy_r (handle, g2);
924     return ret;
925 }
926 
927 GAIAGEO_DECLARE int
gaiaGeomCollPreparedDisjoint(const void * p_cache,gaiaGeomCollPtr geom1,unsigned char * blob1,int size1,gaiaGeomCollPtr geom2,unsigned char * blob2,int size2)928 gaiaGeomCollPreparedDisjoint (const void *p_cache, gaiaGeomCollPtr geom1,
929 			      unsigned char *blob1, int size1,
930 			      gaiaGeomCollPtr geom2, unsigned char *blob2,
931 			      int size2)
932 {
933 /* checks if two Geometries are "spatially disjoint" */
934     int ret;
935     GEOSPreparedGeometry *gPrep;
936     GEOSGeometry *g1;
937     GEOSGeometry *g2;
938     gaiaGeomCollPtr geom;
939     GEOSContextHandle_t handle = NULL;
940     struct splite_internal_cache *cache =
941 	(struct splite_internal_cache *) p_cache;
942     if (cache == NULL)
943 	return -1;
944     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
945 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
946 	return -1;
947     handle = cache->GEOS_handle;
948     if (handle == NULL)
949 	return -1;
950     gaiaResetGeosMsg_r (cache);
951     if (!geom1 || !geom2)
952 	return -1;
953     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
954 	return -1;
955 
956 /* quick check based on MBRs comparison */
957     if (!splite_mbr_overlaps (geom1, geom2))
958 	return 1;
959 
960 /* handling the internal GEOS cache */
961     if (evalGeosCache
962 	(cache, geom1, blob1, size1, geom2, blob2, size2, &gPrep, &geom))
963       {
964 	  g2 = gaiaToGeos_r (cache, geom);
965 	  ret = GEOSPreparedDisjoint_r (handle, gPrep, g2);
966 	  GEOSGeom_destroy_r (handle, g2);
967 	  return ret;
968       }
969 
970     g1 = gaiaToGeos_r (cache, geom1);
971     g2 = gaiaToGeos_r (cache, geom2);
972     ret = GEOSDisjoint_r (handle, g1, g2);
973     GEOSGeom_destroy_r (handle, g1);
974     GEOSGeom_destroy_r (handle, g2);
975     return ret;
976 }
977 
978 GAIAGEO_DECLARE int
gaiaGeomCollOverlaps(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)979 gaiaGeomCollOverlaps (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
980 {
981 /* checks if two Geometries do "spatially overlaps" */
982     int ret = -1;
983 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
984     GEOSGeometry *g1;
985     GEOSGeometry *g2;
986     gaiaResetGeosMsg ();
987     if (!geom1 || !geom2)
988 	return -1;
989     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
990 	return -1;
991 
992 /* quick check based on MBRs comparison */
993     if (!splite_mbr_overlaps (geom1, geom2))
994 	return 0;
995 
996     g1 = gaiaToGeos (geom1);
997     g2 = gaiaToGeos (geom2);
998     ret = GEOSOverlaps (g1, g2);
999     GEOSGeom_destroy (g1);
1000     GEOSGeom_destroy (g2);
1001 #else
1002     if (geom1 == NULL || geom2 == NULL)
1003 	geom1 = NULL;		/* silencing stupid compiler warnings */
1004 #endif
1005     return ret;
1006 }
1007 
1008 GAIAGEO_DECLARE int
gaiaGeomCollOverlaps_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)1009 gaiaGeomCollOverlaps_r (const void *p_cache, gaiaGeomCollPtr geom1,
1010 			gaiaGeomCollPtr geom2)
1011 {
1012 /* checks if two Geometries do "spatially overlaps" */
1013     int ret;
1014     GEOSGeometry *g1;
1015     GEOSGeometry *g2;
1016     struct splite_internal_cache *cache =
1017 	(struct splite_internal_cache *) p_cache;
1018     GEOSContextHandle_t handle = NULL;
1019     if (cache == NULL)
1020 	return -1;
1021     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1022 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1023 	return -1;
1024     handle = cache->GEOS_handle;
1025     if (handle == NULL)
1026 	return -1;
1027     gaiaResetGeosMsg_r (cache);
1028     if (!geom1 || !geom2)
1029 	return -1;
1030     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
1031 	return -1;
1032 
1033 /* quick check based on MBRs comparison */
1034     if (!splite_mbr_overlaps (geom1, geom2))
1035 	return 0;
1036 
1037     g1 = gaiaToGeos_r (cache, geom1);
1038     g2 = gaiaToGeos_r (cache, geom2);
1039     ret = GEOSOverlaps_r (handle, g1, g2);
1040     GEOSGeom_destroy_r (handle, g1);
1041     GEOSGeom_destroy_r (handle, g2);
1042     return ret;
1043 }
1044 
1045 GAIAGEO_DECLARE int
gaiaGeomCollPreparedOverlaps(const void * p_cache,gaiaGeomCollPtr geom1,unsigned char * blob1,int size1,gaiaGeomCollPtr geom2,unsigned char * blob2,int size2)1046 gaiaGeomCollPreparedOverlaps (const void *p_cache, gaiaGeomCollPtr geom1,
1047 			      unsigned char *blob1, int size1,
1048 			      gaiaGeomCollPtr geom2, unsigned char *blob2,
1049 			      int size2)
1050 {
1051 /* checks if two Geometries do "spatially overlaps" */
1052     int ret;
1053     struct splite_internal_cache *cache =
1054 	(struct splite_internal_cache *) p_cache;
1055     GEOSPreparedGeometry *gPrep;
1056     GEOSGeometry *g1;
1057     GEOSGeometry *g2;
1058     gaiaGeomCollPtr geom;
1059     GEOSContextHandle_t handle = NULL;
1060     if (cache == NULL)
1061 	return -1;
1062     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1063 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1064 	return -1;
1065     handle = cache->GEOS_handle;
1066     if (handle == NULL)
1067 	return -1;
1068     gaiaResetGeosMsg_r (cache);
1069     if (!geom1 || !geom2)
1070 	return -1;
1071     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
1072 	return -1;
1073 
1074 /* quick check based on MBRs comparison */
1075     if (!splite_mbr_overlaps (geom1, geom2))
1076 	return 0;
1077 
1078 /* handling the internal GEOS cache */
1079     if (evalGeosCache
1080 	(cache, geom1, blob1, size1, geom2, blob2, size2, &gPrep, &geom))
1081       {
1082 	  g2 = gaiaToGeos_r (cache, geom);
1083 	  ret = GEOSPreparedOverlaps_r (handle, gPrep, g2);
1084 	  GEOSGeom_destroy_r (handle, g2);
1085 	  return ret;
1086       }
1087 
1088     g1 = gaiaToGeos_r (cache, geom1);
1089     g2 = gaiaToGeos_r (cache, geom2);
1090     ret = GEOSOverlaps_r (handle, g1, g2);
1091     GEOSGeom_destroy_r (handle, g1);
1092     GEOSGeom_destroy_r (handle, g2);
1093     return ret;
1094 }
1095 
1096 GAIAGEO_DECLARE int
gaiaGeomCollCrosses(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)1097 gaiaGeomCollCrosses (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
1098 {
1099 /* checks if two Geometries do "spatially crosses" */
1100     int ret = -1;
1101 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
1102     GEOSGeometry *g1;
1103     GEOSGeometry *g2;
1104     gaiaResetGeosMsg ();
1105     if (!geom1 || !geom2)
1106 	return -1;
1107     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
1108 	return -1;
1109 
1110 /* quick check based on MBRs comparison */
1111     if (!splite_mbr_overlaps (geom1, geom2))
1112 	return 0;
1113 
1114     g1 = gaiaToGeos (geom1);
1115     g2 = gaiaToGeos (geom2);
1116     ret = GEOSCrosses (g1, g2);
1117     GEOSGeom_destroy (g1);
1118     GEOSGeom_destroy (g2);
1119 #else
1120     if (geom1 == NULL || geom2 == NULL)
1121 	geom1 = NULL;		/* silencing stupid compiler warnings */
1122 #endif
1123     return ret;
1124 }
1125 
1126 GAIAGEO_DECLARE int
gaiaGeomCollCrosses_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)1127 gaiaGeomCollCrosses_r (const void *p_cache, gaiaGeomCollPtr geom1,
1128 		       gaiaGeomCollPtr geom2)
1129 {
1130 /* checks if two Geometries do "spatially crosses" */
1131     int ret;
1132     GEOSGeometry *g1;
1133     GEOSGeometry *g2;
1134     struct splite_internal_cache *cache =
1135 	(struct splite_internal_cache *) p_cache;
1136     GEOSContextHandle_t handle = NULL;
1137     if (cache == NULL)
1138 	return -1;
1139     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1140 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1141 	return -1;
1142     handle = cache->GEOS_handle;
1143     if (handle == NULL)
1144 	return -1;
1145     gaiaResetGeosMsg_r (cache);
1146     if (!geom1 || !geom2)
1147 	return -1;
1148     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
1149 	return -1;
1150 
1151 /* quick check based on MBRs comparison */
1152     if (!splite_mbr_overlaps (geom1, geom2))
1153 	return 0;
1154 
1155     g1 = gaiaToGeos_r (cache, geom1);
1156     g2 = gaiaToGeos_r (cache, geom2);
1157     ret = GEOSCrosses_r (handle, g1, g2);
1158     GEOSGeom_destroy_r (handle, g1);
1159     GEOSGeom_destroy_r (handle, g2);
1160     return ret;
1161 }
1162 
1163 GAIAGEO_DECLARE int
gaiaGeomCollPreparedCrosses(const void * p_cache,gaiaGeomCollPtr geom1,unsigned char * blob1,int size1,gaiaGeomCollPtr geom2,unsigned char * blob2,int size2)1164 gaiaGeomCollPreparedCrosses (const void *p_cache, gaiaGeomCollPtr geom1,
1165 			     unsigned char *blob1, int size1,
1166 			     gaiaGeomCollPtr geom2, unsigned char *blob2,
1167 			     int size2)
1168 {
1169 /* checks if two Geometries do "spatially crosses" */
1170     int ret;
1171     struct splite_internal_cache *cache =
1172 	(struct splite_internal_cache *) p_cache;
1173     GEOSPreparedGeometry *gPrep;
1174     GEOSGeometry *g1;
1175     GEOSGeometry *g2;
1176     gaiaGeomCollPtr geom;
1177     GEOSContextHandle_t handle = NULL;
1178     if (cache == NULL)
1179 	return -1;
1180     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1181 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1182 	return -1;
1183     handle = cache->GEOS_handle;
1184     if (handle == NULL)
1185 	return -1;
1186     gaiaResetGeosMsg_r (cache);
1187     if (!geom1 || !geom2)
1188 	return -1;
1189     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
1190 	return -1;
1191 
1192 /* quick check based on MBRs comparison */
1193     if (!splite_mbr_overlaps (geom1, geom2))
1194 	return 0;
1195 
1196 /* handling the internal GEOS cache */
1197     if (evalGeosCache
1198 	(cache, geom1, blob1, size1, geom2, blob2, size2, &gPrep, &geom))
1199       {
1200 	  g2 = gaiaToGeos_r (cache, geom);
1201 	  ret = GEOSPreparedCrosses_r (handle, gPrep, g2);
1202 	  GEOSGeom_destroy_r (handle, g2);
1203 	  return ret;
1204       }
1205 
1206     g1 = gaiaToGeos_r (cache, geom1);
1207     g2 = gaiaToGeos_r (cache, geom2);
1208     ret = GEOSCrosses_r (handle, g1, g2);
1209     GEOSGeom_destroy_r (handle, g1);
1210     GEOSGeom_destroy_r (handle, g2);
1211     return ret;
1212 }
1213 
1214 GAIAGEO_DECLARE int
gaiaGeomCollTouches(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)1215 gaiaGeomCollTouches (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
1216 {
1217 /* checks if two Geometries do "spatially touches" */
1218     int ret = -1;
1219 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
1220     GEOSGeometry *g1;
1221     GEOSGeometry *g2;
1222     gaiaResetGeosMsg ();
1223     if (!geom1 || !geom2)
1224 	return -1;
1225     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
1226 	return -1;
1227 
1228 /* quick check based on MBRs comparison */
1229     if (!splite_mbr_overlaps (geom1, geom2))
1230 	return 0;
1231 
1232     g1 = gaiaToGeos (geom1);
1233     g2 = gaiaToGeos (geom2);
1234     ret = GEOSTouches (g1, g2);
1235     GEOSGeom_destroy (g1);
1236     GEOSGeom_destroy (g2);
1237 #else
1238     if (geom1 == NULL || geom2 == NULL)
1239 	geom1 = NULL;		/* silencing stupid compiler warnings */
1240 #endif
1241     return ret;
1242 }
1243 
1244 GAIAGEO_DECLARE int
gaiaGeomCollTouches_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)1245 gaiaGeomCollTouches_r (const void *p_cache, gaiaGeomCollPtr geom1,
1246 		       gaiaGeomCollPtr geom2)
1247 {
1248 /* checks if two Geometries do "spatially touches" */
1249     int ret;
1250     GEOSGeometry *g1;
1251     GEOSGeometry *g2;
1252     struct splite_internal_cache *cache =
1253 	(struct splite_internal_cache *) p_cache;
1254     GEOSContextHandle_t handle = NULL;
1255     if (cache == NULL)
1256 	return -1;
1257     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1258 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1259 	return -1;
1260     handle = cache->GEOS_handle;
1261     if (handle == NULL)
1262 	return -1;
1263     gaiaResetGeosMsg_r (cache);
1264     if (!geom1 || !geom2)
1265 	return -1;
1266     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
1267 	return -1;
1268 
1269 /* quick check based on MBRs comparison */
1270     if (!splite_mbr_overlaps (geom1, geom2))
1271 	return 0;
1272 
1273     g1 = gaiaToGeos_r (cache, geom1);
1274     g2 = gaiaToGeos_r (cache, geom2);
1275     ret = GEOSTouches_r (handle, g1, g2);
1276     GEOSGeom_destroy_r (handle, g1);
1277     GEOSGeom_destroy_r (handle, g2);
1278     return ret;
1279 }
1280 
1281 GAIAGEO_DECLARE int
gaiaGeomCollPreparedTouches(const void * p_cache,gaiaGeomCollPtr geom1,unsigned char * blob1,int size1,gaiaGeomCollPtr geom2,unsigned char * blob2,int size2)1282 gaiaGeomCollPreparedTouches (const void *p_cache, gaiaGeomCollPtr geom1,
1283 			     unsigned char *blob1, int size1,
1284 			     gaiaGeomCollPtr geom2, unsigned char *blob2,
1285 			     int size2)
1286 {
1287 /* checks if two Geometries do "spatially touches" */
1288     int ret;
1289     struct splite_internal_cache *cache =
1290 	(struct splite_internal_cache *) p_cache;
1291     GEOSGeometry *g1;
1292     GEOSGeometry *g2;
1293     GEOSPreparedGeometry *gPrep;
1294     gaiaGeomCollPtr geom;
1295     GEOSContextHandle_t handle = NULL;
1296     if (cache == NULL)
1297 	return -1;
1298     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1299 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1300 	return -1;
1301     handle = cache->GEOS_handle;
1302     if (handle == NULL)
1303 	return -1;
1304     gaiaResetGeosMsg_r (cache);
1305     if (!geom1 || !geom2)
1306 	return -1;
1307     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
1308 	return -1;
1309 
1310 /* quick check based on MBRs comparison */
1311     if (!splite_mbr_overlaps (geom1, geom2))
1312 	return 0;
1313 
1314 /* handling the internal GEOS cache */
1315     if (evalGeosCache
1316 	(cache, geom1, blob1, size1, geom2, blob2, size2, &gPrep, &geom))
1317       {
1318 	  g2 = gaiaToGeos_r (cache, geom);
1319 	  ret = GEOSPreparedTouches_r (handle, gPrep, g2);
1320 	  GEOSGeom_destroy_r (handle, g2);
1321 	  return ret;
1322       }
1323 
1324     g1 = gaiaToGeos_r (cache, geom1);
1325     g2 = gaiaToGeos_r (cache, geom2);
1326     ret = GEOSTouches_r (handle, g1, g2);
1327     GEOSGeom_destroy_r (handle, g1);
1328     GEOSGeom_destroy_r (handle, g2);
1329     return ret;
1330 }
1331 
1332 GAIAGEO_DECLARE int
gaiaGeomCollWithin(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)1333 gaiaGeomCollWithin (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
1334 {
1335 /* checks if GEOM-1 is completely contained within GEOM-2 */
1336     int ret = -1;
1337 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
1338     GEOSGeometry *g1;
1339     GEOSGeometry *g2;
1340     gaiaResetGeosMsg ();
1341     if (!geom1 || !geom2)
1342 	return -1;
1343     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
1344 	return -1;
1345 
1346 /* quick check based on MBRs comparison */
1347     if (!splite_mbr_within (geom1, geom2))
1348 	return 0;
1349 
1350     g1 = gaiaToGeos (geom1);
1351     g2 = gaiaToGeos (geom2);
1352     ret = GEOSWithin (g1, g2);
1353     GEOSGeom_destroy (g1);
1354     GEOSGeom_destroy (g2);
1355 #else
1356     if (geom1 == NULL || geom2 == NULL)
1357 	geom1 = NULL;		/* silencing stupid compiler warnings */
1358 #endif
1359     return ret;
1360 }
1361 
1362 GAIAGEO_DECLARE int
gaiaGeomCollWithin_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)1363 gaiaGeomCollWithin_r (const void *p_cache, gaiaGeomCollPtr geom1,
1364 		      gaiaGeomCollPtr geom2)
1365 {
1366 /* checks if GEOM-1 is completely contained within GEOM-2 */
1367     int ret;
1368     GEOSGeometry *g1;
1369     GEOSGeometry *g2;
1370     struct splite_internal_cache *cache =
1371 	(struct splite_internal_cache *) p_cache;
1372     GEOSContextHandle_t handle = NULL;
1373     if (cache == NULL)
1374 	return -1;
1375     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1376 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1377 	return -1;
1378     handle = cache->GEOS_handle;
1379     if (handle == NULL)
1380 	return -1;
1381     gaiaResetGeosMsg_r (cache);
1382     if (!geom1 || !geom2)
1383 	return -1;
1384     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
1385 	return -1;
1386 
1387 /* quick check based on MBRs comparison */
1388     if (!splite_mbr_within (geom1, geom2))
1389 	return 0;
1390 
1391     g1 = gaiaToGeos_r (cache, geom1);
1392     g2 = gaiaToGeos_r (cache, geom2);
1393     ret = GEOSWithin_r (handle, g1, g2);
1394     GEOSGeom_destroy_r (handle, g1);
1395     GEOSGeom_destroy_r (handle, g2);
1396     return ret;
1397 }
1398 
1399 GAIAGEO_DECLARE int
gaiaGeomCollPreparedWithin(const void * p_cache,gaiaGeomCollPtr geom1,unsigned char * blob1,int size1,gaiaGeomCollPtr geom2,unsigned char * blob2,int size2)1400 gaiaGeomCollPreparedWithin (const void *p_cache, gaiaGeomCollPtr geom1,
1401 			    unsigned char *blob1, int size1,
1402 			    gaiaGeomCollPtr geom2, unsigned char *blob2,
1403 			    int size2)
1404 {
1405 /* checks if GEOM-1 is completely contained within GEOM-2 */
1406     int ret;
1407     struct splite_internal_cache *cache =
1408 	(struct splite_internal_cache *) p_cache;
1409     GEOSPreparedGeometry *gPrep;
1410     GEOSGeometry *g1;
1411     GEOSGeometry *g2;
1412     gaiaGeomCollPtr geom;
1413     GEOSContextHandle_t handle = NULL;
1414     if (cache == NULL)
1415 	return -1;
1416     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1417 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1418 	return -1;
1419     handle = cache->GEOS_handle;
1420     if (handle == NULL)
1421 	return -1;
1422     gaiaResetGeosMsg_r (cache);
1423     if (!geom1 || !geom2)
1424 	return -1;
1425     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
1426 	return -1;
1427 
1428 /* quick check based on MBRs comparison */
1429     if (!splite_mbr_within (geom1, geom2))
1430 	return 0;
1431 
1432 /* handling the internal GEOS cache */
1433     if (evalGeosCache
1434 	(cache, geom1, blob1, size1, geom2, blob2, size2, &gPrep, &geom))
1435       {
1436 	  g2 = gaiaToGeos_r (cache, geom);
1437 	  if (geom == geom2)
1438 	      ret = GEOSPreparedWithin_r (handle, gPrep, g2);
1439 	  else
1440 	      ret = GEOSPreparedContains_r (handle, gPrep, g2);
1441 	  GEOSGeom_destroy_r (handle, g2);
1442 	  return ret;
1443       }
1444 
1445     g1 = gaiaToGeos_r (cache, geom1);
1446     g2 = gaiaToGeos_r (cache, geom2);
1447     ret = GEOSWithin_r (handle, g1, g2);
1448     GEOSGeom_destroy_r (handle, g1);
1449     GEOSGeom_destroy_r (handle, g2);
1450     return ret;
1451 }
1452 
1453 GAIAGEO_DECLARE int
gaiaGeomCollContains(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)1454 gaiaGeomCollContains (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
1455 {
1456 /* checks if GEOM-1 completely contains GEOM-2 */
1457     int ret = -1;
1458 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
1459     GEOSGeometry *g1;
1460     GEOSGeometry *g2;
1461     gaiaResetGeosMsg ();
1462     if (!geom1 || !geom2)
1463 	return -1;
1464     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
1465 	return -1;
1466 
1467 /* quick check based on MBRs comparison */
1468     if (!splite_mbr_contains (geom1, geom2))
1469 	return 0;
1470 
1471     g1 = gaiaToGeos (geom1);
1472     g2 = gaiaToGeos (geom2);
1473     ret = GEOSContains (g1, g2);
1474     GEOSGeom_destroy (g1);
1475     GEOSGeom_destroy (g2);
1476 #else
1477     if (geom1 == NULL || geom2 == NULL)
1478 	geom1 = NULL;		/* silencing stupid compiler warnings */
1479 #endif
1480     return ret;
1481 }
1482 
1483 GAIAGEO_DECLARE int
gaiaGeomCollContains_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)1484 gaiaGeomCollContains_r (const void *p_cache, gaiaGeomCollPtr geom1,
1485 			gaiaGeomCollPtr geom2)
1486 {
1487 /* checks if GEOM-1 completely contains GEOM-2 */
1488     int ret;
1489     GEOSGeometry *g1;
1490     GEOSGeometry *g2;
1491     struct splite_internal_cache *cache =
1492 	(struct splite_internal_cache *) p_cache;
1493     GEOSContextHandle_t handle = NULL;
1494     if (cache == NULL)
1495 	return -1;
1496     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1497 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1498 	return -1;
1499     handle = cache->GEOS_handle;
1500     if (handle == NULL)
1501 	return -1;
1502     gaiaResetGeosMsg_r (cache);
1503     if (!geom1 || !geom2)
1504 	return -1;
1505     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
1506 	return -1;
1507 
1508 /* quick check based on MBRs comparison */
1509     if (!splite_mbr_contains (geom1, geom2))
1510 	return 0;
1511 
1512     g1 = gaiaToGeos_r (cache, geom1);
1513     g2 = gaiaToGeos_r (cache, geom2);
1514     ret = GEOSContains_r (handle, g1, g2);
1515     GEOSGeom_destroy_r (handle, g1);
1516     GEOSGeom_destroy_r (handle, g2);
1517     return ret;
1518 }
1519 
1520 GAIAGEO_DECLARE int
gaiaGeomCollPreparedContains(const void * p_cache,gaiaGeomCollPtr geom1,unsigned char * blob1,int size1,gaiaGeomCollPtr geom2,unsigned char * blob2,int size2)1521 gaiaGeomCollPreparedContains (const void *p_cache, gaiaGeomCollPtr geom1,
1522 			      unsigned char *blob1, int size1,
1523 			      gaiaGeomCollPtr geom2, unsigned char *blob2,
1524 			      int size2)
1525 {
1526 /* checks if GEOM-1 completely contains GEOM-2 */
1527     int ret;
1528     struct splite_internal_cache *cache =
1529 	(struct splite_internal_cache *) p_cache;
1530     GEOSPreparedGeometry *gPrep;
1531     GEOSGeometry *g1;
1532     GEOSGeometry *g2;
1533     gaiaGeomCollPtr geom;
1534     GEOSContextHandle_t handle = NULL;
1535     if (cache == NULL)
1536 	return -1;
1537     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1538 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1539 	return -1;
1540     handle = cache->GEOS_handle;
1541     if (handle == NULL)
1542 	return -1;
1543     gaiaResetGeosMsg_r (cache);
1544     if (!geom1 || !geom2)
1545 	return -1;
1546     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
1547 	return -1;
1548 
1549 /* quick check based on MBRs comparison */
1550     if (!splite_mbr_contains (geom1, geom2))
1551 	return 0;
1552 
1553 /* handling the internal GEOS cache */
1554     if (evalGeosCache
1555 	(cache, geom1, blob1, size1, geom2, blob2, size2, &gPrep, &geom))
1556       {
1557 	  g2 = gaiaToGeos_r (cache, geom);
1558 	  if (geom == geom2)
1559 	      ret = GEOSPreparedContains_r (handle, gPrep, g2);
1560 	  else
1561 	      ret = GEOSPreparedWithin_r (handle, gPrep, g2);
1562 	  GEOSGeom_destroy_r (handle, g2);
1563 	  return ret;
1564       }
1565 
1566     g1 = gaiaToGeos_r (cache, geom1);
1567     g2 = gaiaToGeos_r (cache, geom2);
1568     ret = GEOSContains_r (handle, g1, g2);
1569     GEOSGeom_destroy_r (handle, g1);
1570     GEOSGeom_destroy_r (handle, g2);
1571     return ret;
1572 }
1573 
1574 GAIAGEO_DECLARE int
gaiaGeomCollRelate(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2,const char * pattern)1575 gaiaGeomCollRelate (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2,
1576 		    const char *pattern)
1577 {
1578 /* checks if if GEOM-1 and GEOM-2 have a spatial relationship as specified by the pattern Matrix */
1579     int ret = -1;
1580 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
1581     GEOSGeometry *g1;
1582     GEOSGeometry *g2;
1583     gaiaResetGeosMsg ();
1584     if (!geom1 || !geom2)
1585 	return -1;
1586     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
1587 	return -1;
1588     g1 = gaiaToGeos (geom1);
1589     g2 = gaiaToGeos (geom2);
1590     ret = GEOSRelatePattern (g1, g2, pattern);
1591     GEOSGeom_destroy (g1);
1592     GEOSGeom_destroy (g2);
1593     if (ret == 2)
1594 	return -1;
1595 #else
1596     if (geom1 == NULL || geom2 == NULL || pattern == NULL)
1597 	geom1 = NULL;		/* silencing stupid compiler warnings */
1598 #endif
1599     return ret;
1600 }
1601 
1602 GAIAGEO_DECLARE int
gaiaGeomCollRelate_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2,const char * pattern)1603 gaiaGeomCollRelate_r (const void *p_cache, gaiaGeomCollPtr geom1,
1604 		      gaiaGeomCollPtr geom2, const char *pattern)
1605 {
1606 /* checks if if GEOM-1 and GEOM-2 have a spatial relationship as specified by the pattern Matrix */
1607     int ret;
1608     GEOSGeometry *g1;
1609     GEOSGeometry *g2;
1610     struct splite_internal_cache *cache =
1611 	(struct splite_internal_cache *) p_cache;
1612     GEOSContextHandle_t handle = NULL;
1613     if (cache == NULL)
1614 	return -1;
1615     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1616 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1617 	return -1;
1618     handle = cache->GEOS_handle;
1619     if (handle == NULL)
1620 	return -1;
1621     gaiaResetGeosMsg_r (cache);
1622     if (!geom1 || !geom2)
1623 	return -1;
1624     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
1625 	return -1;
1626     g1 = gaiaToGeos_r (cache, geom1);
1627     g2 = gaiaToGeos_r (cache, geom2);
1628     ret = GEOSRelatePattern_r (handle, g1, g2, pattern);
1629     GEOSGeom_destroy_r (handle, g1);
1630     GEOSGeom_destroy_r (handle, g2);
1631     if (ret == 2)
1632 	return -1;
1633     return ret;
1634 }
1635 
1636 GAIAGEO_DECLARE char *
gaiaGeomCollRelateBoundaryNodeRule(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2,int mode)1637 gaiaGeomCollRelateBoundaryNodeRule (gaiaGeomCollPtr geom1,
1638 				    gaiaGeomCollPtr geom2, int mode)
1639 {
1640 /* return the intesection matrix [DE-9IM] of GEOM-1 and GEOM-2 */
1641 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
1642     GEOSGeometry *g1;
1643     GEOSGeometry *g2;
1644     int bnr;
1645     char *retMatrix;
1646     char *matrix;
1647     int len;
1648     gaiaResetGeosMsg ();
1649     if (!geom1 || !geom2)
1650 	return NULL;
1651     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
1652 	return NULL;
1653     g1 = gaiaToGeos (geom1);
1654     g2 = gaiaToGeos (geom2);
1655     switch (mode)
1656       {
1657       case 2:
1658 	  bnr = GEOSRELATE_BNR_ENDPOINT;
1659 	  break;
1660       case 3:
1661 	  bnr = GEOSRELATE_BNR_MULTIVALENT_ENDPOINT;
1662 	  break;
1663       case 4:
1664 	  bnr = GEOSRELATE_BNR_MONOVALENT_ENDPOINT;
1665 	  break;
1666       default:
1667 	  bnr = GEOSRELATE_BNR_MOD2;
1668 	  break;
1669       };
1670     retMatrix = GEOSRelateBoundaryNodeRule (g1, g2, bnr);
1671     GEOSGeom_destroy (g1);
1672     GEOSGeom_destroy (g2);
1673     if (retMatrix == NULL)
1674 	return NULL;
1675     len = strlen (retMatrix);
1676     matrix = malloc (len + 1);
1677     strcpy (matrix, retMatrix);
1678     GEOSFree (retMatrix);
1679     return matrix;
1680 #else
1681     if (geom1 == NULL || geom2 == NULL || mode == 0)
1682 	geom1 = NULL;		/* silencing stupid compiler warnings */
1683 #endif
1684     return NULL;
1685 }
1686 
1687 GAIAGEO_DECLARE char *
gaiaGeomCollRelateBoundaryNodeRule_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2,int mode)1688 gaiaGeomCollRelateBoundaryNodeRule_r (const void *p_cache,
1689 				      gaiaGeomCollPtr geom1,
1690 				      gaiaGeomCollPtr geom2, int mode)
1691 {
1692 /* return the intesection matrix [DE-9IM] of GEOM-1 and GEOM-2 */
1693     GEOSGeometry *g1;
1694     GEOSGeometry *g2;
1695     int bnr;
1696     char *retMatrix;
1697     char *matrix;
1698     int len;
1699     struct splite_internal_cache *cache =
1700 	(struct splite_internal_cache *) p_cache;
1701     GEOSContextHandle_t handle = NULL;
1702     if (cache == NULL)
1703 	return NULL;
1704     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1705 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1706 	return NULL;
1707     handle = cache->GEOS_handle;
1708     if (handle == NULL)
1709 	return NULL;
1710     gaiaResetGeosMsg_r (cache);
1711     if (!geom1 || !geom2)
1712 	return NULL;
1713     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
1714 	return NULL;
1715     g1 = gaiaToGeos_r (cache, geom1);
1716     g2 = gaiaToGeos_r (cache, geom2);
1717     switch (mode)
1718       {
1719       case 2:
1720 	  bnr = GEOSRELATE_BNR_ENDPOINT;
1721 	  break;
1722       case 3:
1723 	  bnr = GEOSRELATE_BNR_MULTIVALENT_ENDPOINT;
1724 	  break;
1725       case 4:
1726 	  bnr = GEOSRELATE_BNR_MONOVALENT_ENDPOINT;
1727 	  break;
1728       default:
1729 	  bnr = GEOSRELATE_BNR_MOD2;
1730 	  break;
1731       };
1732     retMatrix = GEOSRelateBoundaryNodeRule_r (handle, g1, g2, bnr);
1733     GEOSGeom_destroy_r (handle, g1);
1734     GEOSGeom_destroy_r (handle, g2);
1735     if (retMatrix == NULL)
1736 	return NULL;
1737     len = strlen (retMatrix);
1738     matrix = malloc (len + 1);
1739     strcpy (matrix, retMatrix);
1740     GEOSFree_r (handle, retMatrix);
1741     return matrix;
1742 }
1743 
1744 GAIAGEO_DECLARE int
gaiaIntersectionMatrixPatternMatch(const char * matrix,const char * pattern)1745 gaiaIntersectionMatrixPatternMatch (const char *matrix, const char *pattern)
1746 {
1747 /* evalutes if an intersection matrix [DE-9IM]  matches a matrix pattern */
1748 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
1749     int ret;
1750     gaiaResetGeosMsg ();
1751     if (matrix == NULL || pattern == NULL)
1752 	return -1;
1753     ret = GEOSRelatePatternMatch (matrix, pattern);
1754     if (ret == 0 || ret == 1)
1755 	return ret;
1756 #else
1757     if (matrix == NULL || pattern == NULL)
1758 	matrix = NULL;		/* silencing stupid compiler warnings */
1759 #endif
1760     return -1;
1761 }
1762 
1763 GAIAGEO_DECLARE int
gaiaIntersectionMatrixPatternMatch_r(const void * p_cache,const char * matrix,const char * pattern)1764 gaiaIntersectionMatrixPatternMatch_r (const void *p_cache, const char *matrix,
1765 				      const char *pattern)
1766 {
1767 /* evalutes is an intersection matrix [DE-9IM]  matches a matrix pattern */
1768     int ret;
1769     struct splite_internal_cache *cache =
1770 	(struct splite_internal_cache *) p_cache;
1771     GEOSContextHandle_t handle = NULL;
1772     if (cache == NULL)
1773 	return -1;
1774     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1775 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1776 	return -1;
1777     handle = cache->GEOS_handle;
1778     if (handle == NULL)
1779 	return -1;
1780     gaiaResetGeosMsg_r (cache);
1781     if (matrix == NULL || pattern == NULL)
1782 	return -1;
1783     ret = GEOSRelatePatternMatch_r (handle, matrix, pattern);
1784     if (ret == 0 || ret == 1)
1785 	return ret;
1786     return -1;
1787 }
1788 
1789 GAIAGEO_DECLARE int
gaiaGeomCollLength(gaiaGeomCollPtr geom,double * xlength)1790 gaiaGeomCollLength (gaiaGeomCollPtr geom, double *xlength)
1791 {
1792 /* computes the total length for this Geometry */
1793     int ret = 0;
1794 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
1795     double length;
1796     GEOSGeometry *g;
1797     gaiaResetGeosMsg ();
1798     if (!geom)
1799 	return 0;
1800     if (gaiaIsToxic (geom))
1801 	return 0;
1802     g = gaiaToGeos (geom);
1803     ret = GEOSLength (g, &length);
1804     GEOSGeom_destroy (g);
1805     if (ret)
1806 	*xlength = length;
1807 #else
1808     if (geom == NULL || xlength == NULL)
1809 	geom = NULL;		/* silencing stupid compiler warnings */
1810 #endif
1811     return ret;
1812 }
1813 
1814 GAIAGEO_DECLARE int
gaiaGeomCollLength_r(const void * p_cache,gaiaGeomCollPtr geom,double * xlength)1815 gaiaGeomCollLength_r (const void *p_cache, gaiaGeomCollPtr geom,
1816 		      double *xlength)
1817 {
1818 /* computes the total length for this Geometry */
1819     double length;
1820     int ret;
1821     GEOSGeometry *g;
1822     struct splite_internal_cache *cache =
1823 	(struct splite_internal_cache *) p_cache;
1824     GEOSContextHandle_t handle = NULL;
1825     if (cache == NULL)
1826 	return -1;
1827     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1828 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1829 	return -1;
1830     handle = cache->GEOS_handle;
1831     if (handle == NULL)
1832 	return -1;
1833     gaiaResetGeosMsg_r (cache);
1834     if (!geom)
1835 	return 0;
1836     if (gaiaIsToxic_r (cache, geom))
1837 	return 0;
1838     g = gaiaToGeos_r (cache, geom);
1839     ret = GEOSLength_r (handle, g, &length);
1840     GEOSGeom_destroy_r (handle, g);
1841     if (ret)
1842 	*xlength = length;
1843     return ret;
1844 }
1845 
1846 GAIAGEO_DECLARE int
gaiaGeomCollLengthOrPerimeter(gaiaGeomCollPtr geom,int perimeter,double * xlength)1847 gaiaGeomCollLengthOrPerimeter (gaiaGeomCollPtr geom, int perimeter,
1848 			       double *xlength)
1849 {
1850 /* computes the total length or perimeter for this Geometry */
1851     int ret = 0;
1852 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
1853     double length;
1854     GEOSGeometry *g;
1855     int mode = GAIA2GEOS_ONLY_LINESTRINGS;
1856     if (perimeter)
1857 	mode = GAIA2GEOS_ONLY_POLYGONS;
1858     gaiaResetGeosMsg ();
1859     if (!geom)
1860 	return 0;
1861     if (gaiaIsToxic (geom))
1862 	return 0;
1863     g = gaiaToGeosSelective (geom, mode);
1864     if (g == NULL)
1865       {
1866 	  *xlength = 0.0;
1867 	  return 1;
1868       }
1869     ret = GEOSLength (g, &length);
1870     GEOSGeom_destroy (g);
1871     if (ret)
1872 	*xlength = length;
1873 #else
1874     if (geom == NULL || perimeter == 0 || xlength == NULL)
1875 	geom = NULL;		/* silencing stupid compiler warnings */
1876 #endif
1877     return ret;
1878 }
1879 
1880 GAIAGEO_DECLARE int
gaiaGeomCollLengthOrPerimeter_r(const void * p_cache,gaiaGeomCollPtr geom,int perimeter,double * xlength)1881 gaiaGeomCollLengthOrPerimeter_r (const void *p_cache, gaiaGeomCollPtr geom,
1882 				 int perimeter, double *xlength)
1883 {
1884 /* computes the total length or perimeter for this Geometry */
1885     double length;
1886     int ret;
1887     int mode = GAIA2GEOS_ONLY_LINESTRINGS;
1888     GEOSGeometry *g;
1889     struct splite_internal_cache *cache =
1890 	(struct splite_internal_cache *) p_cache;
1891     GEOSContextHandle_t handle = NULL;
1892     if (cache == NULL)
1893 	return -1;
1894     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1895 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1896 	return -1;
1897     handle = cache->GEOS_handle;
1898     if (handle == NULL)
1899 	return -1;
1900     if (perimeter)
1901 	mode = GAIA2GEOS_ONLY_POLYGONS;
1902     gaiaResetGeosMsg_r (cache);
1903     if (!geom)
1904 	return 0;
1905     if (gaiaIsToxic_r (cache, geom))
1906 	return 0;
1907     g = gaiaToGeosSelective_r (cache, geom, mode);
1908     if (g == NULL)
1909       {
1910 	  *xlength = 0.0;
1911 	  return 1;
1912       }
1913     ret = GEOSLength_r (handle, g, &length);
1914     GEOSGeom_destroy_r (handle, g);
1915     if (ret)
1916 	*xlength = length;
1917     return ret;
1918 }
1919 
1920 GAIAGEO_DECLARE int
gaiaGeomCollArea(gaiaGeomCollPtr geom,double * xarea)1921 gaiaGeomCollArea (gaiaGeomCollPtr geom, double *xarea)
1922 {
1923 /* computes the total area for this Geometry */
1924     int ret = 0;
1925 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
1926     double area;
1927     GEOSGeometry *g;
1928     gaiaResetGeosMsg ();
1929     if (!geom)
1930 	return 0;
1931     if (gaiaIsToxic (geom))
1932 	return 0;
1933     g = gaiaToGeos (geom);
1934     ret = GEOSArea (g, &area);
1935     GEOSGeom_destroy (g);
1936     if (ret)
1937 	*xarea = area;
1938 #else
1939     if (geom == NULL || xarea == NULL)
1940 	geom = NULL;		/* silencing stupid compiler warnings */
1941 #endif
1942     return ret;
1943 }
1944 
1945 GAIAGEO_DECLARE int
gaiaGeomCollArea_r(const void * p_cache,gaiaGeomCollPtr geom,double * xarea)1946 gaiaGeomCollArea_r (const void *p_cache, gaiaGeomCollPtr geom, double *xarea)
1947 {
1948 /* computes the total area for this Geometry */
1949     double area;
1950     int ret;
1951     GEOSGeometry *g;
1952     struct splite_internal_cache *cache =
1953 	(struct splite_internal_cache *) p_cache;
1954     GEOSContextHandle_t handle = NULL;
1955     if (cache == NULL)
1956 	return -1;
1957     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
1958 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
1959 	return -1;
1960     handle = cache->GEOS_handle;
1961     if (handle == NULL)
1962 	return -1;
1963     gaiaResetGeosMsg_r (cache);
1964     if (!geom)
1965 	return 0;
1966     if (gaiaIsToxic_r (cache, geom))
1967 	return 0;
1968     g = gaiaToGeos_r (cache, geom);
1969     ret = GEOSArea_r (handle, g, &area);
1970     GEOSGeom_destroy_r (handle, g);
1971     if (ret)
1972 	*xarea = area;
1973     return ret;
1974 }
1975 
1976 GAIAGEO_DECLARE int
gaiaGeomCollDistance(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2,double * xdist)1977 gaiaGeomCollDistance (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2,
1978 		      double *xdist)
1979 {
1980 /* computes the minimum distance intercurring between GEOM-1 and GEOM-2 */
1981     int ret = 0;
1982 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
1983     double dist;
1984     GEOSGeometry *g1;
1985     GEOSGeometry *g2;
1986     gaiaResetGeosMsg ();
1987     if (!geom1 || !geom2)
1988 	return 0;
1989     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
1990 	return 0;
1991     g1 = gaiaToGeos (geom1);
1992     g2 = gaiaToGeos (geom2);
1993     ret = GEOSDistance (g1, g2, &dist);
1994     GEOSGeom_destroy (g1);
1995     GEOSGeom_destroy (g2);
1996     if (ret)
1997 	*xdist = dist;
1998 #else
1999     if (geom1 == NULL || geom2 == NULL || xdist == NULL)
2000 	geom1 = NULL;		/* silencing stupid compiler warnings */
2001 #endif
2002     return ret;
2003 }
2004 
2005 GAIAGEO_DECLARE int
gaiaGeomCollDistance_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2,double * xdist)2006 gaiaGeomCollDistance_r (const void *p_cache, gaiaGeomCollPtr geom1,
2007 			gaiaGeomCollPtr geom2, double *xdist)
2008 {
2009 /* computes the minimum distance intercurring between GEOM-1 and GEOM-2 */
2010     double dist;
2011     int ret;
2012     GEOSGeometry *g1;
2013     GEOSGeometry *g2;
2014     struct splite_internal_cache *cache =
2015 	(struct splite_internal_cache *) p_cache;
2016     GEOSContextHandle_t handle = NULL;
2017     if (cache == NULL)
2018 	return -1;
2019     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2020 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2021 	return -1;
2022     handle = cache->GEOS_handle;
2023     if (handle == NULL)
2024 	return -1;
2025     gaiaResetGeosMsg_r (cache);
2026     if (!geom1 || !geom2)
2027 	return 0;
2028     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
2029 	return 0;
2030     g1 = gaiaToGeos_r (cache, geom1);
2031     g2 = gaiaToGeos_r (cache, geom2);
2032     ret = GEOSDistance_r (handle, g1, g2, &dist);
2033     GEOSGeom_destroy_r (handle, g1);
2034     GEOSGeom_destroy_r (handle, g2);
2035     if (ret)
2036 	*xdist = dist;
2037     return ret;
2038 }
2039 
2040 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeometryIntersection(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)2041 gaiaGeometryIntersection (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
2042 {
2043 /* builds a new geometry representing the "spatial intersection" of GEOM-1 and GEOM-2 */
2044     gaiaGeomCollPtr geo = NULL;
2045 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
2046     GEOSGeometry *g1;
2047     GEOSGeometry *g2;
2048     GEOSGeometry *g3;
2049     gaiaResetGeosMsg ();
2050     if (!geom1 || !geom2)
2051 	return NULL;
2052     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
2053 	return NULL;
2054 
2055 /* quick check based on MBRs comparison */
2056     if (!splite_mbr_overlaps (geom1, geom2))
2057 	return NULL;
2058 
2059     g1 = gaiaToGeos (geom1);
2060     g2 = gaiaToGeos (geom2);
2061     g3 = GEOSIntersection (g1, g2);
2062     GEOSGeom_destroy (g1);
2063     GEOSGeom_destroy (g2);
2064     if (!g3)
2065 	return NULL;
2066     if (GEOSisEmpty (g3) == 1)
2067       {
2068 	  GEOSGeom_destroy (g3);
2069 	  return NULL;
2070       }
2071     if (geom1->DimensionModel == GAIA_XY_Z)
2072 	geo = gaiaFromGeos_XYZ (g3);
2073     else if (geom1->DimensionModel == GAIA_XY_M)
2074 	geo = gaiaFromGeos_XYM (g3);
2075     else if (geom1->DimensionModel == GAIA_XY_Z_M)
2076 	geo = gaiaFromGeos_XYZM (g3);
2077     else
2078 	geo = gaiaFromGeos_XY (g3);
2079     GEOSGeom_destroy (g3);
2080     if (geo == NULL)
2081 	return NULL;
2082     geo->Srid = geom1->Srid;
2083 #else
2084     if (geom1 == NULL || geom2 == NULL)
2085 	geom1 = NULL;		/* silencing stupid compiler warnings */
2086 #endif
2087     return geo;
2088 }
2089 
2090 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeometryIntersection_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)2091 gaiaGeometryIntersection_r (const void *p_cache, gaiaGeomCollPtr geom1,
2092 			    gaiaGeomCollPtr geom2)
2093 {
2094 /* builds a new geometry representing the "spatial intersection" of GEOM-1 and GEOM-2 */
2095     gaiaGeomCollPtr geo;
2096     GEOSGeometry *g1;
2097     GEOSGeometry *g2;
2098     GEOSGeometry *g3;
2099     struct splite_internal_cache *cache =
2100 	(struct splite_internal_cache *) p_cache;
2101     GEOSContextHandle_t handle = NULL;
2102     if (cache == NULL)
2103 	return NULL;
2104     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2105 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2106 	return NULL;
2107     handle = cache->GEOS_handle;
2108     if (handle == NULL)
2109 	return NULL;
2110     gaiaResetGeosMsg_r (cache);
2111     if (!geom1 || !geom2)
2112 	return NULL;
2113     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
2114 	return NULL;
2115 
2116 /* quick check based on MBRs comparison */
2117     if (!splite_mbr_overlaps (geom1, geom2))
2118 	return NULL;
2119 
2120     g1 = gaiaToGeos_r (cache, geom1);
2121     g2 = gaiaToGeos_r (cache, geom2);
2122     g3 = GEOSIntersection_r (handle, g1, g2);
2123     GEOSGeom_destroy_r (handle, g1);
2124     GEOSGeom_destroy_r (handle, g2);
2125     if (!g3)
2126 	return NULL;
2127     if (GEOSisEmpty_r (handle, g3) == 1)
2128       {
2129 	  GEOSGeom_destroy_r (handle, g3);
2130 	  return NULL;
2131       }
2132     if (geom1->DimensionModel == GAIA_XY_Z)
2133 	geo = gaiaFromGeos_XYZ_r (cache, g3);
2134     else if (geom1->DimensionModel == GAIA_XY_M)
2135 	geo = gaiaFromGeos_XYM_r (cache, g3);
2136     else if (geom1->DimensionModel == GAIA_XY_Z_M)
2137 	geo = gaiaFromGeos_XYZM_r (cache, g3);
2138     else
2139 	geo = gaiaFromGeos_XY_r (cache, g3);
2140     GEOSGeom_destroy_r (handle, g3);
2141     if (geo == NULL)
2142 	return NULL;
2143     geo->Srid = geom1->Srid;
2144     return geo;
2145 }
2146 
2147 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeometryUnion(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)2148 gaiaGeometryUnion (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
2149 {
2150 /* builds a new geometry representing the "spatial union" of GEOM-1 and GEOM-2 */
2151     gaiaGeomCollPtr geo = NULL;
2152 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
2153     GEOSGeometry *g1;
2154     GEOSGeometry *g2;
2155     GEOSGeometry *g3;
2156     gaiaResetGeosMsg ();
2157     if (!geom1 || !geom2)
2158 	return NULL;
2159     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
2160 	return NULL;
2161     g1 = gaiaToGeos (geom1);
2162     g2 = gaiaToGeos (geom2);
2163     g3 = GEOSUnion (g1, g2);
2164     GEOSGeom_destroy (g1);
2165     GEOSGeom_destroy (g2);
2166     if (g3 == NULL)
2167 	return NULL;
2168     if (GEOSisEmpty (g3) == 1)
2169       {
2170 	  GEOSGeom_destroy (g3);
2171 	  return NULL;
2172       }
2173     if (geom1->DimensionModel == GAIA_XY_Z)
2174 	geo = gaiaFromGeos_XYZ (g3);
2175     else if (geom1->DimensionModel == GAIA_XY_M)
2176 	geo = gaiaFromGeos_XYM (g3);
2177     else if (geom1->DimensionModel == GAIA_XY_Z_M)
2178 	geo = gaiaFromGeos_XYZM (g3);
2179     else
2180 	geo = gaiaFromGeos_XY (g3);
2181     GEOSGeom_destroy (g3);
2182     if (geo == NULL)
2183 	return NULL;
2184     geo->Srid = geom1->Srid;
2185     if (geo->DeclaredType == GAIA_POINT &&
2186 	geom1->DeclaredType == GAIA_MULTIPOINT)
2187 	geo->DeclaredType = GAIA_MULTIPOINT;
2188     if (geo->DeclaredType == GAIA_LINESTRING &&
2189 	geom1->DeclaredType == GAIA_MULTILINESTRING)
2190 	geo->DeclaredType = GAIA_MULTILINESTRING;
2191     if (geo->DeclaredType == GAIA_POLYGON &&
2192 	geom1->DeclaredType == GAIA_MULTIPOLYGON)
2193 	geo->DeclaredType = GAIA_MULTIPOLYGON;
2194 #else
2195     if (geom1 == NULL || geom2 == NULL)
2196 	geom1 = NULL;		/* silencing stupid compiler warnings */
2197 #endif
2198     return geo;
2199 }
2200 
2201 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeometryUnion_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)2202 gaiaGeometryUnion_r (const void *p_cache, gaiaGeomCollPtr geom1,
2203 		     gaiaGeomCollPtr geom2)
2204 {
2205 /* builds a new geometry representing the "spatial union" of GEOM-1 and GEOM-2 */
2206     gaiaGeomCollPtr geo;
2207     GEOSGeometry *g1;
2208     GEOSGeometry *g2;
2209     GEOSGeometry *g3;
2210     struct splite_internal_cache *cache =
2211 	(struct splite_internal_cache *) p_cache;
2212     GEOSContextHandle_t handle = NULL;
2213     if (cache == NULL)
2214 	return NULL;
2215     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2216 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2217 	return NULL;
2218     handle = cache->GEOS_handle;
2219     if (handle == NULL)
2220 	return NULL;
2221     gaiaResetGeosMsg_r (cache);
2222     if (!geom1 || !geom2)
2223 	return NULL;
2224     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
2225 	return NULL;
2226     g1 = gaiaToGeos_r (cache, geom1);
2227     g2 = gaiaToGeos_r (cache, geom2);
2228     g3 = GEOSUnion_r (handle, g1, g2);
2229     GEOSGeom_destroy_r (handle, g1);
2230     GEOSGeom_destroy_r (handle, g2);
2231     if (g3 == NULL)
2232 	return NULL;
2233     if (GEOSisEmpty_r (handle, g3) == 1)
2234       {
2235 	  GEOSGeom_destroy_r (handle, g3);
2236 	  return NULL;
2237       }
2238     if (geom1->DimensionModel == GAIA_XY_Z)
2239 	geo = gaiaFromGeos_XYZ_r (cache, g3);
2240     else if (geom1->DimensionModel == GAIA_XY_M)
2241 	geo = gaiaFromGeos_XYM_r (cache, g3);
2242     else if (geom1->DimensionModel == GAIA_XY_Z_M)
2243 	geo = gaiaFromGeos_XYZM_r (cache, g3);
2244     else
2245 	geo = gaiaFromGeos_XY_r (cache, g3);
2246     GEOSGeom_destroy_r (handle, g3);
2247     if (geo == NULL)
2248 	return NULL;
2249     geo->Srid = geom1->Srid;
2250     if (geo->DeclaredType == GAIA_POINT &&
2251 	geom1->DeclaredType == GAIA_MULTIPOINT)
2252 	geo->DeclaredType = GAIA_MULTIPOINT;
2253     if (geo->DeclaredType == GAIA_LINESTRING &&
2254 	geom1->DeclaredType == GAIA_MULTILINESTRING)
2255 	geo->DeclaredType = GAIA_MULTILINESTRING;
2256     if (geo->DeclaredType == GAIA_POLYGON &&
2257 	geom1->DeclaredType == GAIA_MULTIPOLYGON)
2258 	geo->DeclaredType = GAIA_MULTIPOLYGON;
2259     return geo;
2260 }
2261 
2262 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaUnionCascaded(gaiaGeomCollPtr geom)2263 gaiaUnionCascaded (gaiaGeomCollPtr geom)
2264 {
2265 /* UnionCascaded (single Collection of polygons) */
2266     gaiaGeomCollPtr result = NULL;
2267 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
2268     GEOSGeometry *g1;
2269     GEOSGeometry *g2;
2270     int pts = 0;
2271     int lns = 0;
2272     int pgs = 0;
2273     gaiaPointPtr pt;
2274     gaiaLinestringPtr ln;
2275     gaiaPolygonPtr pg;
2276     gaiaResetGeosMsg ();
2277     if (!geom)
2278 	return NULL;
2279     if (gaiaIsToxic (geom))
2280 	return NULL;
2281 
2282 /* testing if geom only contains Polygons */
2283     pt = geom->FirstPoint;
2284     while (pt)
2285       {
2286 	  pts++;
2287 	  pt = pt->Next;
2288       }
2289     ln = geom->FirstLinestring;
2290     while (ln)
2291       {
2292 	  lns++;
2293 	  ln = ln->Next;
2294       }
2295     pg = geom->FirstPolygon;
2296     while (pg)
2297       {
2298 	  pgs++;
2299 	  pg = pg->Next;
2300       }
2301     if (pts || lns)
2302 	return NULL;
2303     if (!pgs)
2304 	return NULL;
2305 
2306     g1 = gaiaToGeos (geom);
2307     g2 = GEOSUnionCascaded (g1);
2308     GEOSGeom_destroy (g1);
2309     if (!g2)
2310 	return NULL;
2311     if (GEOSisEmpty (g2) == 1)
2312       {
2313 	  GEOSGeom_destroy (g2);
2314 	  return NULL;
2315       }
2316     if (geom->DimensionModel == GAIA_XY_Z)
2317 	result = gaiaFromGeos_XYZ (g2);
2318     else if (geom->DimensionModel == GAIA_XY_M)
2319 	result = gaiaFromGeos_XYM (g2);
2320     else if (geom->DimensionModel == GAIA_XY_Z_M)
2321 	result = gaiaFromGeos_XYZM (g2);
2322     else
2323 	result = gaiaFromGeos_XY (g2);
2324     GEOSGeom_destroy (g2);
2325     if (result == NULL)
2326 	return NULL;
2327     result->Srid = geom->Srid;
2328 #else
2329     if (geom == NULL)
2330 	geom = NULL;		/* silencing stupid compiler warnings */
2331 #endif
2332     return result;
2333 }
2334 
2335 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaUnionCascaded_r(const void * p_cache,gaiaGeomCollPtr geom)2336 gaiaUnionCascaded_r (const void *p_cache, gaiaGeomCollPtr geom)
2337 {
2338 /* UnionCascaded (single Collection of polygons) */
2339     GEOSGeometry *g1;
2340     GEOSGeometry *g2;
2341     gaiaGeomCollPtr result;
2342     int pts = 0;
2343     int lns = 0;
2344     int pgs = 0;
2345     gaiaPointPtr pt;
2346     gaiaLinestringPtr ln;
2347     gaiaPolygonPtr pg;
2348     struct splite_internal_cache *cache =
2349 	(struct splite_internal_cache *) p_cache;
2350     GEOSContextHandle_t handle = NULL;
2351     if (cache == NULL)
2352 	return NULL;
2353     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2354 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2355 	return NULL;
2356     handle = cache->GEOS_handle;
2357     if (handle == NULL)
2358 	return NULL;
2359     gaiaResetGeosMsg_r (cache);
2360     if (!geom)
2361 	return NULL;
2362     if (gaiaIsToxic_r (cache, geom))
2363 	return NULL;
2364 
2365 /* testing if geom only contains Polygons */
2366     pt = geom->FirstPoint;
2367     while (pt)
2368       {
2369 	  pts++;
2370 	  pt = pt->Next;
2371       }
2372     ln = geom->FirstLinestring;
2373     while (ln)
2374       {
2375 	  lns++;
2376 	  ln = ln->Next;
2377       }
2378     pg = geom->FirstPolygon;
2379     while (pg)
2380       {
2381 	  pgs++;
2382 	  pg = pg->Next;
2383       }
2384     if (pts || lns)
2385 	return NULL;
2386     if (!pgs)
2387 	return NULL;
2388 
2389     g1 = gaiaToGeos_r (cache, geom);
2390     g2 = GEOSUnionCascaded_r (handle, g1);
2391     GEOSGeom_destroy_r (handle, g1);
2392     if (!g2)
2393 	return NULL;
2394     if (GEOSisEmpty_r (handle, g2) == 1)
2395       {
2396 	  GEOSGeom_destroy_r (handle, g2);
2397 	  return NULL;
2398       }
2399     if (geom->DimensionModel == GAIA_XY_Z)
2400 	result = gaiaFromGeos_XYZ_r (cache, g2);
2401     else if (geom->DimensionModel == GAIA_XY_M)
2402 	result = gaiaFromGeos_XYM_r (cache, g2);
2403     else if (geom->DimensionModel == GAIA_XY_Z_M)
2404 	result = gaiaFromGeos_XYZM_r (cache, g2);
2405     else
2406 	result = gaiaFromGeos_XY_r (cache, g2);
2407     GEOSGeom_destroy_r (handle, g2);
2408     if (result == NULL)
2409 	return NULL;
2410     result->Srid = geom->Srid;
2411     return result;
2412 }
2413 
2414 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeometryDifference(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)2415 gaiaGeometryDifference (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
2416 {
2417 /* builds a new geometry representing the "spatial difference" of GEOM-1 and GEOM-2 */
2418     gaiaGeomCollPtr geo = NULL;
2419 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
2420     GEOSGeometry *g1;
2421     GEOSGeometry *g2;
2422     GEOSGeometry *g3;
2423     gaiaResetGeosMsg ();
2424     if (!geom1 || !geom2)
2425 	return NULL;
2426     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
2427 	return NULL;
2428     g1 = gaiaToGeos (geom1);
2429     g2 = gaiaToGeos (geom2);
2430     g3 = GEOSDifference (g1, g2);
2431     GEOSGeom_destroy (g1);
2432     GEOSGeom_destroy (g2);
2433     if (!g3)
2434 	return NULL;
2435     if (GEOSisEmpty (g3) == 1)
2436       {
2437 	  GEOSGeom_destroy (g3);
2438 	  return NULL;
2439       }
2440     if (geom1->DimensionModel == GAIA_XY_Z)
2441 	geo = gaiaFromGeos_XYZ (g3);
2442     else if (geom1->DimensionModel == GAIA_XY_M)
2443 	geo = gaiaFromGeos_XYM (g3);
2444     else if (geom1->DimensionModel == GAIA_XY_Z_M)
2445 	geo = gaiaFromGeos_XYZM (g3);
2446     else
2447 	geo = gaiaFromGeos_XY (g3);
2448     GEOSGeom_destroy (g3);
2449     if (geo == NULL)
2450 	return NULL;
2451     geo->Srid = geom1->Srid;
2452 #else
2453     if (geom1 == NULL || geom2 == NULL)
2454 	geom1 = NULL;		/* silencing stupid compiler warnings */
2455 #endif
2456     return geo;
2457 }
2458 
2459 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeometryDifference_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)2460 gaiaGeometryDifference_r (const void *p_cache, gaiaGeomCollPtr geom1,
2461 			  gaiaGeomCollPtr geom2)
2462 {
2463 /* builds a new geometry representing the "spatial difference" of GEOM-1 and GEOM-2 */
2464     gaiaGeomCollPtr geo;
2465     GEOSGeometry *g1;
2466     GEOSGeometry *g2;
2467     GEOSGeometry *g3;
2468     struct splite_internal_cache *cache =
2469 	(struct splite_internal_cache *) p_cache;
2470     GEOSContextHandle_t handle = NULL;
2471     if (cache == NULL)
2472 	return NULL;
2473     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2474 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2475 	return NULL;
2476     handle = cache->GEOS_handle;
2477     if (handle == NULL)
2478 	return NULL;
2479     gaiaResetGeosMsg_r (cache);
2480     if (!geom1 || !geom2)
2481 	return NULL;
2482     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
2483 	return NULL;
2484     g1 = gaiaToGeos_r (cache, geom1);
2485     g2 = gaiaToGeos_r (cache, geom2);
2486     g3 = GEOSDifference_r (handle, g1, g2);
2487     GEOSGeom_destroy_r (handle, g1);
2488     GEOSGeom_destroy_r (handle, g2);
2489     if (!g3)
2490 	return NULL;
2491     if (GEOSisEmpty_r (handle, g3) == 1)
2492       {
2493 	  GEOSGeom_destroy_r (handle, g3);
2494 	  return NULL;
2495       }
2496     if (geom1->DimensionModel == GAIA_XY_Z)
2497 	geo = gaiaFromGeos_XYZ_r (cache, g3);
2498     else if (geom1->DimensionModel == GAIA_XY_M)
2499 	geo = gaiaFromGeos_XYM_r (cache, g3);
2500     else if (geom1->DimensionModel == GAIA_XY_Z_M)
2501 	geo = gaiaFromGeos_XYZM_r (cache, g3);
2502     else
2503 	geo = gaiaFromGeos_XY_r (cache, g3);
2504     GEOSGeom_destroy_r (handle, g3);
2505     if (geo == NULL)
2506 	return NULL;
2507     geo->Srid = geom1->Srid;
2508     return geo;
2509 }
2510 
2511 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeometrySymDifference(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)2512 gaiaGeometrySymDifference (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
2513 {
2514 /* builds a new geometry representing the "spatial symmetric difference" of GEOM-1 and GEOM-2 */
2515     gaiaGeomCollPtr geo = NULL;
2516 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
2517     GEOSGeometry *g1;
2518     GEOSGeometry *g2;
2519     GEOSGeometry *g3;
2520     gaiaResetGeosMsg ();
2521     if (!geom1 || !geom2)
2522 	return NULL;
2523     if (gaiaIsToxic (geom1) || gaiaIsToxic (geom2))
2524 	return NULL;
2525     g1 = gaiaToGeos (geom1);
2526     g2 = gaiaToGeos (geom2);
2527     g3 = GEOSSymDifference (g1, g2);
2528     GEOSGeom_destroy (g1);
2529     GEOSGeom_destroy (g2);
2530     if (!g3)
2531 	return NULL;
2532     if (GEOSisEmpty (g3) == 1)
2533       {
2534 	  GEOSGeom_destroy (g3);
2535 	  return NULL;
2536       }
2537     if (geom1->DimensionModel == GAIA_XY_Z)
2538 	geo = gaiaFromGeos_XYZ (g3);
2539     else if (geom1->DimensionModel == GAIA_XY_M)
2540 	geo = gaiaFromGeos_XYM (g3);
2541     else if (geom1->DimensionModel == GAIA_XY_Z_M)
2542 	geo = gaiaFromGeos_XYZM (g3);
2543     else
2544 	geo = gaiaFromGeos_XY (g3);
2545     GEOSGeom_destroy (g3);
2546     if (geo == NULL)
2547 	return NULL;
2548     geo->Srid = geom1->Srid;
2549 #else
2550     if (geom1 == NULL || geom2 == NULL)
2551 	geom1 = NULL;		/* silencing stupid compiler warnings */
2552 #endif
2553     return geo;
2554 }
2555 
2556 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeometrySymDifference_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)2557 gaiaGeometrySymDifference_r (const void *p_cache, gaiaGeomCollPtr geom1,
2558 			     gaiaGeomCollPtr geom2)
2559 {
2560 /* builds a new geometry representing the "spatial symmetric difference" of GEOM-1 and GEOM-2 */
2561     gaiaGeomCollPtr geo;
2562     GEOSGeometry *g1;
2563     GEOSGeometry *g2;
2564     GEOSGeometry *g3;
2565     struct splite_internal_cache *cache =
2566 	(struct splite_internal_cache *) p_cache;
2567     GEOSContextHandle_t handle = NULL;
2568     if (cache == NULL)
2569 	return NULL;
2570     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2571 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2572 	return NULL;
2573     handle = cache->GEOS_handle;
2574     if (handle == NULL)
2575 	return NULL;
2576     gaiaResetGeosMsg_r (cache);
2577     if (!geom1 || !geom2)
2578 	return NULL;
2579     if (gaiaIsToxic_r (cache, geom1) || gaiaIsToxic_r (cache, geom2))
2580 	return NULL;
2581     g1 = gaiaToGeos_r (cache, geom1);
2582     g2 = gaiaToGeos_r (cache, geom2);
2583     g3 = GEOSSymDifference_r (handle, g1, g2);
2584     GEOSGeom_destroy_r (handle, g1);
2585     GEOSGeom_destroy_r (handle, g2);
2586     if (!g3)
2587 	return NULL;
2588     if (GEOSisEmpty_r (handle, g3) == 1)
2589       {
2590 	  GEOSGeom_destroy_r (handle, g3);
2591 	  return NULL;
2592       }
2593     if (geom1->DimensionModel == GAIA_XY_Z)
2594 	geo = gaiaFromGeos_XYZ_r (cache, g3);
2595     else if (geom1->DimensionModel == GAIA_XY_M)
2596 	geo = gaiaFromGeos_XYM_r (cache, g3);
2597     else if (geom1->DimensionModel == GAIA_XY_Z_M)
2598 	geo = gaiaFromGeos_XYZM_r (cache, g3);
2599     else
2600 	geo = gaiaFromGeos_XY_r (cache, g3);
2601     GEOSGeom_destroy_r (handle, g3);
2602     if (geo == NULL)
2603 	return NULL;
2604     geo->Srid = geom1->Srid;
2605     return geo;
2606 }
2607 
2608 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaBoundary(gaiaGeomCollPtr geom)2609 gaiaBoundary (gaiaGeomCollPtr geom)
2610 {
2611 /* builds a new geometry representing the combinatorial boundary of GEOM */
2612     gaiaGeomCollPtr geo = NULL;
2613 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
2614     GEOSGeometry *g1;
2615     GEOSGeometry *g2;
2616     gaiaResetGeosMsg ();
2617     if (!geom)
2618 	return NULL;
2619     if (gaiaIsToxic (geom))
2620 	return NULL;
2621     g1 = gaiaToGeos (geom);
2622     g2 = GEOSBoundary (g1);
2623     GEOSGeom_destroy (g1);
2624     if (!g2)
2625 	return NULL;
2626     if (GEOSisEmpty (g2) == 1)
2627       {
2628 	  GEOSGeom_destroy (g2);
2629 	  return NULL;
2630       }
2631     if (geom->DimensionModel == GAIA_XY_Z)
2632 	geo = gaiaFromGeos_XYZ (g2);
2633     else if (geom->DimensionModel == GAIA_XY_M)
2634 	geo = gaiaFromGeos_XYM (g2);
2635     else if (geom->DimensionModel == GAIA_XY_Z_M)
2636 	geo = gaiaFromGeos_XYZM (g2);
2637     else
2638 	geo = gaiaFromGeos_XY (g2);
2639     GEOSGeom_destroy (g2);
2640     if (geo == NULL)
2641 	return NULL;
2642     geo->Srid = geom->Srid;
2643 #else
2644     if (geom == NULL)
2645 	geom = NULL;		/* silencing stupid compiler warnings */
2646 #endif
2647     return geo;
2648 }
2649 
2650 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaBoundary_r(const void * p_cache,gaiaGeomCollPtr geom)2651 gaiaBoundary_r (const void *p_cache, gaiaGeomCollPtr geom)
2652 {
2653 /* builds a new geometry representing the combinatorial boundary of GEOM */
2654     gaiaGeomCollPtr geo;
2655     GEOSGeometry *g1;
2656     GEOSGeometry *g2;
2657     struct splite_internal_cache *cache =
2658 	(struct splite_internal_cache *) p_cache;
2659     GEOSContextHandle_t handle = NULL;
2660     if (cache == NULL)
2661 	return NULL;
2662     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2663 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2664 	return NULL;
2665     handle = cache->GEOS_handle;
2666     if (handle == NULL)
2667 	return NULL;
2668     gaiaResetGeosMsg_r (cache);
2669     if (!geom)
2670 	return NULL;
2671     if (gaiaIsToxic_r (cache, geom))
2672 	return NULL;
2673     g1 = gaiaToGeos_r (cache, geom);
2674     g2 = GEOSBoundary_r (handle, g1);
2675     GEOSGeom_destroy_r (handle, g1);
2676     if (!g2)
2677 	return NULL;
2678     if (GEOSisEmpty_r (handle, g2) == 1)
2679       {
2680 	  GEOSGeom_destroy_r (handle, g2);
2681 	  return NULL;
2682       }
2683     if (geom->DimensionModel == GAIA_XY_Z)
2684 	geo = gaiaFromGeos_XYZ_r (cache, g2);
2685     else if (geom->DimensionModel == GAIA_XY_M)
2686 	geo = gaiaFromGeos_XYM_r (cache, g2);
2687     else if (geom->DimensionModel == GAIA_XY_Z_M)
2688 	geo = gaiaFromGeos_XYZM_r (cache, g2);
2689     else
2690 	geo = gaiaFromGeos_XY_r (cache, g2);
2691     GEOSGeom_destroy_r (handle, g2);
2692     if (geo == NULL)
2693 	return NULL;
2694     geo->Srid = geom->Srid;
2695     return geo;
2696 }
2697 
2698 GAIAGEO_DECLARE int
gaiaGeomCollCentroid(gaiaGeomCollPtr geom,double * x,double * y)2699 gaiaGeomCollCentroid (gaiaGeomCollPtr geom, double *x, double *y)
2700 {
2701 /* returns a Point representing the centroid for this Geometry */
2702 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
2703     gaiaGeomCollPtr geo;
2704     GEOSGeometry *g1;
2705     GEOSGeometry *g2;
2706     gaiaResetGeosMsg ();
2707     if (!geom)
2708 	return 0;
2709     if (gaiaIsToxic (geom))
2710       {
2711 	  return 0;
2712       }
2713     g1 = gaiaToGeos (geom);
2714     g2 = GEOSGetCentroid (g1);
2715     GEOSGeom_destroy (g1);
2716     if (!g2)
2717 	return 0;
2718     if (GEOSisEmpty (g2) == 1)
2719       {
2720 	  GEOSGeom_destroy (g2);
2721 	  return 0;
2722       }
2723     if (geom->DimensionModel == GAIA_XY_Z)
2724 	geo = gaiaFromGeos_XYZ (g2);
2725     else if (geom->DimensionModel == GAIA_XY_M)
2726 	geo = gaiaFromGeos_XYM (g2);
2727     else if (geom->DimensionModel == GAIA_XY_Z_M)
2728 	geo = gaiaFromGeos_XYZM (g2);
2729     else
2730 	geo = gaiaFromGeos_XY (g2);
2731     GEOSGeom_destroy (g2);
2732     if (geo == NULL)
2733 	return 0;
2734     if (geo->FirstPoint)
2735       {
2736 	  *x = geo->FirstPoint->X;
2737 	  *y = geo->FirstPoint->Y;
2738 	  gaiaFreeGeomColl (geo);
2739 	  return 1;
2740       }
2741     gaiaFreeGeomColl (geo);
2742 #else
2743     if (geom == NULL || x == NULL || y == NULL)
2744 	geom = NULL;		/* silencing stupid compiler warnings */
2745 #endif
2746     return 0;
2747 }
2748 
2749 GAIAGEO_DECLARE int
gaiaGeomCollCentroid_r(const void * p_cache,gaiaGeomCollPtr geom,double * x,double * y)2750 gaiaGeomCollCentroid_r (const void *p_cache, gaiaGeomCollPtr geom, double *x,
2751 			double *y)
2752 {
2753 /* returns a Point representing the centroid for this Geometry */
2754     gaiaGeomCollPtr geo;
2755     GEOSGeometry *g1;
2756     GEOSGeometry *g2;
2757     struct splite_internal_cache *cache =
2758 	(struct splite_internal_cache *) p_cache;
2759     GEOSContextHandle_t handle = NULL;
2760     if (cache == NULL)
2761 	return 0;
2762     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2763 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2764 	return 0;
2765     handle = cache->GEOS_handle;
2766     if (handle == NULL)
2767 	return 0;
2768     gaiaResetGeosMsg_r (cache);
2769     if (!geom)
2770 	return 0;
2771     if (gaiaIsToxic_r (cache, geom))
2772       {
2773 	  return 0;
2774       }
2775     g1 = gaiaToGeos_r (cache, geom);
2776     g2 = GEOSGetCentroid_r (handle, g1);
2777     GEOSGeom_destroy_r (handle, g1);
2778     if (!g2)
2779 	return 0;
2780     if (GEOSisEmpty_r (handle, g2) == 1)
2781       {
2782 	  GEOSGeom_destroy_r (handle, g2);
2783 	  return 0;
2784       }
2785     if (geom->DimensionModel == GAIA_XY_Z)
2786 	geo = gaiaFromGeos_XYZ_r (cache, g2);
2787     else if (geom->DimensionModel == GAIA_XY_M)
2788 	geo = gaiaFromGeos_XYM_r (cache, g2);
2789     else if (geom->DimensionModel == GAIA_XY_Z_M)
2790 	geo = gaiaFromGeos_XYZM_r (cache, g2);
2791     else
2792 	geo = gaiaFromGeos_XY_r (cache, g2);
2793     GEOSGeom_destroy_r (handle, g2);
2794     if (geo == NULL)
2795 	return 0;
2796     if (geo->FirstPoint)
2797       {
2798 	  *x = geo->FirstPoint->X;
2799 	  *y = geo->FirstPoint->Y;
2800 	  gaiaFreeGeomColl (geo);
2801 	  return 1;
2802       }
2803     gaiaFreeGeomColl (geo);
2804     return 0;
2805 }
2806 
2807 GAIAGEO_DECLARE int
gaiaGetPointOnSurface(gaiaGeomCollPtr geom,double * x,double * y)2808 gaiaGetPointOnSurface (gaiaGeomCollPtr geom, double *x, double *y)
2809 {
2810 /* returns a Point guaranteed to lie on the Surface */
2811 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
2812     gaiaGeomCollPtr geo;
2813     GEOSGeometry *g1;
2814     GEOSGeometry *g2;
2815     gaiaResetGeosMsg ();
2816     if (!geom)
2817 	return 0;
2818     if (gaiaIsToxic (geom))
2819       {
2820 	  return 0;
2821       }
2822     g1 = gaiaToGeos (geom);
2823     g2 = GEOSPointOnSurface (g1);
2824     GEOSGeom_destroy (g1);
2825     if (!g2)
2826 	return 0;
2827     if (GEOSisEmpty (g2) == 1)
2828       {
2829 	  GEOSGeom_destroy (g2);
2830 	  return 0;
2831       }
2832     if (geom->DimensionModel == GAIA_XY_Z)
2833 	geo = gaiaFromGeos_XYZ (g2);
2834     else if (geom->DimensionModel == GAIA_XY_M)
2835 	geo = gaiaFromGeos_XYM (g2);
2836     else if (geom->DimensionModel == GAIA_XY_Z_M)
2837 	geo = gaiaFromGeos_XYZM (g2);
2838     else
2839 	geo = gaiaFromGeos_XY (g2);
2840     GEOSGeom_destroy (g2);
2841     if (geo == NULL)
2842 	return 0;
2843     if (geo->FirstPoint)
2844       {
2845 	  *x = geo->FirstPoint->X;
2846 	  *y = geo->FirstPoint->Y;
2847 	  gaiaFreeGeomColl (geo);
2848 	  return 1;
2849       }
2850     gaiaFreeGeomColl (geo);
2851 #else
2852     if (geom == NULL || x == NULL || y == NULL)
2853 	geom = NULL;		/* silencing stupid compiler warnings */
2854 #endif
2855     return 0;
2856 }
2857 
2858 GAIAGEO_DECLARE int
gaiaGetPointOnSurface_r(const void * p_cache,gaiaGeomCollPtr geom,double * x,double * y)2859 gaiaGetPointOnSurface_r (const void *p_cache, gaiaGeomCollPtr geom, double *x,
2860 			 double *y)
2861 {
2862 /* returns a Point guaranteed to lie on the Surface */
2863     gaiaGeomCollPtr geo;
2864     GEOSGeometry *g1;
2865     GEOSGeometry *g2;
2866     struct splite_internal_cache *cache =
2867 	(struct splite_internal_cache *) p_cache;
2868     GEOSContextHandle_t handle = NULL;
2869     if (cache == NULL)
2870 	return 0;
2871     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2872 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2873 	return 0;
2874     handle = cache->GEOS_handle;
2875     if (handle == NULL)
2876 	return 0;
2877     gaiaResetGeosMsg_r (cache);
2878     if (!geom)
2879 	return 0;
2880     if (gaiaIsToxic_r (cache, geom))
2881       {
2882 	  return 0;
2883       }
2884     g1 = gaiaToGeos_r (cache, geom);
2885     g2 = GEOSPointOnSurface_r (handle, g1);
2886     GEOSGeom_destroy_r (handle, g1);
2887     if (!g2)
2888 	return 0;
2889     if (GEOSisEmpty_r (handle, g2) == 1)
2890       {
2891 	  GEOSGeom_destroy_r (handle, g2);
2892 	  return 0;
2893       }
2894     if (geom->DimensionModel == GAIA_XY_Z)
2895 	geo = gaiaFromGeos_XYZ_r (cache, g2);
2896     else if (geom->DimensionModel == GAIA_XY_M)
2897 	geo = gaiaFromGeos_XYM_r (cache, g2);
2898     else if (geom->DimensionModel == GAIA_XY_Z_M)
2899 	geo = gaiaFromGeos_XYZM_r (cache, g2);
2900     else
2901 	geo = gaiaFromGeos_XYZ_r (cache, g2);
2902     GEOSGeom_destroy_r (handle, g2);
2903     if (geo == NULL)
2904 	return 0;
2905     if (geo->FirstPoint)
2906       {
2907 	  *x = geo->FirstPoint->X;
2908 	  *y = geo->FirstPoint->Y;
2909 	  gaiaFreeGeomColl (geo);
2910 	  return 1;
2911       }
2912     gaiaFreeGeomColl (geo);
2913     return 0;
2914 }
2915 
2916 GAIAGEO_DECLARE int
gaiaIsSimple(gaiaGeomCollPtr geom)2917 gaiaIsSimple (gaiaGeomCollPtr geom)
2918 {
2919 /* checks if this GEOMETRYCOLLECTION is a simple one */
2920     int ret = -1;
2921 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
2922     GEOSGeometry *g;
2923     gaiaResetGeosMsg ();
2924     if (!geom)
2925 	return -1;
2926     if (gaiaIsToxic (geom))
2927 	return 0;
2928     g = gaiaToGeos (geom);
2929     ret = GEOSisSimple (g);
2930     GEOSGeom_destroy (g);
2931     if (ret == 2)
2932 	return -1;
2933 #else
2934     if (geom == NULL)
2935 	geom = NULL;		/* silencing stupid compiler warnings */
2936 #endif
2937     return ret;
2938 }
2939 
2940 GAIAGEO_DECLARE int
gaiaIsSimple_r(const void * p_cache,gaiaGeomCollPtr geom)2941 gaiaIsSimple_r (const void *p_cache, gaiaGeomCollPtr geom)
2942 {
2943 /* checks if this GEOMETRYCOLLECTION is a simple one */
2944     int ret;
2945     GEOSGeometry *g;
2946     struct splite_internal_cache *cache =
2947 	(struct splite_internal_cache *) p_cache;
2948     GEOSContextHandle_t handle = NULL;
2949     if (cache == NULL)
2950 	return -1;
2951     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
2952 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
2953 	return -1;
2954     handle = cache->GEOS_handle;
2955     if (handle == NULL)
2956 	return -1;
2957     gaiaResetGeosMsg_r (cache);
2958     if (!geom)
2959 	return -1;
2960     if (gaiaIsToxic_r (cache, geom))
2961 	return -1;
2962     g = gaiaToGeos_r (cache, geom);
2963     ret = GEOSisSimple_r (handle, g);
2964     GEOSGeom_destroy_r (handle, g);
2965     if (ret == 2)
2966 	return -1;
2967     return ret;
2968 }
2969 
2970 GAIAGEO_DECLARE int
gaiaIsRing(gaiaLinestringPtr line)2971 gaiaIsRing (gaiaLinestringPtr line)
2972 {
2973 /* checks if this LINESTRING can be a valid RING */
2974     int ret = -1;
2975 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
2976     gaiaGeomCollPtr geo;
2977     gaiaLinestringPtr line2;
2978     int iv;
2979     double x;
2980     double y;
2981     double z;
2982     double m;
2983     GEOSGeometry *g;
2984     gaiaResetGeosMsg ();
2985     if (!line)
2986 	return -1;
2987     if (line->DimensionModel == GAIA_XY_Z)
2988 	geo = gaiaAllocGeomCollXYZ ();
2989     else if (line->DimensionModel == GAIA_XY_M)
2990 	geo = gaiaAllocGeomCollXYM ();
2991     else if (line->DimensionModel == GAIA_XY_Z_M)
2992 	geo = gaiaAllocGeomCollXYZM ();
2993     else
2994 	geo = gaiaAllocGeomColl ();
2995     line2 = gaiaAddLinestringToGeomColl (geo, line->Points);
2996     for (iv = 0; iv < line2->Points; iv++)
2997       {
2998 	  z = 0.0;
2999 	  m = 0.0;
3000 	  if (line->DimensionModel == GAIA_XY_Z)
3001 	    {
3002 		gaiaGetPointXYZ (line->Coords, iv, &x, &y, &z);
3003 	    }
3004 	  else if (line->DimensionModel == GAIA_XY_M)
3005 	    {
3006 		gaiaGetPointXYM (line->Coords, iv, &x, &y, &m);
3007 	    }
3008 	  else if (line->DimensionModel == GAIA_XY_Z_M)
3009 	    {
3010 		gaiaGetPointXYZM (line->Coords, iv, &x, &y, &z, &m);
3011 	    }
3012 	  else
3013 	    {
3014 		gaiaGetPoint (line->Coords, iv, &x, &y);
3015 	    }
3016 	  if (line2->DimensionModel == GAIA_XY_Z)
3017 	    {
3018 		gaiaSetPointXYZ (line2->Coords, iv, x, y, z);
3019 	    }
3020 	  else if (line2->DimensionModel == GAIA_XY_M)
3021 	    {
3022 		gaiaSetPointXYM (line2->Coords, iv, x, y, m);
3023 	    }
3024 	  else if (line2->DimensionModel == GAIA_XY_Z_M)
3025 	    {
3026 		gaiaSetPointXYZM (line2->Coords, iv, x, y, z, m);
3027 	    }
3028 	  else
3029 	    {
3030 		gaiaSetPoint (line2->Coords, iv, x, y);
3031 	    }
3032       }
3033     if (gaiaIsToxic (geo))
3034       {
3035 	  gaiaFreeGeomColl (geo);
3036 	  return -1;
3037       }
3038     g = gaiaToGeos (geo);
3039     gaiaFreeGeomColl (geo);
3040     ret = GEOSisRing (g);
3041     GEOSGeom_destroy (g);
3042     if (ret == 2)
3043 	return -1;
3044 #else
3045     if (line == NULL)
3046 	line = NULL;		/* silencing stupid compiler warnings */
3047 #endif
3048     return ret;
3049 }
3050 
3051 GAIAGEO_DECLARE int
gaiaIsRing_r(const void * p_cache,gaiaLinestringPtr line)3052 gaiaIsRing_r (const void *p_cache, gaiaLinestringPtr line)
3053 {
3054 /* checks if this LINESTRING can be a valid RING */
3055     gaiaGeomCollPtr geo;
3056     gaiaLinestringPtr line2;
3057     int ret;
3058     int iv;
3059     double x;
3060     double y;
3061     double z;
3062     double m;
3063     GEOSGeometry *g;
3064     struct splite_internal_cache *cache =
3065 	(struct splite_internal_cache *) p_cache;
3066     GEOSContextHandle_t handle = NULL;
3067     if (cache == NULL)
3068 	return -1;
3069     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3070 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3071 	return -1;
3072     handle = cache->GEOS_handle;
3073     if (handle == NULL)
3074 	return -1;
3075     gaiaResetGeosMsg_r (cache);
3076     if (!line)
3077 	return -1;
3078     if (line->DimensionModel == GAIA_XY_Z)
3079 	geo = gaiaAllocGeomCollXYZ ();
3080     else if (line->DimensionModel == GAIA_XY_M)
3081 	geo = gaiaAllocGeomCollXYM ();
3082     else if (line->DimensionModel == GAIA_XY_Z_M)
3083 	geo = gaiaAllocGeomCollXYZM ();
3084     else
3085 	geo = gaiaAllocGeomColl ();
3086     line2 = gaiaAddLinestringToGeomColl (geo, line->Points);
3087     for (iv = 0; iv < line2->Points; iv++)
3088       {
3089 	  z = 0.0;
3090 	  m = 0.0;
3091 	  if (line->DimensionModel == GAIA_XY_Z)
3092 	    {
3093 		gaiaGetPointXYZ (line->Coords, iv, &x, &y, &z);
3094 	    }
3095 	  else if (line->DimensionModel == GAIA_XY_M)
3096 	    {
3097 		gaiaGetPointXYM (line->Coords, iv, &x, &y, &m);
3098 	    }
3099 	  else if (line->DimensionModel == GAIA_XY_Z_M)
3100 	    {
3101 		gaiaGetPointXYZM (line->Coords, iv, &x, &y, &z, &m);
3102 	    }
3103 	  else
3104 	    {
3105 		gaiaGetPoint (line->Coords, iv, &x, &y);
3106 	    }
3107 	  if (line2->DimensionModel == GAIA_XY_Z)
3108 	    {
3109 		gaiaSetPointXYZ (line2->Coords, iv, x, y, z);
3110 	    }
3111 	  else if (line2->DimensionModel == GAIA_XY_M)
3112 	    {
3113 		gaiaSetPointXYM (line2->Coords, iv, x, y, m);
3114 	    }
3115 	  else if (line2->DimensionModel == GAIA_XY_Z_M)
3116 	    {
3117 		gaiaSetPointXYZM (line2->Coords, iv, x, y, z, m);
3118 	    }
3119 	  else
3120 	    {
3121 		gaiaSetPoint (line2->Coords, iv, x, y);
3122 	    }
3123       }
3124     if (gaiaIsToxic_r (cache, geo))
3125       {
3126 	  gaiaFreeGeomColl (geo);
3127 	  return -1;
3128       }
3129     g = gaiaToGeos_r (cache, geo);
3130     gaiaFreeGeomColl (geo);
3131     ret = GEOSisRing_r (handle, g);
3132     GEOSGeom_destroy_r (handle, g);
3133     if (ret == 2)
3134 	return -1;
3135     return ret;
3136 }
3137 
3138 GAIAGEO_DECLARE int
gaiaIsValid(gaiaGeomCollPtr geom)3139 gaiaIsValid (gaiaGeomCollPtr geom)
3140 {
3141 /* checks if this GEOMETRYCOLLECTION is a valid one */
3142     int ret = -1;
3143 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3144     GEOSGeometry *g;
3145     gaiaResetGeosMsg ();
3146     if (!geom)
3147 	return -1;
3148     if (gaiaIsToxic (geom))
3149 	return 0;
3150     if (gaiaIsNotClosedGeomColl (geom))
3151 	return 0;
3152     g = gaiaToGeos (geom);
3153     ret = GEOSisValid (g);
3154     GEOSGeom_destroy (g);
3155     if (ret == 2)
3156 	return -1;
3157 #else
3158     if (geom == NULL)
3159 	geom = NULL;		/* silencing stupid compiler warnings */
3160 #endif
3161     return ret;
3162 }
3163 
3164 GAIAGEO_DECLARE int
gaiaIsValid_r(const void * p_cache,gaiaGeomCollPtr geom)3165 gaiaIsValid_r (const void *p_cache, gaiaGeomCollPtr geom)
3166 {
3167 /* checks if this GEOMETRYCOLLECTION is a valid one */
3168     int ret;
3169     GEOSGeometry *g;
3170     struct splite_internal_cache *cache =
3171 	(struct splite_internal_cache *) p_cache;
3172     GEOSContextHandle_t handle = NULL;
3173     if (cache == NULL)
3174 	return -1;
3175     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3176 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3177 	return -1;
3178     handle = cache->GEOS_handle;
3179     if (handle == NULL)
3180 	return -1;
3181     gaiaResetGeosMsg_r (cache);
3182     if (!geom)
3183 	return -1;
3184     if (gaiaIsToxic_r (cache, geom))
3185 	return 0;
3186     if (gaiaIsNotClosedGeomColl_r (cache, geom))
3187 	return 0;
3188     g = gaiaToGeos_r (cache, geom);
3189     ret = GEOSisValid_r (handle, g);
3190     GEOSGeom_destroy_r (handle, g);
3191     if (ret == 2)
3192 	return -1;
3193     return ret;
3194 }
3195 
3196 GAIAGEO_DECLARE char *
gaiaIsValidReason(gaiaGeomCollPtr geom)3197 gaiaIsValidReason (gaiaGeomCollPtr geom)
3198 {
3199 /* return a TEXT string stating if a Geometry is valid
3200 / and if not valid, a reason why */
3201     char *text = NULL;
3202 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3203     int len;
3204     const char *str;
3205     char *gstr;
3206     GEOSGeometry *g;
3207     gaiaResetGeosMsg ();
3208     if (!geom)
3209       {
3210 	  str = "Invalid: NULL Geometry";
3211 	  len = strlen (str);
3212 	  text = malloc (len + 1);
3213 	  strcpy (text, str);
3214 	  return text;
3215       }
3216     if (gaiaIsToxic (geom))
3217       {
3218 	  str = "Invalid: Toxic Geometry ... too few points";
3219 	  len = strlen (str);
3220 	  text = malloc (len + 1);
3221 	  strcpy (text, str);
3222 	  return text;
3223       }
3224     if (gaiaIsNotClosedGeomColl (geom))
3225       {
3226 	  str = "Invalid: Unclosed Rings were detected";
3227 	  len = strlen (str);
3228 	  text = malloc (len + 1);
3229 	  strcpy (text, str);
3230 	  return text;
3231       }
3232     g = gaiaToGeos (geom);
3233     gstr = GEOSisValidReason (g);
3234     GEOSGeom_destroy (g);
3235     if (gstr == NULL)
3236 	return NULL;
3237     len = strlen (gstr);
3238     text = malloc (len + 1);
3239     strcpy (text, gstr);
3240     GEOSFree (gstr);
3241 #else
3242     if (geom == NULL)
3243 	geom = NULL;		/* silencing stupid compiler warnings */
3244 #endif
3245     return text;
3246 }
3247 
3248 GAIAGEO_DECLARE char *
gaiaIsValidReason_r(const void * p_cache,gaiaGeomCollPtr geom)3249 gaiaIsValidReason_r (const void *p_cache, gaiaGeomCollPtr geom)
3250 {
3251 /* return a TEXT string stating if a Geometry is valid
3252 / and if not valid, a reason why */
3253     char *text;
3254     int len;
3255     const char *str;
3256     char *gstr;
3257     GEOSGeometry *g;
3258     struct splite_internal_cache *cache =
3259 	(struct splite_internal_cache *) p_cache;
3260     GEOSContextHandle_t handle = NULL;
3261     if (cache == NULL)
3262 	return NULL;
3263     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3264 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3265 	return NULL;
3266     handle = cache->GEOS_handle;
3267     if (handle == NULL)
3268 	return NULL;
3269     gaiaResetGeosMsg_r (cache);
3270     if (!geom)
3271       {
3272 	  str = "Invalid: NULL Geometry";
3273 	  len = strlen (str);
3274 	  text = malloc (len + 1);
3275 	  strcpy (text, str);
3276 	  return text;
3277       }
3278     if (gaiaIsToxic_r (cache, geom))
3279       {
3280 	  str = "Invalid: Toxic Geometry ... too few points";
3281 	  len = strlen (str);
3282 	  text = malloc (len + 1);
3283 	  strcpy (text, str);
3284 	  return text;
3285       }
3286     if (gaiaIsNotClosedGeomColl_r (cache, geom))
3287       {
3288 	  str = "Invalid: Unclosed Rings were detected";
3289 	  len = strlen (str);
3290 	  text = malloc (len + 1);
3291 	  strcpy (text, str);
3292 	  return text;
3293       }
3294     g = gaiaToGeos_r (cache, geom);
3295     gstr = GEOSisValidReason_r (handle, g);
3296     GEOSGeom_destroy_r (handle, g);
3297     if (gstr == NULL)
3298 	return NULL;
3299     len = strlen (gstr);
3300     text = malloc (len + 1);
3301     strcpy (text, gstr);
3302     GEOSFree_r (handle, gstr);
3303     return text;
3304 }
3305 
3306 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaIsValidDetailEx(gaiaGeomCollPtr geom,int esri_flag)3307 gaiaIsValidDetailEx (gaiaGeomCollPtr geom, int esri_flag)
3308 {
3309 /* return a Geometry detail causing a Geometry to be invalid */
3310     gaiaGeomCollPtr detail = NULL;
3311 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3312     char *reason = NULL;
3313     GEOSGeometry *g;
3314     GEOSGeometry *d = NULL;
3315     gaiaResetGeosMsg ();
3316     if (!geom)
3317 	return NULL;
3318     if (gaiaIsToxic (geom))
3319 	return NULL;
3320     if (gaiaIsNotClosedGeomColl (geom))
3321 	return NULL;
3322     g = gaiaToGeos (geom);
3323     if (esri_flag)
3324 	esri_flag = GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE;
3325     GEOSisValidDetail (g, esri_flag, &reason, &d);
3326     GEOSGeom_destroy (g);
3327     if (reason != NULL)
3328 	GEOSFree (reason);
3329     if (d == NULL)
3330 	return NULL;
3331     detail = gaiaFromGeos_XY (d);
3332     GEOSGeom_destroy (d);
3333 #else
3334     if (geom == NULL && esri_flag == 0)
3335 	geom = NULL;		/* silencing stupid compiler warnings */
3336 #endif
3337     return detail;
3338 }
3339 
3340 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaIsValidDetail(gaiaGeomCollPtr geom)3341 gaiaIsValidDetail (gaiaGeomCollPtr geom)
3342 {
3343 /* return a Geometry detail causing a Geometry to be invalid */
3344     return gaiaIsValidDetailEx (geom, 0);
3345 }
3346 
3347 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaIsValidDetailEx_r(const void * p_cache,gaiaGeomCollPtr geom,int esri_flag)3348 gaiaIsValidDetailEx_r (const void *p_cache, gaiaGeomCollPtr geom, int esri_flag)
3349 {
3350 /* return a Geometry detail causing a Geometry to be invalid */
3351     char *reason = NULL;
3352     GEOSGeometry *g;
3353     GEOSGeometry *d = NULL;
3354     gaiaGeomCollPtr detail;
3355     struct splite_internal_cache *cache =
3356 	(struct splite_internal_cache *) p_cache;
3357     GEOSContextHandle_t handle = NULL;
3358     if (cache == NULL)
3359 	return NULL;
3360     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3361 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3362 	return NULL;
3363     handle = cache->GEOS_handle;
3364     if (handle == NULL)
3365 	return NULL;
3366     gaiaResetGeosMsg_r (cache);
3367     if (!geom)
3368 	return NULL;
3369     if (gaiaIsToxic (geom))
3370 	return NULL;
3371     if (gaiaIsNotClosedGeomColl_r (cache, geom))
3372 	return NULL;
3373     g = gaiaToGeos_r (cache, geom);
3374     if (esri_flag)
3375 	esri_flag = GEOSVALID_ALLOW_SELFTOUCHING_RING_FORMING_HOLE;
3376     GEOSisValidDetail_r (handle, g, esri_flag, &reason, &d);
3377     GEOSGeom_destroy_r (handle, g);
3378     if (reason != NULL)
3379 	GEOSFree_r (handle, reason);
3380     if (d == NULL)
3381 	return NULL;
3382     detail = gaiaFromGeos_XY_r (cache, d);
3383     GEOSGeom_destroy_r (handle, d);
3384     return detail;
3385 }
3386 
3387 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaIsValidDetail_r(const void * p_cache,gaiaGeomCollPtr geom)3388 gaiaIsValidDetail_r (const void *p_cache, gaiaGeomCollPtr geom)
3389 {
3390 /* return a Geometry detail causing a Geometry to be invalid */
3391     return gaiaIsValidDetailEx_r (p_cache, geom, 0);
3392 }
3393 
3394 GAIAGEO_DECLARE int
gaiaIsClosedGeom_r(const void * cache,gaiaGeomCollPtr geom)3395 gaiaIsClosedGeom_r (const void *cache, gaiaGeomCollPtr geom)
3396 {
3397 /* checks if this geometry is a closed linestring (or multilinestring) */
3398     int ret = 0;
3399     gaiaLinestringPtr ln;
3400     if (cache != NULL)
3401 	gaiaResetGeosMsg_r (cache);
3402     if (!geom)
3403 	return -1;
3404     if (cache != NULL)
3405 	ret = gaiaIsToxic_r (cache, geom);
3406     else
3407 	ret = gaiaIsToxic (geom);
3408     if (ret)
3409 	return 0;
3410     ln = geom->FirstLinestring;
3411     while (ln)
3412       {
3413 	  /* unhappily GEOS v3.2.2 [system package on Debian Lenny and Ubuntu 12.04]
3414 	   * doesn't exposes the GEOSisClosed() API at all !!!!
3415 	   *
3416 	   GEOSGeometry *g;
3417 	   gaiaGeomCollPtr geoColl = gaiaAllocGeomColl();
3418 	   gaiaInsertLinestringInGeomColl(geoColl, gaiaCloneLinestring(ln));
3419 	   g = gaiaToGeos (geoColl);
3420 	   ret = GEOSisClosed (g);
3421 	   GEOSGeom_destroy (g);
3422 	   gaiaFreeGeomColl(geoColl);
3423 	   */
3424 
3425 	  /* so we'll use this internal default in order to circumvent the above issue */
3426 	  double x1;
3427 	  double y1;
3428 	  double z1;
3429 	  double m1;
3430 	  double x2;
3431 	  double y2;
3432 	  double z2;
3433 	  double m2;
3434 	  gaiaLineGetPoint (ln, 0, &x1, &y1, &z1, &m1);
3435 	  gaiaLineGetPoint (ln, ln->Points - 1, &x2, &y2, &z2, &m2);
3436 	  if (x1 == x2 && y1 == y2 && z1 == z2)
3437 	      ret = 1;
3438 	  else
3439 	      ret = 0;
3440 	  if (ret == 0)
3441 	    {
3442 		/* this line isn't closed, so we don't need to continue */
3443 		break;
3444 	    }
3445 	  ln = ln->Next;
3446       }
3447     if (ret == 2)
3448 	return -1;
3449     return ret;
3450 }
3451 
3452 GAIAGEO_DECLARE int
gaiaIsClosedGeom(gaiaGeomCollPtr geom)3453 gaiaIsClosedGeom (gaiaGeomCollPtr geom)
3454 {
3455     gaiaResetGeosMsg ();
3456     return gaiaIsClosedGeom_r (NULL, geom);
3457 }
3458 
3459 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeomCollSimplify(gaiaGeomCollPtr geom,double tolerance)3460 gaiaGeomCollSimplify (gaiaGeomCollPtr geom, double tolerance)
3461 {
3462 /* builds a simplified geometry using the Douglas-Peuker algorihtm */
3463     gaiaGeomCollPtr geo = NULL;
3464 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3465     GEOSGeometry *g1;
3466     GEOSGeometry *g2;
3467     gaiaResetGeosMsg ();
3468     if (!geom)
3469 	return NULL;
3470     if (gaiaIsToxic (geom))
3471 	return NULL;
3472     g1 = gaiaToGeos (geom);
3473     g2 = GEOSSimplify (g1, tolerance);
3474     GEOSGeom_destroy (g1);
3475     if (!g2)
3476 	return NULL;
3477     if (GEOSisEmpty (g2) == 1)
3478       {
3479 	  GEOSGeom_destroy (g2);
3480 	  return NULL;
3481       }
3482     if (geom->DimensionModel == GAIA_XY_Z)
3483 	geo = gaiaFromGeos_XYZ (g2);
3484     else if (geom->DimensionModel == GAIA_XY_M)
3485 	geo = gaiaFromGeos_XYM (g2);
3486     else if (geom->DimensionModel == GAIA_XY_Z_M)
3487 	geo = gaiaFromGeos_XYZM (g2);
3488     else
3489 	geo = gaiaFromGeos_XY (g2);
3490     GEOSGeom_destroy (g2);
3491     if (geo == NULL)
3492 	return NULL;
3493     geo->Srid = geom->Srid;
3494 #else
3495     if (geom == NULL || tolerance == 0.0)
3496 	geom = NULL;		/* silencing stupid compiler warnings */
3497 #endif
3498     return geo;
3499 }
3500 
3501 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeomCollSimplify_r(const void * p_cache,gaiaGeomCollPtr geom,double tolerance)3502 gaiaGeomCollSimplify_r (const void *p_cache, gaiaGeomCollPtr geom,
3503 			double tolerance)
3504 {
3505 /* builds a simplified geometry using the Douglas-Peuker algorihtm */
3506     gaiaGeomCollPtr geo;
3507     GEOSGeometry *g1;
3508     GEOSGeometry *g2;
3509     struct splite_internal_cache *cache =
3510 	(struct splite_internal_cache *) p_cache;
3511     GEOSContextHandle_t handle = NULL;
3512     if (cache == NULL)
3513 	return NULL;
3514     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3515 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3516 	return NULL;
3517     handle = cache->GEOS_handle;
3518     if (handle == NULL)
3519 	return NULL;
3520     gaiaResetGeosMsg_r (cache);
3521     if (!geom)
3522 	return NULL;
3523     if (gaiaIsToxic_r (cache, geom))
3524 	return NULL;
3525     g1 = gaiaToGeos_r (cache, geom);
3526     g2 = GEOSSimplify_r (handle, g1, tolerance);
3527     GEOSGeom_destroy_r (handle, g1);
3528     if (!g2)
3529 	return NULL;
3530     if (GEOSisEmpty_r (handle, g2) == 1)
3531       {
3532 	  GEOSGeom_destroy_r (handle, g2);
3533 	  return NULL;
3534       }
3535     if (geom->DimensionModel == GAIA_XY_Z)
3536 	geo = gaiaFromGeos_XYZ_r (cache, g2);
3537     else if (geom->DimensionModel == GAIA_XY_M)
3538 	geo = gaiaFromGeos_XYM_r (cache, g2);
3539     else if (geom->DimensionModel == GAIA_XY_Z_M)
3540 	geo = gaiaFromGeos_XYZM_r (cache, g2);
3541     else
3542 	geo = gaiaFromGeos_XY_r (cache, g2);
3543     GEOSGeom_destroy_r (handle, g2);
3544     if (geo == NULL)
3545 	return NULL;
3546     geo->Srid = geom->Srid;
3547     return geo;
3548 }
3549 
3550 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeomCollSimplifyPreserveTopology(gaiaGeomCollPtr geom,double tolerance)3551 gaiaGeomCollSimplifyPreserveTopology (gaiaGeomCollPtr geom, double tolerance)
3552 {
3553 /* builds a simplified geometry using the Douglas-Peuker algorihtm [preserving topology] */
3554     gaiaGeomCollPtr geo = NULL;
3555 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3556     GEOSGeometry *g1;
3557     GEOSGeometry *g2;
3558     gaiaResetGeosMsg ();
3559     if (!geom)
3560 	return NULL;
3561     if (gaiaIsToxic (geom))
3562 	return NULL;
3563     g1 = gaiaToGeos (geom);
3564     g2 = GEOSTopologyPreserveSimplify (g1, tolerance);
3565     GEOSGeom_destroy (g1);
3566     if (!g2)
3567 	return NULL;
3568     if (GEOSisEmpty (g2) == 1)
3569       {
3570 	  GEOSGeom_destroy (g2);
3571 	  return NULL;
3572       }
3573     if (geom->DimensionModel == GAIA_XY_Z)
3574 	geo = gaiaFromGeos_XYZ (g2);
3575     else if (geom->DimensionModel == GAIA_XY_M)
3576 	geo = gaiaFromGeos_XYM (g2);
3577     else if (geom->DimensionModel == GAIA_XY_Z_M)
3578 	geo = gaiaFromGeos_XYZM (g2);
3579     else
3580 	geo = gaiaFromGeos_XY (g2);
3581     GEOSGeom_destroy (g2);
3582     if (geo == NULL)
3583 	return NULL;
3584     geo->Srid = geom->Srid;
3585 #else
3586     if (geom == NULL || tolerance == 0.0)
3587 	geom = NULL;		/* silencing stupid compiler warnings */
3588 #endif
3589     return geo;
3590 }
3591 
3592 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeomCollSimplifyPreserveTopology_r(const void * p_cache,gaiaGeomCollPtr geom,double tolerance)3593 gaiaGeomCollSimplifyPreserveTopology_r (const void *p_cache,
3594 					gaiaGeomCollPtr geom, double tolerance)
3595 {
3596 /* builds a simplified geometry using the Douglas-Peuker algorihtm [preserving topology] */
3597     gaiaGeomCollPtr geo;
3598     GEOSGeometry *g1;
3599     GEOSGeometry *g2;
3600     struct splite_internal_cache *cache =
3601 	(struct splite_internal_cache *) p_cache;
3602     GEOSContextHandle_t handle = NULL;
3603     if (cache == NULL)
3604 	return NULL;
3605     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3606 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3607 	return NULL;
3608     handle = cache->GEOS_handle;
3609     if (handle == NULL)
3610 	return NULL;
3611     gaiaResetGeosMsg_r (cache);
3612     if (!geom)
3613 	return NULL;
3614     if (gaiaIsToxic_r (cache, geom))
3615 	return NULL;
3616     g1 = gaiaToGeos_r (cache, geom);
3617     g2 = GEOSTopologyPreserveSimplify_r (handle, g1, tolerance);
3618     GEOSGeom_destroy_r (handle, g1);
3619     if (!g2)
3620 	return NULL;
3621     if (GEOSisEmpty_r (handle, g2) == 1)
3622       {
3623 	  GEOSGeom_destroy_r (handle, g2);
3624 	  return NULL;
3625       }
3626     if (geom->DimensionModel == GAIA_XY_Z)
3627 	geo = gaiaFromGeos_XYZ_r (cache, g2);
3628     else if (geom->DimensionModel == GAIA_XY_M)
3629 	geo = gaiaFromGeos_XYM_r (cache, g2);
3630     else if (geom->DimensionModel == GAIA_XY_Z_M)
3631 	geo = gaiaFromGeos_XYZM_r (cache, g2);
3632     else
3633 	geo = gaiaFromGeos_XY_r (cache, g2);
3634     GEOSGeom_destroy_r (handle, g2);
3635     if (geo == NULL)
3636 	return NULL;
3637     geo->Srid = geom->Srid;
3638     return geo;
3639 }
3640 
3641 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaConvexHull(gaiaGeomCollPtr geom)3642 gaiaConvexHull (gaiaGeomCollPtr geom)
3643 {
3644 /* builds a geometry that is the convex hull of GEOM */
3645     gaiaGeomCollPtr geo = NULL;
3646 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3647     GEOSGeometry *g1;
3648     GEOSGeometry *g2;
3649     gaiaResetGeosMsg ();
3650     if (!geom)
3651 	return NULL;
3652     if (gaiaIsToxic (geom))
3653 	return NULL;
3654     g1 = gaiaToGeos (geom);
3655     g2 = GEOSConvexHull (g1);
3656     GEOSGeom_destroy (g1);
3657     if (!g2)
3658 	return NULL;
3659     if (GEOSisEmpty (g2) == 1)
3660       {
3661 	  GEOSGeom_destroy (g2);
3662 	  return NULL;
3663       }
3664     if (geom->DimensionModel == GAIA_XY_Z)
3665 	geo = gaiaFromGeos_XYZ (g2);
3666     else if (geom->DimensionModel == GAIA_XY_M)
3667 	geo = gaiaFromGeos_XYM (g2);
3668     else if (geom->DimensionModel == GAIA_XY_Z_M)
3669 	geo = gaiaFromGeos_XYZM (g2);
3670     else
3671 	geo = gaiaFromGeos_XY (g2);
3672     GEOSGeom_destroy (g2);
3673     if (geo == NULL)
3674 	return NULL;
3675     geo->Srid = geom->Srid;
3676 #else
3677     if (geom == NULL)
3678 	geom = NULL;		/* silencing stupid compiler warnings */
3679 #endif
3680     return geo;
3681 }
3682 
3683 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaConvexHull_r(const void * p_cache,gaiaGeomCollPtr geom)3684 gaiaConvexHull_r (const void *p_cache, gaiaGeomCollPtr geom)
3685 {
3686 /* builds a geometry that is the convex hull of GEOM */
3687     gaiaGeomCollPtr geo;
3688     GEOSGeometry *g1;
3689     GEOSGeometry *g2;
3690     struct splite_internal_cache *cache =
3691 	(struct splite_internal_cache *) p_cache;
3692     GEOSContextHandle_t handle = NULL;
3693     if (cache == NULL)
3694 	return NULL;
3695     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3696 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3697 	return NULL;
3698     handle = cache->GEOS_handle;
3699     if (handle == NULL)
3700 	return NULL;
3701     gaiaResetGeosMsg_r (cache);
3702     if (!geom)
3703 	return NULL;
3704     if (gaiaIsToxic_r (cache, geom))
3705 	return NULL;
3706     g1 = gaiaToGeos_r (cache, geom);
3707     g2 = GEOSConvexHull_r (handle, g1);
3708     GEOSGeom_destroy_r (handle, g1);
3709     if (!g2)
3710 	return NULL;
3711     if (GEOSisEmpty_r (handle, g2) == 1)
3712       {
3713 	  GEOSGeom_destroy_r (handle, g2);
3714 	  return NULL;
3715       }
3716     if (geom->DimensionModel == GAIA_XY_Z)
3717 	geo = gaiaFromGeos_XYZ_r (cache, g2);
3718     else if (geom->DimensionModel == GAIA_XY_M)
3719 	geo = gaiaFromGeos_XYM_r (cache, g2);
3720     else if (geom->DimensionModel == GAIA_XY_Z_M)
3721 	geo = gaiaFromGeos_XYZM_r (cache, g2);
3722     else
3723 	geo = gaiaFromGeos_XY_r (cache, g2);
3724     GEOSGeom_destroy_r (handle, g2);
3725     if (geo == NULL)
3726 	return NULL;
3727     geo->Srid = geom->Srid;
3728     return geo;
3729 }
3730 
3731 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeomCollBuffer(gaiaGeomCollPtr geom,double radius,int points)3732 gaiaGeomCollBuffer (gaiaGeomCollPtr geom, double radius, int points)
3733 {
3734 /* builds a geometry that is the GIS buffer of GEOM */
3735     gaiaGeomCollPtr geo = NULL;
3736 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3737     GEOSGeometry *g1;
3738     GEOSGeometry *g2;
3739     GEOSBufferParams *params = NULL;
3740     gaiaResetGeosMsg ();
3741     if (!geom)
3742 	return NULL;
3743     if (gaiaIsToxic (geom))
3744 	return NULL;
3745     g1 = gaiaToGeos (geom);
3746 /* setting up Buffer params */
3747     params = GEOSBufferParams_create ();
3748     GEOSBufferParams_setEndCapStyle (params, GEOSBUF_CAP_ROUND);
3749     GEOSBufferParams_setJoinStyle (params, GEOSBUF_JOIN_ROUND);
3750     GEOSBufferParams_setMitreLimit (params, 5.0);
3751     GEOSBufferParams_setQuadrantSegments (params, points);
3752     GEOSBufferParams_setSingleSided (params, 0);
3753 
3754     g2 = GEOSBufferWithParams (g1, params, radius);
3755     GEOSGeom_destroy (g1);
3756     GEOSBufferParams_destroy (params);
3757     if (!g2)
3758 	return NULL;
3759     if (GEOSisEmpty (g2) == 1)
3760       {
3761 	  GEOSGeom_destroy (g2);
3762 	  return NULL;
3763       }
3764     if (geom->DimensionModel == GAIA_XY_Z)
3765 	geo = gaiaFromGeos_XYZ (g2);
3766     else if (geom->DimensionModel == GAIA_XY_M)
3767 	geo = gaiaFromGeos_XYM (g2);
3768     else if (geom->DimensionModel == GAIA_XY_Z_M)
3769 	geo = gaiaFromGeos_XYZM (g2);
3770     else
3771 	geo = gaiaFromGeos_XY (g2);
3772     GEOSGeom_destroy (g2);
3773     if (geo == NULL)
3774 	return NULL;
3775     geo->Srid = geom->Srid;
3776 #else
3777     if (geom == NULL || radius == 0.0 || points == 0)
3778 	geom = NULL;		/* silencing stupid compiler warnings */
3779 #endif
3780     return geo;
3781 }
3782 
3783 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaGeomCollBuffer_r(const void * p_cache,gaiaGeomCollPtr geom,double radius,int points)3784 gaiaGeomCollBuffer_r (const void *p_cache, gaiaGeomCollPtr geom, double radius,
3785 		      int points)
3786 {
3787 /* builds a geometry that is the GIS buffer of GEOM */
3788     gaiaGeomCollPtr geo;
3789     GEOSGeometry *g1;
3790     GEOSGeometry *g2;
3791     GEOSBufferParams *params = NULL;
3792     struct splite_internal_cache *cache =
3793 	(struct splite_internal_cache *) p_cache;
3794     GEOSContextHandle_t handle = NULL;
3795     int quadsegs = 30;
3796     if (cache == NULL)
3797 	return NULL;
3798     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
3799 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
3800 	return NULL;
3801     handle = cache->GEOS_handle;
3802     if (handle == NULL)
3803 	return NULL;
3804     gaiaResetGeosMsg_r (cache);
3805     if (!geom)
3806 	return NULL;
3807     if (gaiaIsToxic_r (cache, geom))
3808 	return NULL;
3809     g1 = gaiaToGeos_r (cache, geom);
3810 /* setting up Buffer params */
3811     params = GEOSBufferParams_create_r (handle);
3812     GEOSBufferParams_setEndCapStyle_r (handle, params,
3813 				       cache->buffer_end_cap_style);
3814     GEOSBufferParams_setJoinStyle_r (handle, params, cache->buffer_join_style);
3815     GEOSBufferParams_setMitreLimit_r (handle, params,
3816 				      cache->buffer_mitre_limit);
3817     if (points > 0)
3818 	quadsegs = points;
3819     else if (cache->buffer_quadrant_segments > 0)
3820 	quadsegs = cache->buffer_quadrant_segments;
3821     GEOSBufferParams_setQuadrantSegments_r (handle, params, quadsegs);
3822     GEOSBufferParams_setSingleSided_r (handle, params, 0);
3823 
3824     g2 = GEOSBufferWithParams_r (handle, g1, params, radius);
3825     GEOSGeom_destroy_r (handle, g1);
3826     GEOSBufferParams_destroy_r (handle, params);
3827     if (!g2)
3828 	return NULL;
3829     if (GEOSisEmpty_r (handle, g2) == 1)
3830       {
3831 	  GEOSGeom_destroy_r (handle, g2);
3832 	  return NULL;
3833       }
3834     if (geom->DimensionModel == GAIA_XY_Z)
3835 	geo = gaiaFromGeos_XYZ_r (cache, g2);
3836     else if (geom->DimensionModel == GAIA_XY_M)
3837 	geo = gaiaFromGeos_XYM_r (cache, g2);
3838     else if (geom->DimensionModel == GAIA_XY_Z_M)
3839 	geo = gaiaFromGeos_XYZM_r (cache, g2);
3840     else
3841 	geo = gaiaFromGeos_XY_r (cache, g2);
3842     GEOSGeom_destroy_r (handle, g2);
3843     if (geo == NULL)
3844 	return NULL;
3845     geo->Srid = geom->Srid;
3846     return geo;
3847 }
3848 
3849 static void
auxFromGeosPolygon(GEOSContextHandle_t handle,const GEOSGeometry * geos,gaiaGeomCollPtr result)3850 auxFromGeosPolygon (GEOSContextHandle_t handle, const GEOSGeometry * geos,
3851 		    gaiaGeomCollPtr result)
3852 {
3853 /* converting a Polygon from GEOS to SpatiaLite */
3854     const GEOSGeometry *geos_ring;
3855     const GEOSCoordSequence *coords;
3856     unsigned int pts;
3857     unsigned int geos_dims;
3858     int interiors;
3859     int iv;
3860     int ib;
3861     double x;
3862     double y;
3863     double z;
3864     gaiaPolygonPtr pg;
3865     gaiaRingPtr rng;
3866 
3867 #ifdef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3868     if (handle == NULL)
3869 	return;
3870 #endif
3871 
3872     if (handle != NULL)
3873       {
3874 	  geos_ring = GEOSGetExteriorRing_r (handle, geos);
3875 	  interiors = GEOSGetNumInteriorRings_r (handle, geos);
3876 	  coords = GEOSGeom_getCoordSeq_r (handle, geos_ring);
3877 	  GEOSCoordSeq_getDimensions_r (handle, coords, &geos_dims);
3878 	  GEOSCoordSeq_getSize_r (handle, coords, &pts);
3879       }
3880     else
3881       {
3882 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3883 	  geos_ring = GEOSGetExteriorRing (geos);
3884 	  interiors = GEOSGetNumInteriorRings (geos);
3885 	  coords = GEOSGeom_getCoordSeq (geos_ring);
3886 	  GEOSCoordSeq_getDimensions (coords, &geos_dims);
3887 	  GEOSCoordSeq_getSize (coords, &pts);
3888 #endif
3889       }
3890 
3891     pg = gaiaAddPolygonToGeomColl (result, pts, interiors);
3892 /* setting up the Exterior ring */
3893     rng = pg->Exterior;
3894     for (iv = 0; iv < (int) pts; iv++)
3895       {
3896 	  if (geos_dims == 3)
3897 	    {
3898 		if (handle != NULL)
3899 		  {
3900 		      GEOSCoordSeq_getX_r (handle, coords, iv, &x);
3901 		      GEOSCoordSeq_getY_r (handle, coords, iv, &y);
3902 		      GEOSCoordSeq_getZ_r (handle, coords, iv, &z);
3903 		  }
3904 		else
3905 		  {
3906 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3907 		      GEOSCoordSeq_getX (coords, iv, &x);
3908 		      GEOSCoordSeq_getY (coords, iv, &y);
3909 		      GEOSCoordSeq_getZ (coords, iv, &z);
3910 #endif
3911 		  }
3912 	    }
3913 	  else
3914 	    {
3915 		if (handle != NULL)
3916 		  {
3917 		      GEOSCoordSeq_getX_r (handle, coords, iv, &x);
3918 		      GEOSCoordSeq_getY_r (handle, coords, iv, &y);
3919 		  }
3920 		else
3921 		  {
3922 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3923 		      GEOSCoordSeq_getX (coords, iv, &x);
3924 		      GEOSCoordSeq_getY (coords, iv, &y);
3925 #endif
3926 		  }
3927 		z = 0.0;
3928 	    }
3929 	  if (rng->DimensionModel == GAIA_XY_Z)
3930 	    {
3931 		gaiaSetPointXYZ (rng->Coords, iv, x, y, z);
3932 	    }
3933 	  else if (rng->DimensionModel == GAIA_XY_M)
3934 	    {
3935 		gaiaSetPointXYM (rng->Coords, iv, x, y, 0.0);
3936 	    }
3937 	  else if (rng->DimensionModel == GAIA_XY_Z_M)
3938 	    {
3939 		gaiaSetPointXYZM (rng->Coords, iv, x, y, z, 0.0);
3940 	    }
3941 	  else
3942 	    {
3943 		gaiaSetPoint (rng->Coords, iv, x, y);
3944 	    }
3945       }
3946 
3947     for (ib = 0; ib < interiors; ib++)
3948       {
3949 	  /* setting up any interior ring */
3950 	  if (handle != NULL)
3951 	    {
3952 		geos_ring = GEOSGetInteriorRingN_r (handle, geos, ib);
3953 		coords = GEOSGeom_getCoordSeq_r (handle, geos_ring);
3954 		GEOSCoordSeq_getDimensions_r (handle, coords, &geos_dims);
3955 		GEOSCoordSeq_getSize_r (handle, coords, &pts);
3956 	    }
3957 	  else
3958 	    {
3959 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3960 		geos_ring = GEOSGetInteriorRingN (geos, ib);
3961 		coords = GEOSGeom_getCoordSeq (geos_ring);
3962 		GEOSCoordSeq_getDimensions (coords, &geos_dims);
3963 		GEOSCoordSeq_getSize (coords, &pts);
3964 #endif
3965 	    }
3966 	  rng = gaiaAddInteriorRing (pg, ib, pts);
3967 	  for (iv = 0; iv < (int) pts; iv++)
3968 	    {
3969 		if (geos_dims == 3)
3970 		  {
3971 		      if (handle != NULL)
3972 			{
3973 			    GEOSCoordSeq_getX_r (handle, coords, iv, &x);
3974 			    GEOSCoordSeq_getY_r (handle, coords, iv, &y);
3975 			    GEOSCoordSeq_getZ_r (handle, coords, iv, &z);
3976 			}
3977 		      else
3978 			{
3979 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3980 			    GEOSCoordSeq_getX (coords, iv, &x);
3981 			    GEOSCoordSeq_getY (coords, iv, &y);
3982 			    GEOSCoordSeq_getZ (coords, iv, &z);
3983 #endif
3984 			}
3985 		  }
3986 		else
3987 		  {
3988 		      if (handle != NULL)
3989 			{
3990 			    GEOSCoordSeq_getX_r (handle, coords, iv, &x);
3991 			    GEOSCoordSeq_getY_r (handle, coords, iv, &y);
3992 			}
3993 		      else
3994 			{
3995 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
3996 			    GEOSCoordSeq_getX (coords, iv, &x);
3997 			    GEOSCoordSeq_getY (coords, iv, &y);
3998 #endif
3999 			}
4000 		      z = 0.0;
4001 		  }
4002 		if (rng->DimensionModel == GAIA_XY_Z)
4003 		  {
4004 		      gaiaSetPointXYZ (rng->Coords, iv, x, y, z);
4005 		  }
4006 		else if (rng->DimensionModel == GAIA_XY_M)
4007 		  {
4008 		      gaiaSetPointXYM (rng->Coords, iv, x, y, 0.0);
4009 		  }
4010 		else if (rng->DimensionModel == GAIA_XY_Z_M)
4011 		  {
4012 		      gaiaSetPointXYZM (rng->Coords, iv, x, y, z, 0.0);
4013 		  }
4014 		else
4015 		  {
4016 		      gaiaSetPoint (rng->Coords, iv, x, y);
4017 		  }
4018 	    }
4019       }
4020 }
4021 
4022 static void
auxGeosMbr(GEOSContextHandle_t handle,const GEOSCoordSequence * cs,unsigned int pts,double * min_x,double * min_y,double * max_x,double * max_y)4023 auxGeosMbr (GEOSContextHandle_t handle, const GEOSCoordSequence * cs,
4024 	    unsigned int pts, double *min_x, double *min_y, double *max_x,
4025 	    double *max_y)
4026 {
4027 /* computing the MBR */
4028     int iv;
4029     double x;
4030     double y;
4031     *min_x = DBL_MAX;
4032     *min_y = DBL_MAX;
4033     *max_x = 0 - DBL_MAX;
4034     *max_y = 0 - DBL_MAX;
4035     for (iv = 0; iv < (int) pts; iv++)
4036       {
4037 	  if (handle != NULL)
4038 	    {
4039 		GEOSCoordSeq_getX_r (handle, cs, iv, &x);
4040 		GEOSCoordSeq_getY_r (handle, cs, iv, &y);
4041 	    }
4042 	  else
4043 	    {
4044 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4045 		GEOSCoordSeq_getX (cs, iv, &x);
4046 		GEOSCoordSeq_getY (cs, iv, &y);
4047 #endif
4048 	    }
4049 	  if (x < *min_x)
4050 	      *min_x = x;
4051 	  if (x > *max_x)
4052 	      *max_x = x;
4053 	  if (y < *min_y)
4054 	      *min_y = y;
4055 	  if (y > *max_y)
4056 	      *max_y = y;
4057       }
4058 }
4059 
4060 static gaiaGeomCollPtr
gaiaPolygonizeCommon(const void * cache,GEOSContextHandle_t handle,gaiaGeomCollPtr geom,int force_multi)4061 gaiaPolygonizeCommon (const void *cache, GEOSContextHandle_t handle,
4062 		      gaiaGeomCollPtr geom, int force_multi)
4063 {
4064 /* attempts to rearrange a generic Geometry into a (multi)polygon */
4065     int ig;
4066     int ib;
4067     int iv;
4068     int interiors;
4069     int geos_dims = 2;
4070     int pts = 0;
4071     int lns = 0;
4072     int pgs = 0;
4073     int items;
4074     int error = 0;
4075     double x;
4076     double y;
4077     double z;
4078     double m;
4079     gaiaGeomCollPtr result = NULL;
4080     gaiaPointPtr pt;
4081     gaiaLinestringPtr ln;
4082     gaiaPolygonPtr pg;
4083     GEOSCoordSequence *cs;
4084     const GEOSGeometry *const *geos_list = NULL;
4085     GEOSGeometry **p_item;
4086     GEOSGeometry *geos;
4087     const GEOSGeometry *geos_item;
4088     const GEOSGeometry *geos_item2;
4089     const GEOSGeometry *geos_ring;
4090     char *valid_polygons = NULL;
4091     const GEOSCoordSequence *coords;
4092     unsigned int pts1;
4093     unsigned int pts2;
4094     double min_x1;
4095     double max_x1;
4096     double min_y1;
4097     double max_y1;
4098     double min_x2;
4099     double max_x2;
4100     double min_y2;
4101     double max_y2;
4102     int ret;
4103 
4104 #ifdef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4105     if (handle == NULL)
4106 	return NULL;
4107 #endif
4108     if (!geom)
4109 	return NULL;
4110 
4111     if (cache != NULL)
4112 	ret = gaiaIsToxic_r (cache, geom);
4113 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4114     else
4115 	ret = gaiaIsToxic (geom);
4116 #endif
4117     if (ret)
4118 	return NULL;
4119 
4120     pt = geom->FirstPoint;
4121     while (pt)
4122       {
4123 	  pts++;
4124 	  pt = pt->Next;
4125       }
4126     pg = geom->FirstPolygon;
4127     while (pg)
4128       {
4129 	  pgs++;
4130 	  pg = pg->Next;
4131       }
4132     if (pts || pgs)
4133 	return NULL;
4134     ln = geom->FirstLinestring;
4135     while (ln)
4136       {
4137 	  lns++;
4138 	  ln = ln->Next;
4139       }
4140     if (!lns)
4141 	return NULL;
4142     if (geom->DimensionModel == GAIA_XY_Z
4143 	|| geom->DimensionModel == GAIA_XY_Z_M)
4144 	geos_dims = 3;
4145 
4146 /* allocating GEOS linestrings */
4147     geos_list = malloc (sizeof (const GEOSGeometry * const *) * lns);
4148     p_item = (GEOSGeometry **) geos_list;
4149     for (iv = 0; iv < lns; iv++)
4150       {
4151 	  /* initializing to NULL */
4152 	  *p_item++ = NULL;
4153       }
4154     p_item = (GEOSGeometry **) geos_list;
4155 
4156 /* initializing GEOS linestrings */
4157     ln = geom->FirstLinestring;
4158     while (ln)
4159       {
4160 	  if (handle != NULL)
4161 	      cs = GEOSCoordSeq_create_r (handle, ln->Points, geos_dims);
4162 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4163 	  else
4164 	      cs = GEOSCoordSeq_create (ln->Points, geos_dims);
4165 #endif
4166 	  for (iv = 0; iv < ln->Points; iv++)
4167 	    {
4168 		/* exterior ring segments */
4169 		z = 0.0;
4170 		if (ln->DimensionModel == GAIA_XY_Z)
4171 		  {
4172 		      gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z);
4173 		  }
4174 		else if (ln->DimensionModel == GAIA_XY_M)
4175 		  {
4176 		      gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m);
4177 		  }
4178 		else if (ln->DimensionModel == GAIA_XY_Z_M)
4179 		  {
4180 		      gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m);
4181 		  }
4182 		else
4183 		  {
4184 		      gaiaGetPoint (ln->Coords, iv, &x, &y);
4185 		  }
4186 		if (geos_dims == 3)
4187 		  {
4188 		      if (handle != NULL)
4189 			{
4190 			    GEOSCoordSeq_setX_r (handle, cs, iv, x);
4191 			    GEOSCoordSeq_setY_r (handle, cs, iv, y);
4192 			    GEOSCoordSeq_setZ_r (handle, cs, iv, z);
4193 			}
4194 		      else
4195 			{
4196 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4197 			    GEOSCoordSeq_setX (cs, iv, x);
4198 			    GEOSCoordSeq_setY (cs, iv, y);
4199 			    GEOSCoordSeq_setZ (cs, iv, z);
4200 #endif
4201 			}
4202 		  }
4203 		else
4204 		  {
4205 		      if (handle != NULL)
4206 			{
4207 			    GEOSCoordSeq_setX_r (handle, cs, iv, x);
4208 			    GEOSCoordSeq_setY_r (handle, cs, iv, y);
4209 			}
4210 		      else
4211 			{
4212 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4213 			    GEOSCoordSeq_setX (cs, iv, x);
4214 			    GEOSCoordSeq_setY (cs, iv, y);
4215 #endif
4216 			}
4217 		  }
4218 	    }
4219 	  if (handle != NULL)
4220 	      *p_item++ = GEOSGeom_createLineString_r (handle, cs);
4221 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4222 	  else
4223 	      *p_item++ = GEOSGeom_createLineString (cs);
4224 #endif
4225 	  ln = ln->Next;
4226       }
4227 
4228 /* calling GEOSPolygonize */
4229     if (handle != NULL)
4230 	geos = GEOSPolygonize_r (handle, geos_list, lns);
4231 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4232     else
4233 	geos = GEOSPolygonize (geos_list, lns);
4234 #endif
4235     if (geos == NULL)
4236 	goto cleanup;
4237 
4238 /*
4239 /
4240 / GEOSPolygonize is expected to return a collection of Polygons
4241 /
4242 / CAVEAT: internal holes are returned as such (interior rings in
4243 /         some Polygon), but are returned as distinct Polygons too
4244 /
4245 / we must check this, so to *not* return Polygons representing holes
4246 /
4247 */
4248     error = 0;
4249     if (handle != NULL)
4250 	items = GEOSGetNumGeometries_r (handle, geos);
4251 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4252     else
4253 	items = GEOSGetNumGeometries (geos);
4254 #endif
4255     for (ig = 0; ig < items; ig++)
4256       {
4257 	  /* looping on elementaty GEOS geometries */
4258 	  if (handle != NULL)
4259 	    {
4260 		geos_item = GEOSGetGeometryN_r (handle, geos, ig);
4261 		if (GEOSGeomTypeId_r (handle, geos_item) != GEOS_POLYGON)
4262 		  {
4263 		      /* not a Polygon ... ouch ... */
4264 		      error = 1;
4265 		      goto cleanup;
4266 		  }
4267 	    }
4268 	  else
4269 	    {
4270 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4271 		geos_item = GEOSGetGeometryN (geos, ig);
4272 		if (GEOSGeomTypeId (geos_item) != GEOS_POLYGON)
4273 		  {
4274 		      /* not a Polygon ... ouch ... */
4275 		      error = 1;
4276 		      goto cleanup;
4277 		  }
4278 #endif
4279 	    }
4280       }
4281 
4282 /* identifying valid Polygons [excluding holes] */
4283     valid_polygons = malloc (sizeof (char *) * items);
4284     for (ig = 0; ig < items; ig++)
4285 	valid_polygons[ig] = 'Y';
4286     for (ig = 0; ig < items; ig++)
4287       {
4288 	  /* looping on elementaty GEOS Polygons */
4289 	  if (handle != NULL)
4290 	    {
4291 		geos_item = GEOSGetGeometryN_r (handle, geos, ig);
4292 		interiors = GEOSGetNumInteriorRings_r (handle, geos_item);
4293 	    }
4294 	  else
4295 	    {
4296 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4297 		geos_item = GEOSGetGeometryN (geos, ig);
4298 		interiors = GEOSGetNumInteriorRings (geos_item);
4299 #endif
4300 	    }
4301 	  for (ib = 0; ib < interiors; ib++)
4302 	    {
4303 		/* looping on any interior ring */
4304 		if (handle != NULL)
4305 		  {
4306 		      geos_ring =
4307 			  GEOSGetInteriorRingN_r (handle, geos_item, ib);
4308 		      coords = GEOSGeom_getCoordSeq_r (handle, geos_ring);
4309 		      GEOSCoordSeq_getSize_r (handle, coords, &pts1);
4310 		  }
4311 		else
4312 		  {
4313 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4314 		      geos_ring = GEOSGetInteriorRingN (geos_item, ib);
4315 		      coords = GEOSGeom_getCoordSeq (geos_ring);
4316 		      GEOSCoordSeq_getSize (coords, &pts1);
4317 #endif
4318 		  }
4319 		auxGeosMbr (handle, coords, pts1, &min_x1, &min_y1, &max_x1,
4320 			    &max_y1);
4321 		for (iv = 0; iv < items; iv++)
4322 		  {
4323 		      if (iv == ig)
4324 			{
4325 			    /* skipping the Polygon itself */
4326 			    continue;
4327 			}
4328 		      if (valid_polygons[iv] == 'N')
4329 			{
4330 			    /* skipping any already invalid Polygon */
4331 			    continue;
4332 			}
4333 		      if (handle != NULL)
4334 			{
4335 			    geos_item2 = GEOSGetGeometryN_r (handle, geos, iv);
4336 			    if (GEOSGetNumInteriorRings_r (handle, geos_item2) >
4337 				0)
4338 			      {
4339 				  /* this Polygon contains holes [surely valid] */
4340 				  continue;
4341 			      }
4342 			    geos_ring =
4343 				GEOSGetExteriorRing_r (handle, geos_item2);
4344 			    coords = GEOSGeom_getCoordSeq_r (handle, geos_ring);
4345 			    GEOSCoordSeq_getSize_r (handle, coords, &pts2);
4346 			}
4347 		      else
4348 			{
4349 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4350 			    geos_item2 = GEOSGetGeometryN (geos, iv);
4351 			    if (GEOSGetNumInteriorRings (geos_item2) > 0)
4352 			      {
4353 				  /* this Polygon contains holes [surely valid] */
4354 				  continue;
4355 			      }
4356 			    geos_ring = GEOSGetExteriorRing (geos_item2);
4357 			    coords = GEOSGeom_getCoordSeq (geos_ring);
4358 			    GEOSCoordSeq_getSize (coords, &pts2);
4359 #endif
4360 			}
4361 		      if (pts1 == pts2)
4362 			{
4363 			    auxGeosMbr (handle, coords, pts2, &min_x2, &min_y2,
4364 					&max_x2, &max_y2);
4365 			    if (min_x1 == min_x2 && min_y1 == min_y2
4366 				&& max_x1 == max_x2 && max_y1 == max_y2)
4367 			      {
4368 				  /* same #points, same MBRs: invalidating */
4369 				  valid_polygons[iv] = 'N';
4370 			      }
4371 			}
4372 		  }
4373 	    }
4374       }
4375 
4376 /* creating the Geometry to be returned */
4377     if (geom->DimensionModel == GAIA_XY_Z)
4378 	result = gaiaAllocGeomCollXYZ ();
4379     else if (geom->DimensionModel == GAIA_XY_M)
4380 	result = gaiaAllocGeomCollXYM ();
4381     else if (geom->DimensionModel == GAIA_XY_Z_M)
4382 	result = gaiaAllocGeomCollXYZM ();
4383     else
4384 	result = gaiaAllocGeomColl ();
4385     if (result == NULL)
4386 	return NULL;
4387     result->Srid = geom->Srid;
4388     if (force_multi)
4389 	result->DeclaredType = GAIA_MULTIPOLYGON;
4390 
4391     for (ig = 0; ig < items; ig++)
4392       {
4393 	  /* looping on GEOS Polygons */
4394 	  if (handle != NULL)
4395 	      geos_item = GEOSGetGeometryN_r (handle, geos, ig);
4396 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4397 	  else
4398 	      geos_item = GEOSGetGeometryN (geos, ig);
4399 #endif
4400 	  if (valid_polygons[ig] == 'Y')
4401 	      auxFromGeosPolygon (handle, geos_item, result);
4402       }
4403 
4404   cleanup:
4405     if (valid_polygons != NULL)
4406 	free (valid_polygons);
4407     if (geos_list != NULL)
4408       {
4409 	  /* memory cleanup */
4410 	  p_item = (GEOSGeometry **) geos_list;
4411 	  for (iv = 0; iv < lns; iv++)
4412 	    {
4413 		if (*p_item != NULL)
4414 		  {
4415 		      if (handle != NULL)
4416 			  GEOSGeom_destroy_r (handle, *p_item);
4417 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4418 		      else
4419 			  GEOSGeom_destroy (*p_item);
4420 #endif
4421 		  }
4422 		p_item++;
4423 	    }
4424 	  p_item = (GEOSGeometry **) geos_list;
4425 	  free (p_item);
4426       }
4427     if (geos != NULL)
4428       {
4429 	  if (handle != NULL)
4430 	      GEOSGeom_destroy_r (handle, geos);
4431 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4432 	  else
4433 	      GEOSGeom_destroy (geos);
4434 #endif
4435       }
4436     if (error || result->FirstPolygon == NULL)
4437       {
4438 	  gaiaFreeGeomColl (result);
4439 	  return NULL;
4440       }
4441     return result;
4442 }
4443 
4444 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaPolygonize(gaiaGeomCollPtr geom,int force_multi)4445 gaiaPolygonize (gaiaGeomCollPtr geom, int force_multi)
4446 {
4447 /* attempts to rearrange a generic Geometry into a (multi)polygon */
4448     gaiaResetGeosMsg ();
4449     return gaiaPolygonizeCommon (NULL, NULL, geom, force_multi);
4450 }
4451 
4452 GAIAGEO_DECLARE gaiaGeomCollPtr
gaiaPolygonize_r(const void * p_cache,gaiaGeomCollPtr geom,int force_multi)4453 gaiaPolygonize_r (const void *p_cache, gaiaGeomCollPtr geom, int force_multi)
4454 {
4455 /* attempts to rearrange a generic Geometry into a (multi)polygon */
4456     struct splite_internal_cache *cache =
4457 	(struct splite_internal_cache *) p_cache;
4458     GEOSContextHandle_t handle = NULL;
4459     if (cache == NULL)
4460 	return NULL;
4461     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
4462 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
4463 	return NULL;
4464     handle = cache->GEOS_handle;
4465     if (handle == NULL)
4466 	return NULL;
4467     gaiaResetGeosMsg_r (cache);
4468     return gaiaPolygonizeCommon (cache, handle, geom, force_multi);
4469 }
4470 
4471 GAIAGEO_DECLARE int
gaiaGeomCollCovers(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)4472 gaiaGeomCollCovers (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
4473 {
4474 /* checks if geom1 "spatially covers" geom2 */
4475     int ret = -1;
4476 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4477     GEOSGeometry *g1;
4478     GEOSGeometry *g2;
4479     gaiaResetGeosMsg ();
4480     if (!geom1 || !geom2)
4481 	return -1;
4482 
4483 /* quick check based on MBRs comparison */
4484     if (!splite_mbr_contains (geom1, geom2))
4485 	return 0;
4486 
4487     g1 = gaiaToGeos (geom1);
4488     g2 = gaiaToGeos (geom2);
4489     ret = GEOSCovers (g1, g2);
4490     GEOSGeom_destroy (g1);
4491     GEOSGeom_destroy (g2);
4492     if (ret == 2)
4493 	return -1;
4494 #else
4495     if (geom1 == NULL || geom2 == NULL)
4496 	geom1 = NULL;		/* silencing stupid compiler warnings */
4497 #endif
4498     return ret;
4499 }
4500 
4501 GAIAGEO_DECLARE int
gaiaGeomCollCovers_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)4502 gaiaGeomCollCovers_r (const void *p_cache, gaiaGeomCollPtr geom1,
4503 		      gaiaGeomCollPtr geom2)
4504 {
4505 /* checks if geom1 "spatially covers" geom2 */
4506     int ret;
4507     GEOSGeometry *g1;
4508     GEOSGeometry *g2;
4509     struct splite_internal_cache *cache =
4510 	(struct splite_internal_cache *) p_cache;
4511     GEOSContextHandle_t handle = NULL;
4512     if (cache == NULL)
4513 	return -1;
4514     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
4515 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
4516 	return -1;
4517     handle = cache->GEOS_handle;
4518     if (handle == NULL)
4519 	return -1;
4520     gaiaResetGeosMsg_r (cache);
4521     if (!geom1 || !geom2)
4522 	return -1;
4523 
4524 /* quick check based on MBRs comparison */
4525     if (!splite_mbr_contains (geom1, geom2))
4526 	return 0;
4527 
4528     g1 = gaiaToGeos_r (cache, geom1);
4529     g2 = gaiaToGeos_r (cache, geom2);
4530     ret = GEOSCovers_r (handle, g1, g2);
4531     GEOSGeom_destroy_r (handle, g1);
4532     GEOSGeom_destroy_r (handle, g2);
4533     if (ret == 2)
4534 	return -1;
4535     return ret;
4536 }
4537 
4538 GAIAGEO_DECLARE int
gaiaGeomCollPreparedCovers(const void * p_cache,gaiaGeomCollPtr geom1,unsigned char * blob1,int size1,gaiaGeomCollPtr geom2,unsigned char * blob2,int size2)4539 gaiaGeomCollPreparedCovers (const void *p_cache, gaiaGeomCollPtr geom1,
4540 			    unsigned char *blob1, int size1,
4541 			    gaiaGeomCollPtr geom2, unsigned char *blob2,
4542 			    int size2)
4543 {
4544 /* checks if geom1 "spatially covers" geom2 */
4545     int ret;
4546     struct splite_internal_cache *cache =
4547 	(struct splite_internal_cache *) p_cache;
4548     GEOSPreparedGeometry *gPrep;
4549     GEOSGeometry *g1;
4550     GEOSGeometry *g2;
4551     gaiaGeomCollPtr geom;
4552     GEOSContextHandle_t handle = NULL;
4553     if (cache == NULL)
4554 	return -1;
4555     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
4556 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
4557 	return -1;
4558     handle = cache->GEOS_handle;
4559     if (handle == NULL)
4560 	return -1;
4561     gaiaResetGeosMsg_r (cache);
4562     if (!geom1 || !geom2)
4563 	return -1;
4564 
4565 /* quick check based on MBRs comparison */
4566     if (!splite_mbr_contains (geom1, geom2))
4567 	return 0;
4568 
4569 /* handling the internal GEOS cache */
4570     if (evalGeosCache
4571 	(cache, geom1, blob1, size1, geom2, blob2, size2, &gPrep, &geom))
4572       {
4573 	  g2 = gaiaToGeos_r (cache, geom);
4574 	  if (geom == geom2)
4575 	      ret = GEOSPreparedCovers_r (handle, gPrep, g2);
4576 	  else
4577 	      ret = GEOSPreparedCoveredBy_r (handle, gPrep, g2);
4578 	  GEOSGeom_destroy_r (handle, g2);
4579 	  if (ret == 2)
4580 	      return -1;
4581 	  return ret;
4582       }
4583 
4584     g1 = gaiaToGeos_r (cache, geom1);
4585     g2 = gaiaToGeos_r (cache, geom2);
4586     ret = GEOSCovers_r (handle, g1, g2);
4587     GEOSGeom_destroy_r (handle, g1);
4588     GEOSGeom_destroy_r (handle, g2);
4589     if (ret == 2)
4590 	return -1;
4591     return ret;
4592 }
4593 
4594 GAIAGEO_DECLARE int
gaiaGeomCollCoveredBy(gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)4595 gaiaGeomCollCoveredBy (gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2)
4596 {
4597 /* checks if geom1 is "spatially covered by" geom2 */
4598     int ret = -1;
4599 #ifndef GEOS_USE_ONLY_R_API	/* obsolete versions non fully thread-safe */
4600     GEOSGeometry *g1;
4601     GEOSGeometry *g2;
4602     gaiaResetGeosMsg ();
4603     if (!geom1 || !geom2)
4604 	return -1;
4605 
4606 /* quick check based on MBRs comparison */
4607     if (!splite_mbr_within (geom1, geom2))
4608 	return 0;
4609 
4610     g1 = gaiaToGeos (geom1);
4611     g2 = gaiaToGeos (geom2);
4612     ret = GEOSCoveredBy (g1, g2);
4613     GEOSGeom_destroy (g1);
4614     GEOSGeom_destroy (g2);
4615     if (ret == 2)
4616 	return -1;
4617 #else
4618     if (geom1 == NULL || geom2 == NULL)
4619 	geom1 = NULL;		/* silencing stupid compiler warnings */
4620 #endif
4621     return ret;
4622 }
4623 
4624 GAIAGEO_DECLARE int
gaiaGeomCollCoveredBy_r(const void * p_cache,gaiaGeomCollPtr geom1,gaiaGeomCollPtr geom2)4625 gaiaGeomCollCoveredBy_r (const void *p_cache, gaiaGeomCollPtr geom1,
4626 			 gaiaGeomCollPtr geom2)
4627 {
4628 /* checks if geom1 is "spatially covered by" geom2 */
4629     int ret;
4630     GEOSGeometry *g1;
4631     GEOSGeometry *g2;
4632     struct splite_internal_cache *cache =
4633 	(struct splite_internal_cache *) p_cache;
4634     GEOSContextHandle_t handle = NULL;
4635     if (cache == NULL)
4636 	return -1;
4637     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
4638 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
4639 	return -1;
4640     handle = cache->GEOS_handle;
4641     if (handle == NULL)
4642 	return -1;
4643     gaiaResetGeosMsg_r (cache);
4644     if (!geom1 || !geom2)
4645 	return -1;
4646 
4647 /* quick check based on MBRs comparison */
4648     if (!splite_mbr_within (geom1, geom2))
4649 	return 0;
4650 
4651     g1 = gaiaToGeos_r (cache, geom1);
4652     g2 = gaiaToGeos_r (cache, geom2);
4653     ret = GEOSCoveredBy_r (handle, g1, g2);
4654     GEOSGeom_destroy_r (handle, g1);
4655     GEOSGeom_destroy_r (handle, g2);
4656     if (ret == 2)
4657 	return -1;
4658     return ret;
4659 }
4660 
4661 GAIAGEO_DECLARE int
gaiaGeomCollPreparedCoveredBy(const void * p_cache,gaiaGeomCollPtr geom1,unsigned char * blob1,int size1,gaiaGeomCollPtr geom2,unsigned char * blob2,int size2)4662 gaiaGeomCollPreparedCoveredBy (const void *p_cache, gaiaGeomCollPtr geom1,
4663 			       unsigned char *blob1, int size1,
4664 			       gaiaGeomCollPtr geom2, unsigned char *blob2,
4665 			       int size2)
4666 {
4667 /* checks if geom1 is "spatially covered by" geom2 */
4668     int ret;
4669     struct splite_internal_cache *cache =
4670 	(struct splite_internal_cache *) p_cache;
4671     GEOSPreparedGeometry *gPrep;
4672     GEOSGeometry *g1;
4673     GEOSGeometry *g2;
4674     GEOSContextHandle_t handle = NULL;
4675     gaiaGeomCollPtr geom;
4676     gaiaResetGeosMsg ();
4677     if (cache == NULL)
4678 	return -1;
4679     if (cache->magic1 != SPATIALITE_CACHE_MAGIC1
4680 	|| cache->magic2 != SPATIALITE_CACHE_MAGIC2)
4681 	return -1;
4682     handle = cache->GEOS_handle;
4683     if (handle == NULL)
4684 	return -1;
4685     gaiaResetGeosMsg_r (cache);
4686     if (!geom1 || !geom2)
4687 	return -1;
4688 
4689 /* quick check based on MBRs comparison */
4690     if (!splite_mbr_within (geom1, geom2))
4691 	return 0;
4692 
4693 /* handling the internal GEOS cache */
4694     if (evalGeosCache
4695 	(cache, geom1, blob1, size1, geom2, blob2, size2, &gPrep, &geom))
4696       {
4697 	  g2 = gaiaToGeos_r (cache, geom);
4698 	  if (geom == geom2)
4699 	      ret = GEOSPreparedCoveredBy_r (handle, gPrep, g2);
4700 	  else
4701 	      ret = GEOSPreparedCovers_r (handle, gPrep, g2);
4702 	  GEOSGeom_destroy_r (handle, g2);
4703 	  if (ret == 2)
4704 	      return -1;
4705 	  return ret;
4706       }
4707 
4708     g1 = gaiaToGeos_r (cache, geom1);
4709     g2 = gaiaToGeos_r (cache, geom2);
4710     ret = GEOSCoveredBy_r (handle, g1, g2);
4711     GEOSGeom_destroy_r (handle, g1);
4712     GEOSGeom_destroy_r (handle, g2);
4713     if (ret == 2)
4714 	return -1;
4715     return ret;
4716 }
4717 
4718 #endif /* end including GEOS */
4719