LCOV - code coverage report
Current view: top level - geom/monetdb5 - geom.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1013 3001 33.8 %
Date: 2021-10-13 02:24:04 Functions: 126 196 64.3 %

          Line data    Source code
       1             : /*
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       5             :  *
       6             :  * Copyright 1997 - July 2008 CWI, August 2008 - 2021 MonetDB B.V.
       7             :  */
       8             : 
       9             : /*
      10             :  * @a Wouter Scherphof, Niels Nes, Foteini Alvanaki
      11             :  * @* The simple geom module
      12             :  */
      13             : 
      14             : #include "geom.h"
      15             : #include "gdk_logger.h"
      16             : #include "mal_exception.h"
      17             : 
      18             : int TYPE_mbr;
      19             : 
      20             : static wkb *geos2wkb(const GEOSGeometry *geosGeometry);
      21             : 
      22             : static inline int
      23             : geometryHasZ(int info)
      24             : {
      25           1 :         return (info & 0x02);
      26             : }
      27             : 
      28             : static inline int
      29             : geometryHasM(int info)
      30             : {
      31           1 :         return (info & 0x01);
      32             : }
      33             : static const wkb wkb_nil = { ~0, 0 };
      34             : static const wkba wkba_nil = {.itemsNum = ~0};
      35             : 
      36             : static wkb *
      37           3 : wkbNULLcopy(void)
      38             : {
      39           3 :         wkb *n = GDKmalloc(sizeof(wkb_nil));
      40           3 :         if (n)
      41           3 :                 *n = wkb_nil;
      42           3 :         return n;
      43             : }
      44             : 
      45             : /* the first argument in the functions is the return variable */
      46             : 
      47             : #ifdef HAVE_PROJ
      48             : 
      49             : /* math.h files do not have M_PI defined */
      50             : #ifndef M_PI
      51             : #define M_PI    ((double) 3.14159265358979323846)       /* pi */
      52             : #endif
      53             : 
      54             : /** convert degrees to radians */
      55             : static inline void
      56             : degrees2radians(double *x, double *y, double *z)
      57             : {
      58             :         double val = M_PI / 180.0;
      59           0 :         *x *= val;
      60           0 :         *y *= val;
      61           0 :         *z *= val;
      62           0 : }
      63             : 
      64             : /** convert radians to degrees */
      65             : static inline void
      66             : radians2degrees(double *x, double *y, double *z)
      67             : {
      68             :         double val = 180.0 / M_PI;
      69           0 :         *x *= val;
      70           0 :         *y *= val;
      71           0 :         *z *= val;
      72           0 : }
      73             : 
      74             : static str
      75           0 : transformCoordSeq(int idx, int coordinatesNum, projPJ proj4_src, projPJ proj4_dst, const GEOSCoordSequence *gcs_old, GEOSCoordSeq gcs_new)
      76             : {
      77           0 :         double x = 0, y = 0, z = 0;
      78             :         int *errorNum = 0;
      79             : 
      80           0 :         if (!GEOSCoordSeq_getX(gcs_old, idx, &x) ||
      81           0 :             !GEOSCoordSeq_getY(gcs_old, idx, &y) ||
      82           0 :             (coordinatesNum > 2 && !GEOSCoordSeq_getZ(gcs_old, idx, &z)))
      83           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos cannot get coordinates");
      84             : 
      85             :         /* check if the passed reference system is geographic (proj=latlong)
      86             :          * and change the degrees to radians because pj_transform works with radians*/
      87           0 :         if (pj_is_latlong(proj4_src))
      88             :                 degrees2radians(&x, &y, &z);
      89             : 
      90           0 :         pj_transform(proj4_src, proj4_dst, 1, 0, &x, &y, &z);
      91             : 
      92           0 :         errorNum = pj_get_errno_ref();
      93           0 :         if (*errorNum != 0) {
      94           0 :                 if (coordinatesNum > 2)
      95           0 :                         throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos cannot transform point (%f %f %f): %s\n", x, y, z, pj_strerrno(*errorNum));
      96             :                 else
      97           0 :                         throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos cannot transform point (%f %f): %s\n", x, y, pj_strerrno(*errorNum));
      98             :         }
      99             : 
     100             :         /* check if the destination reference system is geographic and change
     101             :          * the destination coordinates from radians to degrees */
     102           0 :         if (pj_is_latlong(proj4_dst))
     103             :                 radians2degrees(&x, &y, &z);
     104             : 
     105           0 :         if (!GEOSCoordSeq_setX(gcs_new, idx, x) ||
     106           0 :             !GEOSCoordSeq_setY(gcs_new, idx, y) ||
     107           0 :             (coordinatesNum > 2 && !GEOSCoordSeq_setZ(gcs_new, idx, z)))
     108           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos cannot set coordinates");
     109             : 
     110             :         return MAL_SUCCEED;
     111             : }
     112             : 
     113             : static str
     114           0 : transformPoint(GEOSGeometry **transformedGeometry, const GEOSGeometry *geosGeometry, projPJ proj4_src, projPJ proj4_dst)
     115             : {
     116             :         int coordinatesNum = 0;
     117             :         const GEOSCoordSequence *gcs_old;
     118             :         GEOSCoordSeq gcs_new;
     119             :         str ret = MAL_SUCCEED;
     120             : 
     121           0 :         *transformedGeometry = NULL;
     122             : 
     123             :         /* get the number of coordinates the geometry has */
     124           0 :         coordinatesNum = GEOSGeom_getCoordinateDimension(geosGeometry);
     125             :         /* get the coordinates of the points comprising the geometry */
     126           0 :         gcs_old = GEOSGeom_getCoordSeq(geosGeometry);
     127             : 
     128           0 :         if (gcs_old == NULL)
     129           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
     130             : 
     131             :         /* create the coordinates sequence for the transformed geometry */
     132           0 :         gcs_new = GEOSCoordSeq_create(1, coordinatesNum);
     133           0 :         if (gcs_new == NULL)
     134           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
     135             : 
     136             :         /* create the transformed coordinates */
     137           0 :         ret = transformCoordSeq(0, coordinatesNum, proj4_src, proj4_dst, gcs_old, gcs_new);
     138           0 :         if (ret != MAL_SUCCEED) {
     139           0 :                 GEOSCoordSeq_destroy(gcs_new);
     140           0 :                 return ret;
     141             :         }
     142             : 
     143             :         /* create the geometry from the coordinates sequence */
     144           0 :         *transformedGeometry = GEOSGeom_createPoint(gcs_new);
     145           0 :         if (*transformedGeometry == NULL) {
     146           0 :                 GEOSCoordSeq_destroy(gcs_new);
     147           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
     148             :         }
     149             : 
     150             :         return MAL_SUCCEED;
     151             : }
     152             : 
     153             : static str
     154           0 : transformLine(GEOSCoordSeq *gcs_new, const GEOSGeometry *geosGeometry, projPJ proj4_src, projPJ proj4_dst)
     155             : {
     156             :         int coordinatesNum = 0;
     157             :         const GEOSCoordSequence *gcs_old;
     158           0 :         unsigned int pointsNum = 0, i = 0;
     159             :         str ret = MAL_SUCCEED;
     160             : 
     161             :         /* get the number of coordinates the geometry has */
     162           0 :         coordinatesNum = GEOSGeom_getCoordinateDimension(geosGeometry);
     163             :         /* get the coordinates of the points comprising the geometry */
     164           0 :         gcs_old = GEOSGeom_getCoordSeq(geosGeometry);
     165             : 
     166           0 :         if (gcs_old == NULL)
     167           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
     168             : 
     169             :         /* get the number of points in the geometry */
     170           0 :         if (!GEOSCoordSeq_getSize(gcs_old, &pointsNum))
     171           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getSize failed");
     172             : 
     173             :         /* create the coordinates sequence for the transformed geometry */
     174           0 :         *gcs_new = GEOSCoordSeq_create(pointsNum, coordinatesNum);
     175           0 :         if (*gcs_new == NULL)
     176           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSCoordSeq_create failed");
     177             : 
     178             :         /* create the transformed coordinates */
     179           0 :         for (i = 0; i < pointsNum; i++) {
     180           0 :                 ret = transformCoordSeq(i, coordinatesNum, proj4_src, proj4_dst, gcs_old, *gcs_new);
     181           0 :                 if (ret != MAL_SUCCEED) {
     182           0 :                         GEOSCoordSeq_destroy(*gcs_new);
     183           0 :                         *gcs_new = NULL;
     184           0 :                         return ret;
     185             :                 }
     186             :         }
     187             : 
     188             :         return MAL_SUCCEED;
     189             : }
     190             : 
     191             : static str
     192           0 : transformLineString(GEOSGeometry **transformedGeometry, const GEOSGeometry *geosGeometry, projPJ proj4_src, projPJ proj4_dst)
     193             : {
     194             :         GEOSCoordSeq coordSeq;
     195             :         str ret = MAL_SUCCEED;
     196             : 
     197           0 :         ret = transformLine(&coordSeq, geosGeometry, proj4_src, proj4_dst);
     198           0 :         if (ret != MAL_SUCCEED) {
     199           0 :                 *transformedGeometry = NULL;
     200           0 :                 return ret;
     201             :         }
     202             : 
     203             :         /* create the geometry from the coordinates sequence */
     204           0 :         *transformedGeometry = GEOSGeom_createLineString(coordSeq);
     205           0 :         if (*transformedGeometry == NULL) {
     206           0 :                 GEOSCoordSeq_destroy(coordSeq);
     207           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSGeom_createLineString failed");
     208             :         }
     209             : 
     210             :         return ret;
     211             : }
     212             : 
     213             : static str
     214           0 : transformLinearRing(GEOSGeometry **transformedGeometry, const GEOSGeometry *geosGeometry, projPJ proj4_src, projPJ proj4_dst)
     215             : {
     216           0 :         GEOSCoordSeq coordSeq = NULL;
     217             :         str ret = MAL_SUCCEED;
     218             : 
     219           0 :         ret = transformLine(&coordSeq, geosGeometry, proj4_src, proj4_dst);
     220           0 :         if (ret != MAL_SUCCEED) {
     221           0 :                 *transformedGeometry = NULL;
     222           0 :                 return ret;
     223             :         }
     224             : 
     225             :         /* create the geometry from the coordinates sequence */
     226           0 :         *transformedGeometry = GEOSGeom_createLinearRing(coordSeq);
     227           0 :         if (*transformedGeometry == NULL) {
     228           0 :                 GEOSCoordSeq_destroy(coordSeq);
     229           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSGeom_createLineString failed");
     230             :         }
     231             : 
     232             :         return ret;
     233             : }
     234             : 
     235             : static str
     236           0 : transformPolygon(GEOSGeometry **transformedGeometry, const GEOSGeometry *geosGeometry, projPJ proj4_src, projPJ proj4_dst, int srid)
     237             : {
     238             :         const GEOSGeometry *exteriorRingGeometry;
     239           0 :         GEOSGeometry *transformedExteriorRingGeometry = NULL;
     240             :         GEOSGeometry **transformedInteriorRingGeometries = NULL;
     241             :         int numInteriorRings = 0, i = 0;
     242             :         str ret = MAL_SUCCEED;
     243             : 
     244             :         /* get the exterior ring of the polygon */
     245           0 :         exteriorRingGeometry = GEOSGetExteriorRing(geosGeometry);
     246           0 :         if (exteriorRingGeometry == NULL) {
     247           0 :                 *transformedGeometry = NULL;
     248           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSGetExteriorRing failed");
     249             :         }
     250             : 
     251           0 :         ret = transformLinearRing(&transformedExteriorRingGeometry, exteriorRingGeometry, proj4_src, proj4_dst);
     252           0 :         if (ret != MAL_SUCCEED) {
     253           0 :                 *transformedGeometry = NULL;
     254           0 :                 return ret;
     255             :         }
     256           0 :         GEOSSetSRID(transformedExteriorRingGeometry, srid);
     257             : 
     258           0 :         numInteriorRings = GEOSGetNumInteriorRings(geosGeometry);
     259           0 :         if (numInteriorRings == -1) {
     260           0 :                 *transformedGeometry = NULL;
     261           0 :                 GEOSGeom_destroy(transformedExteriorRingGeometry);
     262           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSGetInteriorRingN failed.");
     263             :         }
     264             : 
     265             :         /* iterate over the interiorRing and transform each one of them */
     266           0 :         transformedInteriorRingGeometries = GDKmalloc(numInteriorRings * sizeof(GEOSGeometry *));
     267           0 :         if (transformedInteriorRingGeometries == NULL) {
     268           0 :                 *transformedGeometry = NULL;
     269           0 :                 GEOSGeom_destroy(transformedExteriorRingGeometry);
     270           0 :                 throw(MAL, "geom.Transform", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     271             :         }
     272           0 :         for (i = 0; i < numInteriorRings; i++) {
     273           0 :                 ret = transformLinearRing(&transformedInteriorRingGeometries[i], GEOSGetInteriorRingN(geosGeometry, i), proj4_src, proj4_dst);
     274           0 :                 if (ret != MAL_SUCCEED) {
     275           0 :                         while (--i >= 0)
     276           0 :                                 GEOSGeom_destroy(transformedInteriorRingGeometries[i]);
     277           0 :                         GDKfree(transformedInteriorRingGeometries);
     278           0 :                         GEOSGeom_destroy(transformedExteriorRingGeometry);
     279           0 :                         *transformedGeometry = NULL;
     280           0 :                         return ret;
     281             :                 }
     282           0 :                 GEOSSetSRID(transformedInteriorRingGeometries[i], srid);
     283             :         }
     284             : 
     285           0 :         *transformedGeometry = GEOSGeom_createPolygon(transformedExteriorRingGeometry, transformedInteriorRingGeometries, numInteriorRings);
     286           0 :         if (*transformedGeometry == NULL) {
     287           0 :                 for (i = 0; i < numInteriorRings; i++)
     288           0 :                         GEOSGeom_destroy(transformedInteriorRingGeometries[i]);
     289           0 :                 ret = createException(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSGeom_createPolygon failed");
     290             :         }
     291           0 :         GDKfree(transformedInteriorRingGeometries);
     292           0 :         GEOSGeom_destroy(transformedExteriorRingGeometry);
     293             : 
     294           0 :         return ret;
     295             : }
     296             : 
     297             : static str
     298           0 : transformMultiGeometry(GEOSGeometry **transformedGeometry, const GEOSGeometry *geosGeometry, projPJ proj4_src, projPJ proj4_dst, int srid, int geometryType)
     299             : {
     300             :         int geometriesNum, subGeometryType, i;
     301             :         GEOSGeometry **transformedMultiGeometries = NULL;
     302             :         const GEOSGeometry *multiGeometry = NULL;
     303             :         str ret = MAL_SUCCEED;
     304             : 
     305           0 :         geometriesNum = GEOSGetNumGeometries(geosGeometry);
     306           0 :         if (geometriesNum == -1)
     307           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSGetNumGeometries failed");
     308           0 :         transformedMultiGeometries = GDKmalloc(geometriesNum * sizeof(GEOSGeometry *));
     309           0 :         if (transformedMultiGeometries == NULL)
     310           0 :                 throw(MAL, "geom.Transform", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     311             : 
     312           0 :         for (i = 0; i < geometriesNum; i++) {
     313           0 :                 if ((multiGeometry = GEOSGetGeometryN(geosGeometry, i)) == NULL)
     314           0 :                         ret = createException(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSGetGeometryN failed");
     315           0 :                 else if ((subGeometryType = GEOSGeomTypeId(multiGeometry) + 1) == 0)
     316           0 :                         ret = createException(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSGeomTypeId failed");
     317             :                 else {
     318           0 :                         switch (subGeometryType) {
     319           0 :                         case wkbPoint_mdb:
     320           0 :                                 ret = transformPoint(&transformedMultiGeometries[i], multiGeometry, proj4_src, proj4_dst);
     321           0 :                                 break;
     322           0 :                         case wkbLineString_mdb:
     323           0 :                                 ret = transformLineString(&transformedMultiGeometries[i], multiGeometry, proj4_src, proj4_dst);
     324           0 :                                 break;
     325           0 :                         case wkbLinearRing_mdb:
     326           0 :                                 ret = transformLinearRing(&transformedMultiGeometries[i], multiGeometry, proj4_src, proj4_dst);
     327           0 :                                 break;
     328           0 :                         case wkbPolygon_mdb:
     329           0 :                                 ret = transformPolygon(&transformedMultiGeometries[i], multiGeometry, proj4_src, proj4_dst, srid);
     330           0 :                                 break;
     331           0 :                         default:
     332           0 :                                 ret = createException(MAL, "geom.Transform", SQLSTATE(38000) "Geos unknown geometry type");
     333           0 :                                 break;
     334             :                         }
     335             :                 }
     336             : 
     337           0 :                 if (ret != MAL_SUCCEED) {
     338           0 :                         while (--i >= 0)
     339           0 :                                 GEOSGeom_destroy(transformedMultiGeometries[i]);
     340           0 :                         GDKfree(transformedMultiGeometries);
     341           0 :                         *transformedGeometry = NULL;
     342           0 :                         return ret;
     343             :                 }
     344             : 
     345           0 :                 GEOSSetSRID(transformedMultiGeometries[i], srid);
     346             :         }
     347             : 
     348           0 :         *transformedGeometry = GEOSGeom_createCollection(geometryType - 1, transformedMultiGeometries, geometriesNum);
     349           0 :         if (*transformedGeometry == NULL) {
     350           0 :                 for (i = 0; i < geometriesNum; i++)
     351           0 :                         GEOSGeom_destroy(transformedMultiGeometries[i]);
     352           0 :                 ret = createException(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation GEOSGeom_createCollection failed");
     353             :         }
     354           0 :         GDKfree(transformedMultiGeometries);
     355             : 
     356           0 :         return ret;
     357             : }
     358             : 
     359             : /* the following function is used in postgis to get projPJ from str.
     360             :  * it is necessary to do it in a detailed way like that because pj_init_plus
     361             :  * does not set all parameters correctly and I cannot test whether the
     362             :  * coordinate reference systems are geographic or not */
     363             : static projPJ
     364           0 : projFromStr(const char *projStr)
     365             : {
     366             :         int t;
     367             :         char *params[1024];     // one for each parameter
     368             :         char *loc;
     369             :         char *str;
     370             :         projPJ result;
     371             : 
     372           0 :         if (projStr == NULL)
     373             :                 return NULL;
     374             : 
     375           0 :         str = GDKstrdup(projStr);
     376           0 :         if (str == NULL)
     377             :                 return NULL;
     378             : 
     379             :         // first we split the string into a bunch of smaller strings,
     380             :         // based on the " " separator
     381             : 
     382           0 :         params[0] = str;        // 1st param, we'll null terminate at the " " soon
     383             : 
     384             :         t = 1;
     385           0 :         for (loc = strchr(str, ' '); loc != NULL; loc = strchr(loc, ' ')) {
     386           0 :                 if (t == (int) (sizeof(params) / sizeof(params[0]))) {
     387             :                         /* too many parameters */
     388           0 :                         GDKfree(str);
     389           0 :                         return NULL;
     390             :                 }
     391           0 :                 *loc++ = 0;     // null terminate and advance
     392           0 :                 params[t++] = loc;
     393             :         }
     394             : 
     395           0 :         result = pj_init(t, params);
     396           0 :         GDKfree(str);
     397             : 
     398           0 :         return result;
     399             : }
     400             : #endif
     401             : 
     402             : /* It gets a geometry and transforms its coordinates to the provided srid */
     403             : str
     404           0 : wkbTransform(wkb **transformedWKB, wkb **geomWKB, int *srid_src, int *srid_dst, char **proj4_src_str, char **proj4_dst_str)
     405             : {
     406             : #ifndef HAVE_PROJ
     407             :         *transformedWKB = NULL;
     408             :         (void) geomWKB;
     409             :         (void) srid_src;
     410             :         (void) srid_dst;
     411             :         (void) proj4_src_str;
     412             :         (void) proj4_dst_str;
     413             :         throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos function Not Implemented");
     414             : #else
     415             :         projPJ proj4_src, proj4_dst;
     416             :         GEOSGeom geosGeometry, transformedGeosGeometry;
     417             :         int geometryType = -1;
     418             : 
     419             :         str ret = MAL_SUCCEED;
     420             : 
     421           0 :         if (*geomWKB == NULL)
     422           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos wkb format is null");
     423             : 
     424           0 :         if (is_wkb_nil(*geomWKB) ||
     425           0 :             is_int_nil(*srid_src) ||
     426           0 :             is_int_nil(*srid_dst) ||
     427           0 :             strNil(*proj4_src_str) ||
     428           0 :             strNil(*proj4_dst_str)) {
     429           0 :                 if ((*transformedWKB = wkbNULLcopy()) == NULL)
     430           0 :                         throw(MAL, "geom.Transform", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     431             :                 return MAL_SUCCEED;
     432             :         }
     433             : 
     434           0 :         if (strcmp(*proj4_src_str, "null") == 0)
     435           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Cannot find in spatial_ref_sys srid %d\n", *srid_src);
     436           0 :         if (strcmp(*proj4_dst_str, "null") == 0)
     437           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Cannot find in spatial_ref_sys srid %d\n", *srid_dst);
     438             : 
     439           0 :         proj4_src = /*pj_init_plus */ projFromStr(*proj4_src_str);
     440           0 :         proj4_dst = /*pj_init_plus */ projFromStr(*proj4_dst_str);
     441           0 :         if (proj4_src == NULL || proj4_dst == NULL) {
     442           0 :                 if (proj4_src)
     443           0 :                         pj_free(proj4_src);
     444           0 :                 if (proj4_dst)
     445           0 :                         pj_free(proj4_dst);
     446           0 :                 throw(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation pj_init failed");
     447             :         }
     448             : 
     449             :         /* get the geosGeometry from the wkb */
     450           0 :         geosGeometry = wkb2geos(*geomWKB);
     451             :         /* get the type of the geometry */
     452           0 :         geometryType = GEOSGeomTypeId(geosGeometry) + 1;
     453             : 
     454           0 :         switch (geometryType) {
     455           0 :         case wkbPoint_mdb:
     456           0 :                 ret = transformPoint(&transformedGeosGeometry, geosGeometry, proj4_src, proj4_dst);
     457           0 :                 break;
     458           0 :         case wkbLineString_mdb:
     459           0 :                 ret = transformLineString(&transformedGeosGeometry, geosGeometry, proj4_src, proj4_dst);
     460           0 :                 break;
     461           0 :         case wkbLinearRing_mdb:
     462           0 :                 ret = transformLinearRing(&transformedGeosGeometry, geosGeometry, proj4_src, proj4_dst);
     463           0 :                 break;
     464           0 :         case wkbPolygon_mdb:
     465           0 :                 ret = transformPolygon(&transformedGeosGeometry, geosGeometry, proj4_src, proj4_dst, *srid_dst);
     466           0 :                 break;
     467           0 :         case wkbMultiPoint_mdb:
     468             :         case wkbMultiLineString_mdb:
     469             :         case wkbMultiPolygon_mdb:
     470           0 :                 ret = transformMultiGeometry(&transformedGeosGeometry, geosGeometry, proj4_src, proj4_dst, *srid_dst, geometryType);
     471           0 :                 break;
     472           0 :         default:
     473           0 :                 transformedGeosGeometry = NULL;
     474           0 :                 ret = createException(MAL, "geom.Transform", SQLSTATE(38000) "Geos unknown geometry type");
     475             :         }
     476             : 
     477           0 :         if (ret == MAL_SUCCEED && transformedGeosGeometry) {
     478             :                 /* set the new srid */
     479           0 :                 GEOSSetSRID(transformedGeosGeometry, *srid_dst);
     480             :                 /* get the wkb */
     481           0 :                 if ((*transformedWKB = geos2wkb(transformedGeosGeometry)) == NULL)
     482           0 :                         ret = createException(MAL, "geom.Transform", SQLSTATE(38000) "Geos operation geos2wkb failed");
     483             :                 /* destroy the geos geometries */
     484           0 :                 GEOSGeom_destroy(transformedGeosGeometry);
     485             :         }
     486             : 
     487           0 :         pj_free(proj4_src);
     488           0 :         pj_free(proj4_dst);
     489           0 :         GEOSGeom_destroy(geosGeometry);
     490             : 
     491           0 :         return ret;
     492             : #endif
     493             : }
     494             : 
     495             : //gets a coord seq and forces it to have dim dimensions adding or removing extra dimensions
     496             : static str
     497           0 : forceDimCoordSeq(int idx, int coordinatesNum, int dim, const GEOSCoordSequence *gcs_old, GEOSCoordSeq gcs_new)
     498             : {
     499           0 :         double x = 0, y = 0, z = 0;
     500             : 
     501             :         //get the coordinates
     502           0 :         if (!GEOSCoordSeq_getX(gcs_old, idx, &x))
     503           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getX failed");
     504           0 :         if (!GEOSCoordSeq_getY(gcs_old, idx, &y))
     505           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getY failed");
     506           0 :         if (coordinatesNum > 2 && dim > 2 &&      //read it only if needed (dim >2)
     507           0 :             !GEOSCoordSeq_getZ(gcs_old, idx, &z))
     508           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getZ failed");
     509             : 
     510             :         //create the new coordinates
     511           0 :         if (!GEOSCoordSeq_setX(gcs_new, idx, x))
     512           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setX failed");
     513           0 :         if (!GEOSCoordSeq_setY(gcs_new, idx, y))
     514           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setY failed");
     515           0 :         if (dim > 2)
     516           0 :                 if (!GEOSCoordSeq_setZ(gcs_new, idx, z))
     517           0 :                         throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setZ failed");
     518             :         return MAL_SUCCEED;
     519             : }
     520             : 
     521             : static str
     522           0 : forceDimPoint(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, int dim)
     523             : {
     524             :         int coordinatesNum = 0;
     525             :         const GEOSCoordSequence *gcs_old;
     526             :         GEOSCoordSeq gcs_new;
     527             :         str ret = MAL_SUCCEED;
     528             : 
     529             :         /* get the number of coordinates the geometry has */
     530           0 :         coordinatesNum = GEOSGeom_getCoordinateDimension(geosGeometry);
     531             :         /* get the coordinates of the points comprising the geometry */
     532           0 :         gcs_old = GEOSGeom_getCoordSeq(geosGeometry);
     533             : 
     534           0 :         if (gcs_old == NULL) {
     535           0 :                 *outGeometry = NULL;
     536           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
     537             :         }
     538             : 
     539             :         /* create the coordinates sequence for the translated geometry */
     540           0 :         if ((gcs_new = GEOSCoordSeq_create(1, dim)) == NULL) {
     541           0 :                 *outGeometry = NULL;
     542           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSCoordSeq_create failed");
     543             :         }
     544             : 
     545             :         /* create the translated coordinates */
     546           0 :         ret = forceDimCoordSeq(0, coordinatesNum, dim, gcs_old, gcs_new);
     547           0 :         if (ret != MAL_SUCCEED) {
     548           0 :                 *outGeometry = NULL;
     549           0 :                 GEOSCoordSeq_destroy(gcs_new);
     550           0 :                 return ret;
     551             :         }
     552             : 
     553             :         /* create the geometry from the coordinates sequence */
     554           0 :         *outGeometry = GEOSGeom_createPoint(gcs_new);
     555           0 :         if (*outGeometry == NULL) {
     556           0 :                 GEOSCoordSeq_destroy(gcs_new);
     557           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSGeom_createPoint failed");
     558             :         }
     559             : 
     560             :         return MAL_SUCCEED;
     561             : }
     562             : 
     563             : static str
     564           0 : forceDimLineString(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, int dim)
     565             : {
     566             :         int coordinatesNum = 0;
     567             :         const GEOSCoordSequence *gcs_old;
     568             :         GEOSCoordSeq gcs_new;
     569           0 :         unsigned int pointsNum = 0, i = 0;
     570             :         str ret = MAL_SUCCEED;
     571             : 
     572             :         /* get the number of coordinates the geometry has */
     573           0 :         coordinatesNum = GEOSGeom_getCoordinateDimension(geosGeometry);
     574             :         /* get the coordinates of the points comprising the geometry */
     575           0 :         gcs_old = GEOSGeom_getCoordSeq(geosGeometry);
     576             : 
     577           0 :         if (gcs_old == NULL)
     578           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
     579             : 
     580             :         /* get the number of points in the geometry */
     581           0 :         if (!GEOSCoordSeq_getSize(gcs_old, &pointsNum))
     582           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getSize failed");
     583             : 
     584             :         /* create the coordinates sequence for the translated geometry */
     585           0 :         gcs_new = GEOSCoordSeq_create(pointsNum, dim);
     586           0 :         if (gcs_new == NULL)
     587           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSCoordSeq_create failed");
     588             : 
     589             :         /* create the translated coordinates */
     590           0 :         for (i = 0; i < pointsNum; i++) {
     591           0 :                 ret = forceDimCoordSeq(i, coordinatesNum, dim, gcs_old, gcs_new);
     592           0 :                 if (ret != MAL_SUCCEED) {
     593           0 :                         GEOSCoordSeq_destroy(gcs_new);
     594           0 :                         return ret;
     595             :                 }
     596             :         }
     597             : 
     598             :         //create the geometry from the translated coordinates sequence
     599           0 :         *outGeometry = GEOSGeom_createLineString(gcs_new);
     600           0 :         if (*outGeometry == NULL) {
     601           0 :                 GEOSCoordSeq_destroy(gcs_new);
     602           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSGeom_createLineString failed");
     603             :         }
     604             : 
     605             :         return MAL_SUCCEED;
     606             : 
     607             : }
     608             : 
     609             : //Although linestring and linearRing are essentially the same we need to distinguish that when creating polygon from the rings
     610             : static str
     611           0 : forceDimLinearRing(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, int dim)
     612             : {
     613             :         int coordinatesNum = 0;
     614             :         const GEOSCoordSequence *gcs_old;
     615             :         GEOSCoordSeq gcs_new;
     616           0 :         unsigned int pointsNum = 0, i = 0;
     617             :         str ret = MAL_SUCCEED;
     618             : 
     619             :         /* get the number of coordinates the geometry has */
     620           0 :         coordinatesNum = GEOSGeom_getCoordinateDimension(geosGeometry);
     621             :         /* get the coordinates of the points comprising the geometry */
     622           0 :         gcs_old = GEOSGeom_getCoordSeq(geosGeometry);
     623             : 
     624           0 :         if (gcs_old == NULL)
     625           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
     626             : 
     627             :         /* get the number of points in the geometry */
     628           0 :         if (!GEOSCoordSeq_getSize(gcs_old, &pointsNum))
     629           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getSize failed");
     630             : 
     631             :         /* create the coordinates sequence for the translated geometry */
     632           0 :         gcs_new = GEOSCoordSeq_create(pointsNum, dim);
     633           0 :         if (gcs_new == NULL)
     634           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSCoordSeq_create failed");
     635             : 
     636             :         /* create the translated coordinates */
     637           0 :         for (i = 0; i < pointsNum; i++) {
     638           0 :                 ret = forceDimCoordSeq(i, coordinatesNum, dim, gcs_old, gcs_new);
     639           0 :                 if (ret != MAL_SUCCEED) {
     640           0 :                         GEOSCoordSeq_destroy(gcs_new);
     641           0 :                         return ret;
     642             :                 }
     643             :         }
     644             : 
     645             :         //create the geometry from the translated coordinates sequence
     646           0 :         *outGeometry = GEOSGeom_createLinearRing(gcs_new);
     647           0 :         if (*outGeometry == NULL) {
     648           0 :                 GEOSCoordSeq_destroy(gcs_new);
     649           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSGeom_createLinearRing failed");
     650             :         }
     651             : 
     652             :         return MAL_SUCCEED;
     653             : }
     654             : 
     655             : static str
     656           0 : forceDimPolygon(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, int dim)
     657             : {
     658             :         const GEOSGeometry *exteriorRingGeometry;
     659           0 :         GEOSGeometry *transformedExteriorRingGeometry = NULL;
     660             :         GEOSGeometry **transformedInteriorRingGeometries = NULL;
     661             :         int numInteriorRings = 0, i = 0;
     662             :         str ret = MAL_SUCCEED;
     663             : 
     664             :         /* get the exterior ring of the polygon */
     665           0 :         exteriorRingGeometry = GEOSGetExteriorRing(geosGeometry);
     666           0 :         if (!exteriorRingGeometry) {
     667           0 :                 *outGeometry = NULL;
     668           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSGetExteriorRing failed");
     669             :         }
     670             : 
     671           0 :         if ((ret = forceDimLinearRing(&transformedExteriorRingGeometry, exteriorRingGeometry, dim)) != MAL_SUCCEED) {
     672           0 :                 *outGeometry = NULL;
     673           0 :                 return ret;
     674             :         }
     675             : 
     676           0 :         numInteriorRings = GEOSGetNumInteriorRings(geosGeometry);
     677           0 :         if (numInteriorRings == -1) {
     678           0 :                 *outGeometry = NULL;
     679           0 :                 GEOSGeom_destroy(transformedExteriorRingGeometry);
     680           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSGetInteriorRingN failed.");
     681             :         }
     682             : 
     683             :         /* iterate over the interiorRing and translate each one of them */
     684           0 :         transformedInteriorRingGeometries = GDKmalloc(numInteriorRings * sizeof(GEOSGeometry *));
     685           0 :         if (transformedInteriorRingGeometries == NULL) {
     686           0 :                 *outGeometry = NULL;
     687           0 :                 GEOSGeom_destroy(transformedExteriorRingGeometry);
     688           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     689             :         }
     690           0 :         for (i = 0; i < numInteriorRings; i++) {
     691           0 :                 if ((ret = forceDimLinearRing(&transformedInteriorRingGeometries[i], GEOSGetInteriorRingN(geosGeometry, i), dim)) != MAL_SUCCEED) {
     692           0 :                         while (--i >= 0)
     693           0 :                                 GEOSGeom_destroy(transformedInteriorRingGeometries[i]);
     694           0 :                         GDKfree(transformedInteriorRingGeometries);
     695           0 :                         GEOSGeom_destroy(transformedExteriorRingGeometry);
     696           0 :                         *outGeometry = NULL;
     697           0 :                         return ret;
     698             :                 }
     699             :         }
     700             : 
     701           0 :         *outGeometry = GEOSGeom_createPolygon(transformedExteriorRingGeometry, transformedInteriorRingGeometries, numInteriorRings);
     702           0 :         if (*outGeometry == NULL) {
     703           0 :                 for (i = 0; i < numInteriorRings; i++)
     704           0 :                         GEOSGeom_destroy(transformedInteriorRingGeometries[i]);
     705           0 :                 ret = createException(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSGeom_createPolygon failed");
     706             :         }
     707           0 :         GDKfree(transformedInteriorRingGeometries);
     708           0 :         GEOSGeom_destroy(transformedExteriorRingGeometry);
     709             : 
     710           0 :         return ret;
     711             : }
     712             : 
     713             : static str forceDimGeometry(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, int dim);
     714             : static str
     715           0 : forceDimMultiGeometry(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, int dim)
     716             : {
     717             :         int geometriesNum, i;
     718             :         GEOSGeometry **transformedMultiGeometries = NULL;
     719             :         str err = MAL_SUCCEED;
     720             : 
     721           0 :         geometriesNum = GEOSGetNumGeometries(geosGeometry);
     722           0 :         transformedMultiGeometries = GDKmalloc(geometriesNum * sizeof(GEOSGeometry *));
     723           0 :         if (transformedMultiGeometries == NULL)
     724           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     725             : 
     726             :         //In order to have the geometries in the output in the same order as in the input
     727             :         //we should read them and put them in the area in reverse order
     728           0 :         for (i = geometriesNum - 1; i >= 0; i--) {
     729           0 :                 const GEOSGeometry *multiGeometry = GEOSGetGeometryN(geosGeometry, i);
     730             : 
     731           0 :                 if ((err = forceDimGeometry(&transformedMultiGeometries[i], multiGeometry, dim)) != MAL_SUCCEED) {
     732           0 :                         while (++i < geometriesNum)
     733           0 :                                 GEOSGeom_destroy(transformedMultiGeometries[i]);
     734           0 :                         GDKfree(transformedMultiGeometries);
     735           0 :                         *outGeometry = NULL;
     736           0 :                         return err;
     737             :                 }
     738             :         }
     739             : 
     740           0 :         *outGeometry = GEOSGeom_createCollection(GEOSGeomTypeId(geosGeometry), transformedMultiGeometries, geometriesNum);
     741           0 :         if (*outGeometry == NULL) {
     742           0 :                 for (i = 0; i < geometriesNum; i++)
     743           0 :                         GEOSGeom_destroy(transformedMultiGeometries[i]);
     744           0 :                 err = createException(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation GEOSGeom_createCollection failed");
     745             :         }
     746           0 :         GDKfree(transformedMultiGeometries);
     747             : 
     748           0 :         return err;
     749             : }
     750             : 
     751             : static str
     752           0 : forceDimGeometry(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, int dim)
     753             : {
     754           0 :         int geometryType = GEOSGeomTypeId(geosGeometry) + 1;
     755             : 
     756             :         //check the type of the geometry
     757           0 :         switch (geometryType) {
     758           0 :         case wkbPoint_mdb:
     759           0 :                 return forceDimPoint(outGeometry, geosGeometry, dim);
     760           0 :         case wkbLineString_mdb:
     761             :         case wkbLinearRing_mdb:
     762           0 :                 return forceDimLineString(outGeometry, geosGeometry, dim);
     763           0 :         case wkbPolygon_mdb:
     764           0 :                 return forceDimPolygon(outGeometry, geosGeometry, dim);
     765           0 :         case wkbMultiPoint_mdb:
     766             :         case wkbMultiLineString_mdb:
     767             :         case wkbMultiPolygon_mdb:
     768             :         case wkbGeometryCollection_mdb:
     769           0 :                 return forceDimMultiGeometry(outGeometry, geosGeometry, dim);
     770           0 :         default:
     771           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation %s unknown geometry type", geom_type2str(geometryType, 0));
     772             :         }
     773             : }
     774             : 
     775             : str
     776           0 : wkbForceDim(wkb **outWKB, wkb **geomWKB, int *dim)
     777             : {
     778             :         GEOSGeometry *outGeometry;
     779             :         GEOSGeom geosGeometry;
     780             :         str err;
     781             : 
     782           0 :         if (is_wkb_nil(*geomWKB) || is_int_nil(*dim)) {
     783           0 :                 if ((*outWKB = wkbNULLcopy()) == NULL)
     784           0 :                         throw(MAL, "geom.ForceDim", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     785             :                 return MAL_SUCCEED;
     786             :         }
     787             : 
     788           0 :         geosGeometry = wkb2geos(*geomWKB);
     789           0 :         if (geosGeometry == NULL) {
     790           0 :                 *outWKB = NULL;
     791           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation wkb2geos failed");
     792             :         }
     793             : 
     794           0 :         if ((err = forceDimGeometry(&outGeometry, geosGeometry, *dim)) != MAL_SUCCEED) {
     795           0 :                 GEOSGeom_destroy(geosGeometry);
     796           0 :                 *outWKB = NULL;
     797           0 :                 return err;
     798             :         }
     799             : 
     800           0 :         GEOSSetSRID(outGeometry, GEOSGetSRID(geosGeometry));
     801             : 
     802           0 :         *outWKB = geos2wkb(outGeometry);
     803             : 
     804           0 :         GEOSGeom_destroy(geosGeometry);
     805           0 :         GEOSGeom_destroy(outGeometry);
     806             : 
     807           0 :         if (*outWKB == NULL)
     808           0 :                 throw(MAL, "geom.ForceDim", SQLSTATE(38000) "Geos operation geos2wkb failed");
     809             : 
     810             :         return MAL_SUCCEED;
     811             : }
     812             : 
     813             : static str
     814           0 : segmentizePoint(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry)
     815             : {
     816             :         const GEOSCoordSequence *gcs_old;
     817             :         GEOSCoordSeq gcs_new;
     818             : 
     819             :         //nothing much to do. Just create a copy of the point
     820             :         //get the coordinates
     821           0 :         if ((gcs_old = GEOSGeom_getCoordSeq(geosGeometry)) == NULL) {
     822           0 :                 *outGeometry = NULL;
     823           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
     824             :         }
     825             :         //create a copy of it
     826           0 :         if ((gcs_new = GEOSCoordSeq_clone(gcs_old)) == NULL) {
     827           0 :                 *outGeometry = NULL;
     828           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_clone failed");
     829             :         }
     830             :         //create the geometry from the coordinates sequence
     831           0 :         *outGeometry = GEOSGeom_createPoint(gcs_new);
     832           0 :         if (*outGeometry == NULL) {
     833           0 :                 GEOSCoordSeq_destroy(gcs_new);
     834           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSGeom_createPoint failed");
     835             :         }
     836             : 
     837             :         return MAL_SUCCEED;
     838             : }
     839             : 
     840             : static str
     841           0 : segmentizeLineString(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, double sz, int isRing)
     842             : {
     843             :         int coordinatesNum = 0;
     844             :         const GEOSCoordSequence *gcs_old;
     845             :         GEOSCoordSeq gcs_new;
     846           0 :         unsigned int pointsNum = 0, additionalPoints = 0, i = 0, j = 0;
     847             :         double xl = 0.0, yl = 0.0, zl = 0.0;
     848             :         double *xCoords_org, *yCoords_org, *zCoords_org;
     849             :         str err = MAL_SUCCEED;
     850             : 
     851             :         //get the number of coordinates the geometry has
     852           0 :         coordinatesNum = GEOSGeom_getCoordinateDimension(geosGeometry);
     853             :         //get the coordinates of the points comprising the geometry
     854           0 :         if ((gcs_old = GEOSGeom_getCoordSeq(geosGeometry)) == NULL) {
     855           0 :                 *outGeometry = NULL;
     856           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
     857             :         }
     858             :         //get the number of points in the geometry
     859           0 :         if (!GEOSCoordSeq_getSize(gcs_old, &pointsNum)) {
     860           0 :                 *outGeometry = NULL;
     861           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getSize failed");
     862             :         }
     863             :         //store the points so that I do not have to read them multiple times using geos
     864           0 :         if ((xCoords_org = GDKmalloc(pointsNum * sizeof(double))) == NULL) {
     865           0 :                 *outGeometry = NULL;
     866           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(HY013) MAL_MALLOC_FAIL " for %u double values", pointsNum);
     867             :         }
     868           0 :         if ((yCoords_org = GDKmalloc(pointsNum * sizeof(double))) == NULL) {
     869           0 :                 GDKfree(xCoords_org);
     870           0 :                 *outGeometry = NULL;
     871           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(HY013) MAL_MALLOC_FAIL " for %u double values", pointsNum);
     872             :         }
     873           0 :         if ((zCoords_org = GDKmalloc(pointsNum * sizeof(double))) == NULL) {
     874           0 :                 GDKfree(xCoords_org);
     875           0 :                 GDKfree(yCoords_org);
     876           0 :                 *outGeometry = NULL;
     877           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(HY013) MAL_MALLOC_FAIL " for %u double values", pointsNum);
     878             :         }
     879             : 
     880           0 :         if (!GEOSCoordSeq_getX(gcs_old, 0, &xCoords_org[0])) {
     881           0 :                 err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getX failed");
     882           0 :                 goto bailout;
     883             :         }
     884           0 :         if (!GEOSCoordSeq_getY(gcs_old, 0, &yCoords_org[0])) {
     885           0 :                 err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getY failed");
     886           0 :                 goto bailout;
     887             :         }
     888           0 :         if (coordinatesNum > 2 && !GEOSCoordSeq_getZ(gcs_old, 0, &zCoords_org[0])) {
     889           0 :                 err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getZ failed");
     890           0 :                 goto bailout;
     891             :         }
     892             : 
     893           0 :         xl = xCoords_org[0];
     894           0 :         yl = yCoords_org[0];
     895           0 :         zl = zCoords_org[0];
     896             : 
     897             :         //check how many new points should be added
     898           0 :         for (i = 1; i < pointsNum; i++) {
     899             :                 double dist;
     900             : 
     901           0 :                 if (!GEOSCoordSeq_getX(gcs_old, i, &xCoords_org[i])) {
     902           0 :                         err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getX failed");
     903           0 :                         goto bailout;
     904             :                 }
     905           0 :                 if (!GEOSCoordSeq_getY(gcs_old, i, &yCoords_org[i])) {
     906           0 :                         err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getY failed");
     907           0 :                         goto bailout;
     908             :                 }
     909           0 :                 if (coordinatesNum > 2 && !GEOSCoordSeq_getZ(gcs_old, i, &zCoords_org[i])) {
     910           0 :                         err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getZ failed");
     911           0 :                         goto bailout;
     912             :                 }
     913             : 
     914             :                 //compute the distance of the current point to the last added one
     915           0 :                 while ((dist = sqrt(pow(xl - xCoords_org[i], 2) + pow(yl - yCoords_org[i], 2) + pow(zl - zCoords_org[i], 2))) > sz) {
     916           0 :                         TRC_DEBUG(GEOM, "Old : (%f, %f, %f) vs (%f, %f, %f) = %f\n", xl, yl, zl, xCoords_org[i], yCoords_org[i], zCoords_org[i], dist);
     917             : 
     918           0 :                         additionalPoints++;
     919             :                         //compute the point
     920           0 :                         xl = xl + (xCoords_org[i] - xl) * sz / dist;
     921           0 :                         yl = yl + (yCoords_org[i] - yl) * sz / dist;
     922           0 :                         zl = zl + (zCoords_org[i] - zl) * sz / dist;
     923             :                 }
     924             : 
     925           0 :                 xl = xCoords_org[i];
     926           0 :                 yl = yCoords_org[i];
     927           0 :                 zl = zCoords_org[i];
     928             : 
     929             :         }
     930             : 
     931           0 :         TRC_DEBUG(GEOM, "Adding %u\n", additionalPoints);
     932             : 
     933             :         //create the coordinates sequence for the translated geometry
     934           0 :         if ((gcs_new = GEOSCoordSeq_create(pointsNum + additionalPoints, coordinatesNum)) == NULL) {
     935           0 :                 *outGeometry = NULL;
     936           0 :                 err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_create failed");
     937           0 :                 goto bailout;
     938             :         }
     939             :         //add the first point
     940           0 :         if (!GEOSCoordSeq_setX(gcs_new, 0, xCoords_org[0])) {
     941           0 :                 err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setX failed");
     942           0 :                 GEOSCoordSeq_destroy(gcs_new);
     943           0 :                 goto bailout;
     944             :         }
     945           0 :         if (!GEOSCoordSeq_setY(gcs_new, 0, yCoords_org[0])) {
     946           0 :                 err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setY failed");
     947           0 :                 GEOSCoordSeq_destroy(gcs_new);
     948           0 :                 goto bailout;
     949             :         }
     950           0 :         if (coordinatesNum > 2 && !GEOSCoordSeq_setZ(gcs_new, 0, zCoords_org[0])) {
     951           0 :                 err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setZ failed");
     952           0 :                 GEOSCoordSeq_destroy(gcs_new);
     953           0 :                 goto bailout;
     954             :         }
     955             : 
     956           0 :         xl = xCoords_org[0];
     957           0 :         yl = yCoords_org[0];
     958           0 :         zl = zCoords_org[0];
     959             : 
     960             :         //check and add the rest of the points
     961           0 :         for (i = 1; i < pointsNum; i++) {
     962             :                 //compute the distance of the current point to the last added one
     963             :                 double dist;
     964           0 :                 while ((dist = sqrt(pow(xl - xCoords_org[i], 2) + pow(yl - yCoords_org[i], 2) + pow(zl - zCoords_org[i], 2))) > sz) {
     965           0 :                         TRC_DEBUG(GEOM, "Old: (%f, %f, %f) vs (%f, %f, %f) = %f\n", xl, yl, zl, xCoords_org[i], yCoords_org[i], zCoords_org[i], dist);
     966             : 
     967           0 :                         assert(j < additionalPoints);
     968             : 
     969             :                         //compute intermediate point
     970           0 :                         xl = xl + (xCoords_org[i] - xl) * sz / dist;
     971           0 :                         yl = yl + (yCoords_org[i] - yl) * sz / dist;
     972           0 :                         zl = zl + (zCoords_org[i] - zl) * sz / dist;
     973             : 
     974             :                         //add the intermediate point
     975           0 :                         if (!GEOSCoordSeq_setX(gcs_new, i + j, xl)) {
     976           0 :                                 err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setX failed");
     977           0 :                                 GEOSCoordSeq_destroy(gcs_new);
     978           0 :                                 goto bailout;
     979             :                         }
     980           0 :                         if (!GEOSCoordSeq_setY(gcs_new, i + j, yl)) {
     981           0 :                                 err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setY failed");
     982           0 :                                 GEOSCoordSeq_destroy(gcs_new);
     983           0 :                                 goto bailout;
     984             :                         }
     985           0 :                         if (coordinatesNum > 2 && !GEOSCoordSeq_setZ(gcs_new, i + j, zl)) {
     986           0 :                                 err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setZ failed");
     987           0 :                                 GEOSCoordSeq_destroy(gcs_new);
     988           0 :                                 goto bailout;
     989             :                         }
     990             : 
     991           0 :                         j++;
     992             :                 }
     993             : 
     994             :                 //add the original point
     995           0 :                 if (!GEOSCoordSeq_setX(gcs_new, i + j, xCoords_org[i])) {
     996           0 :                         err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setX failed");
     997           0 :                         GEOSCoordSeq_destroy(gcs_new);
     998           0 :                         goto bailout;
     999             :                 }
    1000           0 :                 if (!GEOSCoordSeq_setY(gcs_new, i + j, yCoords_org[i])) {
    1001           0 :                         err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setY failed");
    1002           0 :                         GEOSCoordSeq_destroy(gcs_new);
    1003           0 :                         goto bailout;
    1004             :                 }
    1005           0 :                 if (coordinatesNum > 2 && !GEOSCoordSeq_setZ(gcs_new, i + j, zCoords_org[i])) {
    1006           0 :                         err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setZ failed");
    1007           0 :                         GEOSCoordSeq_destroy(gcs_new);
    1008           0 :                         goto bailout;
    1009             :                 }
    1010             : 
    1011           0 :                 xl = xCoords_org[i];
    1012           0 :                 yl = yCoords_org[i];
    1013           0 :                 zl = zCoords_org[i];
    1014             : 
    1015             :         }
    1016             : 
    1017             :         //create the geometry from the translated coordinates sequence
    1018           0 :         if (isRing)
    1019           0 :                 *outGeometry = GEOSGeom_createLinearRing(gcs_new);
    1020             :         else
    1021           0 :                 *outGeometry = GEOSGeom_createLineString(gcs_new);
    1022             : 
    1023           0 :         if (*outGeometry == NULL) {
    1024           0 :                 err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSGeom_%s failed", isRing ? "LinearRing" : "LineString");
    1025           0 :                 GEOSCoordSeq_destroy(gcs_new);
    1026             :         }
    1027             : 
    1028           0 :   bailout:
    1029           0 :         GDKfree(xCoords_org);
    1030           0 :         GDKfree(yCoords_org);
    1031           0 :         GDKfree(zCoords_org);
    1032             : 
    1033           0 :         return err;
    1034             : }
    1035             : 
    1036             : static str
    1037           0 : segmentizePolygon(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, double sz)
    1038             : {
    1039             :         const GEOSGeometry *exteriorRingGeometry;
    1040           0 :         GEOSGeometry *transformedExteriorRingGeometry = NULL;
    1041             :         GEOSGeometry **transformedInteriorRingGeometries = NULL;
    1042             :         int numInteriorRings = 0, i = 0;
    1043             :         str err;
    1044             : 
    1045             :         /* get the exterior ring of the polygon */
    1046           0 :         exteriorRingGeometry = GEOSGetExteriorRing(geosGeometry);
    1047           0 :         if (exteriorRingGeometry == NULL) {
    1048           0 :                 *outGeometry = NULL;
    1049           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSGetExteriorRing failed");
    1050             :         }
    1051             : 
    1052           0 :         if ((err = segmentizeLineString(&transformedExteriorRingGeometry, exteriorRingGeometry, sz, 1)) != MAL_SUCCEED) {
    1053           0 :                 *outGeometry = NULL;
    1054           0 :                 return err;
    1055             :         }
    1056             : 
    1057           0 :         numInteriorRings = GEOSGetNumInteriorRings(geosGeometry);
    1058           0 :         if (numInteriorRings == -1) {
    1059           0 :                 *outGeometry = NULL;
    1060           0 :                 GEOSGeom_destroy(transformedExteriorRingGeometry);
    1061           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSGetInteriorRingN failed.");
    1062             :         }
    1063             :         //iterate over the interiorRing and segmentize each one of them
    1064           0 :         transformedInteriorRingGeometries = GDKmalloc(numInteriorRings * sizeof(GEOSGeometry *));
    1065           0 :         if (transformedInteriorRingGeometries == NULL) {
    1066           0 :                 *outGeometry = NULL;
    1067           0 :                 GEOSGeom_destroy(transformedExteriorRingGeometry);
    1068           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1069             :         }
    1070           0 :         for (i = 0; i < numInteriorRings; i++) {
    1071           0 :                 if ((err = segmentizeLineString(&transformedInteriorRingGeometries[i], GEOSGetInteriorRingN(geosGeometry, i), sz, 1)) != MAL_SUCCEED) {
    1072           0 :                         while (--i >= 0)
    1073           0 :                                 GEOSGeom_destroy(transformedInteriorRingGeometries[i]);
    1074           0 :                         GDKfree(transformedInteriorRingGeometries);
    1075           0 :                         GEOSGeom_destroy(transformedExteriorRingGeometry);
    1076           0 :                         *outGeometry = NULL;
    1077           0 :                         return err;
    1078             :                 }
    1079             :         }
    1080             : 
    1081           0 :         *outGeometry = GEOSGeom_createPolygon(transformedExteriorRingGeometry, transformedInteriorRingGeometries, numInteriorRings);
    1082           0 :         if (*outGeometry == NULL) {
    1083           0 :                 for (i = 0; i < numInteriorRings; i++)
    1084           0 :                         GEOSGeom_destroy(transformedInteriorRingGeometries[i]);
    1085           0 :                 err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSGeom_createPolygon failed");
    1086             :         }
    1087           0 :         GDKfree(transformedInteriorRingGeometries);
    1088           0 :         GEOSGeom_destroy(transformedExteriorRingGeometry);
    1089             : 
    1090           0 :         return err;
    1091             : }
    1092             : 
    1093             : static str segmentizeGeometry(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, double sz);
    1094             : static str
    1095           0 : segmentizeMultiGeometry(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, double sz)
    1096             : {
    1097             :         int geometriesNum, i;
    1098             :         GEOSGeometry **transformedMultiGeometries = NULL;
    1099             :         str err = MAL_SUCCEED;
    1100             : 
    1101           0 :         geometriesNum = GEOSGetNumGeometries(geosGeometry);
    1102           0 :         transformedMultiGeometries = GDKmalloc(geometriesNum * sizeof(GEOSGeometry *));
    1103           0 :         if (transformedMultiGeometries == NULL)
    1104           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1105             : 
    1106             :         //In order to have the geometries in the output in the same order as in the input
    1107             :         //we should read them and put them in the area in reverse order
    1108           0 :         for (i = geometriesNum - 1; i >= 0; i--) {
    1109           0 :                 const GEOSGeometry *multiGeometry = GEOSGetGeometryN(geosGeometry, i);
    1110             : 
    1111           0 :                 if ((err = segmentizeGeometry(&transformedMultiGeometries[i], multiGeometry, sz)) != MAL_SUCCEED) {
    1112           0 :                         while (++i < geometriesNum)
    1113           0 :                                 GEOSGeom_destroy(transformedMultiGeometries[i]);
    1114           0 :                         GDKfree(transformedMultiGeometries);
    1115           0 :                         *outGeometry = NULL;
    1116           0 :                         return err;
    1117             :                 }
    1118             :         }
    1119             : 
    1120           0 :         *outGeometry = GEOSGeom_createCollection(GEOSGeomTypeId(geosGeometry), transformedMultiGeometries, geometriesNum);
    1121           0 :         if (*outGeometry == NULL) {
    1122           0 :                 for (i = 0; i < geometriesNum; i++)
    1123           0 :                         GEOSGeom_destroy(transformedMultiGeometries[i]);
    1124           0 :                 err = createException(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation GEOSGeom_createCollection failed");
    1125             :         }
    1126           0 :         GDKfree(transformedMultiGeometries);
    1127             : 
    1128           0 :         return err;
    1129             : }
    1130             : 
    1131             : static str
    1132           0 : segmentizeGeometry(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, double sz)
    1133             : {
    1134           0 :         int geometryType = GEOSGeomTypeId(geosGeometry) + 1;
    1135             : 
    1136             :         //check the type of the geometry
    1137           0 :         switch (geometryType) {
    1138           0 :         case wkbPoint_mdb:
    1139           0 :                 return segmentizePoint(outGeometry, geosGeometry);
    1140           0 :         case wkbLineString_mdb:
    1141             :         case wkbLinearRing_mdb:
    1142           0 :                 return segmentizeLineString(outGeometry, geosGeometry, sz, 0);
    1143           0 :         case wkbPolygon_mdb:
    1144           0 :                 return segmentizePolygon(outGeometry, geosGeometry, sz);
    1145           0 :         case wkbMultiPoint_mdb:
    1146             :         case wkbMultiLineString_mdb:
    1147             :         case wkbMultiPolygon_mdb:
    1148             :         case wkbGeometryCollection_mdb:
    1149           0 :                 return segmentizeMultiGeometry(outGeometry, geosGeometry, sz);
    1150           0 :         default:
    1151           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(38000) " Geos %s Unknown geometry type", geom_type2str(geometryType, 0));
    1152             :         }
    1153             : }
    1154             : 
    1155             : str
    1156           0 : wkbSegmentize(wkb **outWKB, wkb **geomWKB, dbl *sz)
    1157             : {
    1158             :         GEOSGeometry *outGeometry;
    1159             :         GEOSGeom geosGeometry;
    1160             :         str err;
    1161             : 
    1162           0 :         if (is_wkb_nil(*geomWKB) || is_dbl_nil(*sz)) {
    1163           0 :                 if ((*outWKB = wkbNULLcopy()) == NULL)
    1164           0 :                         throw(MAL, "geom.Segmentize", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1165             :                 return MAL_SUCCEED;
    1166             :         }
    1167             : 
    1168           0 :         geosGeometry = wkb2geos(*geomWKB);
    1169           0 :         if (geosGeometry == NULL) {
    1170           0 :                 *outWKB = NULL;
    1171           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation wkb2geos failed");
    1172             :         }
    1173             : 
    1174           0 :         if ((err = segmentizeGeometry(&outGeometry, geosGeometry, *sz)) != MAL_SUCCEED) {
    1175           0 :                 GEOSGeom_destroy(geosGeometry);
    1176           0 :                 *outWKB = NULL;
    1177           0 :                 return err;
    1178             :         }
    1179             : 
    1180           0 :         GEOSSetSRID(outGeometry, GEOSGetSRID(geosGeometry));
    1181             : 
    1182           0 :         *outWKB = geos2wkb(outGeometry);
    1183             : 
    1184           0 :         GEOSGeom_destroy(geosGeometry);
    1185           0 :         GEOSGeom_destroy(outGeometry);
    1186             : 
    1187           0 :         if (*outWKB == NULL)
    1188           0 :                 throw(MAL, "geom.Segmentize", SQLSTATE(38000) "Geos operation geos2wkb failed");
    1189             : 
    1190             :         return MAL_SUCCEED;
    1191             : }
    1192             : 
    1193             : //gets a coord seq and moves it dx, dy, dz
    1194             : static str
    1195           0 : translateCoordSeq(int idx, int coordinatesNum, double dx, double dy, double dz, const GEOSCoordSequence *gcs_old, GEOSCoordSeq gcs_new)
    1196             : {
    1197           0 :         double x = 0, y = 0, z = 0;
    1198             : 
    1199             :         //get the coordinates
    1200           0 :         if (!GEOSCoordSeq_getX(gcs_old, idx, &x))
    1201           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getX failed");
    1202           0 :         if (!GEOSCoordSeq_getY(gcs_old, idx, &y))
    1203           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getY failed");
    1204           0 :         if (coordinatesNum > 2)
    1205           0 :                 if (!GEOSCoordSeq_getZ(gcs_old, idx, &z))
    1206           0 :                         throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getZ failed");
    1207             : 
    1208             :         //create new coordinates moved by dx, dy, dz
    1209           0 :         if (!GEOSCoordSeq_setX(gcs_new, idx, (x + dx)))
    1210           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setX failed");
    1211           0 :         if (!GEOSCoordSeq_setY(gcs_new, idx, (y + dy)))
    1212           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setY failed");
    1213           0 :         if (coordinatesNum > 2)
    1214           0 :                 if (!GEOSCoordSeq_setZ(gcs_new, idx, (z + dz)))
    1215           0 :                         throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setZ failed");
    1216             : 
    1217             :         return MAL_SUCCEED;
    1218             : }
    1219             : 
    1220             : static str
    1221           0 : translatePoint(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, double dx, double dy, double dz)
    1222             : {
    1223             :         int coordinatesNum = 0;
    1224             :         const GEOSCoordSequence *gcs_old;
    1225             :         GEOSCoordSeq gcs_new;
    1226             :         str err;
    1227             : 
    1228             :         /* get the number of coordinates the geometry has */
    1229           0 :         coordinatesNum = GEOSGeom_getCoordinateDimension(geosGeometry);
    1230             :         /* get the coordinates of the points comprising the geometry */
    1231           0 :         gcs_old = GEOSGeom_getCoordSeq(geosGeometry);
    1232             : 
    1233           0 :         if (gcs_old == NULL) {
    1234           0 :                 *outGeometry = NULL;
    1235           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
    1236             :         }
    1237             : 
    1238             :         /* create the coordinates sequence for the translated geometry */
    1239           0 :         gcs_new = GEOSCoordSeq_create(1, coordinatesNum);
    1240           0 :         if (gcs_new == NULL) {
    1241           0 :                 *outGeometry = NULL;
    1242           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSCoordSeq_create failed");
    1243             :         }
    1244             : 
    1245             :         /* create the translated coordinates */
    1246           0 :         if ((err = translateCoordSeq(0, coordinatesNum, dx, dy, dz, gcs_old, gcs_new)) != MAL_SUCCEED) {
    1247           0 :                 GEOSCoordSeq_destroy(gcs_new);
    1248           0 :                 *outGeometry = NULL;
    1249           0 :                 return err;
    1250             :         }
    1251             : 
    1252             :         /* create the geometry from the coordinates sequence */
    1253           0 :         *outGeometry = GEOSGeom_createPoint(gcs_new);
    1254           0 :         if (*outGeometry == NULL) {
    1255           0 :                 err = createException(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSGeom_createPoint failed");
    1256           0 :                 GEOSCoordSeq_destroy(gcs_new);
    1257             :         }
    1258             : 
    1259             :         return err;
    1260             : }
    1261             : 
    1262             : static str
    1263           0 : translateLineString(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, double dx, double dy, double dz)
    1264             : {
    1265             :         int coordinatesNum = 0;
    1266             :         const GEOSCoordSequence *gcs_old;
    1267             :         GEOSCoordSeq gcs_new;
    1268           0 :         unsigned int pointsNum = 0, i = 0;
    1269             :         str err;
    1270             : 
    1271             :         /* get the number of coordinates the geometry has */
    1272           0 :         coordinatesNum = GEOSGeom_getCoordinateDimension(geosGeometry);
    1273             :         /* get the coordinates of the points comprising the geometry */
    1274           0 :         gcs_old = GEOSGeom_getCoordSeq(geosGeometry);
    1275             : 
    1276           0 :         if (gcs_old == NULL)
    1277           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
    1278             : 
    1279             :         /* get the number of points in the geometry */
    1280           0 :         GEOSCoordSeq_getSize(gcs_old, &pointsNum);
    1281             : 
    1282             :         /* create the coordinates sequence for the translated geometry */
    1283           0 :         gcs_new = GEOSCoordSeq_create(pointsNum, coordinatesNum);
    1284           0 :         if (gcs_new == NULL) {
    1285           0 :                 *outGeometry = NULL;
    1286           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSCoordSeq_create failed");
    1287             :         }
    1288             : 
    1289             :         /* create the translated coordinates */
    1290           0 :         for (i = 0; i < pointsNum; i++) {
    1291           0 :                 if ((err = translateCoordSeq(i, coordinatesNum, dx, dy, dz, gcs_old, gcs_new)) != MAL_SUCCEED) {
    1292           0 :                         GEOSCoordSeq_destroy(gcs_new);
    1293           0 :                         return err;
    1294             :                 }
    1295             :         }
    1296             : 
    1297             :         //create the geometry from the translated coordinates sequence
    1298           0 :         *outGeometry = GEOSGeom_createLineString(gcs_new);
    1299           0 :         if (*outGeometry == NULL) {
    1300           0 :                 err = createException(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSGeom_createLineString failed");
    1301           0 :                 GEOSCoordSeq_destroy(gcs_new);
    1302             :         }
    1303             : 
    1304             :         return err;
    1305             : }
    1306             : 
    1307             : //Necessary for composing a polygon from rings
    1308             : static str
    1309           0 : translateLinearRing(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, double dx, double dy, double dz)
    1310             : {
    1311             :         int coordinatesNum = 0;
    1312             :         const GEOSCoordSequence *gcs_old;
    1313             :         GEOSCoordSeq gcs_new;
    1314           0 :         unsigned int pointsNum = 0, i = 0;
    1315             :         str err;
    1316             : 
    1317             :         /* get the number of coordinates the geometry has */
    1318           0 :         coordinatesNum = GEOSGeom_getCoordinateDimension(geosGeometry);
    1319             :         /* get the coordinates of the points comprising the geometry */
    1320           0 :         gcs_old = GEOSGeom_getCoordSeq(geosGeometry);
    1321             : 
    1322           0 :         if (gcs_old == NULL)
    1323           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
    1324             : 
    1325             :         /* get the number of points in the geometry */
    1326           0 :         GEOSCoordSeq_getSize(gcs_old, &pointsNum);
    1327             : 
    1328             :         /* create the coordinates sequence for the translated geometry */
    1329           0 :         gcs_new = GEOSCoordSeq_create(pointsNum, coordinatesNum);
    1330           0 :         if (gcs_new == NULL) {
    1331           0 :                 *outGeometry = NULL;
    1332           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSCoordSeq_create failed");
    1333             :         }
    1334             : 
    1335             :         /* create the translated coordinates */
    1336           0 :         for (i = 0; i < pointsNum; i++) {
    1337           0 :                 if ((err = translateCoordSeq(i, coordinatesNum, dx, dy, dz, gcs_old, gcs_new)) != MAL_SUCCEED) {
    1338           0 :                         GEOSCoordSeq_destroy(gcs_new);
    1339           0 :                         return err;
    1340             :                 }
    1341             :         }
    1342             : 
    1343             :         //create the geometry from the translated coordinates sequence
    1344           0 :         *outGeometry = GEOSGeom_createLinearRing(gcs_new);
    1345           0 :         if (*outGeometry == NULL) {
    1346           0 :                 err = createException(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSGeom_createLinearRing failed");
    1347           0 :                 GEOSCoordSeq_destroy(gcs_new);
    1348             :         }
    1349             : 
    1350             :         return err;
    1351             : }
    1352             : 
    1353             : static str
    1354           0 : translatePolygon(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, double dx, double dy, double dz)
    1355             : {
    1356             :         const GEOSGeometry *exteriorRingGeometry;
    1357           0 :         GEOSGeometry *transformedExteriorRingGeometry = NULL;
    1358             :         GEOSGeometry **transformedInteriorRingGeometries = NULL;
    1359             :         int numInteriorRings = 0, i = 0;
    1360             :         str err = MAL_SUCCEED;
    1361             : 
    1362             :         /* get the exterior ring of the polygon */
    1363           0 :         exteriorRingGeometry = GEOSGetExteriorRing(geosGeometry);
    1364           0 :         if (exteriorRingGeometry == NULL) {
    1365           0 :                 *outGeometry = NULL;
    1366           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSGetExteriorRing failed");
    1367             :         }
    1368             : 
    1369           0 :         if ((err = translateLinearRing(&transformedExteriorRingGeometry, exteriorRingGeometry, dx, dy, dz)) != MAL_SUCCEED) {
    1370           0 :                 *outGeometry = NULL;
    1371           0 :                 return err;
    1372             :         }
    1373             : 
    1374           0 :         numInteriorRings = GEOSGetNumInteriorRings(geosGeometry);
    1375           0 :         if (numInteriorRings == -1) {
    1376           0 :                 *outGeometry = NULL;
    1377           0 :                 GEOSGeom_destroy(transformedExteriorRingGeometry);
    1378           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSGetInteriorRingN failed.");
    1379             :         }
    1380             : 
    1381             :         /* iterate over the interiorRing and translate each one of them */
    1382           0 :         transformedInteriorRingGeometries = GDKmalloc(numInteriorRings * sizeof(GEOSGeometry *));
    1383           0 :         if (transformedInteriorRingGeometries == NULL) {
    1384           0 :                 *outGeometry = NULL;
    1385           0 :                 GEOSGeom_destroy(transformedExteriorRingGeometry);
    1386           0 :                 throw(MAL, "geom.Translate", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1387             :         }
    1388           0 :         for (i = 0; i < numInteriorRings; i++) {
    1389           0 :                 if ((err = translateLinearRing(&transformedInteriorRingGeometries[i], GEOSGetInteriorRingN(geosGeometry, i), dx, dy, dz)) != MAL_SUCCEED) {
    1390           0 :                         while (--i >= 0)
    1391           0 :                                 GEOSGeom_destroy(transformedInteriorRingGeometries[i]);
    1392           0 :                         GDKfree(transformedInteriorRingGeometries);
    1393           0 :                         GEOSGeom_destroy(transformedExteriorRingGeometry);
    1394           0 :                         *outGeometry = NULL;
    1395           0 :                         return err;
    1396             :                 }
    1397             :         }
    1398             : 
    1399           0 :         *outGeometry = GEOSGeom_createPolygon(transformedExteriorRingGeometry, transformedInteriorRingGeometries, numInteriorRings);
    1400           0 :         if (*outGeometry == NULL) {
    1401           0 :                 for (i = 0; i < numInteriorRings; i++)
    1402           0 :                         GEOSGeom_destroy(transformedInteriorRingGeometries[i]);
    1403           0 :                 err = createException(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSGeom_createPolygon failed");
    1404             :         }
    1405           0 :         GDKfree(transformedInteriorRingGeometries);
    1406           0 :         GEOSGeom_destroy(transformedExteriorRingGeometry);
    1407             : 
    1408           0 :         return err;
    1409             : }
    1410             : 
    1411             : static str translateGeometry(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, double dx, double dy, double dz);
    1412             : static str
    1413           0 : translateMultiGeometry(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, double dx, double dy, double dz)
    1414             : {
    1415             :         int geometriesNum, i;
    1416             :         GEOSGeometry **transformedMultiGeometries = NULL;
    1417             :         str err = MAL_SUCCEED;
    1418             : 
    1419           0 :         geometriesNum = GEOSGetNumGeometries(geosGeometry);
    1420           0 :         transformedMultiGeometries = GDKmalloc(geometriesNum * sizeof(GEOSGeometry *));
    1421           0 :         if (transformedMultiGeometries == NULL)
    1422           0 :                 throw(MAL, "geom.Translate", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1423             : 
    1424             :         //In order to have the geometries in the output in the same order as in the input
    1425             :         //we should read them and put them in the area in reverse order
    1426           0 :         for (i = geometriesNum - 1; i >= 0; i--) {
    1427           0 :                 const GEOSGeometry *multiGeometry = GEOSGetGeometryN(geosGeometry, i);
    1428             : 
    1429           0 :                 if ((err = translateGeometry(&transformedMultiGeometries[i], multiGeometry, dx, dy, dz)) != MAL_SUCCEED) {
    1430           0 :                         while (i++ < geometriesNum)
    1431           0 :                                 GEOSGeom_destroy(transformedMultiGeometries[i]);
    1432           0 :                         GDKfree(transformedMultiGeometries);
    1433           0 :                         *outGeometry = NULL;
    1434           0 :                         return err;
    1435             :                 }
    1436             :         }
    1437             : 
    1438           0 :         *outGeometry = GEOSGeom_createCollection(GEOSGeomTypeId(geosGeometry), transformedMultiGeometries, geometriesNum);
    1439           0 :         if (*outGeometry == NULL) {
    1440           0 :                 for (i = 0; i < geometriesNum; i++)
    1441           0 :                         GEOSGeom_destroy(transformedMultiGeometries[i]);
    1442           0 :                 err = createException(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation GEOSGeom_createCollection failed");
    1443             :         }
    1444           0 :         GDKfree(transformedMultiGeometries);
    1445             : 
    1446           0 :         return err;
    1447             : }
    1448             : 
    1449             : static str
    1450           0 : translateGeometry(GEOSGeometry **outGeometry, const GEOSGeometry *geosGeometry, double dx, double dy, double dz)
    1451             : {
    1452           0 :         int geometryType = GEOSGeomTypeId(geosGeometry) + 1;
    1453             : 
    1454             :         //check the type of the geometry
    1455           0 :         switch (geometryType) {
    1456           0 :         case wkbPoint_mdb:
    1457           0 :                 return translatePoint(outGeometry, geosGeometry, dx, dy, dz);
    1458           0 :         case wkbLineString_mdb:
    1459             :         case wkbLinearRing_mdb:
    1460           0 :                 return translateLineString(outGeometry, geosGeometry, dx, dy, dz);
    1461           0 :         case wkbPolygon_mdb:
    1462           0 :                 return translatePolygon(outGeometry, geosGeometry, dx, dy, dz);
    1463           0 :         case wkbMultiPoint_mdb:
    1464             :         case wkbMultiLineString_mdb:
    1465             :         case wkbMultiPolygon_mdb:
    1466             :         case wkbGeometryCollection_mdb:
    1467           0 :                 return translateMultiGeometry(outGeometry, geosGeometry, dx, dy, dz);
    1468           0 :         default:
    1469           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos %s unknown geometry type", geom_type2str(geometryType, 0));
    1470             :         }
    1471             : }
    1472             : 
    1473             : str
    1474           0 : wkbTranslate(wkb **outWKB, wkb **geomWKB, dbl *dx, dbl *dy, dbl *dz)
    1475             : {
    1476             :         GEOSGeometry *outGeometry;
    1477             :         GEOSGeom geosGeometry;
    1478             :         str err;
    1479             : 
    1480           0 :         if (is_wkb_nil(*geomWKB) || is_dbl_nil(*dx) || is_dbl_nil(*dy) || is_dbl_nil(*dz)) {
    1481           0 :                 if ((*outWKB = wkbNULLcopy()) == NULL)
    1482           0 :                         throw(MAL, "geom.Translate", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1483             :                 return MAL_SUCCEED;
    1484             :         }
    1485             : 
    1486           0 :         geosGeometry = wkb2geos(*geomWKB);
    1487           0 :         if (geosGeometry == NULL) {
    1488           0 :                 *outWKB = NULL;
    1489           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation wkb2geos failed");
    1490             :         }
    1491             : 
    1492           0 :         if ((err = translateGeometry(&outGeometry, geosGeometry, *dx, *dy, *dz)) != MAL_SUCCEED) {
    1493           0 :                 GEOSGeom_destroy(geosGeometry);
    1494           0 :                 *outWKB = NULL;
    1495           0 :                 return err;
    1496             :         }
    1497             : 
    1498           0 :         GEOSSetSRID(outGeometry, GEOSGetSRID(geosGeometry));
    1499             : 
    1500           0 :         *outWKB = geos2wkb(outGeometry);
    1501             : 
    1502           0 :         GEOSGeom_destroy(geosGeometry);
    1503           0 :         GEOSGeom_destroy(outGeometry);
    1504             : 
    1505           0 :         if (*outWKB == NULL)
    1506           0 :                 throw(MAL, "geom.Translate", SQLSTATE(38000) "Geos operation geos2wkb failed");
    1507             : 
    1508             :         return MAL_SUCCEED;
    1509             : }
    1510             : 
    1511             : //It creates a Delaunay triangulation
    1512             : //flag = 0 => returns a collection of polygons
    1513             : //flag = 1 => returns a multilinestring
    1514             : str
    1515           4 : wkbDelaunayTriangles(wkb **outWKB, wkb **geomWKB, dbl *tolerance, int *flag)
    1516             : {
    1517             :         GEOSGeom outGeometry;
    1518             :         GEOSGeom geosGeometry;
    1519             : 
    1520           4 :         if (is_wkb_nil(*geomWKB) || is_dbl_nil(*tolerance) || is_int_nil(*flag)) {
    1521           0 :                 if ((*outWKB = wkbNULLcopy()) == NULL)
    1522           0 :                         throw(MAL, "geom.DelaunayTriangles", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1523             :                 return MAL_SUCCEED;
    1524             :         }
    1525             : 
    1526           4 :         geosGeometry = wkb2geos(*geomWKB);
    1527           4 :         outGeometry = GEOSDelaunayTriangulation(geosGeometry, *tolerance, *flag);
    1528           4 :         GEOSGeom_destroy(geosGeometry);
    1529           4 :         if (outGeometry == NULL) {
    1530           0 :                 *outWKB = NULL;
    1531           0 :                 throw(MAL, "geom.DelaunayTriangles", SQLSTATE(38000) "Geos operation GEOSDelaunayTriangulation failed");
    1532             :         }
    1533             : 
    1534           4 :         *outWKB = geos2wkb(outGeometry);
    1535           4 :         GEOSGeom_destroy(outGeometry);
    1536             : 
    1537           4 :         if (*outWKB == NULL)
    1538           0 :                 throw(MAL, "geom.DelaunayTriangles", SQLSTATE(38000) "Geos operation geos2wkb failed");
    1539             : 
    1540             :         return MAL_SUCCEED;
    1541             : }
    1542             : 
    1543             : str
    1544           2 : wkbPointOnSurface(wkb **resWKB, wkb **geomWKB)
    1545             : {
    1546             :         GEOSGeom geosGeometry, resGeosGeometry;
    1547             : 
    1548           2 :         if (is_wkb_nil(*geomWKB)) {
    1549           0 :                 if ((*resWKB = wkbNULLcopy()) == NULL)
    1550           0 :                         throw(MAL, "geom.PointOnSurface", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1551             :                 return MAL_SUCCEED;
    1552             :         }
    1553             : 
    1554           2 :         geosGeometry = wkb2geos(*geomWKB);
    1555           2 :         if (geosGeometry == NULL) {
    1556           0 :                 *resWKB = NULL;
    1557           0 :                 throw(MAL, "geom.PointOnSurface", SQLSTATE(38000) "Geos operation wkb2geos failed");
    1558             :         }
    1559             : 
    1560           2 :         resGeosGeometry = GEOSPointOnSurface(geosGeometry);
    1561           2 :         if (resGeosGeometry == NULL) {
    1562           0 :                 *resWKB = NULL;
    1563           0 :                 GEOSGeom_destroy(geosGeometry);
    1564           0 :                 throw(MAL, "geom.PointOnSurface", SQLSTATE(38000) "Geos operation GEOSPointOnSurface failed");
    1565             :         }
    1566             :         //set the srid of the point the same as the srid of the input geometry
    1567           2 :         GEOSSetSRID(resGeosGeometry, GEOSGetSRID(geosGeometry));
    1568             : 
    1569           2 :         *resWKB = geos2wkb(resGeosGeometry);
    1570             : 
    1571           2 :         GEOSGeom_destroy(geosGeometry);
    1572           2 :         GEOSGeom_destroy(resGeosGeometry);
    1573             : 
    1574           2 :         if (*resWKB == NULL)
    1575           0 :                 throw(MAL, "geom.PointOnSurface", SQLSTATE(38000) "Geos operation geos2wkb failed");
    1576             : 
    1577             :         return MAL_SUCCEED;
    1578             : }
    1579             : 
    1580             : static str
    1581          23 : dumpGeometriesSingle(BAT *idBAT, BAT *geomBAT, const GEOSGeometry *geosGeometry, unsigned int *lvl, const char *path)
    1582             : {
    1583             :         char *newPath = NULL;
    1584          23 :         size_t pathLength = strlen(path);
    1585          23 :         wkb *singleWKB = geos2wkb(geosGeometry);
    1586             :         str err = MAL_SUCCEED;
    1587             : 
    1588          23 :         if (singleWKB == NULL)
    1589           0 :                 throw(MAL, "geom.Dump", SQLSTATE(38000) "Geos operation geos2wkb failed");
    1590             : 
    1591             :         //change the path only if it is empty
    1592          23 :         if (pathLength == 0) {
    1593             :                 const int lvlDigitsNum = 10;    //MAX_UNIT = 4,294,967,295
    1594             : 
    1595           6 :                 (*lvl)++;
    1596             : 
    1597           6 :                 newPath = GDKmalloc(lvlDigitsNum + 1);
    1598           6 :                 if (newPath == NULL) {
    1599           0 :                         GDKfree(singleWKB);
    1600           0 :                         throw(MAL, "geom.Dump", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1601             :                 }
    1602           6 :                 snprintf(newPath, lvlDigitsNum + 1, "%u", *lvl);
    1603             :         } else {
    1604             :                 //remove the comma at the end of the path
    1605             : #ifdef __COVERITY__
    1606             :                 /* coverity complains about the allocated space being
    1607             :                  * too small, but we just want to reduce the length of
    1608             :                  * the string by one, so the length in the #else part
    1609             :                  * is exactly what we need */
    1610             :                 newPath = GDKmalloc(pathLength + 1);
    1611             : #else
    1612          17 :                 newPath = GDKmalloc(pathLength);
    1613             : #endif
    1614          17 :                 if (newPath == NULL) {
    1615           0 :                         GDKfree(singleWKB);
    1616           0 :                         throw(MAL, "geom.Dump", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1617             :                 }
    1618          17 :                 strcpy_len(newPath, path, pathLength);
    1619             :         }
    1620          46 :         if (BUNappend(idBAT, newPath, false) != GDK_SUCCEED ||
    1621          23 :             BUNappend(geomBAT, singleWKB, false) != GDK_SUCCEED)
    1622           0 :                 err = createException(MAL, "geom.Dump", SQLSTATE(38000) "Geos operation BUNappend failed");
    1623             : 
    1624          23 :         GDKfree(newPath);
    1625          23 :         GDKfree(singleWKB);
    1626             : 
    1627          23 :         return err;
    1628             : }
    1629             : 
    1630             : static str dumpGeometriesGeometry(BAT *idBAT, BAT *geomBAT, const GEOSGeometry *geosGeometry, const char *path);
    1631             : static str
    1632          12 : dumpGeometriesMulti(BAT *idBAT, BAT *geomBAT, const GEOSGeometry *geosGeometry, const char *path)
    1633             : {
    1634             :         int i;
    1635             :         const GEOSGeometry *multiGeometry = NULL;
    1636             :         unsigned int lvl = 0;
    1637          12 :         size_t pathLength = strlen(path);
    1638             :         char *newPath;
    1639             :         str err = MAL_SUCCEED;
    1640             : 
    1641          12 :         int geometriesNum = GEOSGetNumGeometries(geosGeometry);
    1642             : 
    1643          12 :         pathLength += 10 + 1 + 1; /* 10 for lvl, 1 for ",", 1 for NULL byte */
    1644          12 :         newPath = GDKmalloc(pathLength);
    1645          12 :         if (newPath == NULL)
    1646           0 :                 throw(MAL, "geom.Dump", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1647             : 
    1648          30 :         for (i = 0; i < geometriesNum; i++) {
    1649          18 :                 multiGeometry = GEOSGetGeometryN(geosGeometry, i);
    1650          18 :                 if (multiGeometry == NULL) {
    1651           0 :                         err = createException(MAL, "geom.Dump", SQLSTATE(38000) "Geos operation GEOSGetGeometryN failed");
    1652           0 :                         break;
    1653             :                 }
    1654          18 :                 lvl++;
    1655             : 
    1656          18 :                 snprintf(newPath, pathLength, "%s%u,", path, lvl);
    1657             : 
    1658             :                 //*secondLevel = 0;
    1659          18 :                 err = dumpGeometriesGeometry(idBAT, geomBAT, multiGeometry, newPath);
    1660          18 :                 if (err != MAL_SUCCEED)
    1661             :                         break;
    1662             :         }
    1663          12 :         GDKfree(newPath);
    1664          12 :         return err;
    1665             : }
    1666             : 
    1667             : static str
    1668          27 : dumpGeometriesGeometry(BAT *idBAT, BAT *geomBAT, const GEOSGeometry *geosGeometry, const char *path)
    1669             : {
    1670          27 :         int geometryType = GEOSGeomTypeId(geosGeometry) + 1;
    1671          27 :         unsigned int lvl = 0;
    1672             : 
    1673             :         //check the type of the geometry
    1674          27 :         switch (geometryType) {
    1675          15 :         case wkbPoint_mdb:
    1676             :         case wkbLineString_mdb:
    1677             :         case wkbLinearRing_mdb:
    1678             :         case wkbPolygon_mdb:
    1679             :                 //Single Geometry
    1680          15 :                 return dumpGeometriesSingle(idBAT, geomBAT, geosGeometry, &lvl, path);
    1681          12 :         case wkbMultiPoint_mdb:
    1682             :         case wkbMultiLineString_mdb:
    1683             :         case wkbMultiPolygon_mdb:
    1684             :         case wkbGeometryCollection_mdb:
    1685             :                 //Multi Geometry
    1686             :                 //check if the geometry was empty
    1687          12 :                 if (GEOSisEmpty(geosGeometry) == 1) {
    1688             :                         str err;
    1689             :                         //handle it as single
    1690           8 :                         if ((err = dumpGeometriesSingle(idBAT, geomBAT, geosGeometry, &lvl, path)) != MAL_SUCCEED)
    1691             :                                 return err;
    1692             :                 }
    1693             : 
    1694          12 :                 return dumpGeometriesMulti(idBAT, geomBAT, geosGeometry, path);
    1695           0 :         default:
    1696           0 :                 throw(MAL, "geom.Dump", SQLSTATE(38000) "Geos %s unknown geometry type", geom_type2str(geometryType, 0));
    1697             :         }
    1698             : }
    1699             : 
    1700             : str
    1701           9 : wkbDump(bat *idBAT_id, bat *geomBAT_id, wkb **geomWKB)
    1702             : {
    1703             :         BAT *idBAT = NULL, *geomBAT = NULL;
    1704             :         GEOSGeom geosGeometry;
    1705             :         unsigned int geometriesNum;
    1706             :         str err;
    1707             : 
    1708           9 :         if (is_wkb_nil(*geomWKB)) {
    1709             : 
    1710             :                 //create new empty BAT for the output
    1711           0 :                 if ((idBAT = COLnew(0, TYPE_str, 0, TRANSIENT)) == NULL) {
    1712           0 :                         *idBAT_id = bat_nil;
    1713           0 :                         throw(MAL, "geom.DumpPoints", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1714             :                 }
    1715             : 
    1716           0 :                 if ((geomBAT = COLnew(0, ATOMindex("wkb"), 0, TRANSIENT)) == NULL) {
    1717           0 :                         BBPunfix(idBAT->batCacheid);
    1718           0 :                         *geomBAT_id = bat_nil;
    1719           0 :                         throw(MAL, "geom.DumpPoints", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1720             :                 }
    1721             : 
    1722           0 :                 BBPkeepref(*idBAT_id = idBAT->batCacheid);
    1723             : 
    1724           0 :                 BBPkeepref(*geomBAT_id = geomBAT->batCacheid);
    1725             : 
    1726           0 :                 return MAL_SUCCEED;
    1727             :         }
    1728             : 
    1729           9 :         geosGeometry = wkb2geos(*geomWKB);
    1730             : 
    1731             :         //count the number of geometries
    1732           9 :         geometriesNum = GEOSGetNumGeometries(geosGeometry);
    1733             : 
    1734           9 :         if ((idBAT = COLnew(0, TYPE_str, geometriesNum, TRANSIENT)) == NULL) {
    1735           0 :                 GEOSGeom_destroy(geosGeometry);
    1736           0 :                 throw(MAL, "geom.Dump", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1737             :         }
    1738             : 
    1739           9 :         if ((geomBAT = COLnew(0, ATOMindex("wkb"), geometriesNum, TRANSIENT)) == NULL) {
    1740           0 :                 BBPunfix(idBAT->batCacheid);
    1741           0 :                 GEOSGeom_destroy(geosGeometry);
    1742           0 :                 throw(MAL, "geom.Dump", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1743             :         }
    1744             : 
    1745           9 :         err = dumpGeometriesGeometry(idBAT, geomBAT, geosGeometry, "");
    1746           9 :         GEOSGeom_destroy(geosGeometry);
    1747           9 :         if (err != MAL_SUCCEED) {
    1748           0 :                 BBPunfix(idBAT->batCacheid);
    1749           0 :                 BBPunfix(geomBAT->batCacheid);
    1750           0 :                 return err;
    1751             :         }
    1752             : 
    1753           9 :         BBPkeepref(*idBAT_id = idBAT->batCacheid);
    1754           9 :         BBPkeepref(*geomBAT_id = geomBAT->batCacheid);
    1755           9 :         return MAL_SUCCEED;
    1756             : }
    1757             : 
    1758             : static str
    1759          72 : dumpPointsPoint(BAT *idBAT, BAT *geomBAT, const GEOSGeometry *geosGeometry, unsigned int *lvl, const char *path)
    1760             : {
    1761             :         char *newPath = NULL;
    1762          72 :         size_t pathLength = strlen(path);
    1763          72 :         wkb *pointWKB = geos2wkb(geosGeometry);
    1764             :         const int lvlDigitsNum = 10;    //MAX_UNIT = 4,294,967,295
    1765             :         str err = MAL_SUCCEED;
    1766             : 
    1767          72 :         if (pointWKB == NULL)
    1768           0 :                 throw(MAL, "geom.Dump", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1769             : 
    1770          72 :         (*lvl)++;
    1771          72 :         newPath = GDKmalloc(pathLength + lvlDigitsNum + 1);
    1772          72 :         if (newPath == NULL) {
    1773           0 :                 GDKfree(pointWKB);
    1774           0 :                 throw(MAL, "geom.Dump", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1775             :         }
    1776          72 :         sprintf(newPath, "%s%u", path, *lvl);
    1777             : 
    1778         144 :         if (BUNappend(idBAT, newPath, false) != GDK_SUCCEED ||
    1779          72 :             BUNappend(geomBAT, pointWKB, false) != GDK_SUCCEED)
    1780           0 :                 err = createException(MAL, "geom.Dump", SQLSTATE(38000) "Geos operation BUNappend failed");
    1781             : 
    1782          72 :         GDKfree(newPath);
    1783          72 :         GDKfree(pointWKB);
    1784             : 
    1785          72 :         return err;
    1786             : }
    1787             : 
    1788             : static str
    1789          16 : dumpPointsLineString(BAT *idBAT, BAT *geomBAT, const GEOSGeometry *geosGeometry, const char *path)
    1790             : {
    1791          16 :         int pointsNum = 0;
    1792             :         str err;
    1793             :         int i = 0;
    1794          16 :         int check = 0;
    1795          16 :         unsigned int lvl = 0;
    1796          16 :         wkb *geomWKB = geos2wkb(geosGeometry);
    1797             : 
    1798          16 :         err = wkbNumPoints(&pointsNum, &geomWKB, &check);
    1799          16 :         GDKfree(geomWKB);
    1800          16 :         if (err != MAL_SUCCEED)
    1801             :                 return err;
    1802             : 
    1803          86 :         for (i = 0; i < pointsNum && err == MAL_SUCCEED; i++) {
    1804          70 :                 GEOSGeometry *pointGeometry = GEOSGeomGetPointN(geosGeometry, i);
    1805             : 
    1806          70 :                 if (pointGeometry == NULL)
    1807           0 :                         throw(MAL, "geom.DumpPoints", SQLSTATE(38000) "Geos operation GEOSGeomGetPointN failed");
    1808             : 
    1809          70 :                 err = dumpPointsPoint(idBAT, geomBAT, pointGeometry, &lvl, path);
    1810          70 :                 GEOSGeom_destroy(pointGeometry);
    1811             :         }
    1812             : 
    1813             :         return err;
    1814             : }
    1815             : 
    1816             : static str
    1817           9 : dumpPointsPolygon(BAT *idBAT, BAT *geomBAT, const GEOSGeometry *geosGeometry, unsigned int *lvl, const char *path)
    1818             : {
    1819             :         const GEOSGeometry *exteriorRingGeometry;
    1820             :         int numInteriorRings = 0, i = 0;
    1821             :         str err;
    1822             :         const int lvlDigitsNum = 10;    //MAX_UNIT = 4,294,967,295
    1823           9 :         size_t pathLength = strlen(path);
    1824             :         char *newPath;
    1825             :         char *extraStr = ",";
    1826             :         int extraLength = 1;
    1827             : 
    1828             :         //get the exterior ring of the polygon
    1829           9 :         exteriorRingGeometry = GEOSGetExteriorRing(geosGeometry);
    1830           9 :         if (exteriorRingGeometry == NULL)
    1831           0 :                 throw(MAL, "geom.DumpPoints", SQLSTATE(38000) "Geos operation GEOSGetExteriorRing failed");
    1832             : 
    1833           9 :         (*lvl)++;
    1834           9 :         newPath = GDKmalloc(pathLength + lvlDigitsNum + extraLength + 1);
    1835           9 :         if (newPath == NULL)
    1836           0 :                 throw(MAL, "geom.DumpPoints", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1837           9 :         snprintf(newPath, pathLength + lvlDigitsNum + extraLength + 1,
    1838             :                          "%s%u%s", path, *lvl, extraStr);
    1839             : 
    1840             :         //get the points in the exterior ring
    1841           9 :         err = dumpPointsLineString(idBAT, geomBAT, exteriorRingGeometry, newPath);
    1842           9 :         GDKfree(newPath);
    1843           9 :         if (err != MAL_SUCCEED)
    1844             :                 return err;
    1845             : 
    1846             :         //check the interior rings
    1847           9 :         numInteriorRings = GEOSGetNumInteriorRings(geosGeometry);
    1848           9 :         if (numInteriorRings == -1)
    1849           0 :                 throw(MAL, "geom.DumpPoints", SQLSTATE(38000) "Geos operation GEOSGetNumInteriorRings failed");
    1850             : 
    1851             :         // iterate over the interiorRing and transform each one of them
    1852          14 :         for (i = 0; i < numInteriorRings; i++) {
    1853           5 :                 (*lvl)++;
    1854             : 
    1855           5 :                 newPath = GDKmalloc(pathLength + lvlDigitsNum + extraLength + 1);
    1856           5 :                 if (newPath == NULL)
    1857           0 :                         throw(MAL, "geom.DumpPoints", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1858           5 :                 snprintf(newPath, pathLength + lvlDigitsNum + extraLength + 1,
    1859             :                                  "%s%u%s", path, *lvl, extraStr);
    1860             : 
    1861           5 :                 err = dumpPointsLineString(idBAT, geomBAT, GEOSGetInteriorRingN(geosGeometry, i), newPath);
    1862           5 :                 GDKfree(newPath);
    1863           5 :                 if (err != MAL_SUCCEED)
    1864           0 :                         return err;
    1865             :         }
    1866             : 
    1867             :         return MAL_SUCCEED;
    1868             : }
    1869             : 
    1870             : static str dumpPointsGeometry(BAT *idBAT, BAT *geomBAT, const GEOSGeometry *geosGeometry, const char *path);
    1871             : static str
    1872           4 : dumpPointsMultiGeometry(BAT *idBAT, BAT *geomBAT, const GEOSGeometry *geosGeometry, const char *path)
    1873             : {
    1874             :         int geometriesNum, i;
    1875             :         const GEOSGeometry *multiGeometry = NULL;
    1876             :         str err;
    1877             :         unsigned int lvl = 0;
    1878           4 :         size_t pathLength = strlen(path);
    1879             :         char *newPath = NULL;
    1880             :         char *extraStr = ",";
    1881             :         int extraLength = 1;
    1882             : 
    1883           4 :         geometriesNum = GEOSGetNumGeometries(geosGeometry);
    1884             : 
    1885          14 :         for (i = 0; i < geometriesNum; i++) {
    1886             :                 const int lvlDigitsNum = 10;    //MAX_UNIT = 4,294,967,295
    1887             : 
    1888          10 :                 multiGeometry = GEOSGetGeometryN(geosGeometry, i);
    1889          10 :                 lvl++;
    1890             : 
    1891          10 :                 newPath = GDKmalloc(pathLength + lvlDigitsNum + extraLength + 1);
    1892          10 :                 if (newPath == NULL)
    1893           0 :                         throw(MAL, "geom.DumpPoints", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1894          10 :                 snprintf(newPath, pathLength + lvlDigitsNum + extraLength + 1,
    1895             :                                  "%s%u%s", path, lvl, extraStr);
    1896             : 
    1897             :                 //*secondLevel = 0;
    1898          10 :                 err = dumpPointsGeometry(idBAT, geomBAT, multiGeometry, newPath);
    1899          10 :                 GDKfree(newPath);
    1900          10 :                 if (err != MAL_SUCCEED)
    1901           0 :                         return err;
    1902             :         }
    1903             : 
    1904             :         return MAL_SUCCEED;
    1905             : }
    1906             : 
    1907             : static str
    1908          17 : dumpPointsGeometry(BAT *idBAT, BAT *geomBAT, const GEOSGeometry *geosGeometry, const char *path)
    1909             : {
    1910          17 :         int geometryType = GEOSGeomTypeId(geosGeometry) + 1;
    1911          17 :         unsigned int lvl = 0;
    1912             : 
    1913             :         //check the type of the geometry
    1914          17 :         switch (geometryType) {
    1915           2 :         case wkbPoint_mdb:
    1916           2 :                 return dumpPointsPoint(idBAT, geomBAT, geosGeometry, &lvl, path);
    1917           2 :         case wkbLineString_mdb:
    1918             :         case wkbLinearRing_mdb:
    1919           2 :                 return dumpPointsLineString(idBAT, geomBAT, geosGeometry, path);
    1920           9 :         case wkbPolygon_mdb:
    1921           9 :                 return dumpPointsPolygon(idBAT, geomBAT, geosGeometry, &lvl, path);
    1922           4 :         case wkbMultiPoint_mdb:
    1923             :         case wkbMultiLineString_mdb:
    1924             :         case wkbMultiPolygon_mdb:
    1925             :         case wkbGeometryCollection_mdb:
    1926           4 :                 return dumpPointsMultiGeometry(idBAT, geomBAT, geosGeometry, path);
    1927           0 :         default:
    1928           0 :                 throw(MAL, "geom.DumpPoints", SQLSTATE(38000) "Geoes %s unknown geometry type", geom_type2str(geometryType, 0));
    1929             :         }
    1930             : }
    1931             : 
    1932             : str
    1933           7 : wkbDumpPoints(bat *idBAT_id, bat *geomBAT_id, wkb **geomWKB)
    1934             : {
    1935             :         BAT *idBAT = NULL, *geomBAT = NULL;
    1936             :         GEOSGeom geosGeometry;
    1937           7 :         int check = 0;
    1938             :         int pointsNum;
    1939             :         str err;
    1940             : 
    1941           7 :         if (is_wkb_nil(*geomWKB)) {
    1942             : 
    1943             :                 //create new empty BAT for the output
    1944           0 :                 if ((idBAT = COLnew(0, TYPE_str, 0, TRANSIENT)) == NULL) {
    1945           0 :                         *idBAT_id = int_nil;
    1946           0 :                         throw(MAL, "geom.DumpPoints", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1947             :                 }
    1948             : 
    1949           0 :                 if ((geomBAT = COLnew(0, ATOMindex("wkb"), 0, TRANSIENT)) == NULL) {
    1950           0 :                         BBPunfix(idBAT->batCacheid);
    1951           0 :                         *geomBAT_id = int_nil;
    1952           0 :                         throw(MAL, "geom.DumpPoints", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1953             :                 }
    1954             : 
    1955           0 :                 BBPkeepref(*idBAT_id = idBAT->batCacheid);
    1956             : 
    1957           0 :                 BBPkeepref(*geomBAT_id = geomBAT->batCacheid);
    1958             : 
    1959           0 :                 return MAL_SUCCEED;
    1960             :         }
    1961             : 
    1962           7 :         geosGeometry = wkb2geos(*geomWKB);
    1963             : 
    1964           7 :         if ((err = wkbNumPoints(&pointsNum, geomWKB, &check)) != MAL_SUCCEED) {
    1965           0 :                 GEOSGeom_destroy(geosGeometry);
    1966           0 :                 return err;
    1967             :         }
    1968             : 
    1969           7 :         if ((idBAT = COLnew(0, TYPE_str, pointsNum, TRANSIENT)) == NULL) {
    1970           0 :                 GEOSGeom_destroy(geosGeometry);
    1971           0 :                 throw(MAL, "geom.Dump", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1972             :         }
    1973             : 
    1974           7 :         if ((geomBAT = COLnew(0, ATOMindex("wkb"), pointsNum, TRANSIENT)) == NULL) {
    1975           0 :                 BBPunfix(idBAT->batCacheid);
    1976           0 :                 GEOSGeom_destroy(geosGeometry);
    1977           0 :                 throw(MAL, "geom.Dump", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1978             :         }
    1979             : 
    1980           7 :         err = dumpPointsGeometry(idBAT, geomBAT, geosGeometry, "");
    1981           7 :         GEOSGeom_destroy(geosGeometry);
    1982           7 :         if (err != MAL_SUCCEED) {
    1983           0 :                 BBPunfix(idBAT->batCacheid);
    1984           0 :                 BBPunfix(geomBAT->batCacheid);
    1985           0 :                 return err;
    1986             :         }
    1987             : 
    1988           7 :         BBPkeepref(*idBAT_id = idBAT->batCacheid);
    1989           7 :         BBPkeepref(*geomBAT_id = geomBAT->batCacheid);
    1990           7 :         return MAL_SUCCEED;
    1991             : }
    1992             : 
    1993             : str
    1994         248 : geom_2_geom(wkb **resWKB, wkb **valueWKB, int *columnType, int *columnSRID)
    1995             : {
    1996             :         GEOSGeom geosGeometry;
    1997             :         int geoCoordinatesNum = 2;
    1998             :         int valueType = 0;
    1999             : 
    2000         248 :         int valueSRID = (*valueWKB)->srid;
    2001             : 
    2002         248 :         if (is_wkb_nil(*valueWKB) || is_int_nil(*columnType) || is_int_nil(*columnSRID)) {
    2003           0 :                 *resWKB = wkbNULLcopy();
    2004           0 :                 if (*resWKB == NULL)
    2005           0 :                         throw(MAL, "calc.wkb", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2006             :                 return MAL_SUCCEED;
    2007             :         }
    2008             : 
    2009             :         /* get the geosGeometry from the wkb */
    2010         248 :         geosGeometry = wkb2geos(*valueWKB);
    2011         248 :         if (geosGeometry == NULL)
    2012           0 :                 throw(MAL, "calc.wkb", SQLSTATE(38000) "Geos operation wkb2geos failed");
    2013             : 
    2014             :         /* get the number of coordinates the geometry has */
    2015         248 :         geoCoordinatesNum = GEOSGeom_getCoordinateDimension(geosGeometry);
    2016             :         /* get the type of the geometry */
    2017         248 :         valueType = (GEOSGeomTypeId(geosGeometry) + 1) << 2;
    2018             : 
    2019         248 :         if (geoCoordinatesNum > 2)
    2020          94 :                 valueType += (1 << 1);
    2021         248 :         if (geoCoordinatesNum > 3)
    2022           0 :                 valueType += 1;
    2023             : 
    2024         248 :         if (valueSRID != *columnSRID || valueType != *columnType) {
    2025         113 :                 GEOSGeom_destroy(geosGeometry);
    2026         113 :                 throw(MAL, "calc.wkb", SQLSTATE(38000) "Geos column needs geometry(%d, %d) and value is geometry(%d, %d)\n", *columnType, *columnSRID, valueType, valueSRID);
    2027             :         }
    2028             : 
    2029             :         /* get the wkb from the geosGeometry */
    2030         135 :         *resWKB = geos2wkb(geosGeometry);
    2031         135 :         GEOSGeom_destroy(geosGeometry);
    2032             : 
    2033         135 :         if (*resWKB == NULL)
    2034           0 :                 throw(MAL, "calc.wkb", SQLSTATE(38000) "Geos operation geos2wkb failed");
    2035             : 
    2036             :         return MAL_SUCCEED;
    2037             : }
    2038             : 
    2039             : /*check if the geometry has z coordinate*/
    2040             : str
    2041           1 : geoHasZ(int *res, int *info)
    2042             : {
    2043           1 :         if (is_int_nil(*info))
    2044           0 :                 *res = int_nil;
    2045           1 :         else if (geometryHasZ(*info))
    2046           0 :                 *res = 1;
    2047             :         else
    2048           1 :                 *res = 0;
    2049           1 :         return MAL_SUCCEED;
    2050             : 
    2051             : }
    2052             : 
    2053             : /*check if the geometry has m coordinate*/
    2054             : str
    2055           1 : geoHasM(int *res, int *info)
    2056             : {
    2057           1 :         if (is_int_nil(*info))
    2058           0 :                 *res = int_nil;
    2059           1 :         else if (geometryHasM(*info))
    2060           0 :                 *res = 1;
    2061             :         else
    2062           1 :                 *res = 0;
    2063           1 :         return MAL_SUCCEED;
    2064             : }
    2065             : 
    2066             : /*check the geometry subtype*/
    2067             : /*returns the length of the resulting string*/
    2068             : str
    2069          82 : geoGetType(char **res, int *info, int *flag)
    2070             : {
    2071          82 :         if (is_int_nil(*info) || is_int_nil(*flag)) {
    2072           0 :                 if ((*res = GDKstrdup(str_nil)) == NULL)
    2073           0 :                         throw(MAL, "geom.getType", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2074             :                 return MAL_SUCCEED;
    2075             :         }
    2076          82 :         if ((*res = GDKstrdup(geom_type2str(*info >> 2, *flag))) == NULL)
    2077           0 :                 throw(MAL, "geom.getType", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2078             :         return MAL_SUCCEED;
    2079             : }
    2080             : 
    2081             : /* initialize geos */
    2082             : /* NULL: generic nil mbr. */
    2083             : /* returns a pointer to a nil-mbr. */
    2084             : static mbr mbrNIL;              /* to be filled in */
    2085             : 
    2086             : #include "gdk_geomlogger.h"
    2087             : 
    2088             : str
    2089         254 : geom_prelude(void *ret)
    2090             : {
    2091             :         (void) ret;
    2092         254 :         mbrNIL.xmin = flt_nil;
    2093         254 :         mbrNIL.xmax = flt_nil;
    2094         254 :         mbrNIL.ymin = flt_nil;
    2095         254 :         mbrNIL.ymax = flt_nil;
    2096         254 :         libgeom_init();
    2097         254 :         TYPE_mbr = malAtomSize(sizeof(mbr), "mbr");
    2098             : 
    2099         254 :         return MAL_SUCCEED;
    2100             : }
    2101             : 
    2102             : /* clean geos */
    2103             : str
    2104         253 : geom_epilogue(void *ret)
    2105             : {
    2106             :         (void) ret;
    2107         253 :         libgeom_exit();
    2108         253 :         return MAL_SUCCEED;
    2109             : }
    2110             : 
    2111             : /* Check if fixed-sized atom mbr is null */
    2112             : static bool
    2113        3218 : is_mbr_nil(const mbr *m)
    2114             : {
    2115        3218 :         return (m == NULL || is_flt_nil(m->xmin) || is_flt_nil(m->ymin) || is_flt_nil(m->xmax) || is_flt_nil(m->ymax));
    2116             : }
    2117             : 
    2118             : /* returns the size of variable-sized atom wkb */
    2119             : static var_t
    2120        8569 : wkb_size(size_t len)
    2121             : {
    2122        8569 :         if (len == ~(size_t) 0)
    2123             :                 len = 0;
    2124        8569 :         assert(offsetof(wkb, data) + len <= VAR_MAX);
    2125        8569 :         return (var_t) (offsetof(wkb, data) + len);
    2126             : }
    2127             : 
    2128             : /* returns the size of variable-sized atom wkba */
    2129             : static var_t
    2130           0 : wkba_size(int items)
    2131             : {
    2132             :         var_t size;
    2133             : 
    2134           0 :         if (items == ~0)
    2135             :                 items = 0;
    2136           0 :         size = (var_t) (offsetof(wkba, data) + items * sizeof(wkb *));
    2137           0 :         assert(size <= VAR_MAX);
    2138             : 
    2139           0 :         return size;
    2140             : }
    2141             : 
    2142             : /* Creates WKB representation (including srid) from WKT representation */
    2143             : /* return number of parsed characters. */
    2144             : static str
    2145         956 : wkbFROMSTR_withSRID(const char *geomWKT, size_t *len, wkb **geomWKB, int srid, size_t *nread)
    2146             : {
    2147             :         GEOSGeom geosGeometry = NULL;   /* The geometry object that is parsed from the src string. */
    2148             :         GEOSWKTReader *WKT_reader;
    2149             :         const char *polyhedralSurface = "POLYHEDRALSURFACE";
    2150             :         const char *multiPolygon = "MULTIPOLYGON";
    2151             :         char *geomWKT_new = NULL;
    2152             :         size_t parsedCharacters = 0;
    2153             : 
    2154         956 :         *nread = 0;
    2155             : 
    2156             :         /* we always allocate new memory */
    2157         956 :         GDKfree(*geomWKB);
    2158         956 :         *len = 0;
    2159         956 :         *geomWKB = NULL;
    2160             : 
    2161        1912 :         if (strNil(geomWKT)) {
    2162           0 :                 *geomWKB = wkbNULLcopy();
    2163           0 :                 if (*geomWKB == NULL)
    2164           0 :                         throw(MAL, "wkb.FromText", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2165           0 :                 *len = sizeof(wkb_nil);
    2166           0 :                 return MAL_SUCCEED;
    2167             :         }
    2168             :         //check whether the representation is binary (hex)
    2169         956 :         if (geomWKT[0] == '0') {
    2170         289 :                 str ret = wkbFromBinary(geomWKB, &geomWKT);
    2171             : 
    2172         289 :                 if (ret != MAL_SUCCEED)
    2173             :                         return ret;
    2174         289 :                 *nread = strlen(geomWKT);
    2175         289 :                 *len = (size_t) wkb_size((*geomWKB)->len);
    2176         289 :                 return MAL_SUCCEED;
    2177             :         }
    2178             :         //check whether the geometry type is polyhedral surface
    2179             :         //geos cannot handle this type of geometry but since it is
    2180             :         //a special type of multipolygon I just change the type before
    2181             :         //continuing. Of course this means that isValid for example does
    2182             :         //not work correctly.
    2183         667 :         if (strncasecmp(geomWKT, polyhedralSurface, strlen(polyhedralSurface)) == 0) {
    2184           2 :                 size_t sizeOfInfo = strlen(geomWKT) - strlen(polyhedralSurface) + strlen(multiPolygon) + 1;
    2185           2 :                 geomWKT_new = GDKmalloc(sizeOfInfo);
    2186           2 :                 if (geomWKT_new == NULL)
    2187           0 :                         throw(MAL, "wkb.FromText", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2188           2 :                 snprintf(geomWKT_new, sizeOfInfo, "%s%s", multiPolygon, geomWKT + strlen(polyhedralSurface));
    2189           2 :                 geomWKT = geomWKT_new;
    2190             :         }
    2191             :         ////////////////////////// UP TO HERE ///////////////////////////
    2192             : 
    2193         667 :         WKT_reader = GEOSWKTReader_create();
    2194         667 :         if (WKT_reader == NULL) {
    2195           0 :                 if (geomWKT_new)
    2196           0 :                         GDKfree(geomWKT_new);
    2197           0 :                 throw(MAL, "wkb.FromText", SQLSTATE(38000) "Geos operation GEOSWKTReader_create failed");
    2198             :         }
    2199         667 :         geosGeometry = GEOSWKTReader_read(WKT_reader, geomWKT);
    2200         667 :         GEOSWKTReader_destroy(WKT_reader);
    2201             : 
    2202         667 :         if (geosGeometry == NULL) {
    2203          57 :                 if (geomWKT_new)
    2204           0 :                         GDKfree(geomWKT_new);
    2205          57 :                 throw(MAL, "wkb.FromText", SQLSTATE(38000) "Geos operation GEOSWKTReader_read failed");
    2206             :         }
    2207             : 
    2208         610 :         if (GEOSGeomTypeId(geosGeometry) == -1) {
    2209           0 :                 if (geomWKT_new)
    2210           0 :                         GDKfree(geomWKT_new);
    2211           0 :                 GEOSGeom_destroy(geosGeometry);
    2212           0 :                 throw(MAL, "wkb.FromText", SQLSTATE(38000) "Geos operation GEOSGeomTypeId failed");
    2213             :         }
    2214             : 
    2215         610 :         GEOSSetSRID(geosGeometry, srid);
    2216             :         /* the srid was lost with the transformation of the GEOSGeom to wkb
    2217             :          * so we decided to store it in the wkb */
    2218             : 
    2219             :         /* we have a GEOSGeometry with number of coordinates and SRID and we
    2220             :          * want to get the wkb out of it */
    2221         610 :         *geomWKB = geos2wkb(geosGeometry);
    2222         610 :         GEOSGeom_destroy(geosGeometry);
    2223         610 :         if (*geomWKB == NULL) {
    2224           0 :                 if (geomWKT_new)
    2225           0 :                         GDKfree(geomWKT_new);
    2226           0 :                 throw(MAL, "wkb.FromText", SQLSTATE(38000) "Geos operation geos2wkb failed");
    2227             :         }
    2228             : 
    2229         610 :         *len = (size_t) wkb_size((*geomWKB)->len);
    2230         610 :         parsedCharacters = strlen(geomWKT);
    2231         610 :         assert(parsedCharacters <= GDK_int_max);
    2232             : 
    2233         610 :         GDKfree(geomWKT_new);
    2234             : 
    2235         610 :         *nread = parsedCharacters;
    2236         610 :         return MAL_SUCCEED;
    2237             : }
    2238             : 
    2239             : static ssize_t
    2240           0 : wkbaFROMSTR_withSRID(const char *fromStr, size_t *len, wkba **toArray, int srid)
    2241             : {
    2242             :         int items, i;
    2243             :         size_t skipBytes = 0;
    2244             : 
    2245             : //IS THERE SPACE OR SOME OTHER CHARACTER?
    2246             : 
    2247             :         //read the number of items from the beginning of the string
    2248           0 :         memcpy(&items, fromStr, sizeof(int));
    2249             :         skipBytes += sizeof(int);
    2250           0 :         *toArray = GDKmalloc(wkba_size(items));
    2251           0 :         if (*toArray == NULL)
    2252             :                 return -1;
    2253             : 
    2254           0 :         for (i = 0; i < items; i++) {
    2255             :                 size_t parsedBytes;
    2256           0 :                 str err = wkbFROMSTR_withSRID(fromStr + skipBytes, len, &(*toArray)->data[i], srid, &parsedBytes);
    2257           0 :                 if (err != MAL_SUCCEED) {
    2258           0 :                         GDKerror("%s", getExceptionMessageAndState(err));
    2259           0 :                         freeException(err);
    2260           0 :                         return -1;
    2261             :                 }
    2262           0 :                 skipBytes += parsedBytes;
    2263             :         }
    2264             : 
    2265           0 :         assert(skipBytes <= GDK_int_max);
    2266           0 :         return (ssize_t) skipBytes;
    2267             : }
    2268             : 
    2269             : /* create the WKB out of the GEOSGeometry
    2270             :  * It makes sure to make all checks before returning
    2271             :  * the input geosGeometry should not be altered by this function
    2272             :  * return NULL on error */
    2273             : static wkb *
    2274         984 : geos2wkb(const GEOSGeometry *geosGeometry)
    2275             : {
    2276         984 :         size_t wkbLen = 0;
    2277             :         unsigned char *w = NULL;
    2278             :         wkb *geomWKB;
    2279             : 
    2280             :         // if the geosGeometry is NULL create a NULL WKB
    2281         984 :         if (geosGeometry == NULL) {
    2282           0 :                 return wkbNULLcopy();
    2283             :         }
    2284             : 
    2285         984 :         GEOS_setWKBOutputDims(GEOSGeom_getCoordinateDimension(geosGeometry));
    2286         984 :         w = GEOSGeomToWKB_buf(geosGeometry, &wkbLen);
    2287             : 
    2288         984 :         if (w == NULL)
    2289             :                 return NULL;
    2290             : 
    2291         984 :         assert(wkbLen <= GDK_int_max);
    2292             : 
    2293         984 :         geomWKB = GDKmalloc(wkb_size(wkbLen));
    2294             :         //If malloc failed create a NULL wkb
    2295         984 :         if (geomWKB == NULL) {
    2296           0 :                 GEOSFree(w);
    2297           0 :                 return NULL;
    2298             :         }
    2299             : 
    2300         984 :         geomWKB->len = (int) wkbLen;
    2301         984 :         geomWKB->srid = GEOSGetSRID(geosGeometry);
    2302         984 :         memcpy(&geomWKB->data, w, wkbLen);
    2303         984 :         GEOSFree(w);
    2304             : 
    2305         984 :         return geomWKB;
    2306             : }
    2307             : 
    2308             : /* gets the mbr from the geometry */
    2309             : mbr *
    2310         645 : mbrFromGeos(const GEOSGeom geosGeometry)
    2311             : {
    2312             :         GEOSGeom envelope;
    2313             :         mbr *geomMBR;
    2314         645 :         double xmin = 0, ymin = 0, xmax = 0, ymax = 0;
    2315             : 
    2316         645 :         geomMBR = GDKmalloc(sizeof(mbr));
    2317         644 :         if (geomMBR == NULL)    //problem in reserving space
    2318             :                 return NULL;
    2319             : 
    2320             :         /* if input is null or GEOSEnvelope created exception then create a nill mbr */
    2321         644 :         if (!geosGeometry || (envelope = GEOSEnvelope(geosGeometry)) == NULL) {
    2322           0 :                 *geomMBR = mbrNIL;
    2323           0 :                 return geomMBR;
    2324             :         }
    2325             : 
    2326         646 :         if ((GEOSGeomTypeId(envelope) + 1) == wkbPoint_mdb) {
    2327             : #if GEOS_CAPI_VERSION_MAJOR >= 1 && GEOS_CAPI_VERSION_MINOR >= 3
    2328         429 :                 const GEOSCoordSequence *coords = GEOSGeom_getCoordSeq(envelope);
    2329             : #else
    2330             :                 const GEOSCoordSeq coords = GEOSGeom_getCoordSeq(envelope);
    2331             : #endif
    2332         429 :                 GEOSCoordSeq_getX(coords, 0, &xmin);
    2333         428 :                 GEOSCoordSeq_getY(coords, 0, &ymin);
    2334         429 :                 assert(GDK_flt_min <= xmin && xmin <= GDK_flt_max);
    2335         429 :                 assert(GDK_flt_min <= ymin && ymin <= GDK_flt_max);
    2336         429 :                 geomMBR->xmin = (float) xmin;
    2337         429 :                 geomMBR->ymin = (float) ymin;
    2338         429 :                 geomMBR->xmax = (float) xmin;
    2339         429 :                 geomMBR->ymax = (float) ymin;
    2340             :         } else {                // GEOSGeomTypeId(envelope) == GEOS_POLYGON
    2341             : #if GEOS_CAPI_VERSION_MAJOR >= 1 && GEOS_CAPI_VERSION_MINOR >= 3
    2342         217 :                 const GEOSGeometry *ring = GEOSGetExteriorRing(envelope);
    2343             : #else
    2344             :                 const GEOSGeom ring = GEOSGetExteriorRing(envelope);
    2345             : #endif
    2346         217 :                 if (ring) {
    2347             : #if GEOS_CAPI_VERSION_MAJOR >= 1 && GEOS_CAPI_VERSION_MINOR >= 3
    2348         217 :                         const GEOSCoordSequence *coords = GEOSGeom_getCoordSeq(ring);
    2349             : #else
    2350             :                         const GEOSCoordSeq coords = GEOSGeom_getCoordSeq(ring);
    2351             : #endif
    2352         217 :                         GEOSCoordSeq_getX(coords, 0, &xmin);        //left-lower corner
    2353         217 :                         GEOSCoordSeq_getY(coords, 0, &ymin);
    2354         217 :                         GEOSCoordSeq_getX(coords, 2, &xmax);        //right-upper corner
    2355         217 :                         GEOSCoordSeq_getY(coords, 2, &ymax);
    2356         217 :                         assert(GDK_flt_min <= xmin && xmin <= GDK_flt_max);
    2357         217 :                         assert(GDK_flt_min <= ymin && ymin <= GDK_flt_max);
    2358         217 :                         assert(GDK_flt_min <= xmax && xmax <= GDK_flt_max);
    2359         217 :                         assert(GDK_flt_min <= ymax && ymax <= GDK_flt_max);
    2360         217 :                         geomMBR->xmin = (float) xmin;
    2361         217 :                         geomMBR->ymin = (float) ymin;
    2362         217 :                         geomMBR->xmax = (float) xmax;
    2363         217 :                         geomMBR->ymax = (float) ymax;
    2364             :                 }
    2365             :         }
    2366         646 :         GEOSGeom_destroy(envelope);
    2367         646 :         return geomMBR;
    2368             : }
    2369             : 
    2370             : //Returns the wkb in a hex representation */
    2371             : static char hexit[] = "0123456789ABCDEF";
    2372             : 
    2373             : str
    2374           0 : wkbAsBinary(char **toStr, wkb **geomWKB)
    2375             : {
    2376             :         char *s;
    2377             :         int i;
    2378             : 
    2379           0 :         if (is_wkb_nil(*geomWKB)) {
    2380           0 :                 if ((*toStr = GDKstrdup(str_nil)) == NULL)
    2381           0 :                         throw(MAL, "geom.AsBinary", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2382             :                 return MAL_SUCCEED;
    2383             :         }
    2384           0 :         if ((*toStr = GDKmalloc(1 + (*geomWKB)->len * 2)) == NULL)
    2385           0 :                 throw(MAL, "geom.AsBinary", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2386             : 
    2387             :         s = *toStr;
    2388           0 :         for (i = 0; i < (*geomWKB)->len; i++) {
    2389           0 :                 int val = ((*geomWKB)->data[i] >> 4) & 0xf;
    2390           0 :                 *s++ = hexit[val];
    2391           0 :                 val = (*geomWKB)->data[i] & 0xf;
    2392           0 :                 *s++ = hexit[val];
    2393           0 :                 TRC_DEBUG(GEOM, "%d: First: %c - Second: %c ==> Original %c (%d)\n", i, *(s-2), *(s-1), (*geomWKB)->data[i], (int)((*geomWKB)->data[i]));
    2394             :         }
    2395           0 :         *s = '\0';
    2396           0 :         return MAL_SUCCEED;
    2397             : }
    2398             : 
    2399             : static int
    2400             : decit(char hex)
    2401             : {
    2402       23258 :         switch (hex) {
    2403             :         case '0':
    2404             :                 return 0;
    2405             :         case '1':
    2406             :                 return 1;
    2407             :         case '2':
    2408             :                 return 2;
    2409             :         case '3':
    2410             :                 return 3;
    2411             :         case '4':
    2412             :                 return 4;
    2413             :         case '5':
    2414             :                 return 5;
    2415             :         case '6':
    2416             :                 return 6;
    2417             :         case '7':
    2418             :                 return 7;
    2419             :         case '8':
    2420             :                 return 8;
    2421             :         case '9':
    2422             :                 return 9;
    2423             :         case 'A':
    2424             :         case 'a':
    2425             :                 return 10;
    2426             :         case 'B':
    2427             :         case 'b':
    2428             :                 return 11;
    2429             :         case 'C':
    2430             :         case 'c':
    2431             :                 return 12;
    2432             :         case 'D':
    2433             :         case 'd':
    2434             :                 return 13;
    2435             :         case 'E':
    2436             :         case 'e':
    2437             :                 return 14;
    2438             :         case 'F':
    2439             :         case 'f':
    2440             :                 return 15;
    2441             :         default:
    2442             :                 return -1;
    2443             :         }
    2444             : }
    2445             : 
    2446             : str
    2447         289 : wkbFromBinary(wkb **geomWKB, const char **inStr)
    2448             : {
    2449             :         size_t strLength, wkbLength, i;
    2450             :         wkb *w;
    2451             : 
    2452         578 :         if (strNil(*inStr)) {
    2453           0 :                 if ((*geomWKB = wkbNULLcopy()) == NULL)
    2454           0 :                         throw(MAL, "geom.FromBinary", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2455             :                 return MAL_SUCCEED;
    2456             :         }
    2457             : 
    2458         289 :         strLength = strlen(*inStr);
    2459         289 :         if (strLength & 1)
    2460           0 :                 throw(MAL, "geom.FromBinary", SQLSTATE(38000) "Geos odd length input string");
    2461             : 
    2462         289 :         wkbLength = strLength / 2;
    2463         289 :         assert(wkbLength <= GDK_int_max);
    2464             : 
    2465         289 :         w = GDKmalloc(wkb_size(wkbLength));
    2466         289 :         if (w == NULL)
    2467           0 :                 throw(MAL, "geom.FromBinary", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2468             : 
    2469             :         //compute the value for s
    2470       11918 :         for (i = 0; i < strLength; i += 2) {
    2471       11629 :                 int firstHalf = decit((*inStr)[i]);
    2472       11629 :                 int secondHalf = decit((*inStr)[i + 1]);
    2473       11629 :                 if (firstHalf == -1 || secondHalf == -1) {
    2474           0 :                         GDKfree(w);
    2475           0 :                         throw(MAL, "geom.FromBinary", SQLSTATE(38000) "Geos incorrectly formatted input string");
    2476             :                 }
    2477       11629 :                 w->data[i / 2] = (firstHalf << 4) | secondHalf;
    2478             :         }
    2479             : 
    2480         289 :         w->len = (int) wkbLength;
    2481         289 :         w->srid = 0;
    2482         289 :         *geomWKB = w;
    2483             : 
    2484         289 :         return MAL_SUCCEED;
    2485             : }
    2486             : 
    2487             : str
    2488           0 : mbrFromMBR(mbr **w, mbr **src)
    2489             : {
    2490           0 :         *w = GDKmalloc(sizeof(mbr));
    2491           0 :         if (*w == NULL)
    2492           0 :                 throw(MAL, "calc.mbr", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2493             : 
    2494           0 :         **w = **src;
    2495           0 :         return MAL_SUCCEED;
    2496             : }
    2497             : 
    2498             : static ssize_t wkbTOSTR(char **geomWKT, size_t *len, const void *GEOMWKB, bool external);
    2499             : 
    2500             : str
    2501           0 : wkbFromWKB(wkb **w, wkb **src)
    2502             : {
    2503           0 :         *w = GDKmalloc(wkb_size((*src)->len));
    2504           0 :         if (*w == NULL)
    2505           0 :                 throw(MAL, "calc.wkb", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2506             : 
    2507           0 :         if (is_wkb_nil(*src)) {
    2508           0 :                 **w = wkb_nil;
    2509             :         } else {
    2510           0 :                 (*w)->len = (*src)->len;
    2511           0 :                 (*w)->srid = (*src)->srid;
    2512           0 :                 memcpy((*w)->data, (*src)->data, (*src)->len);
    2513             :         }
    2514             :         return MAL_SUCCEED;
    2515             : }
    2516             : 
    2517             : /* creates a wkb from the given textual representation */
    2518             : /* int* tpe is needed to verify that the type of the FromText function used is the
    2519             :  * same with the type of the geometry created from the wkt representation */
    2520             : str
    2521         954 : wkbFromText(wkb **geomWKB, str *geomWKT, int *srid, int *tpe)
    2522             : {
    2523         954 :         size_t len = 0;
    2524             :         int te = 0;
    2525             :         str err;
    2526             :         size_t parsedBytes;
    2527             : 
    2528         954 :         *geomWKB = NULL;
    2529        1908 :         if (strNil(*geomWKT) || is_int_nil(*srid) || is_int_nil(*tpe)) {
    2530           0 :                 if ((*geomWKB = wkbNULLcopy()) == NULL)
    2531           0 :                         throw(MAL, "wkb.FromText", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2532             :                 return MAL_SUCCEED;
    2533             :         }
    2534         954 :         err = wkbFROMSTR_withSRID(*geomWKT, &len, geomWKB, *srid, &parsedBytes);
    2535         954 :         if (err != MAL_SUCCEED)
    2536             :                 return err;
    2537             : 
    2538         898 :         if (is_wkb_nil(*geomWKB) || *tpe == 0 ||
    2539         243 :             *tpe == wkbGeometryCollection_mdb ||
    2540         243 :             ((te = *((*geomWKB)->data + 1) & 0x0f) + (*tpe > 2)) == *tpe) {
    2541             :                 return MAL_SUCCEED;
    2542             :         }
    2543             : 
    2544          49 :         GDKfree(*geomWKB);
    2545          49 :         *geomWKB = NULL;
    2546             : 
    2547          49 :         te += (te > 2);
    2548          49 :         if (*tpe > 0 && te != *tpe)
    2549          49 :                 throw(SQL, "wkb.FromText", SQLSTATE(38000) "Geometry not type '%d: %s' but '%d: %s' instead", *tpe, geom_type2str(*tpe, 0), te, geom_type2str(te, 0));
    2550           0 :         throw(MAL, "wkb.FromText", SQLSTATE(38000) "%s", "cannot parse string");
    2551             : }
    2552             : 
    2553             : /* create textual representation of the wkb */
    2554             : str
    2555          95 : wkbAsText(char **txt, wkb **geomWKB, int *withSRID)
    2556             : {
    2557          95 :         size_t len = 0;
    2558          95 :         char *wkt = NULL;
    2559             :         const char *sridTxt = "SRID:";
    2560             : 
    2561          95 :         if (is_wkb_nil(*geomWKB) || (withSRID && is_int_nil(*withSRID))) {
    2562           0 :                 if ((*txt = GDKstrdup(str_nil)) == NULL)
    2563           0 :                         throw(MAL, "geom.AsText", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2564             :                 return MAL_SUCCEED;
    2565             :         }
    2566             : 
    2567          95 :         if ((*geomWKB)->srid < 0)
    2568           0 :                 throw(MAL, "geom.AsText", SQLSTATE(38000) "Geod negative SRID");
    2569             : 
    2570          95 :         if (wkbTOSTR(&wkt, &len, *geomWKB, false) < 0)
    2571           0 :                 throw(MAL, "geom.AsText", SQLSTATE(38000) "Geos failed to create Text from Well Known Format");
    2572             : 
    2573          95 :         if (withSRID == NULL || *withSRID == 0) {       //accepting NULL withSRID to make internal use of it easier
    2574          60 :                 *txt = wkt;
    2575          60 :                 return MAL_SUCCEED;
    2576             :         }
    2577             : 
    2578             :         /* 10 for maximum number of digits to represent an INT */
    2579          35 :         len = strlen(wkt) + 10 + strlen(sridTxt) + 2;
    2580          35 :         *txt = GDKmalloc(len);
    2581          35 :         if (*txt == NULL) {
    2582           0 :                 GDKfree(wkt);
    2583           0 :                 throw(MAL, "geom.AsText", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2584             :         }
    2585             : 
    2586          35 :         snprintf(*txt, len, "%s%d;%s", sridTxt, (*geomWKB)->srid, wkt);
    2587             : 
    2588          35 :         GDKfree(wkt);
    2589          35 :         return MAL_SUCCEED;
    2590             : }
    2591             : 
    2592             : str
    2593           0 : wkbMLineStringToPolygon(wkb **geomWKB, str *geomWKT, int *srid, int *flag)
    2594             : {
    2595           0 :         int itemsNum = 0, i, type = wkbMultiLineString_mdb;
    2596             :         str ret = MAL_SUCCEED;
    2597           0 :         wkb *inputWKB = NULL;
    2598             : 
    2599             :         wkb **linestringsWKB;
    2600             :         double *linestringsArea;
    2601             :         bit ordered = 0;
    2602             : 
    2603           0 :         if (strNil(*geomWKT) || is_int_nil(*srid) || is_int_nil(*flag)) {
    2604           0 :                 if ((*geomWKB = wkbNULLcopy()) == NULL)
    2605           0 :                         throw(MAL, "geom.MLineStringToPolygon", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2606             :                 return MAL_SUCCEED;
    2607             :         }
    2608             : 
    2609           0 :         *geomWKB = NULL;
    2610             : 
    2611             :         //make wkb from wkt
    2612           0 :         ret = wkbFromText(&inputWKB, geomWKT, srid, &type);
    2613           0 :         if (ret != MAL_SUCCEED)
    2614             :                 return ret;
    2615             : 
    2616             :         //read the number of linestrings in the input
    2617           0 :         ret = wkbNumGeometries(&itemsNum, &inputWKB);
    2618           0 :         if (ret != MAL_SUCCEED) {
    2619           0 :                 GDKfree(inputWKB);
    2620           0 :                 return ret;
    2621             :         }
    2622             : 
    2623           0 :         linestringsWKB = GDKmalloc(itemsNum * sizeof(wkb *));
    2624           0 :         linestringsArea = GDKmalloc(itemsNum * sizeof(double));
    2625           0 :         if (linestringsWKB == NULL || linestringsArea == NULL) {
    2626           0 :                 itemsNum = 0;
    2627           0 :                 ret = createException(MAL, "geom.MLineStringToPolygon", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2628           0 :                 goto bailout;
    2629             :         }
    2630             : 
    2631             :         //create one polygon for each lineString and compute the area of each of them
    2632           0 :         for (i = 1; i <= itemsNum; i++) {
    2633             :                 wkb *polygonWKB;
    2634             : 
    2635           0 :                 ret = wkbGeometryN(&linestringsWKB[i - 1], &inputWKB, &i);
    2636           0 :                 if (ret != MAL_SUCCEED || linestringsWKB[i - 1] == NULL) {
    2637           0 :                         itemsNum = i - 1;
    2638           0 :                         goto bailout;
    2639             :                 }
    2640             : 
    2641           0 :                 ret = wkbMakePolygon(&polygonWKB, &linestringsWKB[i - 1], NULL, srid);
    2642           0 :                 if (ret != MAL_SUCCEED) {
    2643           0 :                         itemsNum = i;
    2644           0 :                         goto bailout;
    2645             :                 }
    2646             : 
    2647           0 :                 ret = wkbArea(&linestringsArea[i - 1], &polygonWKB);
    2648           0 :                 GDKfree(polygonWKB);
    2649           0 :                 if (ret != MAL_SUCCEED) {
    2650           0 :                         itemsNum = i;
    2651           0 :                         goto bailout;
    2652             :                 }
    2653             :         }
    2654             : 
    2655           0 :         GDKfree(inputWKB);
    2656           0 :         inputWKB = NULL;
    2657             : 
    2658             :         //order the linestrings with decreasing (polygons) area
    2659           0 :         while (!ordered) {
    2660             :                 ordered = 1;
    2661             : 
    2662           0 :                 for (i = 0; i < itemsNum - 1; i++) {
    2663           0 :                         if (linestringsArea[i + 1] > linestringsArea[i]) {
    2664             :                                 //switch
    2665           0 :                                 wkb *linestringWKB = linestringsWKB[i];
    2666             :                                 double linestringArea = linestringsArea[i];
    2667             : 
    2668           0 :                                 linestringsWKB[i] = linestringsWKB[i + 1];
    2669           0 :                                 linestringsArea[i] = linestringsArea[i + 1];
    2670             : 
    2671           0 :                                 linestringsWKB[i + 1] = linestringWKB;
    2672           0 :                                 linestringsArea[i + 1] = linestringArea;
    2673             : 
    2674             :                                 ordered = 0;
    2675             :                         }
    2676             :                 }
    2677             :         }
    2678             : 
    2679           0 :         if (*flag == 0) {
    2680             :                 //the biggest polygon is the external shell
    2681             :                 GEOSCoordSeq coordSeq_external;
    2682             :                 GEOSGeom externalGeometry, linearRingExternalGeometry, *internalGeometries, finalGeometry;
    2683             : 
    2684           0 :                 externalGeometry = wkb2geos(linestringsWKB[0]);
    2685           0 :                 if (externalGeometry == NULL) {
    2686           0 :                         ret = createException(MAL, "geom.MLineStringToPolygon", SQLSTATE(38000) "Geos operation wkb2geos failed");
    2687           0 :                         goto bailout;
    2688             :                 }
    2689             : 
    2690           0 :                 coordSeq_external = GEOSCoordSeq_clone(GEOSGeom_getCoordSeq(externalGeometry));
    2691           0 :                 GEOSGeom_destroy(externalGeometry);
    2692           0 :                 if (coordSeq_external == NULL) {
    2693           0 :                         ret = createException(MAL, "geom.MLineStringToPolygon", SQLSTATE(38000) "Geos operation GEOSCoordSeq_clone failed");
    2694           0 :                         goto bailout;
    2695             :                 }
    2696           0 :                 linearRingExternalGeometry = GEOSGeom_createLinearRing(coordSeq_external);
    2697           0 :                 if (linearRingExternalGeometry == NULL) {
    2698           0 :                         GEOSCoordSeq_destroy(coordSeq_external);
    2699           0 :                         ret = createException(MAL, "geom.MLineStringToPolygon", SQLSTATE(38000) "Geos operation GEOSGeom_createLinearRing failed");
    2700           0 :                         goto bailout;
    2701             :                 }
    2702             : 
    2703             :                 //all remaining should be internal
    2704           0 :                 internalGeometries = GDKmalloc((itemsNum - 1) * sizeof(GEOSGeom));
    2705           0 :                 if (internalGeometries == NULL) {
    2706           0 :                         GEOSGeom_destroy(linearRingExternalGeometry);
    2707           0 :                         ret = createException(MAL, "geom.MLineStringToPolygon", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2708           0 :                         goto bailout;
    2709             :                 }
    2710           0 :                 for (i = 1; i < itemsNum; i++) {
    2711             :                         GEOSCoordSeq coordSeq_internal;
    2712             :                         GEOSGeom internalGeometry;
    2713             : 
    2714           0 :                         internalGeometry = wkb2geos(linestringsWKB[i]);
    2715           0 :                         if (internalGeometry == NULL) {
    2716           0 :                                 GEOSGeom_destroy(linearRingExternalGeometry);
    2717           0 :                                 while (--i >= 1)
    2718           0 :                                         GEOSGeom_destroy(internalGeometries[i - 1]);
    2719           0 :                                 GDKfree(internalGeometries);
    2720           0 :                                 ret = createException(MAL, "geom.MLineStringToPolygon", SQLSTATE(38000) "Geos operation wkb2geos failed");
    2721           0 :                                 goto bailout;
    2722             :                         }
    2723             : 
    2724           0 :                         coordSeq_internal = GEOSCoordSeq_clone(GEOSGeom_getCoordSeq(internalGeometry));
    2725           0 :                         GEOSGeom_destroy(internalGeometry);
    2726           0 :                         if (coordSeq_internal == NULL) {
    2727           0 :                                 GEOSGeom_destroy(linearRingExternalGeometry);
    2728           0 :                                 while (--i >= 1)
    2729           0 :                                         GEOSGeom_destroy(internalGeometries[i - 1]);
    2730           0 :                                 GDKfree(internalGeometries);
    2731           0 :                                 ret = createException(MAL, "geom.MLineStringToPolygon", SQLSTATE(38000) "Geos operation wkb2geos failed");
    2732           0 :                                 goto bailout;
    2733             :                         }
    2734           0 :                         internalGeometries[i - 1] = GEOSGeom_createLinearRing(coordSeq_internal);
    2735           0 :                         if (internalGeometries[i - 1] == NULL) {
    2736           0 :                                 GEOSGeom_destroy(linearRingExternalGeometry);
    2737           0 :                                 GEOSCoordSeq_destroy(coordSeq_internal);
    2738           0 :                                 while (--i >= 1)
    2739           0 :                                         GEOSGeom_destroy(internalGeometries[i - 1]);
    2740           0 :                                 GDKfree(internalGeometries);
    2741           0 :                                 ret = createException(MAL, "geom.MLineStringToPolygon", SQLSTATE(38000) "Geos operation GEOSGeom_createLinearRing failed");
    2742           0 :                                 goto bailout;
    2743             :                         }
    2744             :                 }
    2745             : 
    2746           0 :                 finalGeometry = GEOSGeom_createPolygon(linearRingExternalGeometry, internalGeometries, itemsNum - 1);
    2747           0 :                 GEOSGeom_destroy(linearRingExternalGeometry);
    2748           0 :                 if (finalGeometry == NULL) {
    2749           0 :                         for (i = 0; i < itemsNum - 1; i++)
    2750           0 :                                 GEOSGeom_destroy(internalGeometries[i]);
    2751           0 :                         GDKfree(internalGeometries);
    2752           0 :                         ret = createException(MAL, "geom.MLineStringToPolygon", SQLSTATE(38000) "Geos error creating Polygon from LinearRing");
    2753           0 :                         goto bailout;
    2754             :                 }
    2755           0 :                 GDKfree(internalGeometries);
    2756             :                 //check of the created polygon is valid
    2757           0 :                 if (GEOSisValid(finalGeometry) != 1) {
    2758             :                         //suppress the GEOS message
    2759           0 :                         GDKclrerr();
    2760             : 
    2761           0 :                         GEOSGeom_destroy(finalGeometry);
    2762             : 
    2763           0 :                         ret = createException(MAL, "geom.MLineStringToPolygon", SQLSTATE(38000) "Geos the provided MultiLineString does not create a valid Polygon");
    2764           0 :                         goto bailout;
    2765             :                 }
    2766             : 
    2767           0 :                 GEOSSetSRID(finalGeometry, *srid);
    2768           0 :                 *geomWKB = geos2wkb(finalGeometry);
    2769           0 :                 GEOSGeom_destroy(finalGeometry);
    2770           0 :                 if (*geomWKB == NULL)
    2771           0 :                         ret = createException(MAL, "geom.MLineStringToPolygon", SQLSTATE(38000) "Geos operation geos2wkb failed");
    2772           0 :         } else if (*flag == 1) {
    2773           0 :                 ret = createException(MAL, "geom.MLineStringToPolygon", SQLSTATE(38000) "Geos multipolygon from string has not been defined");
    2774             :         } else {
    2775           0 :                 ret = createException(MAL, "geom.MLineStringToPolygon", SQLSTATE(38000) "Geos unknown flag");
    2776             :         }
    2777             : 
    2778           0 :   bailout:
    2779           0 :         GDKfree(inputWKB);
    2780           0 :         for (i = 0; i < itemsNum; i++)
    2781           0 :                 GDKfree(linestringsWKB[i]);
    2782           0 :         GDKfree(linestringsWKB);
    2783           0 :         GDKfree(linestringsArea);
    2784           0 :         return ret;
    2785             : }
    2786             : 
    2787             : str
    2788          47 : wkbMakePoint(wkb **out, dbl *x, dbl *y, dbl *z, dbl *m, int *zmFlag)
    2789             : {
    2790             :         GEOSGeom geosGeometry;
    2791             :         GEOSCoordSeq seq;
    2792             : 
    2793          47 :         if (is_dbl_nil(*x) || is_dbl_nil(*y) || is_dbl_nil(*z) || is_dbl_nil(*m) || is_int_nil(*zmFlag)) {
    2794           0 :                 if ((*out = wkbNULLcopy()) == NULL)
    2795           0 :                         throw(MAL, "geom.MakePoint", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2796             :                 return MAL_SUCCEED;
    2797             :         }
    2798             : 
    2799             :         //create the point from the coordinates
    2800          47 :         switch (*zmFlag) {
    2801          35 :         case 0:                 /* x, y */
    2802          35 :                 seq = GEOSCoordSeq_create(1, 2);
    2803          35 :                 break;
    2804          12 :         case 1:                 /* x, y, m */
    2805             :         case 10:                /* x, y, z */
    2806          12 :                 seq = GEOSCoordSeq_create(1, 3);
    2807          12 :                 break;
    2808           0 :         case 11:                /* x, y, z, m */
    2809           0 :                 throw(MAL, "geom.MakePoint", SQLSTATE(38000) "Geos POINTZM is not supported");
    2810           0 :         default:
    2811           0 :                 throw(MAL, "geom.MakePoint", SQLSTATE(38000) "Geos "ILLEGAL_ARGUMENT);
    2812             :         }
    2813             : 
    2814          47 :         if (seq == NULL)
    2815           0 :                 throw(MAL, "geom.MakePoint", SQLSTATE(38000) "Geos operation GEOSCoordSeq_create failed");
    2816             : 
    2817          94 :         if (!GEOSCoordSeq_setOrdinate(seq, 0, 0, *x) ||
    2818          47 :             !GEOSCoordSeq_setOrdinate(seq, 0, 1, *y) ||
    2819          47 :             (*zmFlag == 1 && !GEOSCoordSeq_setOrdinate(seq, 0, 2, *m)) ||
    2820          47 :             (*zmFlag == 10 && !GEOSCoordSeq_setOrdinate(seq, 0, 2, *z))) {
    2821           0 :                 GEOSCoordSeq_destroy(seq);
    2822           0 :                 throw(MAL, "geom.MakePoint", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setOrdinate failed");
    2823             :         }
    2824             : 
    2825          47 :         if ((geosGeometry = GEOSGeom_createPoint(seq)) == NULL) {
    2826           0 :                 GEOSCoordSeq_destroy(seq);
    2827           0 :                 throw(MAL, "geom.MakePoint", SQLSTATE(38000) "Geos opertion GEOSGeometry failed");
    2828             :         }
    2829             : 
    2830          47 :         *out = geos2wkb(geosGeometry);
    2831          47 :         GEOSGeom_destroy(geosGeometry);
    2832             : 
    2833          47 :         if (is_wkb_nil(*out)) {
    2834           0 :                 GDKfree(*out);
    2835           0 :                 *out = NULL;
    2836           0 :                 throw(MAL, "geom.MakePoint", SQLSTATE(38000) "Geos to create WKB from GEOSGeometry failed");
    2837             :         }
    2838             : 
    2839             :         return MAL_SUCCEED;
    2840             : }
    2841             : 
    2842             : /* common code for functions that return integer */
    2843             : static str
    2844         161 : wkbBasicInt(int *out, wkb *geom, int (*func) (const GEOSGeometry *), const char *name)
    2845             : {
    2846             :         GEOSGeom geosGeometry;
    2847             :         str ret = MAL_SUCCEED;
    2848             : 
    2849         161 :         if (is_wkb_nil(geom)) {
    2850           0 :                 *out = int_nil;
    2851           0 :                 return MAL_SUCCEED;
    2852             :         }
    2853             : 
    2854         161 :         if ((geosGeometry = wkb2geos(geom)) == NULL)
    2855           0 :                 throw(MAL, name, SQLSTATE(38000) "Geos operation wkb2geos failed");
    2856             : 
    2857         161 :         *out = (*func) (geosGeometry);
    2858             : 
    2859         161 :         GEOSGeom_destroy(geosGeometry);
    2860             : 
    2861             :         //if there was an error returned by geos
    2862         161 :         if (GDKerrbuf && GDKerrbuf[0]) {
    2863             :                 //create an exception with this name
    2864           0 :                 ret = createException(MAL, name, SQLSTATE(38000) "Geos operation %s", GDKerrbuf);
    2865             : 
    2866             :                 //clear the error buffer
    2867           0 :                 GDKclrerr();
    2868             :         }
    2869             : 
    2870             :         return ret;
    2871             : }
    2872             : 
    2873             : /* returns the type of the geometry as a string*/
    2874             : str
    2875          82 : wkbGeometryType(char **out, wkb **geomWKB, int *flag)
    2876             : {
    2877          82 :         int typeId = 0;
    2878             :         str ret = MAL_SUCCEED;
    2879             : 
    2880          82 :         ret = wkbBasicInt(&typeId, *geomWKB, GEOSGeomTypeId, "geom.GeometryType");
    2881          82 :         if (ret != MAL_SUCCEED)
    2882             :                 return ret;
    2883          82 :         if (!is_int_nil(typeId))        /* geoGetType deals with nil */
    2884          82 :                 typeId = (typeId + 1) << 2;
    2885          82 :         return geoGetType(out, &typeId, flag);
    2886             : }
    2887             : 
    2888             : /* returns the number of dimensions of the geometry */
    2889             : str
    2890          29 : wkbCoordDim(int *out, wkb **geom)
    2891             : {
    2892          29 :         return wkbBasicInt(out, *geom, GEOSGeom_getCoordinateDimension, "geom.CoordDim");
    2893             : }
    2894             : 
    2895             : /* returns the inherent dimension of the geometry, e.g 0 for point */
    2896             : str
    2897          32 : wkbDimension(int *dimension, wkb **geomWKB)
    2898             : {
    2899          32 :         return wkbBasicInt(dimension, *geomWKB, GEOSGeom_getDimensions, "geom.Dimension");
    2900             : }
    2901             : 
    2902             : /* returns the srid of the geometry */
    2903             : str
    2904           1 : wkbGetSRID(int *out, wkb **geomWKB)
    2905             : {
    2906           1 :         return wkbBasicInt(out, *geomWKB, GEOSGetSRID, "geom.GetSRID");
    2907             : }
    2908             : 
    2909             : /* sets the srid of the geometry */
    2910             : str
    2911           0 : wkbSetSRID(wkb **resultGeomWKB, wkb **geomWKB, int *srid)
    2912             : {
    2913             :         GEOSGeom geosGeometry;
    2914             : 
    2915           0 :         if (is_wkb_nil(*geomWKB) || is_int_nil(*srid)) {
    2916           0 :                 if ((*resultGeomWKB = wkbNULLcopy()) == NULL)
    2917           0 :                         throw(MAL, "geom.setSRID", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2918             :                 return MAL_SUCCEED;
    2919             :         }
    2920           0 :         if ((geosGeometry = wkb2geos(*geomWKB)) == NULL)
    2921           0 :                 throw(MAL, "geom.setSRID", SQLSTATE(38000) "Geos operation wkb2geos failed");
    2922             : 
    2923           0 :         GEOSSetSRID(geosGeometry, *srid);
    2924           0 :         *resultGeomWKB = geos2wkb(geosGeometry);
    2925           0 :         GEOSGeom_destroy(geosGeometry);
    2926             : 
    2927           0 :         if (*resultGeomWKB == NULL)
    2928           0 :                 throw(MAL, "geom.setSRID", SQLSTATE(38000) "Geos operation geos2wkb failed");
    2929             : 
    2930             :         return MAL_SUCCEED;
    2931             : }
    2932             : 
    2933             : /* depending on the specific function it returns the X,Y or Z coordinate of a point */
    2934             : str
    2935          22 : wkbGetCoordinate(dbl *out, wkb **geom, int *dimNum)
    2936             : {
    2937             :         GEOSGeom geosGeometry;
    2938             :         const GEOSCoordSequence *gcs;
    2939             :         str err = MAL_SUCCEED;
    2940             : 
    2941          22 :         if (is_wkb_nil(*geom) || is_int_nil(*dimNum)) {
    2942           0 :                 *out = dbl_nil;
    2943           0 :                 return MAL_SUCCEED;
    2944             :         }
    2945             : 
    2946          22 :         geosGeometry = wkb2geos(*geom);
    2947          22 :         if (geosGeometry == NULL) {
    2948           0 :                 *out = dbl_nil;
    2949           0 :                 throw(MAL, "geom.GetCoordinate", SQLSTATE(38000) "Geos operation wkb2geos failed");
    2950             :         }
    2951             : 
    2952          22 :         if ((GEOSGeomTypeId(geosGeometry) + 1) != wkbPoint_mdb) {
    2953             :                 char *geomSTR;
    2954             : 
    2955           4 :                 GEOSGeom_destroy(geosGeometry);
    2956           4 :                 if ((err = wkbAsText(&geomSTR, geom, NULL)) != MAL_SUCCEED)
    2957             :                         return err;
    2958           4 :                 err = createException(MAL, "geom.GetCoordinate", SQLSTATE(38000) "Geometry \"%s\" not a Point", geomSTR);
    2959           4 :                 GDKfree(geomSTR);
    2960           4 :                 return err;
    2961             :         }
    2962             : 
    2963          18 :         gcs = GEOSGeom_getCoordSeq(geosGeometry);
    2964             :         /* gcs shouldn't be freed, it's internal to the GEOSGeom */
    2965             : 
    2966          18 :         if (gcs == NULL) {
    2967           0 :                 err = createException(MAL, "geom.GetCoordinate", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
    2968          18 :         } else if (!GEOSCoordSeq_getOrdinate(gcs, 0, *dimNum, out))
    2969           0 :                 err = createException(MAL, "geom.GetCoordinate", SQLSTATE(38000) "Geos operation GEOSCoordSeq_getOrdinate failed");
    2970          18 :         else if (isnan(*out))
    2971           3 :                 *out = dbl_nil;
    2972          18 :         GEOSGeom_destroy(geosGeometry);
    2973             : 
    2974          18 :         return err;
    2975             : }
    2976             : 
    2977             : /*common code for functions that return geometry */
    2978             : static str
    2979          33 : wkbBasic(wkb **out, wkb **geom, GEOSGeometry *(*func) (const GEOSGeometry *), const char *name)
    2980             : {
    2981             :         GEOSGeom geosGeometry, outGeometry;
    2982             :         str err = MAL_SUCCEED;
    2983             : 
    2984          33 :         if (is_wkb_nil(*geom)) {
    2985           0 :                 if ((*out = wkbNULLcopy()) == NULL)
    2986           0 :                         throw(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    2987             :                 return MAL_SUCCEED;
    2988             :         }
    2989          33 :         if ((geosGeometry = wkb2geos(*geom)) == NULL) {
    2990           0 :                 *out = NULL;
    2991           0 :                 throw(MAL, name, SQLSTATE(38000) "Geos operation wkb2geos failed");
    2992             :         }
    2993             : 
    2994          33 :         if ((outGeometry = (*func) (geosGeometry)) == NULL) {
    2995           0 :                 err = createException(MAL, name, SQLSTATE(38000) "Geos operation GEOS%s failed", name + 5);
    2996             :         } else {
    2997             :                 //set the srid equal to the srid of the initial geometry
    2998          33 :                 if ((*geom)->srid)   //GEOSSetSRID has assertion for srid != 0
    2999           2 :                         GEOSSetSRID(outGeometry, (*geom)->srid);
    3000             : 
    3001          33 :                 if ((*out = geos2wkb(outGeometry)) == NULL)
    3002           0 :                         err = createException(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3003             : 
    3004          33 :                 GEOSGeom_destroy(outGeometry);
    3005             :         }
    3006          33 :         GEOSGeom_destroy(geosGeometry);
    3007             : 
    3008          33 :         return err;
    3009             : }
    3010             : 
    3011             : str
    3012          32 : wkbBoundary(wkb **boundaryWKB, wkb **geomWKB)
    3013             : {
    3014          32 :         return wkbBasic(boundaryWKB, geomWKB, GEOSBoundary, "geom.Boundary");
    3015             : }
    3016             : 
    3017             : str
    3018           1 : wkbEnvelope(wkb **out, wkb **geom)
    3019             : {
    3020           1 :         return wkbBasic(out, geom, GEOSEnvelope, "geom.Envelope");
    3021             : }
    3022             : 
    3023             : str
    3024           0 : wkbEnvelopeFromCoordinates(wkb **out, dbl *xmin, dbl *ymin, dbl *xmax, dbl *ymax, int *srid)
    3025             : {
    3026             :         GEOSGeom geosGeometry, linearRingGeometry;
    3027             :         GEOSCoordSeq coordSeq;
    3028             : 
    3029           0 :         if (is_dbl_nil(*xmin) || is_dbl_nil(*ymin) || is_dbl_nil(*xmax) || is_dbl_nil(*ymax) || is_int_nil(*srid)) {
    3030           0 :                 if ((*out = wkbNULLcopy()) == NULL)
    3031           0 :                         throw(MAL, "geom.MakeEnvelope", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3032             :                 return MAL_SUCCEED;
    3033             :         }
    3034             : 
    3035             :         //create the coordinates sequence
    3036           0 :         if ((coordSeq = GEOSCoordSeq_create(5, 2)) == NULL)
    3037           0 :                 throw(MAL, "geom.MakeEnvelope", SQLSTATE(38000) "Geos operation GEOSCoordSeq_create failed");
    3038             : 
    3039             :         //set the values
    3040           0 :         if (!GEOSCoordSeq_setX(coordSeq, 0, *xmin) ||
    3041           0 :             !GEOSCoordSeq_setY(coordSeq, 0, *ymin) ||
    3042           0 :             !GEOSCoordSeq_setX(coordSeq, 1, *xmin) ||
    3043           0 :             !GEOSCoordSeq_setY(coordSeq, 1, *ymax) ||
    3044           0 :             !GEOSCoordSeq_setX(coordSeq, 2, *xmax) ||
    3045           0 :             !GEOSCoordSeq_setY(coordSeq, 2, *ymax) ||
    3046           0 :             !GEOSCoordSeq_setX(coordSeq, 3, *xmax) ||
    3047           0 :             !GEOSCoordSeq_setY(coordSeq, 3, *ymin) ||
    3048           0 :             !GEOSCoordSeq_setX(coordSeq, 4, *xmin) ||
    3049           0 :             !GEOSCoordSeq_setY(coordSeq, 4, *ymin)) {
    3050           0 :                 GEOSCoordSeq_destroy(coordSeq);
    3051           0 :                 throw(MAL, "geom.MakeEnvelope", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setX/Y failed");
    3052             :         }
    3053             : 
    3054           0 :         linearRingGeometry = GEOSGeom_createLinearRing(coordSeq);
    3055           0 :         if (linearRingGeometry == NULL) {
    3056             :                 //Gives segmentation fault GEOSCoordSeq_destroy(coordSeq);
    3057           0 :                 GEOSCoordSeq_destroy(coordSeq);
    3058           0 :                 throw(MAL, "geom.MakeEnvelope", SQLSTATE(38000) "Geos error creating LinearRing from coordinates");
    3059             :         }
    3060             : 
    3061           0 :         geosGeometry = GEOSGeom_createPolygon(linearRingGeometry, NULL, 0);
    3062           0 :         if (geosGeometry == NULL) {
    3063           0 :                 GEOSGeom_destroy(linearRingGeometry);
    3064           0 :                 throw(MAL, "geom.MakeEnvelope", SQLSTATE(38000) "Geos error creating Polygon from LinearRing");
    3065             :         }
    3066             : 
    3067           0 :         GEOSSetSRID(geosGeometry, *srid);
    3068             : 
    3069           0 :         *out = geos2wkb(geosGeometry);
    3070             : 
    3071           0 :         GEOSGeom_destroy(geosGeometry);
    3072             : 
    3073           0 :         return MAL_SUCCEED;
    3074             : }
    3075             : 
    3076             : str
    3077           0 : wkbMakePolygon(wkb **out, wkb **external, bat *internalBAT_id, int *srid)
    3078             : {
    3079             :         GEOSGeom geosGeometry, externalGeometry, linearRingGeometry;
    3080           0 :         bit closed = 0;
    3081             :         GEOSCoordSeq coordSeq_copy;
    3082             :         str err;
    3083             : 
    3084           0 :         if (is_wkb_nil(*external) || is_int_nil(*srid)) {
    3085           0 :                 if ((*out = wkbNULLcopy()) == NULL)
    3086           0 :                         throw(MAL, "geom.Polygon", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3087             :                 return MAL_SUCCEED;
    3088             :         }
    3089             : 
    3090           0 :         externalGeometry = wkb2geos(*external);
    3091           0 :         if (externalGeometry == NULL)
    3092           0 :                 throw(MAL, "geom.Polygon", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3093             : 
    3094             :         //check the type of the external geometry
    3095           0 :         if ((GEOSGeomTypeId(externalGeometry) + 1) != wkbLineString_mdb) {
    3096           0 :                 *out = NULL;
    3097           0 :                 GEOSGeom_destroy(externalGeometry);
    3098           0 :                 throw(MAL, "geom.Polygon", SQLSTATE(38000) "Geometries should be LineString");
    3099             :         }
    3100             :         //check whether the linestring is closed
    3101           0 :         if ((err = wkbIsClosed(&closed, external)) != MAL_SUCCEED) {
    3102           0 :                 GEOSGeom_destroy(externalGeometry);
    3103           0 :                 return err;
    3104             :         }
    3105           0 :         if (!closed) {
    3106           0 :                 *out = NULL;
    3107           0 :                 GEOSGeom_destroy(externalGeometry);
    3108           0 :                 throw(MAL, "geom.Polygon", SQLSTATE(38000) "Geos lineString should be closed");
    3109             :         }
    3110             :         //create a copy of the coordinates
    3111           0 :         coordSeq_copy = GEOSCoordSeq_clone(GEOSGeom_getCoordSeq(externalGeometry));
    3112           0 :         GEOSGeom_destroy(externalGeometry);
    3113           0 :         if (coordSeq_copy == NULL)
    3114           0 :                 throw(MAL, "geom.Polygon", SQLSTATE(38000) "Geos operation GEOSCoordSeq_clone failed");
    3115             : 
    3116             :         //create a linearRing using the copy of the coordinates
    3117           0 :         linearRingGeometry = GEOSGeom_createLinearRing(coordSeq_copy);
    3118           0 :         if (linearRingGeometry == NULL) {
    3119           0 :                 GEOSCoordSeq_destroy(coordSeq_copy);
    3120           0 :                 throw(MAL, "geom.Polygon", SQLSTATE(38000) "Geos operation GEOSGeom_createLinearRing failed");
    3121             :         }
    3122             : 
    3123             :         //create a polygon using the linearRing
    3124           0 :         if (internalBAT_id == NULL) {
    3125           0 :                 geosGeometry = GEOSGeom_createPolygon(linearRingGeometry, NULL, 0);
    3126           0 :                 if (geosGeometry == NULL) {
    3127           0 :                         *out = NULL;
    3128           0 :                         GEOSGeom_destroy(linearRingGeometry);
    3129           0 :                         throw(MAL, "geom.Polygon", SQLSTATE(38000) "Geos error creating Polygon from LinearRing");
    3130             :                 }
    3131             :         } else {
    3132             :                 /* TODO: Looks like incomplete code: what should be
    3133             :                  * done with internalBAT_id? --sjoerd */
    3134             :                 geosGeometry = NULL;
    3135             :         }
    3136             : 
    3137           0 :         GEOSSetSRID(geosGeometry, *srid);
    3138             : 
    3139           0 :         *out = geos2wkb(geosGeometry);
    3140           0 :         GEOSGeom_destroy(geosGeometry);
    3141             : 
    3142           0 :         return MAL_SUCCEED;
    3143             : }
    3144             : 
    3145             : //Gets two Point or LineString geometries and returns a line
    3146             : str
    3147           0 : wkbMakeLine(wkb **out, wkb **geom1WKB, wkb **geom2WKB)
    3148             : {
    3149             :         GEOSGeom outGeometry, geom1Geometry, geom2Geometry;
    3150             :         GEOSCoordSeq outCoordSeq = NULL;
    3151             :         const GEOSCoordSequence *geom1CoordSeq = NULL, *geom2CoordSeq = NULL;
    3152           0 :         unsigned int i = 0, geom1Size = 0, geom2Size = 0;
    3153           0 :         unsigned geom1Dimension = 0, geom2Dimension = 0;
    3154             :         double x, y, z;
    3155             :         str err = MAL_SUCCEED;
    3156             : 
    3157           0 :         *out = NULL;
    3158           0 :         if (is_wkb_nil(*geom1WKB) || is_wkb_nil(*geom2WKB)) {
    3159           0 :                 if ((*out = wkbNULLcopy()) == NULL)
    3160           0 :                         throw(MAL, "geom.MakeLine", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3161             :                 return MAL_SUCCEED;
    3162             :         }
    3163             : 
    3164           0 :         geom1Geometry = wkb2geos(*geom1WKB);
    3165           0 :         if (!geom1Geometry) {
    3166           0 :                 *out = NULL;
    3167           0 :                 throw(MAL, "geom.MakeLine", SQLSTATE(38000) "Geos operation wkb2geos failed");
    3168             :         }
    3169             : 
    3170           0 :         geom2Geometry = wkb2geos(*geom2WKB);
    3171           0 :         if (!geom2Geometry) {
    3172           0 :                 *out = NULL;
    3173           0 :                 GEOSGeom_destroy(geom1Geometry);
    3174           0 :                 throw(MAL, "geom.MakeLine", SQLSTATE(38000) "Geos operation wkb2geos failed");
    3175             :         }
    3176             :         //make sure the geometries are of the same srid
    3177           0 :         if (GEOSGetSRID(geom1Geometry) != GEOSGetSRID(geom2Geometry)) {
    3178           0 :                 err = createException(MAL, "geom.MakeLine", SQLSTATE(38000) "Geometries of different SRID");
    3179           0 :                 goto bailout;
    3180             :         }
    3181             :         //check the types of the geometries
    3182           0 :         if (GEOSGeomTypeId(geom1Geometry) + 1 != wkbPoint_mdb &&
    3183           0 :                  GEOSGeomTypeId(geom1Geometry) + 1 != wkbLineString_mdb &&
    3184           0 :                  GEOSGeomTypeId(geom2Geometry) + 1 != wkbPoint_mdb &&
    3185           0 :                  GEOSGeomTypeId(geom2Geometry) + 1 != wkbLineString_mdb) {
    3186           0 :                 err = createException(MAL, "geom.MakeLine", SQLSTATE(38000) "Geometries should be Point or LineString");
    3187           0 :                 goto bailout;
    3188             :         }
    3189             :         //get the coordinate sequences of the geometries
    3190           0 :         if ((geom1CoordSeq = GEOSGeom_getCoordSeq(geom1Geometry)) == NULL ||
    3191           0 :                  (geom2CoordSeq = GEOSGeom_getCoordSeq(geom2Geometry)) == NULL) {
    3192           0 :                 err = createException(MAL, "geom.MakeLine", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
    3193           0 :                 goto bailout;
    3194             :         }
    3195             :         //make sure that the dimensions of the geometries are the same
    3196           0 :         if (!GEOSCoordSeq_getDimensions(geom1CoordSeq, &geom1Dimension) ||
    3197           0 :                  !GEOSCoordSeq_getDimensions(geom2CoordSeq, &geom2Dimension)) {
    3198           0 :                 err = createException(MAL, "geom.MakeLine", SQLSTATE(38000) "Geos operation GEOSGeom_getDimensions failed");
    3199           0 :                 goto bailout;
    3200             :         }
    3201           0 :         if (geom1Dimension != geom2Dimension) {
    3202           0 :                 err = createException(MAL, "geom.MakeLine", SQLSTATE(38000) "Geometries should be of the same dimension");
    3203           0 :                 goto bailout;
    3204             :         }
    3205             :         //get the number of coordinates in the two geometries
    3206           0 :         if (!GEOSCoordSeq_getSize(geom1CoordSeq, &geom1Size) ||
    3207           0 :                  !GEOSCoordSeq_getSize(geom2CoordSeq, &geom2Size)) {
    3208           0 :                 err = createException(MAL, "geom.MakeLine", SQLSTATE(38000) "Geos operation GEOSGeom_getSize failed");
    3209           0 :                 goto bailout;
    3210             :         }
    3211             :         //create the coordSeq for the new geometry
    3212           0 :         if ((outCoordSeq = GEOSCoordSeq_create(geom1Size + geom2Size, geom1Dimension)) == NULL) {
    3213           0 :                 err = createException(MAL, "geom.MakeLine", SQLSTATE(38000) "Geos operation GEOSCoordSeq_create failed");
    3214           0 :                 goto bailout;
    3215             :         }
    3216           0 :         for (i = 0; i < geom1Size; i++) {
    3217           0 :                 GEOSCoordSeq_getX(geom1CoordSeq, i, &x);
    3218           0 :                 GEOSCoordSeq_getY(geom1CoordSeq, i, &y);
    3219           0 :                 if (!GEOSCoordSeq_setX(outCoordSeq, i, x) ||
    3220           0 :                     !GEOSCoordSeq_setY(outCoordSeq, i, y)) {
    3221           0 :                         err = createException(MAL, "geom.MakeLine", SQLSTATE(38000) "Geos operation GEOSCoordSeq_set[XY] failed");
    3222           0 :                         goto bailout;
    3223             :                 }
    3224           0 :                 if (geom1Dimension > 2) {
    3225           0 :                         GEOSCoordSeq_getZ(geom1CoordSeq, i, &z);
    3226           0 :                         if (!GEOSCoordSeq_setZ(outCoordSeq, i, z)) {
    3227           0 :                                 err = createException(MAL, "geom.MakeLine", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setZ failed");
    3228           0 :                                 goto bailout;
    3229             :                         }
    3230             :                 }
    3231             :         }
    3232           0 :         for (i = 0; i < geom2Size; i++) {
    3233           0 :                 GEOSCoordSeq_getX(geom2CoordSeq, i, &x);
    3234           0 :                 GEOSCoordSeq_getY(geom2CoordSeq, i, &y);
    3235           0 :                 if (!GEOSCoordSeq_setX(outCoordSeq, i + geom1Size, x) ||
    3236           0 :                     !GEOSCoordSeq_setY(outCoordSeq, i + geom1Size, y)) {
    3237           0 :                         err = createException(MAL, "geom.MakeLine", SQLSTATE(38000) "Geos operation GEOSCoordSeq_set[XY] failed");
    3238           0 :                         goto bailout;
    3239             :                 }
    3240           0 :                 if (geom2Dimension > 2) {
    3241           0 :                         GEOSCoordSeq_getZ(geom2CoordSeq, i, &z);
    3242           0 :                         if (!GEOSCoordSeq_setZ(outCoordSeq, i + geom1Size, z)) {
    3243           0 :                                 err = createException(MAL, "geom.MakeLine", SQLSTATE(38000) "Geos operation GEOSCoordSeq_setZ failed");
    3244           0 :                                 goto bailout;
    3245             :                         }
    3246             :                 }
    3247             :         }
    3248             : 
    3249           0 :         if ((outGeometry = GEOSGeom_createLineString(outCoordSeq)) == NULL) {
    3250           0 :                 err = createException(MAL, "geom.MakeLine", SQLSTATE(38000) "Geos operation GEOSGeom_createLineString failed");
    3251           0 :                 goto bailout;
    3252             :         }
    3253             :         outCoordSeq = NULL;
    3254             : 
    3255           0 :         GEOSSetSRID(outGeometry, GEOSGetSRID(geom1Geometry));
    3256           0 :         *out = geos2wkb(outGeometry);
    3257           0 :         GEOSGeom_destroy(outGeometry);
    3258             : 
    3259           0 :   bailout:
    3260           0 :         if (outCoordSeq)
    3261           0 :                 GEOSCoordSeq_destroy(outCoordSeq);
    3262           0 :         GEOSGeom_destroy(geom1Geometry);
    3263           0 :         GEOSGeom_destroy(geom2Geometry);
    3264             : 
    3265           0 :         return err;
    3266             : }
    3267             : 
    3268             : //Gets a BAT with geometries and returns a single LineString
    3269             : str
    3270           0 : wkbMakeLineAggr(wkb **outWKB, bat *inBAT_id)
    3271             : {
    3272             :         BAT *inBAT = NULL;
    3273             :         BATiter inBAT_iter;
    3274             :         BUN i;
    3275             :         wkb *aWKB, *bWKB;
    3276             :         str err;
    3277             : 
    3278             :         //get the BATs
    3279           0 :         if ((inBAT = BATdescriptor(*inBAT_id)) == NULL) {
    3280           0 :                 throw(MAL, "geom.MakeLine", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
    3281             :         }
    3282             : 
    3283             :         /* TODO: what should be returned if the input BAT is less than
    3284             :          * two rows? --sjoerd */
    3285           0 :         if (BATcount(inBAT) == 0) {
    3286           0 :                 BBPunfix(inBAT->batCacheid);
    3287           0 :                 if ((*outWKB = wkbNULLcopy()) == NULL)
    3288           0 :                         throw(MAL, "geom.MakeLine", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3289             :                 return MAL_SUCCEED;
    3290             :         }
    3291             :         //iterator over the BATs
    3292           0 :         inBAT_iter = bat_iterator(inBAT);
    3293           0 :         aWKB = (wkb *) BUNtvar(inBAT_iter, 0);
    3294           0 :         if (BATcount(inBAT) == 1) {
    3295           0 :                 bat_iterator_end(&inBAT_iter);
    3296           0 :                 err = wkbFromWKB(outWKB, &aWKB);
    3297           0 :                 BBPunfix(inBAT->batCacheid);
    3298           0 :                 if (err) {
    3299           0 :                         freeException(err);
    3300           0 :                         throw(MAL, "geom.MakeLine", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3301             :                 }
    3302             :                 return MAL_SUCCEED;
    3303             :         }
    3304           0 :         bWKB = (wkb *) BUNtvar(inBAT_iter, 1);
    3305             :         //create the first line using the first two geometries
    3306           0 :         err = wkbMakeLine(outWKB, &aWKB, &bWKB);
    3307             : 
    3308             :         // add one more segment for each following row
    3309           0 :         for (i = 2; err == MAL_SUCCEED && i < BATcount(inBAT); i++) {
    3310           0 :                 aWKB = *outWKB;
    3311           0 :                 bWKB = (wkb *) BUNtvar(inBAT_iter, i);
    3312           0 :                 *outWKB = NULL;
    3313             : 
    3314           0 :                 err = wkbMakeLine(outWKB, &aWKB, &bWKB);
    3315           0 :                 GDKfree(aWKB);
    3316             :         }
    3317             : 
    3318           0 :         bat_iterator_end(&inBAT_iter);
    3319           0 :         BBPunfix(inBAT->batCacheid);
    3320             : 
    3321           0 :         return err;
    3322             : }
    3323             : 
    3324             : /* Returns the first or last point of a linestring */
    3325             : static str
    3326           2 : wkbBorderPoint(wkb **out, wkb **geom, GEOSGeometry *(*func) (const GEOSGeometry *), const char *name)
    3327             : {
    3328             :         GEOSGeom geosGeometry;
    3329             :         GEOSGeom new;
    3330             :         str err = MAL_SUCCEED;
    3331             : 
    3332           2 :         if (is_wkb_nil(*geom)) {
    3333           0 :                 if ((*out = wkbNULLcopy()) == NULL)
    3334           0 :                         throw(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3335             :                 return MAL_SUCCEED;
    3336             :         }
    3337             : 
    3338           2 :         *out = NULL;
    3339           2 :         geosGeometry = wkb2geos(*geom);
    3340           2 :         if (geosGeometry == NULL) {
    3341           0 :                 throw(MAL, name, SQLSTATE(38000) "Geos operation wkb2geos failed");
    3342             :         }
    3343             : 
    3344           2 :         if (GEOSGeomTypeId(geosGeometry) != GEOS_LINESTRING) {
    3345           0 :                 err = createException(MAL, name, SQLSTATE(38000) "Geometry not a LineString");
    3346             :         } else {
    3347           2 :                 new = (*func) (geosGeometry);
    3348           2 :                 if (new == NULL) {
    3349           0 :                         err = createException(MAL, name, SQLSTATE(38000) "Geos operation GEOSGeomGet%s failed", name + 5);
    3350             :                 } else {
    3351           2 :                         *out = geos2wkb(new);
    3352           2 :                         GEOSGeom_destroy(new);
    3353             :                 }
    3354             :         }
    3355           2 :         GEOSGeom_destroy(geosGeometry);
    3356             : 
    3357           2 :         return err;
    3358             : }
    3359             : 
    3360             : /* Returns the first point in a linestring */
    3361             : str
    3362           1 : wkbStartPoint(wkb **out, wkb **geom)
    3363             : {
    3364           1 :         return wkbBorderPoint(out, geom, GEOSGeomGetStartPoint, "geom.StartPoint");
    3365             : }
    3366             : 
    3367             : /* Returns the last point in a linestring */
    3368             : str
    3369           1 : wkbEndPoint(wkb **out, wkb **geom)
    3370             : {
    3371           1 :         return wkbBorderPoint(out, geom, GEOSGeomGetEndPoint, "geom.EndPoint");
    3372             : }
    3373             : 
    3374             : static str
    3375          58 : numPointsLineString(unsigned int *out, const GEOSGeometry *geosGeometry)
    3376             : {
    3377             :         /* get the coordinates of the points comprising the geometry */
    3378          58 :         const GEOSCoordSequence *coordSeq = GEOSGeom_getCoordSeq(geosGeometry);
    3379             : 
    3380          58 :         if (coordSeq == NULL)
    3381           0 :                 throw(MAL, "geom.NumPoints", SQLSTATE(38000) "Geos operation GEOSGeom_getCoordSeq failed");
    3382             : 
    3383             :         /* get the number of points in the geometry */
    3384          58 :         if (!GEOSCoordSeq_getSize(coordSeq, out)) {
    3385           0 :                 *out = int_nil;
    3386           0 :                 throw(MAL, "geom.NumPoints", SQLSTATE(38000) "Geos operation GEOSGeomGetNumPoints failed");
    3387             :         }
    3388             : 
    3389             :         return MAL_SUCCEED;
    3390             : }
    3391             : 
    3392             : static str
    3393          11 : numPointsPolygon(unsigned int *out, const GEOSGeometry *geosGeometry)
    3394             : {
    3395             :         const GEOSGeometry *exteriorRingGeometry;
    3396             :         int numInteriorRings = 0, i = 0;
    3397             :         str err;
    3398             :         unsigned int pointsN = 0;
    3399             : 
    3400             :         /* get the exterior ring of the polygon */
    3401          11 :         exteriorRingGeometry = GEOSGetExteriorRing(geosGeometry);
    3402          11 :         if (!exteriorRingGeometry) {
    3403           0 :                 *out = int_nil;
    3404           0 :                 throw(MAL, "geom.NumPoints", SQLSTATE(38000) "Geos operation GEOSGetExteriorRing failed");
    3405             :         }
    3406             :         //get the points in the exterior ring
    3407          11 :         if ((err = numPointsLineString(out, exteriorRingGeometry)) != MAL_SUCCEED) {
    3408           0 :                 *out = int_nil;
    3409           0 :                 return err;
    3410             :         }
    3411          11 :         pointsN = *out;
    3412             : 
    3413             :         //check the interior rings
    3414          11 :         numInteriorRings = GEOSGetNumInteriorRings(geosGeometry);
    3415          11 :         if (numInteriorRings == -1) {
    3416           0 :                 *out = int_nil;
    3417           0 :                 throw(MAL, "geom.NumPoints", SQLSTATE(38000) "Geos operation GEOSGetNumInteriorRings failed");
    3418             :         }
    3419             :         // iterate over the interiorRing and transform each one of them
    3420          16 :         for (i = 0; i < numInteriorRings; i++) {
    3421           5 :                 if ((err = numPointsLineString(out, GEOSGetInteriorRingN(geosGeometry, i))) != MAL_SUCCEED) {
    3422           0 :                         *out = int_nil;
    3423           0 :                         return err;
    3424             :                 }
    3425           5 :                 pointsN += *out;
    3426             :         }
    3427             : 
    3428          11 :         *out = pointsN;
    3429          11 :         return MAL_SUCCEED;
    3430             : }
    3431             : 
    3432             : static str numPointsGeometry(unsigned int *out, const GEOSGeometry *geosGeometry);
    3433             : static str
    3434           9 : numPointsMultiGeometry(unsigned int *out, const GEOSGeometry *geosGeometry)
    3435             : {
    3436             :         int geometriesNum, i;
    3437             :         const GEOSGeometry *multiGeometry = NULL;
    3438             :         str err;
    3439             :         unsigned int pointsN = 0;
    3440             : 
    3441           9 :         geometriesNum = GEOSGetNumGeometries(geosGeometry);
    3442             : 
    3443          31 :         for (i = 0; i < geometriesNum; i++) {
    3444          22 :                 multiGeometry = GEOSGetGeometryN(geosGeometry, i);
    3445          22 :                 if ((err = numPointsGeometry(out, multiGeometry)) != MAL_SUCCEED) {
    3446           0 :                         *out = int_nil;
    3447           0 :                         return err;
    3448             :                 }
    3449          22 :                 pointsN += *out;
    3450             :         }
    3451             : 
    3452           9 :         *out = pointsN;
    3453           9 :         return MAL_SUCCEED;
    3454             : }
    3455             : 
    3456             : static str
    3457          62 : numPointsGeometry(unsigned int *out, const GEOSGeometry *geosGeometry)
    3458             : {
    3459          62 :         int geometryType = GEOSGeomTypeId(geosGeometry) + 1;
    3460             : 
    3461             :         //check the type of the geometry
    3462          62 :         switch (geometryType) {
    3463          42 :         case wkbPoint_mdb:
    3464             :         case wkbLineString_mdb:
    3465             :         case wkbLinearRing_mdb:
    3466          42 :                 return numPointsLineString(out, geosGeometry);
    3467          11 :         case wkbPolygon_mdb:
    3468          11 :                 return numPointsPolygon(out, geosGeometry);
    3469           9 :         case wkbMultiPoint_mdb:
    3470             :         case wkbMultiLineString_mdb:
    3471             :         case wkbMultiPolygon_mdb:
    3472             :         case wkbGeometryCollection_mdb:
    3473           9 :                 return numPointsMultiGeometry(out, geosGeometry);
    3474           0 :         default:
    3475           0 :                 throw(MAL, "geom.NumPoints", SQLSTATE(38000) "Geos geometry type %s unknown", geom_type2str(geometryType, 0));
    3476             :         }
    3477             : }
    3478             : 
    3479             : /* Returns the number of points in a geometry */
    3480             : str
    3481          42 : wkbNumPoints(int *out, wkb **geom, int *check)
    3482             : {
    3483             :         GEOSGeom geosGeometry;
    3484             :         int geometryType = 0;
    3485             :         str err = MAL_SUCCEED;
    3486          42 :         char *geomSTR = NULL;
    3487             :         unsigned int pointsNum;
    3488             : 
    3489          42 :         if (is_wkb_nil(*geom) || is_int_nil(*check)) {
    3490           0 :                 *out = int_nil;
    3491           0 :                 return MAL_SUCCEED;
    3492             :         }
    3493             : 
    3494          42 :         geosGeometry = wkb2geos(*geom);
    3495          42 :         if (geosGeometry == NULL) {
    3496           0 :                 *out = int_nil;
    3497           0 :                 throw(MAL, "geom.NumPoints", SQLSTATE(38000) "Geos operation wkb2geos failed");
    3498             :         }
    3499             : 
    3500          42 :         geometryType = GEOSGeomTypeId(geosGeometry) + 1;
    3501             : 
    3502          42 :         if (*check && geometryType != wkbLineString_mdb) {
    3503           2 :                 *out = int_nil;
    3504           2 :                 GEOSGeom_destroy(geosGeometry);
    3505             : 
    3506           2 :                 if ((err = wkbAsText(&geomSTR, geom, NULL)) == MAL_SUCCEED) {
    3507           2 :                         err = createException(MAL, "geom.NumPoints", SQLSTATE(38000) "Geometry \"%s\" not a LineString", geomSTR);
    3508           2 :                         GDKfree(geomSTR);
    3509             :                 }
    3510           2 :                 return err;
    3511             :         }
    3512             : 
    3513          40 :         if ((err = numPointsGeometry(&pointsNum, geosGeometry)) != MAL_SUCCEED) {
    3514           0 :                 *out = int_nil;
    3515           0 :                 GEOSGeom_destroy(geosGeometry);
    3516           0 :                 return err;
    3517             :         }
    3518             : 
    3519          40 :         if (pointsNum > INT_MAX) {
    3520           0 :                 GEOSGeom_destroy(geosGeometry);
    3521           0 :                 *out = int_nil;
    3522           0 :                 throw(MAL, "geom.NumPoints", SQLSTATE(38000) "Geos operation Overflow");
    3523             :         }
    3524             : 
    3525          40 :         *out = pointsNum;
    3526          40 :         GEOSGeom_destroy(geosGeometry);
    3527             : 
    3528          40 :         return MAL_SUCCEED;
    3529             : }
    3530             : 
    3531             : /* Returns the n-th point of the geometry */
    3532             : str
    3533           1 : wkbPointN(wkb **out, wkb **geom, int *n)
    3534             : {
    3535             :         int rN = -1;
    3536             :         GEOSGeom geosGeometry;
    3537             :         GEOSGeom new;
    3538             :         str err = MAL_SUCCEED;
    3539             : 
    3540           1 :         if (is_wkb_nil(*geom) || is_int_nil(*n)) {
    3541           0 :                 if ((*out = wkbNULLcopy()) == NULL)
    3542           0 :                         throw(MAL, "geom.PointN", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3543             :                 return MAL_SUCCEED;
    3544             :         }
    3545             : 
    3546           1 :         geosGeometry = wkb2geos(*geom);
    3547           1 :         if (geosGeometry == NULL) {
    3548           0 :                 *out = NULL;
    3549           0 :                 throw(MAL, "geom.PointN", SQLSTATE(38000) "Geos operation wkb2geos failed");
    3550             :         }
    3551             : 
    3552           1 :         if (GEOSGeomTypeId(geosGeometry) != GEOS_LINESTRING) {
    3553           0 :                 *out = NULL;
    3554           0 :                 GEOSGeom_destroy(geosGeometry);
    3555           0 :                 throw(MAL, "geom.PointN", SQLSTATE(38000) "Geometry not a LineString");
    3556             :         }
    3557             :         //check number of points
    3558           1 :         rN = GEOSGeomGetNumPoints(geosGeometry);
    3559           1 :         if (rN == -1) {
    3560           0 :                 *out = NULL;
    3561           0 :                 GEOSGeom_destroy(geosGeometry);
    3562           0 :                 throw(MAL, "geom.PointN", SQLSTATE(38000) "Geos operation GEOSGeomGetNumPoints failed");
    3563             :         }
    3564             : 
    3565           1 :         if (rN <= *n || *n < 0) {
    3566           0 :                 *out = NULL;
    3567           0 :                 GEOSGeom_destroy(geosGeometry);
    3568           0 :                 throw(MAL, "geom.PointN", SQLSTATE(38000) "Geos unable to retrieve point %d (not enough points)", *n);
    3569             :         }
    3570             : 
    3571           1 :         if ((new = GEOSGeomGetPointN(geosGeometry, *n)) == NULL) {
    3572           0 :                 err = createException(MAL, "geom.PointN", SQLSTATE(38000) "Geos operation GEOSGeomGetPointN failed");
    3573             :         } else {
    3574           1 :                 if ((*out = geos2wkb(new)) == NULL)
    3575           0 :                         err = createException(MAL, "geom.PointN", SQLSTATE(38000) "Geos operation GEOSGeomGetPointN failed");
    3576           1 :                 GEOSGeom_destroy(new);
    3577             :         }
    3578           1 :         GEOSGeom_destroy(geosGeometry);
    3579             : 
    3580           1 :         return err;
    3581             : }
    3582             : 
    3583             : /* Returns the exterior ring of the polygon*/
    3584             : str
    3585           2 : wkbExteriorRing(wkb **exteriorRingWKB, wkb **geom)
    3586             : {
    3587             :         GEOSGeom geosGeometry;
    3588             :         const GEOSGeometry *exteriorRingGeometry;
    3589             :         str err = MAL_SUCCEED;
    3590             : 
    3591           2 :         if (is_wkb_nil(*geom)) {
    3592           0 :                 if ((*exteriorRingWKB = wkbNULLcopy()) == NULL)
    3593           0 :                         throw(MAL, "geom.ExteriorRing", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3594             :                 return MAL_SUCCEED;
    3595             :         }
    3596             : 
    3597           2 :         geosGeometry = wkb2geos(*geom);
    3598           2 :         if (geosGeometry == NULL) {
    3599           0 :                 *exteriorRingWKB = NULL;
    3600           0 :                 throw(MAL, "geom.ExteriorRing", SQLSTATE(38000) "Geos operation wkb2geos failed");
    3601             :         }
    3602             : 
    3603           2 :         if (GEOSGeomTypeId(geosGeometry) != GEOS_POLYGON) {
    3604           0 :                 *exteriorRingWKB = NULL;
    3605           0 :                 GEOSGeom_destroy(geosGeometry);
    3606           0 :                 throw(MAL, "geom.ExteriorRing", SQLSTATE(38000) "Geometry not a Polygon");
    3607             : 
    3608             :         }
    3609             :         /* get the exterior ring of the geometry */
    3610           2 :         if ((exteriorRingGeometry = GEOSGetExteriorRing(geosGeometry)) == NULL)
    3611           0 :                 err = createException(MAL, "geom.ExteriorRing", SQLSTATE(38000) "Geos operation GEOSGetExteriorRing failed");
    3612             :         else {
    3613             :                 /* get the wkb representation of it */
    3614           2 :                 if ((*exteriorRingWKB = geos2wkb(exteriorRingGeometry)) == NULL)
    3615           0 :                         err = createException(MAL, "geom.ExteriorRing", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3616             :         }
    3617           2 :         GEOSGeom_destroy(geosGeometry);
    3618             : 
    3619           2 :         return err;
    3620             : }
    3621             : 
    3622             : /* Returns the n-th interior ring of a polygon */
    3623             : str
    3624           1 : wkbInteriorRingN(wkb **interiorRingWKB, wkb **geom, int *ringNum)
    3625             : {
    3626             :         GEOSGeom geosGeometry = NULL;
    3627             :         const GEOSGeometry *interiorRingGeometry;
    3628             :         int rN = -1;
    3629             :         str err = MAL_SUCCEED;
    3630             : 
    3631             :         //initialize to NULL
    3632           1 :         *interiorRingWKB = NULL;
    3633             : 
    3634           1 :         if (is_wkb_nil(*geom) || is_int_nil(*ringNum)) {
    3635           0 :                 if ((*interiorRingWKB = wkbNULLcopy()) == NULL)
    3636           0 :                         throw(MAL, "geom.InteriorRingN", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3637             :                 return MAL_SUCCEED;
    3638             :         }
    3639             : 
    3640           1 :         geosGeometry = wkb2geos(*geom);
    3641           1 :         if (geosGeometry == NULL) {
    3642           0 :                 *interiorRingWKB = NULL;
    3643           0 :                 throw(MAL, "geom.InteriorRingN", SQLSTATE(38000) "Geos operation wkb2geos failed");
    3644             :         }
    3645             : 
    3646           1 :         if ((GEOSGeomTypeId(geosGeometry) + 1) != wkbPolygon_mdb) {
    3647           0 :                 *interiorRingWKB = NULL;
    3648           0 :                 GEOSGeom_destroy(geosGeometry);
    3649           0 :                 throw(MAL, "geom.InteriorRingN", SQLSTATE(38000) "Geometry not a Polygon");
    3650             : 
    3651             :         }
    3652             :         //check number of internal rings
    3653           1 :         rN = GEOSGetNumInteriorRings(geosGeometry);
    3654           1 :         if (rN == -1) {
    3655           0 :                 *interiorRingWKB = NULL;
    3656           0 :                 GEOSGeom_destroy(geosGeometry);
    3657           0 :                 throw(MAL, "geom.InteriorRingN", SQLSTATE(38000) "Geos operation GEOSGetInteriorRingN failed.");
    3658             :         }
    3659             : 
    3660           1 :         if (rN < *ringNum || *ringNum <= 0) {
    3661           0 :                 GEOSGeom_destroy(geosGeometry);
    3662             :                 //NOT AN ERROR throw(MAL, "geom.interiorRingN", SQLSTATE(38000) "Geos operation GEOSGetInteriorRingN failed. Not enough interior rings");
    3663           0 :                 if ((*interiorRingWKB = wkbNULLcopy()) == NULL)
    3664           0 :                         throw(MAL, "geom.InteriorRingN", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3665             :                 return MAL_SUCCEED;
    3666             :         }
    3667             : 
    3668             :         /* get the interior ring of the geometry */
    3669           1 :         if ((interiorRingGeometry = GEOSGetInteriorRingN(geosGeometry, *ringNum - 1)) == NULL) {
    3670           0 :                 err = createException(MAL, "geom.InteriorRingN", SQLSTATE(38000) "Geos operation GEOSGetInteriorRingN failed");
    3671             :         } else {
    3672             :                 /* get the wkb representation of it */
    3673           1 :                 if ((*interiorRingWKB = geos2wkb(interiorRingGeometry)) == NULL)
    3674           0 :                         err = createException(MAL, "geom.InteriorRingN", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3675             :         }
    3676           1 :         GEOSGeom_destroy(geosGeometry);
    3677             : 
    3678           1 :         return err;
    3679             : }
    3680             : 
    3681             : str
    3682           0 : wkbInteriorRings(wkba **geomArray, wkb **geomWKB)
    3683             : {
    3684           0 :         int interiorRingsNum = 0, i = 0;
    3685             :         GEOSGeom geosGeometry;
    3686             :         str ret = MAL_SUCCEED;
    3687             : 
    3688           0 :         if (is_wkb_nil(*geomWKB)) {
    3689           0 :                 if ((*geomArray = GDKmalloc(wkba_size(~0))) == NULL)
    3690           0 :                         throw(MAL, "geom.InteriorRings", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3691           0 :                 **geomArray = wkba_nil;
    3692           0 :                 return MAL_SUCCEED;
    3693             :         }
    3694             : 
    3695           0 :         geosGeometry = wkb2geos(*geomWKB);
    3696           0 :         if (geosGeometry == NULL) {
    3697           0 :                 throw(MAL, "geom.InteriorRings", SQLSTATE(38000) "Geos operation  wkb2geos failed");
    3698             :         }
    3699             : 
    3700           0 :         if ((GEOSGeomTypeId(geosGeometry) + 1) != wkbPolygon_mdb) {
    3701           0 :                 GEOSGeom_destroy(geosGeometry);
    3702           0 :                 throw(MAL, "geom.interiorRings", SQLSTATE(38000) "Geometry not a Polygon");
    3703             : 
    3704             :         }
    3705             : 
    3706           0 :         ret = wkbNumRings(&interiorRingsNum, geomWKB, &i);
    3707             : 
    3708           0 :         if (ret != MAL_SUCCEED) {
    3709           0 :                 GEOSGeom_destroy(geosGeometry);
    3710           0 :                 return ret;
    3711             :         }
    3712             : 
    3713           0 :         *geomArray = GDKmalloc(wkba_size(interiorRingsNum));
    3714           0 :         if (*geomArray == NULL) {
    3715           0 :                 GEOSGeom_destroy(geosGeometry);
    3716           0 :                 throw(MAL, "geom.InteriorRings", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3717             :         }
    3718           0 :         (*geomArray)->itemsNum = interiorRingsNum;
    3719             : 
    3720           0 :         for (i = 0; i < interiorRingsNum; i++) {
    3721             :                 const GEOSGeometry *interiorRingGeometry;
    3722             :                 wkb *interiorRingWKB;
    3723             : 
    3724             :                 // get the interior ring of the geometry
    3725           0 :                 interiorRingGeometry = GEOSGetInteriorRingN(geosGeometry, i);
    3726           0 :                 if (interiorRingGeometry == NULL) {
    3727           0 :                         while (--i >= 0)
    3728           0 :                                 GDKfree((*geomArray)->data[i]);
    3729           0 :                         GDKfree(*geomArray);
    3730           0 :                         GEOSGeom_destroy(geosGeometry);
    3731           0 :                         *geomArray = NULL;
    3732           0 :                         throw(MAL, "geom.InteriorRings", SQLSTATE(38000) "Geos operation GEOSGetInteriorRingN failed");
    3733             :                 }
    3734             :                 // get the wkb representation of it
    3735           0 :                 interiorRingWKB = geos2wkb(interiorRingGeometry);
    3736           0 :                 if (interiorRingWKB == NULL) {
    3737           0 :                         while (--i >= 0)
    3738           0 :                                 GDKfree((*geomArray)->data[i]);
    3739           0 :                         GDKfree(*geomArray);
    3740           0 :                         GEOSGeom_destroy(geosGeometry);
    3741           0 :                         *geomArray = NULL;
    3742           0 :                         throw(MAL, "geom.InteriorRings", SQLSTATE(38000) "Geos operation wkb2geos failed");
    3743             :                 }
    3744             : 
    3745           0 :                 (*geomArray)->data[i] = interiorRingWKB;
    3746             :         }
    3747           0 :         GEOSGeom_destroy(geosGeometry);
    3748             : 
    3749           0 :         return MAL_SUCCEED;
    3750             : }
    3751             : 
    3752             : /* Returns the number of interior rings in the first polygon of the provided geometry
    3753             :  * plus the exterior ring depending on the value of exteriorRing*/
    3754             : str
    3755          27 : wkbNumRings(int *out, wkb **geom, int *exteriorRing)
    3756             : {
    3757             :         str ret = MAL_SUCCEED;
    3758             :         bit empty;
    3759             :         GEOSGeom geosGeometry;
    3760             : 
    3761          27 :         if (is_wkb_nil(*geom) || is_int_nil(*exteriorRing)) {
    3762           0 :                 *out = int_nil;
    3763           0 :                 return MAL_SUCCEED;
    3764             :         }
    3765             : 
    3766             :         //check if the geometry is empty
    3767          27 :         if ((ret = wkbIsEmpty(&empty, geom)) != MAL_SUCCEED) {
    3768             :                 return ret;
    3769             :         }
    3770          27 :         if (empty) {
    3771             :                 //the geometry is empty
    3772           0 :                 *out = 0;
    3773           0 :                 return MAL_SUCCEED;
    3774             :         }
    3775             :         //check the type of the geometry
    3776          27 :         geosGeometry = wkb2geos(*geom);
    3777             : 
    3778          27 :         if (geosGeometry == NULL)
    3779           0 :                 throw(MAL, "geom.NumRings", SQLSTATE(38000) "Geos problem converting WKB to GEOS");
    3780             : 
    3781          27 :         if (GEOSGeomTypeId(geosGeometry) + 1 == wkbMultiPolygon_mdb) {
    3782             :                 //use the first polygon as done by PostGIS
    3783           8 :                 wkb *new = geos2wkb(GEOSGetGeometryN(geosGeometry, 0));
    3784           8 :                 if (new == NULL) {
    3785           0 :                         ret = createException(MAL, "geom.NumRings", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3786             :                 } else {
    3787           8 :                         ret = wkbBasicInt(out, new, GEOSGetNumInteriorRings, "geom.NumRings");
    3788           8 :                         GDKfree(new);
    3789             :                 }
    3790          19 :         } else if (GEOSGeomTypeId(geosGeometry) + 1 == wkbPolygon_mdb) {
    3791           9 :                 ret = wkbBasicInt(out, *geom, GEOSGetNumInteriorRings, "geom.NumRings");
    3792             :         } else {
    3793             :                 //It is not a polygon so the number of rings is 0
    3794          10 :                 *out = -*exteriorRing; /* compensate for += later */
    3795             :         }
    3796             : 
    3797          27 :         GEOSGeom_destroy(geosGeometry);
    3798             : 
    3799          27 :         if (ret != MAL_SUCCEED)
    3800             :                 return ret;
    3801             : 
    3802          27 :         *out += *exteriorRing;
    3803             : 
    3804          27 :         return MAL_SUCCEED;
    3805             : }
    3806             : 
    3807             : /* it handles functions that take as input a single geometry and return Boolean */
    3808             : static str
    3809         782 : wkbBasicBoolean(bit *out, wkb **geom, char (*func) (const GEOSGeometry *), const char *name)
    3810             : {
    3811             :         int ret;
    3812             :         GEOSGeom geosGeometry;
    3813             : 
    3814         782 :         if (is_wkb_nil(*geom)) {
    3815           0 :                 *out = bit_nil;
    3816           0 :                 return MAL_SUCCEED;
    3817             :         }
    3818             : 
    3819         782 :         geosGeometry = wkb2geos(*geom);
    3820         782 :         if (geosGeometry == NULL)
    3821           0 :                 throw(MAL, name, SQLSTATE(38000) "Geos operation wkb2geom failed");
    3822             : 
    3823         782 :         ret = (*func) (geosGeometry);   //it is supposed to return char but treating it as such gives wrong results
    3824         782 :         GEOSGeom_destroy(geosGeometry);
    3825             : 
    3826         782 :         if (ret == 2) {
    3827           0 :                 GDKclrerr();
    3828             :                 ret = 0;
    3829             :         }
    3830             : 
    3831         782 :         *out = ret;
    3832             : 
    3833         782 :         return MAL_SUCCEED;
    3834             : }
    3835             : 
    3836             : /* the function checks whether the geometry is closed. GEOS works only with
    3837             :  * linestring geometries but PostGIS returns true in any geometry that is not
    3838             :  * a linestring. I made it to be like PostGIS */
    3839             : static str
    3840          36 : geosIsClosed(bit *out, const GEOSGeometry *geosGeometry)
    3841             : {
    3842          36 :         int geometryType = GEOSGeomTypeId(geosGeometry) + 1;
    3843             :         int i = 0;
    3844             :         str err;
    3845             :         int geometriesNum;
    3846             : 
    3847          36 :         *out = bit_nil;
    3848             : 
    3849          36 :         switch (geometryType) {
    3850           0 :         case -1:
    3851           0 :                 throw(MAL, "geom.IsClosed", SQLSTATE(38000) "Geos operation GEOSGeomTypeId failed");
    3852          11 :         case wkbPoint_mdb:
    3853             :         case wkbPolygon_mdb:
    3854             :         case wkbMultiPoint_mdb:
    3855             :         case wkbMultiPolygon_mdb:
    3856             :                 //In all these case it is always true
    3857          11 :                 *out = 1;
    3858          11 :                 break;
    3859          18 :         case wkbLineString_mdb:
    3860             :                 //check
    3861          18 :                 if ((i = GEOSisClosed(geosGeometry)) == 2)
    3862           0 :                         throw(MAL, "geom.IsClosed", SQLSTATE(38000) "Geos operation GEOSisClosed failed");
    3863          18 :                 *out = i;
    3864          18 :                 break;
    3865           7 :         case wkbMultiLineString_mdb:
    3866             :         case wkbGeometryCollection_mdb:
    3867             :                 //check each one separately
    3868           7 :                 geometriesNum = GEOSGetNumGeometries(geosGeometry);
    3869           7 :                 if (geometriesNum < 0)
    3870           0 :                         throw(MAL, "geom.IsClosed", SQLSTATE(38000) "Geos operation GEOSGetNumGeometries failed");
    3871             : 
    3872          15 :                 for (i = 0; i < geometriesNum; i++) {
    3873          13 :                         const GEOSGeometry *gN = GEOSGetGeometryN(geosGeometry, i);
    3874          13 :                         if (!gN)
    3875           0 :                                 throw(MAL, "geom.IsClosed", SQLSTATE(38000) "Geos operation GEOSGetGeometryN failed");
    3876             : 
    3877          13 :                         if ((err = geosIsClosed(out, gN)) != MAL_SUCCEED) {
    3878           0 :                                 return err;
    3879             :                         }
    3880             : 
    3881          13 :                         if (!*out)      //no reason to check further logical AND will always be 0
    3882             :                                 return MAL_SUCCEED;
    3883             :                 }
    3884             : 
    3885             :                 break;
    3886           0 :         default:
    3887           0 :                 throw(MAL, "geom.IsClosed", SQLSTATE(38000) "Geos geometry type unknown");
    3888             :         }
    3889             : 
    3890             :         return MAL_SUCCEED;
    3891             : }
    3892             : 
    3893             : str
    3894          24 : wkbIsClosed(bit *out, wkb **geomWKB)
    3895             : {
    3896             :         str err;
    3897             :         GEOSGeom geosGeometry;
    3898             : 
    3899          24 :         if (is_wkb_nil(*geomWKB)) {
    3900           0 :                 *out = bit_nil;
    3901           0 :                 return MAL_SUCCEED;
    3902             :         }
    3903             : 
    3904             :         //if empty geometry return false
    3905          24 :         if ((err = wkbIsEmpty(out, geomWKB)) != MAL_SUCCEED) {
    3906             :                 return err;
    3907             :         }
    3908          24 :         if (*out) {
    3909           1 :                 *out = 0;
    3910           1 :                 return MAL_SUCCEED;
    3911             :         }
    3912             : 
    3913          23 :         geosGeometry = wkb2geos(*geomWKB);
    3914          23 :         if (geosGeometry == NULL)
    3915           0 :                 throw(MAL, "geom.IsClosed", SQLSTATE(38000) "Geos operation wkb2geos failed");
    3916             : 
    3917          23 :         err = geosIsClosed(out, geosGeometry);
    3918          23 :         GEOSGeom_destroy(geosGeometry);
    3919             : 
    3920          23 :         return err;
    3921             : }
    3922             : 
    3923             : str
    3924         733 : wkbIsEmpty(bit *out, wkb **geomWKB)
    3925             : {
    3926         733 :         return wkbBasicBoolean(out, geomWKB, GEOSisEmpty, "geom.IsEmpty");
    3927             : }
    3928             : 
    3929             : str
    3930          16 : wkbIsRing(bit *out, wkb **geomWKB)
    3931             : {
    3932          16 :         return wkbBasicBoolean(out, geomWKB, GEOSisRing, "geom.IsRing");
    3933             : }
    3934             : 
    3935             : str
    3936          15 : wkbIsSimple(bit *out, wkb **geomWKB)
    3937             : {
    3938          15 :         return wkbBasicBoolean(out, geomWKB, GEOSisSimple, "geom.IsSimple");
    3939             : }
    3940             : 
    3941             : /*geom prints a message saying the reason why the geometry is not valid but
    3942             :  * since there is also isValidReason I skip this here */
    3943             : str
    3944          18 : wkbIsValid(bit *out, wkb **geomWKB)
    3945             : {
    3946          18 :         str err = wkbBasicBoolean(out, geomWKB, GEOSisValid, "geom.IsValid");
    3947             :         /* GOESisValid may cause GDKerror to be called: ignore it */
    3948          18 :         if (err == MAL_SUCCEED)
    3949          18 :                 GDKclrerr();
    3950          18 :         return err;
    3951             : }
    3952             : 
    3953             : str
    3954           0 : wkbIsValidReason(char **reason, wkb **geomWKB)
    3955             : {
    3956             :         GEOSGeom geosGeometry;
    3957             :         char *GEOSReason = NULL;
    3958             : 
    3959           0 :         if (is_wkb_nil(*geomWKB)) {
    3960           0 :                 if ((*reason = GDKstrdup(str_nil)) == NULL)
    3961           0 :                         throw(MAL, "geom.IsValidReason", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3962             :                 return MAL_SUCCEED;
    3963             :         }
    3964             : 
    3965           0 :         geosGeometry = wkb2geos(*geomWKB);
    3966           0 :         if (geosGeometry == NULL)
    3967           0 :                 throw(MAL, "geom.IsValidReason", SQLSTATE(38000) "Geos operation wkb2geom failed");
    3968             : 
    3969           0 :         GEOSReason = GEOSisValidReason(geosGeometry);
    3970             : 
    3971           0 :         GEOSGeom_destroy(geosGeometry);
    3972             : 
    3973           0 :         if (GEOSReason == NULL)
    3974           0 :                 throw(MAL, "geom.IsValidReason", SQLSTATE(38000) "Geos operation GEOSisValidReason failed");
    3975             : 
    3976           0 :         *reason = GDKstrdup(GEOSReason);
    3977           0 :         GEOSFree(GEOSReason);
    3978           0 :         if (*reason == NULL)
    3979           0 :                 throw(MAL, "geom.IsValidReason", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3980             : 
    3981             :         return MAL_SUCCEED;
    3982             : }
    3983             : 
    3984             : /* I should check it since it does not work */
    3985             : str
    3986           0 : wkbIsValidDetail(char **out, wkb **geom)
    3987             : {
    3988             :         int res = -1;
    3989           0 :         char *GEOSreason = NULL;
    3990           0 :         GEOSGeom GEOSlocation = NULL;
    3991             :         GEOSGeom geosGeometry;
    3992             : 
    3993           0 :         if (is_wkb_nil(*geom)) {
    3994           0 :                 if ((*out = GDKstrdup(str_nil)) == NULL)
    3995           0 :                         throw(MAL, "geom.IsValidReason", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    3996             :                 return MAL_SUCCEED;
    3997             :         }
    3998             : 
    3999           0 :         if ((geosGeometry = wkb2geos(*geom)) == NULL) {
    4000           0 :                 *out = NULL;
    4001           0 :                 throw(MAL, "geom.IsValidDetail", SQLSTATE(38000) "Geos operation wkb2geos failed");
    4002             :         }
    4003             : 
    4004           0 :         res = GEOSisValidDetail(geosGeometry, 1, &GEOSreason, &GEOSlocation);
    4005             : 
    4006           0 :         GEOSGeom_destroy(geosGeometry);
    4007             : 
    4008           0 :         if (res == 2) {
    4009           0 :                 throw(MAL, "geom.IsValidDetail", SQLSTATE(38000) "Geos operation GEOSisValidDetail failed");
    4010             :         }
    4011             : 
    4012           0 :         *out = GDKstrdup(GEOSreason);
    4013             : 
    4014           0 :         GEOSFree(GEOSreason);
    4015           0 :         GEOSGeom_destroy(GEOSlocation);
    4016             : 
    4017           0 :         if (*out == NULL)
    4018           0 :                 throw(MAL, "geom.IsValidReason", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4019             : 
    4020             :         return MAL_SUCCEED;
    4021             : }
    4022             : 
    4023             : /* returns the area of the geometry */
    4024             : str
    4025           2 : wkbArea(dbl *out, wkb **geomWKB)
    4026             : {
    4027             :         GEOSGeom geosGeometry;
    4028             : 
    4029           2 :         if (is_wkb_nil(*geomWKB)) {
    4030           0 :                 *out = dbl_nil;
    4031           0 :                 return MAL_SUCCEED;
    4032             :         }
    4033             : 
    4034           2 :         geosGeometry = wkb2geos(*geomWKB);
    4035           2 :         if (geosGeometry == NULL) {
    4036           0 :                 *out = dbl_nil;
    4037           0 :                 throw(MAL, "geom.Area", SQLSTATE(38000) "Geos operation wkb2geos failed");
    4038             :         }
    4039             : 
    4040           2 :         if (!GEOSArea(geosGeometry, out)) {
    4041           0 :                 GEOSGeom_destroy(geosGeometry);
    4042           0 :                 *out = dbl_nil;
    4043           0 :                 throw(MAL, "geom.Area", SQLSTATE(38000) "Geos operation GEOSArea failed");
    4044             :         }
    4045             : 
    4046           2 :         GEOSGeom_destroy(geosGeometry);
    4047             : 
    4048           2 :         return MAL_SUCCEED;
    4049             : }
    4050             : 
    4051             : /* returns the centroid of the geometry */
    4052             : str
    4053           2 : wkbCentroid(wkb **out, wkb **geom)
    4054             : {
    4055             :         GEOSGeom geosGeometry;
    4056             :         GEOSGeom outGeometry;
    4057             : 
    4058           2 :         if (is_wkb_nil(*geom)) {
    4059           0 :                 if ((*out = wkbNULLcopy()) == NULL)
    4060           0 :                         throw(MAL, "geom.Centroid", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4061             :                 return MAL_SUCCEED;
    4062             :         }
    4063           2 :         geosGeometry = wkb2geos(*geom);
    4064           2 :         if (geosGeometry == NULL)
    4065           0 :                 throw(MAL, "geom.Centroid", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4066             : 
    4067           2 :         outGeometry = GEOSGetCentroid(geosGeometry);
    4068           2 :         GEOSSetSRID(outGeometry, GEOSGetSRID(geosGeometry));    //the centroid has the same SRID with the the input geometry
    4069           2 :         *out = geos2wkb(outGeometry);
    4070             : 
    4071           2 :         GEOSGeom_destroy(geosGeometry);
    4072           2 :         GEOSGeom_destroy(outGeometry);
    4073             : 
    4074           2 :         return MAL_SUCCEED;
    4075             : 
    4076             : }
    4077             : 
    4078             : /*  Returns the 2-dimensional Cartesian minimum distance (based on spatial ref) between two geometries in projected units */
    4079             : str
    4080           2 : wkbDistance(dbl *out, wkb **a, wkb **b)
    4081             : {
    4082             :         GEOSGeom ga, gb;
    4083             :         str err = MAL_SUCCEED;
    4084             : 
    4085           2 :         if (is_wkb_nil(*a) || is_wkb_nil(*b)) {
    4086           0 :                 *out = dbl_nil;
    4087           0 :                 return MAL_SUCCEED;
    4088             :         }
    4089             : 
    4090           2 :         ga = wkb2geos(*a);
    4091           2 :         gb = wkb2geos(*b);
    4092           2 :         if (ga == NULL || gb == NULL) {
    4093           0 :                 if (ga)
    4094           0 :                         GEOSGeom_destroy(ga);
    4095           0 :                 if (gb)
    4096           0 :                         GEOSGeom_destroy(gb);
    4097           0 :                 *out = dbl_nil;
    4098           0 :                 throw(MAL, "geom.Distance", SQLSTATE(38000) "Geos operation wkb2geos failed");
    4099             :         }
    4100             : 
    4101           2 :         if (GEOSGetSRID(ga) != GEOSGetSRID(gb)) {
    4102           0 :                 err = createException(MAL, "geom.Distance", SQLSTATE(38000) "Geometries of different SRID");
    4103           2 :         } else if (!GEOSDistance(ga, gb, out)) {
    4104           0 :                 err = createException(MAL, "geom.Distance", SQLSTATE(38000) "Geos operation GEOSDistance failed");
    4105             :         }
    4106             : 
    4107           2 :         GEOSGeom_destroy(ga);
    4108           2 :         GEOSGeom_destroy(gb);
    4109             : 
    4110           2 :         return err;
    4111             : }
    4112             : 
    4113             : /* Returns the 2d length of the geometry if it is a linestring or multilinestring */
    4114             : str
    4115           2 : wkbLength(dbl *out, wkb **a)
    4116             : {
    4117             :         GEOSGeom geosGeometry;
    4118             :         str err = MAL_SUCCEED;
    4119             : 
    4120           2 :         if (is_wkb_nil(*a)) {
    4121           0 :                 *out = dbl_nil;
    4122           0 :                 return MAL_SUCCEED;
    4123             :         }
    4124             : 
    4125           2 :         geosGeometry = wkb2geos(*a);
    4126           2 :         if (geosGeometry == NULL) {
    4127           0 :                 throw(MAL, "geom.Length", SQLSTATE(38000) "Geos operation wkb2geos failed");
    4128             :         }
    4129             : 
    4130           2 :         if (!GEOSLength(geosGeometry, out))
    4131           0 :                 err = createException(MAL, "geom.Length", SQLSTATE(38000) "Geos operation GEOSLength failed");
    4132             : 
    4133           2 :         GEOSGeom_destroy(geosGeometry);
    4134             : 
    4135           2 :         return err;
    4136             : }
    4137             : 
    4138             : /* Returns a geometry that represents the convex hull of this geometry.
    4139             :  * The convex hull of a geometry represents the minimum convex geometry
    4140             :  * that encloses all geometries within the set. */
    4141             : str
    4142           1 : wkbConvexHull(wkb **out, wkb **geom)
    4143             : {
    4144             :         str ret = MAL_SUCCEED;
    4145             :         GEOSGeom geosGeometry;
    4146             :         GEOSGeom convexHullGeometry = NULL;
    4147             : 
    4148           1 :         if (is_wkb_nil(*geom)) {
    4149           0 :                 if ((*out = wkbNULLcopy()) == NULL)
    4150           0 :                         throw(MAL, "geom.ConvexHull", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4151             :                 return MAL_SUCCEED;
    4152             :         }
    4153           1 :         if ((geosGeometry = wkb2geos(*geom)) == NULL)
    4154           0 :                 throw(MAL, "geom.ConvexHull", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4155             : 
    4156           1 :         if ((convexHullGeometry = GEOSConvexHull(geosGeometry)) == NULL) {
    4157           0 :                 ret = createException(MAL, "geom.ConvexHull", SQLSTATE(38000) "Geos operation GEOSConvexHull failed");
    4158             :         } else {
    4159           1 :                 GEOSSetSRID(convexHullGeometry, (*geom)->srid);
    4160           1 :                 *out = geos2wkb(convexHullGeometry);
    4161           1 :                 GEOSGeom_destroy(convexHullGeometry);
    4162           1 :                 if (*out == NULL)
    4163           0 :                         ret = createException(MAL, "geom.ConvexHull", SQLSTATE(38000) "Geos operation geos2wkb failed");
    4164             :         }
    4165           1 :         GEOSGeom_destroy(geosGeometry);
    4166             : 
    4167           1 :         return ret;
    4168             : 
    4169             : }
    4170             : 
    4171             : /* Gets two geometries and returns a new geometry */
    4172             : static str
    4173           4 : wkbanalysis(wkb **out, wkb **geom1WKB, wkb **geom2WKB, GEOSGeometry *(*func) (const GEOSGeometry *, const GEOSGeometry *), const char *name)
    4174             : {
    4175             :         GEOSGeom outGeometry, geom1Geometry, geom2Geometry;
    4176             :         str err = MAL_SUCCEED;
    4177             : 
    4178           4 :         if (is_wkb_nil(*geom1WKB) || is_wkb_nil(*geom2WKB)) {
    4179           0 :                 if ((*out = wkbNULLcopy()) == NULL)
    4180           0 :                         throw(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4181             :                 return MAL_SUCCEED;
    4182             :         }
    4183             : 
    4184           4 :         geom1Geometry = wkb2geos(*geom1WKB);
    4185           4 :         geom2Geometry = wkb2geos(*geom2WKB);
    4186           4 :         if (geom1Geometry == NULL || geom2Geometry == NULL) {
    4187           0 :                 *out = NULL;
    4188           0 :                 if (geom1Geometry)
    4189           0 :                         GEOSGeom_destroy(geom1Geometry);
    4190           0 :                 if (geom2Geometry)
    4191           0 :                         GEOSGeom_destroy(geom2Geometry);
    4192           0 :                 throw(MAL, name, SQLSTATE(38000) "Geos operation wkb2geos failed");
    4193             :         }
    4194             : 
    4195             :         //make sure the geometries are of the same srid
    4196           4 :         if (GEOSGetSRID(geom1Geometry) != GEOSGetSRID(geom2Geometry)) {
    4197           0 :                 err = createException(MAL, name, SQLSTATE(38000) "Geometries of different SRID");
    4198           4 :         } else if ((outGeometry = (*func) (geom1Geometry, geom2Geometry)) == NULL) {
    4199           0 :                 err = createException(MAL, name, SQLSTATE(38000) "Geos operation GEOS%s failed", name + 5);
    4200             :         } else {
    4201           4 :                 GEOSSetSRID(outGeometry, GEOSGetSRID(geom1Geometry));
    4202           4 :                 *out = geos2wkb(outGeometry);
    4203           4 :                 GEOSGeom_destroy(outGeometry);
    4204             :         }
    4205           4 :         GEOSGeom_destroy(geom1Geometry);
    4206           4 :         GEOSGeom_destroy(geom2Geometry);
    4207             : 
    4208           4 :         return err;
    4209             : }
    4210             : 
    4211             : str
    4212           1 : wkbIntersection(wkb **out, wkb **a, wkb **b)
    4213             : {
    4214           1 :         return wkbanalysis(out, a, b, GEOSIntersection, "geom.Intersection");
    4215             : }
    4216             : 
    4217             : str
    4218           1 : wkbUnion(wkb **out, wkb **a, wkb **b)
    4219             : {
    4220           1 :         return wkbanalysis(out, a, b, GEOSUnion, "geom.Union");
    4221             : }
    4222             : 
    4223             : //Gets a BAT with geometries and returns a single LineString
    4224             : str
    4225           0 : wkbUnionAggr(wkb **outWKB, bat *inBAT_id)
    4226             : {
    4227             :         BAT *inBAT = NULL;
    4228             :         BATiter inBAT_iter;
    4229             :         BUN i;
    4230             :         str err;
    4231             :         wkb *aWKB, *bWKB;
    4232             : 
    4233             :         //get the BATs
    4234           0 :         if (!(inBAT = BATdescriptor(*inBAT_id))) {
    4235           0 :                 throw(MAL, "geom.Union", SQLSTATE(38000) "Geos problem retrieving columns");
    4236             :         }
    4237             : 
    4238           0 :         if (BATcount(inBAT) == 0) {
    4239           0 :                 BBPunfix(inBAT->batCacheid);
    4240           0 :                 if ((*outWKB = wkbNULLcopy()) == NULL)
    4241           0 :                         throw(MAL, "geom.Union", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4242             :                 return MAL_SUCCEED;
    4243             :         }
    4244             : 
    4245             :         //iterator over the BATs
    4246           0 :         inBAT_iter = bat_iterator(inBAT);
    4247             : 
    4248           0 :         aWKB = (wkb *) BUNtvar(inBAT_iter, 0);
    4249           0 :         if (BATcount(inBAT) == 1) {
    4250           0 :                 bat_iterator_end(&inBAT_iter);
    4251           0 :                 err = wkbFromWKB(outWKB, &aWKB);
    4252           0 :                 BBPunfix(inBAT->batCacheid);
    4253           0 :                 if (err) {
    4254           0 :                         freeException(err);
    4255           0 :                         throw(MAL, "geom.Union", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4256             :                 }
    4257             :                 return MAL_SUCCEED;
    4258             :         }
    4259           0 :         bWKB = (wkb *) BUNtvar(inBAT_iter, 1);
    4260             :         //create the first union using the first two geometries
    4261           0 :         err = wkbUnion(outWKB, &aWKB, &bWKB);
    4262           0 :         for (i = 2; err == MAL_SUCCEED && i < BATcount(inBAT); i++) {
    4263           0 :                 aWKB = *outWKB;
    4264           0 :                 bWKB = (wkb *) BUNtvar(inBAT_iter, i);
    4265           0 :                 *outWKB = NULL;
    4266             : 
    4267           0 :                 err = wkbUnion(outWKB, &aWKB, &bWKB);
    4268           0 :                 GDKfree(aWKB);
    4269             :         }
    4270             : 
    4271           0 :         bat_iterator_end(&inBAT_iter);
    4272           0 :         BBPunfix(inBAT->batCacheid);
    4273             : 
    4274           0 :         return err;
    4275             : 
    4276             : }
    4277             : 
    4278             : str
    4279           1 : wkbDifference(wkb **out, wkb **a, wkb **b)
    4280             : {
    4281           1 :         return wkbanalysis(out, a, b, GEOSDifference, "geom.Difference");
    4282             : }
    4283             : 
    4284             : str
    4285           1 : wkbSymDifference(wkb **out, wkb **a, wkb **b)
    4286             : {
    4287           1 :         return wkbanalysis(out, a, b, GEOSSymDifference, "geom.SymDifference");
    4288             : }
    4289             : 
    4290             : /* Returns a geometry that represents all points whose distance from this Geometry is less than or equal to distance. */
    4291             : str
    4292          11 : wkbBuffer(wkb **out, wkb **geom, dbl *distance)
    4293             : {
    4294             :         GEOSGeom geosGeometry;
    4295             :         GEOSGeom new;
    4296             : 
    4297          11 :         if (is_wkb_nil(*geom) || is_dbl_nil(*distance)) {
    4298           0 :                 if ((*out = wkbNULLcopy()) == NULL)
    4299           0 :                         throw(MAL, "geom.Buffer", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4300             :                 return MAL_SUCCEED;
    4301             :         }
    4302             : 
    4303          11 :         geosGeometry = wkb2geos(*geom);
    4304          11 :         if (geosGeometry == NULL) {
    4305           0 :                 throw(MAL, "geom.Buffer", SQLSTATE(38000) "Geos operation wkb2geos failed");
    4306             :         }
    4307             : 
    4308          11 :         if ((new = GEOSBuffer(geosGeometry, *distance, 18)) == NULL) {
    4309           0 :                 GEOSGeom_destroy(geosGeometry);
    4310           0 :                 throw(MAL, "geom.Buffer", SQLSTATE(38000) "Geos operation GEOSBuffer failed");
    4311             :         }
    4312          11 :         *out = geos2wkb(new);
    4313          11 :         GEOSGeom_destroy(new);
    4314          11 :         GEOSGeom_destroy(geosGeometry);
    4315             : 
    4316          11 :         if (*out == NULL)
    4317           0 :                 throw(MAL, "geom.Buffer", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4318             : 
    4319          11 :         (*out)->srid = (*geom)->srid;
    4320             : 
    4321          11 :         return MAL_SUCCEED;
    4322             : }
    4323             : 
    4324             : /* Gets two geometries and returns a Boolean by comparing them */
    4325             : static str
    4326          69 : wkbspatial(bit *out, wkb **geomWKB_a, wkb **geomWKB_b, char (*func) (const GEOSGeometry *, const GEOSGeometry *), const char *name)
    4327             : {
    4328             :         int res;
    4329             :         GEOSGeom geosGeometry_a, geosGeometry_b;
    4330             : 
    4331          69 :         if (is_wkb_nil(*geomWKB_a) || is_wkb_nil(*geomWKB_b)) {
    4332           0 :                 *out = bit_nil;
    4333           0 :                 return MAL_SUCCEED;
    4334             :         }
    4335             : 
    4336          69 :         geosGeometry_a = wkb2geos(*geomWKB_a);
    4337          69 :         geosGeometry_b = wkb2geos(*geomWKB_b);
    4338          69 :         if (geosGeometry_a == NULL || geosGeometry_b == NULL) {
    4339           0 :                 if (geosGeometry_a)
    4340           0 :                         GEOSGeom_destroy(geosGeometry_a);
    4341           0 :                 if (geosGeometry_b)
    4342           0 :                         GEOSGeom_destroy(geosGeometry_b);
    4343           0 :                 throw(MAL, name, SQLSTATE(38000) "Geos operation wkb2geos failed");
    4344             :         }
    4345             : 
    4346          69 :         if (GEOSGetSRID(geosGeometry_a) != GEOSGetSRID(geosGeometry_b)) {
    4347           0 :                 GEOSGeom_destroy(geosGeometry_a);
    4348           0 :                 GEOSGeom_destroy(geosGeometry_b);
    4349           0 :                 throw(MAL, name, SQLSTATE(38000) "Geometries of different SRID");
    4350             :         }
    4351             : 
    4352          69 :         res = (*func) (geosGeometry_a, geosGeometry_b);
    4353             : 
    4354          69 :         GEOSGeom_destroy(geosGeometry_a);
    4355          69 :         GEOSGeom_destroy(geosGeometry_b);
    4356             : 
    4357          69 :         if (res == 2)
    4358           0 :                 throw(MAL, name, SQLSTATE(38000) "Geos operation GEOS%s failed", name + 5);
    4359             : 
    4360          69 :         *out = res;
    4361             : 
    4362          69 :         return MAL_SUCCEED;
    4363             : }
    4364             : 
    4365             : str
    4366          29 : wkbContains(bit *out, wkb **geomWKB_a, wkb **geomWKB_b)
    4367             : {
    4368          29 :         return wkbspatial(out, geomWKB_a, geomWKB_b, GEOSContains, "geom.Contains");
    4369             : }
    4370             : 
    4371             : str
    4372           2 : wkbCrosses(bit *out, wkb **geomWKB_a, wkb **geomWKB_b)
    4373             : {
    4374           2 :         return wkbspatial(out, geomWKB_a, geomWKB_b, GEOSCrosses, "geom.Crosses");
    4375             : }
    4376             : 
    4377             : str
    4378           2 : wkbDisjoint(bit *out, wkb **geomWKB_a, wkb **geomWKB_b)
    4379             : {
    4380           2 :         return wkbspatial(out, geomWKB_a, geomWKB_b, GEOSDisjoint, "geom.Disjoint");
    4381             : }
    4382             : 
    4383             : str
    4384           6 : wkbEquals(bit *out, wkb **geomWKB_a, wkb **geomWKB_b)
    4385             : {
    4386           6 :         return wkbspatial(out, geomWKB_a, geomWKB_b, GEOSEquals, "geom.Equals");
    4387             : }
    4388             : 
    4389             : str
    4390           2 : wkbIntersects(bit *out, wkb **geomWKB_a, wkb **geomWKB_b)
    4391             : {
    4392           2 :         return wkbspatial(out, geomWKB_a, geomWKB_b, GEOSIntersects, "geom.Intersects");
    4393             : }
    4394             : 
    4395             : str
    4396           6 : wkbOverlaps(bit *out, wkb **geomWKB_a, wkb **geomWKB_b)
    4397             : {
    4398           6 :         return wkbspatial(out, geomWKB_a, geomWKB_b, GEOSOverlaps, "geom.Overlaps");
    4399             : }
    4400             : 
    4401             : str
    4402           1 : wkbRelate(bit *out, wkb **geomWKB_a, wkb **geomWKB_b, str *pattern)
    4403             : {
    4404             :         int res;
    4405             :         GEOSGeom geosGeometry_a, geosGeometry_b;
    4406             : 
    4407           2 :         if (is_wkb_nil(*geomWKB_a) || is_wkb_nil(*geomWKB_b) || strNil(*pattern)) {
    4408           0 :                 *out = bit_nil;
    4409           0 :                 return MAL_SUCCEED;
    4410             :         }
    4411             : 
    4412           1 :         geosGeometry_a = wkb2geos(*geomWKB_a);
    4413           1 :         geosGeometry_b = wkb2geos(*geomWKB_b);
    4414           1 :         if (geosGeometry_a == NULL || geosGeometry_b == NULL) {
    4415           0 :                 if (geosGeometry_a)
    4416           0 :                         GEOSGeom_destroy(geosGeometry_a);
    4417           0 :                 if (geosGeometry_b)
    4418           0 :                         GEOSGeom_destroy(geosGeometry_b);
    4419           0 :                 throw(MAL, "geom.RelatePattern", SQLSTATE(38000) "Geos operation wkb2geos failed");
    4420             :         }
    4421             : 
    4422           1 :         if (GEOSGetSRID(geosGeometry_a) != GEOSGetSRID(geosGeometry_b)) {
    4423           0 :                 GEOSGeom_destroy(geosGeometry_a);
    4424           0 :                 GEOSGeom_destroy(geosGeometry_b);
    4425           0 :                 throw(MAL, "geom.RelatePattern", SQLSTATE(38000) "Geometries of different SRID");
    4426             :         }
    4427             : 
    4428           1 :         res = GEOSRelatePattern(geosGeometry_a, geosGeometry_b, *pattern);
    4429             : 
    4430           1 :         GEOSGeom_destroy(geosGeometry_a);
    4431           1 :         GEOSGeom_destroy(geosGeometry_b);
    4432             : 
    4433           1 :         if (res == 2)
    4434           0 :                 throw(MAL, "geom.RelatePattern", SQLSTATE(38000) "Geos operation GEOSRelatePattern failed");
    4435             : 
    4436           1 :         *out = res;
    4437             : 
    4438           1 :         return MAL_SUCCEED;
    4439             : }
    4440             : 
    4441             : str
    4442           2 : wkbTouches(bit *out, wkb **geomWKB_a, wkb **geomWKB_b)
    4443             : {
    4444           2 :         return wkbspatial(out, geomWKB_a, geomWKB_b, GEOSTouches, "geom.Touches");
    4445             : }
    4446             : 
    4447             : str
    4448           2 : wkbWithin(bit *out, wkb **geomWKB_a, wkb **geomWKB_b)
    4449             : {
    4450           2 :         return wkbspatial(out, geomWKB_a, geomWKB_b, GEOSWithin, "geom.Within");
    4451             : }
    4452             : 
    4453             : str
    4454           9 : wkbCovers(bit *out, wkb **geomWKB_a, wkb **geomWKB_b)
    4455             : {
    4456           9 :         return wkbspatial(out, geomWKB_a, geomWKB_b, GEOSCovers, "geom.Covers");
    4457             : }
    4458             : 
    4459             : str
    4460           9 : wkbCoveredBy(bit *out, wkb **geomWKB_a, wkb **geomWKB_b)
    4461             : {
    4462           9 :         return wkbspatial(out, geomWKB_a, geomWKB_b, GEOSCoveredBy, "geom.CoveredBy");
    4463             : }
    4464             : 
    4465             : str
    4466           0 : wkbDWithin(bit *out, wkb **geomWKB_a, wkb **geomWKB_b, dbl *distance)
    4467             : {
    4468             :         double distanceComputed;
    4469             :         str err;
    4470             : 
    4471           0 :         if (is_wkb_nil(*geomWKB_a) || is_wkb_nil(*geomWKB_b) || is_dbl_nil(*distance)) {
    4472           0 :                 *out = bit_nil;
    4473           0 :                 return MAL_SUCCEED;
    4474             :         }
    4475           0 :         if ((err = wkbDistance(&distanceComputed, geomWKB_a, geomWKB_b)) != MAL_SUCCEED) {
    4476             :                 return err;
    4477             :         }
    4478             : 
    4479           0 :         *out = (distanceComputed <= *distance);
    4480             : 
    4481           0 :         return MAL_SUCCEED;
    4482             : }
    4483             : 
    4484             : /*returns the n-th geometry in a multi-geometry */
    4485             : str
    4486          13 : wkbGeometryN(wkb **out, wkb **geom, const int *geometryNum)
    4487             : {
    4488             :         int geometriesNum = -1;
    4489             :         GEOSGeom geosGeometry = NULL;
    4490             : 
    4491             :         //no geometry at this position
    4492          13 :         if (is_wkb_nil(*geom) || is_int_nil(*geometryNum) || *geometryNum <= 0) {
    4493           1 :                 if ((*out = wkbNULLcopy()) == NULL)
    4494           0 :                         throw(MAL, "geom.GeometryN", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4495             :                 return MAL_SUCCEED;
    4496             :         }
    4497             : 
    4498          12 :         geosGeometry = wkb2geos(*geom);
    4499             : 
    4500          12 :         if (geosGeometry == NULL) {
    4501           0 :                 *out = NULL;
    4502           0 :                 throw(MAL, "geom.GeometryN", SQLSTATE(38000) "Geos operation wkb2geos failed");
    4503             :         }
    4504             : 
    4505          12 :         geometriesNum = GEOSGetNumGeometries(geosGeometry);
    4506          12 :         if (geometriesNum < 0) {
    4507           0 :                 *out = NULL;
    4508           0 :                 GEOSGeom_destroy(geosGeometry);
    4509           0 :                 throw(MAL, "geom.GeometryN", SQLSTATE(38000) "Geos operation GEOSGetNumGeometries failed");
    4510             :         }
    4511          12 :         if (geometriesNum == 1 || //geometry is not a multi geometry
    4512          11 :             geometriesNum < *geometryNum) { //no geometry at this position
    4513           2 :                 GEOSGeom_destroy(geosGeometry);
    4514           2 :                 if ((*out = wkbNULLcopy()) == NULL)
    4515           0 :                         throw(MAL, "geom.GeometryN", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4516             :                 return MAL_SUCCEED;
    4517             :         }
    4518             : 
    4519          10 :         *out = geos2wkb(GEOSGetGeometryN(geosGeometry, *geometryNum - 1));
    4520          10 :         GEOSGeom_destroy(geosGeometry);
    4521          10 :         if (*out == NULL)
    4522           0 :                 throw(MAL, "geom.GeometryN", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4523             : 
    4524             :         return MAL_SUCCEED;
    4525             : }
    4526             : 
    4527             : /* returns the number of geometries */
    4528             : str
    4529          10 : wkbNumGeometries(int *out, wkb **geom)
    4530             : {
    4531             :         GEOSGeom geosGeometry;
    4532             : 
    4533          10 :         if (is_wkb_nil(*geom)) {
    4534           0 :                 *out = int_nil;
    4535           0 :                 return MAL_SUCCEED;
    4536             :         }
    4537             : 
    4538          10 :         geosGeometry = wkb2geos(*geom);
    4539          10 :         if (geosGeometry == NULL) {
    4540           0 :                 *out = int_nil;
    4541           0 :                 throw(MAL, "geom.NumGeometries", SQLSTATE(38000) "Geos operation wkb2geos failed");
    4542             :         }
    4543             : 
    4544          10 :         *out = GEOSGetNumGeometries(geosGeometry);
    4545          10 :         GEOSGeom_destroy(geosGeometry);
    4546          10 :         if (*out < 0) {
    4547           0 :                 *out = int_nil;
    4548           0 :                 throw(MAL, "geom.GeometryN", SQLSTATE(38000) "Geos operation GEOSGetNumGeometries failed");
    4549             :         }
    4550             : 
    4551             :         return MAL_SUCCEED;
    4552             : }
    4553             : 
    4554             : /* MBR */
    4555             : 
    4556             : /* Creates the mbr for the given geom_geometry. */
    4557             : str
    4558         653 : wkbMBR(mbr **geomMBR, wkb **geomWKB)
    4559             : {
    4560             :         GEOSGeom geosGeometry;
    4561             :         str ret = MAL_SUCCEED;
    4562             :         bit empty;
    4563             : 
    4564             :         //check if the geometry is nil
    4565         653 :         if (is_wkb_nil(*geomWKB)) {
    4566           2 :                 if ((*geomMBR = GDKmalloc(sizeof(mbr))) == NULL)
    4567           0 :                         throw(MAL, "geom.MBR", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4568           2 :                 **geomMBR = mbrNIL;
    4569           2 :                 return MAL_SUCCEED;
    4570             :         }
    4571             :         //check if the geometry is empty
    4572         651 :         if ((ret = wkbIsEmpty(&empty, geomWKB)) != MAL_SUCCEED) {
    4573             :                 return ret;
    4574             :         }
    4575         651 :         if (empty) {
    4576           5 :                 if ((*geomMBR = GDKmalloc(sizeof(mbr))) == NULL)
    4577           0 :                         throw(MAL, "geom.MBR", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4578           5 :                 **geomMBR = mbrNIL;
    4579           5 :                 return MAL_SUCCEED;
    4580             :         }
    4581             : 
    4582         646 :         geosGeometry = wkb2geos(*geomWKB);
    4583         645 :         if (geosGeometry == NULL) {
    4584           0 :                 *geomMBR = NULL;
    4585           0 :                 throw(MAL, "geom.MBR", SQLSTATE(38000) "Geos problem converting GEOS to WKB");
    4586             :         }
    4587             : 
    4588         645 :         *geomMBR = mbrFromGeos(geosGeometry);
    4589             : 
    4590         646 :         GEOSGeom_destroy(geosGeometry);
    4591             : 
    4592         646 :         if (*geomMBR == NULL || is_mbr_nil(*geomMBR)) {
    4593           0 :                 GDKfree(*geomMBR);
    4594           0 :                 *geomMBR = NULL;
    4595           0 :                 throw(MAL, "wkb.mbr", SQLSTATE(38000) "Geos failed to create mbr");
    4596             :         }
    4597             : 
    4598             :         return MAL_SUCCEED;
    4599             : }
    4600             : 
    4601             : str
    4602          23 : wkbBox2D(mbr **box, wkb **point1, wkb **point2)
    4603             : {
    4604             :         GEOSGeom point1_geom, point2_geom;
    4605          23 :         double xmin = 0.0, ymin = 0.0, xmax = 0.0, ymax = 0.0;
    4606             :         str err = MAL_SUCCEED;
    4607             : 
    4608             :         //check null input
    4609          23 :         if (is_wkb_nil(*point1) || is_wkb_nil(*point2)) {
    4610           1 :                 if ((*box = GDKmalloc(sizeof(mbr))) == NULL)
    4611           0 :                         throw(MAL, "geom.MakeBox2D", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4612           1 :                 **box = mbrNIL;
    4613           1 :                 return MAL_SUCCEED;
    4614             :         }
    4615             :         //check input not point geometries
    4616          22 :         point1_geom = wkb2geos(*point1);
    4617          22 :         point2_geom = wkb2geos(*point2);
    4618          22 :         if (point1_geom == NULL || point2_geom == NULL) {
    4619           0 :                 if (point1_geom)
    4620           0 :                         GEOSGeom_destroy(point1_geom);
    4621           0 :                 if (point2_geom)
    4622           0 :                         GEOSGeom_destroy(point2_geom);
    4623           0 :                 throw(MAL, "geom.MakeBox2D", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4624             :         }
    4625          44 :         if (GEOSGeomTypeId(point1_geom) + 1 != wkbPoint_mdb ||
    4626          22 :             GEOSGeomTypeId(point2_geom) + 1 != wkbPoint_mdb) {
    4627           1 :                 err = createException(MAL, "geom.MakeBox2D", SQLSTATE(38000) "Geometries should be points");
    4628          42 :         } else if (GEOSGeomGetX(point1_geom, &xmin) == -1 ||
    4629          42 :                    GEOSGeomGetY(point1_geom, &ymin) == -1 ||
    4630          42 :                    GEOSGeomGetX(point2_geom, &xmax) == -1 ||
    4631          21 :                    GEOSGeomGetY(point2_geom, &ymax) == -1) {
    4632             : 
    4633           0 :                 err = createException(MAL, "geom.MakeBox2D", SQLSTATE(38000) "Geos error in reading the points' coordinates");
    4634             :         } else {
    4635             :                 //Assign the coordinates. Ensure that they are in correct order
    4636          21 :                 *box = GDKmalloc(sizeof(mbr));
    4637          21 :                 if (*box == NULL) {
    4638           0 :                         err = createException(MAL, "geom.MakeBox2D", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    4639             :                 } else {
    4640          21 :                         (*box)->xmin = (float) (xmin < xmax ? xmin : xmax);
    4641          21 :                         (*box)->ymin = (float) (ymin < ymax ? ymin : ymax);
    4642          21 :                         (*box)->xmax = (float) (xmax > xmin ? xmax : xmin);
    4643          31 :                         (*box)->ymax = (float) (ymax > ymin ? ymax : ymin);
    4644             :                 }
    4645             :         }
    4646          22 :         GEOSGeom_destroy(point1_geom);
    4647          22 :         GEOSGeom_destroy(point2_geom);
    4648             : 
    4649          22 :         return err;
    4650             : }
    4651             : 
    4652             : static str
    4653         154 : mbrrelation_wkb(bit *out, wkb **geom1WKB, wkb **geom2WKB, str (*func)(bit *, mbr **, mbr **))
    4654             : {
    4655         154 :         mbr *geom1MBR = NULL, *geom2MBR = NULL;
    4656             :         str ret = MAL_SUCCEED;
    4657             : 
    4658         154 :         if (is_wkb_nil(*geom1WKB) || is_wkb_nil(*geom2WKB)) {
    4659          29 :                 *out = bit_nil;
    4660          29 :                 return MAL_SUCCEED;
    4661             :         }
    4662             : 
    4663         125 :         ret = wkbMBR(&geom1MBR, geom1WKB);
    4664         125 :         if (ret != MAL_SUCCEED) {
    4665             :                 return ret;
    4666             :         }
    4667             : 
    4668         125 :         ret = wkbMBR(&geom2MBR, geom2WKB);
    4669         125 :         if (ret != MAL_SUCCEED) {
    4670           0 :                 GDKfree(geom1MBR);
    4671           0 :                 return ret;
    4672             :         }
    4673             : 
    4674         125 :         ret = (*func) (out, &geom1MBR, &geom2MBR);
    4675             : 
    4676         125 :         GDKfree(geom1MBR);
    4677         125 :         GDKfree(geom2MBR);
    4678             : 
    4679         125 :         return ret;
    4680             : }
    4681             : 
    4682             : /*returns true if the two mbrs overlap */
    4683             : str
    4684          13 : mbrOverlaps(bit *out, mbr **b1, mbr **b2)
    4685             : {
    4686          13 :         if (is_mbr_nil(*b1) || is_mbr_nil(*b2))
    4687           0 :                 *out = bit_nil;
    4688             :         else                    //they cannot overlap if b2 is left, right, above or below b1
    4689          26 :                 *out = !((*b2)->ymax < (*b1)->ymin ||
    4690          11 :                          (*b2)->ymin > (*b1)->ymax ||
    4691          11 :                          (*b2)->xmax < (*b1)->xmin ||
    4692          11 :                          (*b2)->xmin > (*b1)->xmax);
    4693          13 :         return MAL_SUCCEED;
    4694             : }
    4695             : 
    4696             : /*returns true if the mbrs of the two geometries overlap */
    4697             : str
    4698           5 : mbrOverlaps_wkb(bit *out, wkb **geom1WKB, wkb **geom2WKB)
    4699             : {
    4700           5 :         return mbrrelation_wkb(out, geom1WKB, geom2WKB, mbrOverlaps);
    4701             : }
    4702             : 
    4703             : /* returns true if b1 is above b2 */
    4704             : str
    4705           5 : mbrAbove(bit *out, mbr **b1, mbr **b2)
    4706             : {
    4707           5 :         if (is_mbr_nil(*b1) || is_mbr_nil(*b2))
    4708           0 :                 *out = bit_nil;
    4709             :         else
    4710           5 :                 *out = ((*b1)->ymin > (*b2)->ymax);
    4711           5 :         return MAL_SUCCEED;
    4712             : }
    4713             : 
    4714             : /*returns true if the mbrs of geom1 is above the mbr of geom2 */
    4715             : str
    4716           6 : mbrAbove_wkb(bit *out, wkb **geom1WKB, wkb **geom2WKB)
    4717             : {
    4718           6 :         return mbrrelation_wkb(out, geom1WKB, geom2WKB, mbrAbove);
    4719             : }
    4720             : 
    4721             : /* returns true if b1 is below b2 */
    4722             : str
    4723           5 : mbrBelow(bit *out, mbr **b1, mbr **b2)
    4724             : {
    4725           5 :         if (is_mbr_nil(*b1) || is_mbr_nil(*b2))
    4726           0 :                 *out = bit_nil;
    4727             :         else
    4728           5 :                 *out = ((*b1)->ymax < (*b2)->ymin);
    4729           5 :         return MAL_SUCCEED;
    4730             : }
    4731             : 
    4732             : /*returns true if the mbrs of geom1 is below the mbr of geom2 */
    4733             : str
    4734           6 : mbrBelow_wkb(bit *out, wkb **geom1WKB, wkb **geom2WKB)
    4735             : {
    4736           6 :         return mbrrelation_wkb(out, geom1WKB, geom2WKB, mbrBelow);
    4737             : }
    4738             : 
    4739             : /* returns true if box1 is left of box2 */
    4740             : str
    4741          64 : mbrLeft(bit *out, mbr **b1, mbr **b2)
    4742             : {
    4743          64 :         if (is_mbr_nil(*b1) || is_mbr_nil(*b2))
    4744           0 :                 *out = bit_nil;
    4745             :         else
    4746          64 :                 *out = ((*b1)->xmax < (*b2)->xmin);
    4747          64 :         return MAL_SUCCEED;
    4748             : }
    4749             : 
    4750             : /*returns true if the mbrs of geom1 is on the left of the mbr of geom2 */
    4751             : str
    4752          83 : mbrLeft_wkb(bit *out, wkb **geom1WKB, wkb **geom2WKB)
    4753             : {
    4754          83 :         return mbrrelation_wkb(out, geom1WKB, geom2WKB, mbrLeft);
    4755             : }
    4756             : 
    4757             : /* returns true if box1 is right of box2 */
    4758             : str
    4759          10 : mbrRight(bit *out, mbr **b1, mbr **b2)
    4760             : {
    4761          10 :         if (is_mbr_nil(*b1) || is_mbr_nil(*b2))
    4762           0 :                 *out = bit_nil;
    4763             :         else
    4764          10 :                 *out = ((*b1)->xmin > (*b2)->xmax);
    4765          10 :         return MAL_SUCCEED;
    4766             : }
    4767             : 
    4768             : /*returns true if the mbrs of geom1 is on the right of the mbr of geom2 */
    4769             : str
    4770          11 : mbrRight_wkb(bit *out, wkb **geom1WKB, wkb **geom2WKB)
    4771             : {
    4772          11 :         return mbrrelation_wkb(out, geom1WKB, geom2WKB, mbrRight);
    4773             : }
    4774             : 
    4775             : /* returns true if box1 overlaps or is above box2 when only the Y coordinate is considered*/
    4776             : str
    4777           0 : mbrOverlapOrAbove(bit *out, mbr **b1, mbr **b2)
    4778             : {
    4779           0 :         if (is_mbr_nil(*b1) || is_mbr_nil(*b2))
    4780           0 :                 *out = bit_nil;
    4781             :         else
    4782           0 :                 *out = ((*b1)->ymin >= (*b2)->ymin);
    4783           0 :         return MAL_SUCCEED;
    4784             : }
    4785             : 
    4786             : /*returns true if the mbrs of geom1 overlaps or is above the mbr of geom2 */
    4787             : str
    4788           0 : mbrOverlapOrAbove_wkb(bit *out, wkb **geom1WKB, wkb **geom2WKB)
    4789             : {
    4790           0 :         return mbrrelation_wkb(out, geom1WKB, geom2WKB, mbrOverlapOrAbove);
    4791             : }
    4792             : 
    4793             : /* returns true if box1 overlaps or is below box2 when only the Y coordinate is considered*/
    4794             : str
    4795           0 : mbrOverlapOrBelow(bit *out, mbr **b1, mbr **b2)
    4796             : {
    4797           0 :         if (is_mbr_nil(*b1) || is_mbr_nil(*b2))
    4798           0 :                 *out = bit_nil;
    4799             :         else
    4800           0 :                 *out = ((*b1)->ymax <= (*b2)->ymax);
    4801           0 :         return MAL_SUCCEED;
    4802             : }
    4803             : 
    4804             : /*returns true if the mbrs of geom1 overlaps or is below the mbr of geom2 */
    4805             : str
    4806           0 : mbrOverlapOrBelow_wkb(bit *out, wkb **geom1WKB, wkb **geom2WKB)
    4807             : {
    4808           0 :         return mbrrelation_wkb(out, geom1WKB, geom2WKB, mbrOverlapOrBelow);
    4809             : }
    4810             : 
    4811             : /* returns true if box1 overlaps or is left of box2 when only the X coordinate is considered*/
    4812             : str
    4813           9 : mbrOverlapOrLeft(bit *out, mbr **b1, mbr **b2)
    4814             : {
    4815           9 :         if (is_mbr_nil(*b1) || is_mbr_nil(*b2))
    4816           0 :                 *out = bit_nil;
    4817             :         else
    4818           9 :                 *out = ((*b1)->xmax <= (*b2)->xmax);
    4819           9 :         return MAL_SUCCEED;
    4820             : }
    4821             : 
    4822             : /*returns true if the mbrs of geom1 overlaps or is on the left of the mbr of geom2 */
    4823             : str
    4824           5 : mbrOverlapOrLeft_wkb(bit *out, wkb **geom1WKB, wkb **geom2WKB)
    4825             : {
    4826           5 :         return mbrrelation_wkb(out, geom1WKB, geom2WKB, mbrOverlapOrLeft);
    4827             : }
    4828             : 
    4829             : /* returns true if box1 overlaps or is right of box2 when only the X coordinate is considered*/
    4830             : str
    4831           9 : mbrOverlapOrRight(bit *out, mbr **b1, mbr **b2)
    4832             : {
    4833           9 :         if (is_mbr_nil(*b1) || is_mbr_nil(*b2))
    4834           0 :                 *out = bit_nil;
    4835             :         else
    4836           9 :                 *out = ((*b1)->xmin >= (*b2)->xmin);
    4837           9 :         return MAL_SUCCEED;
    4838             : }
    4839             : 
    4840             : /*returns true if the mbrs of geom1 overlaps or is on the right of the mbr of geom2 */
    4841             : str
    4842           5 : mbrOverlapOrRight_wkb(bit *out, wkb **geom1WKB, wkb **geom2WKB)
    4843             : {
    4844           5 :         return mbrrelation_wkb(out, geom1WKB, geom2WKB, mbrOverlapOrRight);
    4845             : }
    4846             : 
    4847             : /* returns true if b1 is contained in b2 */
    4848             : str
    4849          44 : mbrContained(bit *out, mbr **b1, mbr **b2)
    4850             : {
    4851          44 :         if (is_mbr_nil(*b1) || is_mbr_nil(*b2))
    4852           0 :                 *out = bit_nil;
    4853             :         else
    4854          71 :                 *out = (((*b1)->xmin >= (*b2)->xmin) && ((*b1)->xmax <= (*b2)->xmax) && ((*b1)->ymin >= (*b2)->ymin) && ((*b1)->ymax <= (*b2)->ymax));
    4855          44 :         return MAL_SUCCEED;
    4856             : }
    4857             : 
    4858             : /*returns true if the mbrs of geom1 is contained in the mbr of geom2 */
    4859             : str
    4860          17 : mbrContained_wkb(bit *out, wkb **geom1WKB, wkb **geom2WKB)
    4861             : {
    4862          17 :         return mbrrelation_wkb(out, geom1WKB, geom2WKB, mbrContained);
    4863             : }
    4864             : 
    4865             : /*returns true if b1 contains b2 */
    4866             : str
    4867          25 : mbrContains(bit *out, mbr **b1, mbr **b2)
    4868             : {
    4869          25 :         return mbrContained(out, b2, b1);
    4870             : }
    4871             : 
    4872             : /*returns true if the mbrs of geom1 contains the mbr of geom2 */
    4873             : str
    4874           5 : mbrContains_wkb(bit *out, wkb **geom1WKB, wkb **geom2WKB)
    4875             : {
    4876           5 :         return mbrrelation_wkb(out, geom1WKB, geom2WKB, mbrContains);
    4877             : }
    4878             : 
    4879             : /* returns true if the boxes are the same */
    4880             : str
    4881          30 : mbrEqual(bit *out, mbr **b1, mbr **b2)
    4882             : {
    4883          30 :         if (is_mbr_nil(*b1) && is_mbr_nil(*b2))
    4884           0 :                 *out = 1;
    4885          30 :         else if (is_mbr_nil(*b1) || is_mbr_nil(*b2))
    4886           0 :                 *out = 0;
    4887             :         else
    4888          53 :                 *out = (((*b1)->xmin == (*b2)->xmin) && ((*b1)->xmax == (*b2)->xmax) && ((*b1)->ymin == (*b2)->ymin) && ((*b1)->ymax == (*b2)->ymax));
    4889          30 :         return MAL_SUCCEED;
    4890             : }
    4891             : 
    4892             : /*returns true if the mbrs of geom1 and the mbr of geom2 are the same */
    4893             : str
    4894          11 : mbrEqual_wkb(bit *out, wkb **geom1WKB, wkb **geom2WKB)
    4895             : {
    4896          11 :         return mbrrelation_wkb(out, geom1WKB, geom2WKB, mbrEqual);
    4897             : }
    4898             : 
    4899             : /* returns the Euclidean distance of the centroids of the boxes */
    4900             : str
    4901         125 : mbrDistance(dbl *out, mbr **b1, mbr **b2)
    4902             : {
    4903             :         double b1_Cx = 0.0, b1_Cy = 0.0, b2_Cx = 0.0, b2_Cy = 0.0;
    4904             : 
    4905         125 :         if (is_mbr_nil(*b1) || is_mbr_nil(*b2)) {
    4906           0 :                 *out = dbl_nil;
    4907           0 :                 return MAL_SUCCEED;
    4908             :         }
    4909             :         //compute the centroids of the two polygons
    4910         125 :         b1_Cx = ((*b1)->xmin + (*b1)->xmax) / 2.0;
    4911         125 :         b1_Cy = ((*b1)->ymin + (*b1)->ymax) / 2.0;
    4912         125 :         b2_Cx = ((*b2)->xmin + (*b2)->xmax) / 2.0;
    4913         125 :         b2_Cy = ((*b2)->ymin + (*b2)->ymax) / 2.0;
    4914             : 
    4915             :         //compute the euclidean distance
    4916         125 :         *out = sqrt(pow(b2_Cx - b1_Cx, 2.0) + pow(b2_Cy - b1_Cy, 2.0));
    4917             : 
    4918         125 :         return MAL_SUCCEED;
    4919             : }
    4920             : 
    4921             : /*returns the Euclidean distance of the centroids of the mbrs of the two geometries */
    4922             : str
    4923         170 : mbrDistance_wkb(dbl *out, wkb **geom1WKB, wkb **geom2WKB)
    4924             : {
    4925         170 :         mbr *geom1MBR = NULL, *geom2MBR = NULL;
    4926             :         str ret = MAL_SUCCEED;
    4927             : 
    4928         170 :         if (is_wkb_nil(*geom1WKB) || is_wkb_nil(*geom2WKB)) {
    4929          45 :                 *out = dbl_nil;
    4930          45 :                 return MAL_SUCCEED;
    4931             :         }
    4932             : 
    4933         125 :         ret = wkbMBR(&geom1MBR, geom1WKB);
    4934         125 :         if (ret != MAL_SUCCEED) {
    4935             :                 return ret;
    4936             :         }
    4937             : 
    4938         125 :         ret = wkbMBR(&geom2MBR, geom2WKB);
    4939         125 :         if (ret != MAL_SUCCEED) {
    4940           0 :                 GDKfree(geom1MBR);
    4941           0 :                 return ret;
    4942             :         }
    4943             : 
    4944         125 :         ret = mbrDistance(out, &geom1MBR, &geom2MBR);
    4945             : 
    4946         125 :         GDKfree(geom1MBR);
    4947         125 :         GDKfree(geom2MBR);
    4948             : 
    4949         125 :         return ret;
    4950             : }
    4951             : 
    4952             : /* get Xmin, Ymin, Xmax, Ymax coordinates of mbr */
    4953             : str
    4954         216 : wkbCoordinateFromMBR(dbl *coordinateValue, mbr **geomMBR, int *coordinateIdx)
    4955             : {
    4956             :         //check if the MBR is null
    4957         216 :         if (is_mbr_nil(*geomMBR) || is_int_nil(*coordinateIdx)) {
    4958           8 :                 *coordinateValue = dbl_nil;
    4959           8 :                 return MAL_SUCCEED;
    4960             :         }
    4961             : 
    4962         208 :         switch (*coordinateIdx) {
    4963          52 :         case 1:
    4964          52 :                 *coordinateValue = (*geomMBR)->xmin;
    4965          52 :                 break;
    4966          52 :         case 2:
    4967          52 :                 *coordinateValue = (*geomMBR)->ymin;
    4968          52 :                 break;
    4969          52 :         case 3:
    4970          52 :                 *coordinateValue = (*geomMBR)->xmax;
    4971          52 :                 break;
    4972          52 :         case 4:
    4973          52 :                 *coordinateValue = (*geomMBR)->ymax;
    4974          52 :                 break;
    4975           0 :         default:
    4976           0 :                 throw(MAL, "geom.coordinateFromMBR", SQLSTATE(38000) "Geos unrecognized coordinateIdx: %d\n", *coordinateIdx);
    4977             :         }
    4978             : 
    4979             :         return MAL_SUCCEED;
    4980             : }
    4981             : 
    4982             : str
    4983           0 : wkbCoordinateFromWKB(dbl *coordinateValue, wkb **geomWKB, int *coordinateIdx)
    4984             : {
    4985             :         mbr *geomMBR;
    4986             :         str ret = MAL_SUCCEED;
    4987             :         bit empty;
    4988             : 
    4989           0 :         if (is_wkb_nil(*geomWKB) || is_int_nil(*coordinateIdx)) {
    4990           0 :                 *coordinateValue = dbl_nil;
    4991           0 :                 return MAL_SUCCEED;
    4992             :         }
    4993             : 
    4994             :         //check if the geometry is empty
    4995           0 :         if ((ret = wkbIsEmpty(&empty, geomWKB)) != MAL_SUCCEED) {
    4996             :                 return ret;
    4997             :         }
    4998             : 
    4999           0 :         if (empty) {
    5000           0 :                 *coordinateValue = dbl_nil;
    5001           0 :                 return MAL_SUCCEED;
    5002             :         }
    5003             : 
    5004           0 :         if ((ret = wkbMBR(&geomMBR, geomWKB)) != MAL_SUCCEED)
    5005             :                 return ret;
    5006             : 
    5007           0 :         ret = wkbCoordinateFromMBR(coordinateValue, &geomMBR, coordinateIdx);
    5008             : 
    5009           0 :         GDKfree(geomMBR);
    5010             : 
    5011           0 :         return ret;
    5012             : }
    5013             : 
    5014             : static ssize_t mbrFROMSTR(const char *src, size_t *len, void **ATOM, bool external);
    5015             : 
    5016             : str
    5017           2 : mbrFromString(mbr **w, const char **src)
    5018             : {
    5019           2 :         size_t len = *w ? sizeof(mbr) : 0;
    5020             :         char *errbuf;
    5021             :         str ex;
    5022             : 
    5023           2 :         if (mbrFROMSTR(*src, &len, (void **) w, false) >= 0)
    5024             :                 return MAL_SUCCEED;
    5025           0 :         GDKfree(*w);
    5026           0 :         *w = NULL;
    5027           0 :         errbuf = GDKerrbuf;
    5028           0 :         if (errbuf) {
    5029           0 :                 if (strncmp(errbuf, "!ERROR: ", 8) == 0)
    5030           0 :                         errbuf += 8;
    5031             :         } else {
    5032             :                 errbuf = "cannot parse string";
    5033             :         }
    5034             : 
    5035           0 :         ex = createException(MAL, "mbr.FromString", SQLSTATE(38000) "Geos %s", errbuf);
    5036             : 
    5037           0 :         GDKclrerr();
    5038             : 
    5039           0 :         return ex;
    5040             : }
    5041             : 
    5042             : str
    5043           0 : wkbIsnil(bit *r, wkb **v)
    5044             : {
    5045           0 :         *r = is_wkb_nil(*v);
    5046           0 :         return MAL_SUCCEED;
    5047             : }
    5048             : 
    5049             : /* COMMAND mbr
    5050             :  * Creates the mbr for the given geom_geometry.
    5051             :  */
    5052             : 
    5053             : str
    5054           0 : ordinatesMBR(mbr **res, flt *minX, flt *minY, flt *maxX, flt *maxY)
    5055             : {
    5056           0 :         if ((*res = GDKmalloc(sizeof(mbr))) == NULL)
    5057           0 :                 throw(MAL, "geom.mbr", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    5058           0 :         if (is_flt_nil(*minX) || is_flt_nil(*minY) || is_flt_nil(*maxX) || is_flt_nil(*maxY))
    5059           0 :                 **res = mbrNIL;
    5060             :         else {
    5061           0 :                 (*res)->xmin = *minX;
    5062           0 :                 (*res)->ymin = *minY;
    5063           0 :                 (*res)->xmax = *maxX;
    5064           0 :                 (*res)->ymax = *maxY;
    5065             :         }
    5066             :         return MAL_SUCCEED;
    5067             : }
    5068             : 
    5069             : /***********************************************/
    5070             : /************* wkb type functions **************/
    5071             : /***********************************************/
    5072             : 
    5073             : /* Creates the string representation (WKT) of a WKB */
    5074             : /* return length of resulting string. */
    5075             : static ssize_t
    5076         864 : wkbTOSTR(char **geomWKT, size_t *len, const void *GEOMWKB, bool external)
    5077             : {
    5078             :         const wkb *geomWKB = GEOMWKB;
    5079             :         char *wkt = NULL;
    5080             :         size_t dstStrLen = 5;   /* "nil" */
    5081             : 
    5082             :         /* from WKB to GEOSGeometry */
    5083         864 :         GEOSGeom geosGeometry = wkb2geos(geomWKB);
    5084             : 
    5085         864 :         if (geosGeometry) {
    5086             :                 size_t l;
    5087         864 :                 GEOSWKTWriter *WKT_wr = GEOSWKTWriter_create();
    5088             :                 //set the number of dimensions in the writer so that it can
    5089             :                 //read correctly the geometry coordinates
    5090         864 :                 GEOSWKTWriter_setOutputDimension(WKT_wr, GEOSGeom_getCoordinateDimension(geosGeometry));
    5091         864 :                 GEOSWKTWriter_setTrim(WKT_wr, 1);
    5092         864 :                 wkt = GEOSWKTWriter_write(WKT_wr, geosGeometry);
    5093         864 :                 if (wkt == NULL) {
    5094           0 :                         GDKerror("GEOSWKTWriter_write failed\n");
    5095           0 :                         return -1;
    5096             :                 }
    5097         864 :                 GEOSWKTWriter_destroy(WKT_wr);
    5098         864 :                 GEOSGeom_destroy(geosGeometry);
    5099             : 
    5100         864 :                 l = strlen(wkt);
    5101             :                 dstStrLen = l;
    5102         864 :                 if (external)
    5103         721 :                         dstStrLen += 2; /* add quotes */
    5104         864 :                 if (*len < dstStrLen + 1 || *geomWKT == NULL) {
    5105          95 :                         *len = dstStrLen + 1;
    5106          95 :                         GDKfree(*geomWKT);
    5107          95 :                         if ((*geomWKT = GDKmalloc(*len)) == NULL) {
    5108           0 :                                 GEOSFree(wkt);
    5109           0 :                                 return -1;
    5110             :                         }
    5111             :                 }
    5112         864 :                 if (external)
    5113         721 :                         snprintf(*geomWKT, *len, "\"%s\"", wkt);
    5114             :                 else
    5115         143 :                         strcpy(*geomWKT, wkt);
    5116         864 :                 GEOSFree(wkt);
    5117             : 
    5118         864 :                 return (ssize_t) dstStrLen;
    5119             :         }
    5120             : 
    5121             :         /* geosGeometry == NULL */
    5122           0 :         if (*len < 4 || *geomWKT == NULL) {
    5123           0 :                 GDKfree(*geomWKT);
    5124           0 :                 if ((*geomWKT = GDKmalloc(*len = 4)) == NULL)
    5125             :                         return -1;
    5126             :         }
    5127           0 :         if (external) {
    5128           0 :                 strcpy(*geomWKT, "nil");
    5129           0 :                 return 3;
    5130             :         }
    5131           0 :         strcpy(*geomWKT, str_nil);
    5132           0 :         return 1;
    5133             : }
    5134             : 
    5135             : static ssize_t
    5136           2 : wkbFROMSTR(const char *geomWKT, size_t *len, void **GEOMWKB, bool external)
    5137             : {
    5138             :         wkb **geomWKB = (wkb **) GEOMWKB;
    5139             :         size_t parsedBytes;
    5140             :         str err;
    5141             : 
    5142           2 :         if (external && strncmp(geomWKT, "nil", 3) == 0) {
    5143           0 :                 *geomWKB = wkbNULLcopy();
    5144           0 :                 if (*geomWKB == NULL)
    5145             :                         return -1;
    5146           0 :                 return 3;
    5147             :         }
    5148           2 :         err = wkbFROMSTR_withSRID(geomWKT, len, geomWKB, 0, &parsedBytes);
    5149           2 :         if (err != MAL_SUCCEED) {
    5150           1 :                 GDKerror("%s", getExceptionMessageAndState(err));
    5151           1 :                 freeException(err);
    5152           1 :                 return -1;
    5153             :         }
    5154           1 :         return (ssize_t) parsedBytes;
    5155             : }
    5156             : 
    5157             : static BUN
    5158           0 : wkbHASH(const void *W)
    5159             : {
    5160             :         const wkb *w = W;
    5161             :         int i;
    5162             :         BUN h = 0;
    5163             : 
    5164           0 :         for (i = 0; i < (w->len - 1); i += 2) {
    5165           0 :                 int a = *(w->data + i), b = *(w->data + i + 1);
    5166           0 :                 h = (h << 3) ^ (h >> 11) ^ (h >> 17) ^ (b << 8) ^ a;
    5167             :         }
    5168           0 :         return h;
    5169             : }
    5170             : 
    5171             : /* returns a pointer to a null wkb */
    5172             : static const void *
    5173         254 : wkbNULL(void)
    5174             : {
    5175         254 :         return &wkb_nil;
    5176             : }
    5177             : 
    5178             : static int
    5179        8451 : wkbCOMP(const void *L, const void *R)
    5180             : {
    5181             :         const wkb *l = L, *r = R;
    5182        8451 :         int len = l->len;
    5183             : 
    5184        8451 :         if (len != r->len)
    5185        4751 :                 return len - r->len;
    5186             : 
    5187        3700 :         if (len == ~(int) 0)
    5188             :                 return (0);
    5189             : 
    5190        3006 :         return memcmp(l->data, r->data, len);
    5191             : }
    5192             : 
    5193             : /* read wkb from log */
    5194             : static void *
    5195         453 : wkbREAD(void *A, size_t *dstlen, stream *s, size_t cnt)
    5196             : {
    5197             :         wkb *a = A;
    5198             :         int len;
    5199             :         int srid;
    5200             : 
    5201             :         (void) cnt;
    5202         453 :         assert(cnt == 1);
    5203         453 :         if (mnstr_readInt(s, &len) != 1)
    5204             :                 return NULL;
    5205         453 :         if (mnstr_readInt(s, &srid) != 1)
    5206             :                 return NULL;
    5207         453 :         size_t wkblen = (size_t) wkb_size(len);
    5208         453 :         if (a == NULL || *dstlen < wkblen) {
    5209           0 :                 if ((a = GDKrealloc(a, wkblen)) == NULL)
    5210             :                         return NULL;
    5211           0 :                 *dstlen = wkblen;
    5212             :         }
    5213         453 :         a->len = len;
    5214         453 :         a->srid = srid;
    5215         453 :         if (len > 0 && mnstr_read(s, (char *) a->data, len, 1) != 1) {
    5216           0 :                 GDKfree(a);
    5217           0 :                 return NULL;
    5218             :         }
    5219             :         return a;
    5220             : }
    5221             : 
    5222             : /* write wkb to log */
    5223             : static gdk_return
    5224         464 : wkbWRITE(const void *A, stream *s, size_t cnt)
    5225             : {
    5226             :         const wkb *a = A;
    5227         464 :         int len = a->len;
    5228         464 :         int srid = a->srid;
    5229             : 
    5230             :         (void) cnt;
    5231         464 :         assert(cnt == 1);
    5232         464 :         if (!mnstr_writeInt(s, len))    /* 64bit: check for overflow */
    5233             :                 return GDK_FAIL;
    5234         464 :         if (!mnstr_writeInt(s, srid))   /* 64bit: check for overflow */
    5235             :                 return GDK_FAIL;
    5236         925 :         if (len > 0 &&               /* 64bit: check for overflow */
    5237         461 :             mnstr_write(s, (char *) a->data, len, 1) < 0)
    5238           0 :                 return GDK_FAIL;
    5239             :         return GDK_SUCCEED;
    5240             : }
    5241             : 
    5242             : static var_t
    5243        1408 : wkbPUT(BAT *b, var_t *bun, const void *VAL)
    5244             : {
    5245             :         const wkb *val = VAL;
    5246             :         char *base;
    5247             : 
    5248        1408 :         *bun = HEAP_malloc(b, wkb_size(val->len));
    5249        1408 :         base = b->tvheap->base;
    5250        1408 :         if (*bun != (var_t) -1) {
    5251        1408 :                 memcpy(&base[*bun], val, wkb_size(val->len));
    5252        1408 :                 b->tvheap->dirty = true;
    5253             :         }
    5254        1408 :         return *bun;
    5255             : }
    5256             : 
    5257             : static void
    5258           0 : wkbDEL(Heap *h, var_t *index)
    5259             : {
    5260           0 :         HEAP_free(h, *index);
    5261           0 : }
    5262             : 
    5263             : static size_t
    5264        3128 : wkbLENGTH(const void *P)
    5265             : {
    5266             :         const wkb *p = P;
    5267        3128 :         var_t len = wkb_size(p->len);
    5268        3128 :         assert(len <= GDK_int_max);
    5269        3128 :         return (size_t) len;
    5270             : }
    5271             : 
    5272             : static gdk_return
    5273         682 : wkbHEAP(Heap *heap, size_t capacity)
    5274             : {
    5275         682 :         return HEAP_initialize(heap, capacity, 0, (int) sizeof(var_t));
    5276             : }
    5277             : 
    5278             : /***********************************************/
    5279             : /************* mbr type functions **************/
    5280             : /***********************************************/
    5281             : 
    5282             : #define MBR_WKTLEN 256
    5283             : 
    5284             : /* TOSTR: print atom in a string. */
    5285             : /* return length of resulting string. */
    5286             : static ssize_t
    5287          91 : mbrTOSTR(char **dst, size_t *len, const void *ATOM, bool external)
    5288             : {
    5289             :         const mbr *atom = ATOM;
    5290             :         char tempWkt[MBR_WKTLEN];
    5291             :         size_t dstStrLen;
    5292             : 
    5293          91 :         if (!is_mbr_nil(atom)) {
    5294          91 :                 dstStrLen = (size_t) snprintf(tempWkt, MBR_WKTLEN,
    5295             :                                               "BOX (%f %f, %f %f)",
    5296          91 :                                               atom->xmin, atom->ymin,
    5297          91 :                                               atom->xmax, atom->ymax);
    5298             :         } else {
    5299           0 :                 tempWkt[0] = 0; /* not used */
    5300             :                 dstStrLen = 0;
    5301             :         }
    5302             : 
    5303          91 :         if (*len < dstStrLen + 4 || *dst == NULL) {
    5304           0 :                 GDKfree(*dst);
    5305           0 :                 if ((*dst = GDKmalloc(*len = dstStrLen + 4)) == NULL)
    5306             :                         return -1;
    5307             :         }
    5308             : 
    5309          91 :         if (dstStrLen > 4) {
    5310          91 :                 if (external) {
    5311          71 :                         snprintf(*dst, *len, "\"%s\"", tempWkt);
    5312          71 :                         dstStrLen += 2;
    5313             :                 } else {
    5314          20 :                         strcpy(*dst, tempWkt);
    5315             :                 }
    5316           0 :         } else if (external) {
    5317           0 :                 strcpy(*dst, "nil");
    5318             :                 dstStrLen = 3;
    5319             :         } else {
    5320           0 :                 strcpy(*dst, str_nil);
    5321             :                 dstStrLen = 1;
    5322             :         }
    5323          91 :         return (ssize_t) dstStrLen;
    5324             : }
    5325             : 
    5326             : /* FROMSTR: parse string to mbr. */
    5327             : /* return number of parsed characters. */
    5328             : static ssize_t
    5329           2 : mbrFROMSTR(const char *src, size_t *len, void **ATOM, bool external)
    5330             : {
    5331             :         mbr **atom = (mbr **) ATOM;
    5332             :         size_t nchars = 0;      /* The number of characters parsed; the return value. */
    5333             :         GEOSGeom geosMbr = NULL;        /* The geometry object that is parsed from the src string. */
    5334           2 :         double xmin = 0, ymin = 0, xmax = 0, ymax = 0;
    5335             :         const char *c;
    5336             : 
    5337           2 :         if (*len < sizeof(mbr) || *atom == NULL) {
    5338           2 :                 GDKfree(*atom);
    5339           2 :                 if ((*atom = GDKmalloc(*len = sizeof(mbr))) == NULL)
    5340             :                         return -1;
    5341             :         }
    5342           2 :         if (external && strncmp(src, "nil", 3) == 0) {
    5343           0 :                 **atom = mbrNIL;
    5344           0 :                 return 3;
    5345             :         }
    5346           2 :         if (strNil(src)) {
    5347           0 :                 **atom = mbrNIL;
    5348           0 :                 return 1;
    5349             :         }
    5350             : 
    5351           2 :         if ((strstr(src, "mbr") == src || strstr(src, "MBR") == src
    5352           0 :              || strstr(src, "box") == src || strstr(src, "BOX") == src)
    5353           2 :             && (c = strstr(src, "(")) != NULL) {
    5354             :                 /* Parse the mbr */
    5355           2 :                 if ((c - src) != 3 && (c - src) != 4) {
    5356           0 :                         GDKerror("ParseException: Expected a string like 'MBR(0 0,1 1)' or 'MBR (0 0,1 1)'\n");
    5357           0 :                         return -1;
    5358             :                 }
    5359             : 
    5360           2 :                 if (sscanf(c, "(%lf %lf,%lf %lf)", &xmin, &ymin, &xmax, &ymax) != 4) {
    5361           0 :                         GDKerror("ParseException: Not enough coordinates.\n");
    5362           0 :                         return -1;
    5363             :                 }
    5364           0 :         } else if ((geosMbr = GEOSGeomFromWKT(src)) == NULL) {
    5365           0 :                 GDKerror("GEOSGeomFromWKT failed\n");
    5366           0 :                 return -1;
    5367             :         }
    5368             : 
    5369             :         if (geosMbr == NULL) {
    5370           2 :                 assert(GDK_flt_min <= xmin && xmin <= GDK_flt_max);
    5371           2 :                 assert(GDK_flt_min <= xmax && xmax <= GDK_flt_max);
    5372           2 :                 assert(GDK_flt_min <= ymin && ymin <= GDK_flt_max);
    5373           2 :                 assert(GDK_flt_min <= ymax && ymax <= GDK_flt_max);
    5374           2 :                 (*atom)->xmin = (float) xmin;
    5375           2 :                 (*atom)->ymin = (float) ymin;
    5376           2 :                 (*atom)->xmax = (float) xmax;
    5377           2 :                 (*atom)->ymax = (float) ymax;
    5378           2 :                 nchars = strlen(src);
    5379             :         }
    5380           2 :         if (geosMbr)
    5381           0 :                 GEOSGeom_destroy(geosMbr);
    5382           2 :         assert(nchars <= GDK_int_max);
    5383           2 :         return (ssize_t) nchars;
    5384             : }
    5385             : 
    5386             : /* HASH: compute a hash value. */
    5387             : /* returns a positive integer hash value */
    5388             : static BUN
    5389           0 : mbrHASH(const void *ATOM)
    5390             : {
    5391             :         const mbr *atom = ATOM;
    5392           0 :         return (BUN) (((int) atom->xmin * (int)atom->ymin) *((int) atom->xmax * (int)atom->ymax));
    5393             : }
    5394             : 
    5395             : static const void *
    5396         254 : mbrNULL(void)
    5397             : {
    5398         254 :         return &mbrNIL;
    5399             : }
    5400             : 
    5401             : /* COMP: compare two mbrs. */
    5402             : /* returns int <0 if l<r, 0 if l==r, >0 else */
    5403             : static int
    5404         820 : mbrCOMP(const void *L, const void *R)
    5405             : {
    5406             :         /* simple lexicographical ordering on (x,y) */
    5407             :         const mbr *l = L, *r = R;
    5408             :         int res;
    5409         820 :         if (is_mbr_nil(l))
    5410         166 :                 return -!is_mbr_nil(r);
    5411         654 :         if (is_mbr_nil(r))
    5412             :                 return 1;
    5413         330 :         if (l->xmin == r->xmin)
    5414         173 :                 res = (l->ymin < r->ymin) ? -1 : (l->ymin != r->ymin);
    5415             :         else
    5416         157 :                 res = (l->xmin < r->xmin) ? -1 : 1;
    5417         108 :         if (res == 0) {
    5418          93 :                 if (l->xmax == r->xmax)
    5419          54 :                         res = (l->ymax < r->ymax) ? -1 : (l->ymax != r->ymax);
    5420             :                 else
    5421          39 :                         res = (l->xmax < r->xmax) ? -1 : 1;
    5422             :         }
    5423             :         return res;
    5424             : }
    5425             : 
    5426             : /* read mbr from log */
    5427             : static void *
    5428          10 : mbrREAD(void *A, size_t *dstlen, stream *s, size_t cnt)
    5429             : {
    5430             :         mbr *a = A;
    5431             :         mbr *c;
    5432             :         size_t i;
    5433             :         int v[4];
    5434             :         flt vals[4];
    5435             : 
    5436          10 :         if (a == NULL || *dstlen < cnt * sizeof(mbr)) {
    5437           0 :                 if ((a = GDKrealloc(a, cnt * sizeof(mbr))) == NULL)
    5438             :                         return NULL;
    5439           0 :                 *dstlen = cnt * sizeof(mbr);
    5440             :         }
    5441          24 :         for (i = 0, c = a; i < cnt; i++, c++) {
    5442          14 :                 if (!mnstr_readIntArray(s, v, 4)) {
    5443           0 :                         if (a != A)
    5444           0 :                                 GDKfree(a);
    5445           0 :                         return NULL;
    5446             :                 }
    5447             :                 memcpy(vals, v, 4 * sizeof(int));
    5448          14 :                 c->xmin = vals[0];
    5449          14 :                 c->ymin = vals[1];
    5450          14 :                 c->xmax = vals[2];
    5451          14 :                 c->ymax = vals[3];
    5452             :         }
    5453             :         return a;
    5454             : }
    5455             : 
    5456             : /* write mbr to log */
    5457             : static gdk_return
    5458          14 : mbrWRITE(const void *C, stream *s, size_t cnt)
    5459             : {
    5460             :         const mbr *c = C;
    5461             :         size_t i;
    5462             :         flt vals[4];
    5463             :         int v[4];
    5464             : 
    5465          28 :         for (i = 0; i < cnt; i++, c++) {
    5466          14 :                 vals[0] = c->xmin;
    5467          14 :                 vals[1] = c->ymin;
    5468          14 :                 vals[2] = c->xmax;
    5469          14 :                 vals[3] = c->ymax;
    5470          14 :                 memcpy(v, vals, 4 * sizeof(int));
    5471          14 :                 if (!mnstr_writeIntArray(s, v, 4))
    5472             :                         return GDK_FAIL;
    5473             :         }
    5474             :         return GDK_SUCCEED;
    5475             : }
    5476             : 
    5477             : /************************************************/
    5478             : /************* wkba type functions **************/
    5479             : /************************************************/
    5480             : 
    5481             : /* Creates the string representation of a wkb_array */
    5482             : /* return length of resulting string. */
    5483             : static ssize_t
    5484           0 : wkbaTOSTR(char **toStr, size_t *len, const void *FROMARRAY, bool external)
    5485             : {
    5486             :         const wkba *fromArray = FROMARRAY;
    5487           0 :         int items = fromArray->itemsNum, i;
    5488           0 :         int itemsNumDigits = (int) ceil(log10(items));
    5489             :         size_t dataSize;        //, skipBytes=0;
    5490             :         char **partialStrs;
    5491           0 :         char *toStrPtr = NULL, *itemsNumStr = GDKmalloc(itemsNumDigits + 1);
    5492             : 
    5493           0 :         if (itemsNumStr == NULL)
    5494             :                 return -1;
    5495             : 
    5496           0 :         dataSize = (size_t) snprintf(itemsNumStr, itemsNumDigits + 1, "%d", items);
    5497             : 
    5498             :         // reserve space for an array with pointers to the partial
    5499             :         // strings, i.e. for each wkbTOSTR
    5500           0 :         partialStrs = GDKzalloc(items * sizeof(char *));
    5501           0 :         if (partialStrs == NULL) {
    5502           0 :                 GDKfree(itemsNumStr);
    5503           0 :                 return -1;
    5504             :         }
    5505             :         //create the string version of each wkb
    5506           0 :         for (i = 0; i < items; i++) {
    5507           0 :                 size_t llen = 0;
    5508             :                 ssize_t ds;
    5509           0 :                 ds = wkbTOSTR(&partialStrs[i], &llen, fromArray->data[i], false);
    5510           0 :                 if (ds < 0) {
    5511           0 :                         GDKfree(itemsNumStr);
    5512           0 :                         while (i >= 0)
    5513           0 :                                 GDKfree(partialStrs[i--]);
    5514           0 :                         GDKfree(partialStrs);
    5515           0 :                         return -1;
    5516             :                 }
    5517           0 :                 dataSize += ds;
    5518             : 
    5519           0 :                 if (strNil(partialStrs[i])) {
    5520           0 :                         GDKfree(itemsNumStr);
    5521           0 :                         while (i >= 0)
    5522           0 :                                 GDKfree(partialStrs[i--]);
    5523           0 :                         GDKfree(partialStrs);
    5524           0 :                         if (*len < 4 || *toStr == NULL) {
    5525           0 :                                 GDKfree(*toStr);
    5526           0 :                                 if ((*toStr = GDKmalloc(*len = 4)) == NULL)
    5527             :                                         return -1;
    5528             :                         }
    5529           0 :                         if (external) {
    5530           0 :                                 strcpy(*toStr, "nil");
    5531           0 :                                 return 3;
    5532             :                         }
    5533           0 :                         strcpy(*toStr, str_nil);
    5534           0 :                         return 1;
    5535             :                 }
    5536             :         }
    5537             : 
    5538             :         //add [] around itemsNum
    5539           0 :         dataSize += 2;
    5540             :         //add ", " before each item
    5541           0 :         dataSize += 2 * sizeof(char) * items;
    5542             : 
    5543             :         //copy all partial strings to a single one
    5544           0 :         if (*len < dataSize + 3 || *toStr == NULL) {
    5545           0 :                 GDKfree(*toStr);
    5546           0 :                 *toStr = GDKmalloc(*len = dataSize + 3);        /* plus quotes + termination character */
    5547           0 :                 if (*toStr == NULL) {
    5548           0 :                         for (i = 0; i < items; i++)
    5549           0 :                                 GDKfree(partialStrs[i]);
    5550           0 :                         GDKfree(partialStrs);
    5551           0 :                         GDKfree(itemsNumStr);
    5552           0 :                         return -1;
    5553             :                 }
    5554             :         }
    5555           0 :         toStrPtr = *toStr;
    5556           0 :         if (external)
    5557           0 :                 *toStrPtr++ = '\"';
    5558           0 :         *toStrPtr++ = '[';
    5559           0 :         strcpy(toStrPtr, itemsNumStr);
    5560           0 :         toStrPtr += strlen(itemsNumStr);
    5561           0 :         *toStrPtr++ = ']';
    5562           0 :         for (i = 0; i < items; i++) {
    5563           0 :                 if (i == 0)
    5564           0 :                         *toStrPtr++ = ':';
    5565             :                 else
    5566           0 :                         *toStrPtr++ = ',';
    5567           0 :                 *toStrPtr++ = ' ';
    5568             : 
    5569             :                 //strcpy(toStrPtr, partialStrs[i]);
    5570           0 :                 memcpy(toStrPtr, partialStrs[i], strlen(partialStrs[i]));
    5571           0 :                 toStrPtr += strlen(partialStrs[i]);
    5572           0 :                 GDKfree(partialStrs[i]);
    5573             :         }
    5574             : 
    5575           0 :         if (external)
    5576           0 :                 *toStrPtr++ = '\"';
    5577           0 :         *toStrPtr = '\0';
    5578             : 
    5579           0 :         GDKfree(partialStrs);
    5580           0 :         GDKfree(itemsNumStr);
    5581             : 
    5582           0 :         return (ssize_t) (toStrPtr - *toStr);
    5583             : }
    5584             : 
    5585             : /* return number of parsed characters. */
    5586             : static ssize_t
    5587           0 : wkbaFROMSTR(const char *fromStr, size_t *len, void **TOARRAY, bool external)
    5588             : {
    5589             :         wkba **toArray = (wkba **) TOARRAY;
    5590           0 :         if (external && strncmp(fromStr, "nil", 3) == 0) {
    5591           0 :                 size_t sz = wkba_size(~0);
    5592           0 :                 if ((*len < sz || *toArray == NULL)
    5593           0 :                     && (*toArray = GDKmalloc(sz)) == NULL)
    5594             :                         return -1;
    5595           0 :                 **toArray = wkba_nil;
    5596           0 :                 return 3;
    5597             :         }
    5598           0 :         return wkbaFROMSTR_withSRID(fromStr, len, toArray, 0);
    5599             : }
    5600             : 
    5601             : /* returns a pointer to a null wkba */
    5602             : static const void *
    5603         254 : wkbaNULL(void)
    5604             : {
    5605         254 :         return &wkba_nil;
    5606             : }
    5607             : 
    5608             : static BUN
    5609           0 : wkbaHASH(const void *WARRAY)
    5610             : {
    5611             :         const wkba *wArray = WARRAY;
    5612             :         int j, i;
    5613             :         BUN h = 0;
    5614             : 
    5615           0 :         for (j = 0; j < wArray->itemsNum; j++) {
    5616           0 :                 wkb *w = wArray->data[j];
    5617           0 :                 for (i = 0; i < (w->len - 1); i += 2) {
    5618           0 :                         int a = *(w->data + i), b = *(w->data + i + 1);
    5619           0 :                         h = (h << 3) ^ (h >> 11) ^ (h >> 17) ^ (b << 8) ^ a;
    5620             :                 }
    5621             :         }
    5622           0 :         return h;
    5623             : }
    5624             : 
    5625             : static int
    5626           0 : wkbaCOMP(const void *L, const void *R)
    5627             : {
    5628             :         const wkba *l = L, *r = R;
    5629             :         int i, res = 0;
    5630             : 
    5631             :         //compare the number of items
    5632           0 :         if (l->itemsNum != r->itemsNum)
    5633           0 :                 return l->itemsNum - r->itemsNum;
    5634             : 
    5635           0 :         if (l->itemsNum == ~(int) 0)
    5636             :                 return (0);
    5637             : 
    5638             :         //compare each wkb separately
    5639           0 :         for (i = 0; i < l->itemsNum; i++)
    5640           0 :                 res += wkbCOMP(l->data[i], r->data[i]);
    5641             : 
    5642             :         return res;
    5643             : }
    5644             : 
    5645             : /* read wkb from log */
    5646             : static void *
    5647           0 : wkbaREAD(void *A, size_t *dstlen, stream *s, size_t cnt)
    5648             : {
    5649             :         wkba *a = A;
    5650             :         int items, i;
    5651             : 
    5652             :         (void) cnt;
    5653           0 :         assert(cnt == 1);
    5654             : 
    5655           0 :         if (mnstr_readInt(s, &items) != 1)
    5656             :                 return NULL;
    5657             : 
    5658           0 :         size_t wkbalen = (size_t) wkba_size(items);
    5659           0 :         if (a == NULL || *dstlen < wkbalen) {
    5660           0 :                 if ((a = GDKrealloc(a, wkbalen)) == NULL)
    5661             :                         return NULL;
    5662           0 :                 *dstlen = wkbalen;
    5663             :         }
    5664             : 
    5665           0 :         a->itemsNum = items;
    5666             : 
    5667           0 :         for (i = 0; i < items; i++) {
    5668           0 :                 size_t wlen = 0;
    5669           0 :                 a->data[i] = wkbREAD(NULL, &wlen, s, cnt);
    5670             :         }
    5671             : 
    5672             :         return a;
    5673             : }
    5674             : 
    5675             : /* write wkb to log */
    5676             : static gdk_return
    5677           0 : wkbaWRITE(const void *A, stream *s, size_t cnt)
    5678             : {
    5679             :         const wkba *a = A;
    5680           0 :         int i, items = a->itemsNum;
    5681             :         gdk_return ret = GDK_SUCCEED;
    5682             : 
    5683             :         (void) cnt;
    5684           0 :         assert(cnt == 1);
    5685             : 
    5686           0 :         if (!mnstr_writeInt(s, items))
    5687             :                 return GDK_FAIL;
    5688           0 :         for (i = 0; i < items; i++) {
    5689           0 :                 ret = wkbWRITE(a->data[i], s, cnt);
    5690             : 
    5691           0 :                 if (ret != GDK_SUCCEED)
    5692           0 :                         return ret;
    5693             :         }
    5694             :         return GDK_SUCCEED;
    5695             : }
    5696             : 
    5697             : static var_t
    5698           0 : wkbaPUT(BAT *b, var_t *bun, const void *VAL)
    5699             : {
    5700             :         const wkba *val = VAL;
    5701             :         char *base;
    5702             : 
    5703           0 :         *bun = HEAP_malloc(b, wkba_size(val->itemsNum));
    5704           0 :         base = b->tvheap->base;
    5705           0 :         if (*bun != (var_t) -1) {
    5706           0 :                 memcpy(&base[*bun], val, wkba_size(val->itemsNum));
    5707           0 :                 b->tvheap->dirty = true;
    5708             :         }
    5709           0 :         return *bun;
    5710             : }
    5711             : 
    5712             : static void
    5713           0 : wkbaDEL(Heap *h, var_t *index)
    5714             : {
    5715           0 :         HEAP_free(h, *index);
    5716           0 : }
    5717             : 
    5718             : static size_t
    5719           0 : wkbaLENGTH(const void *P)
    5720             : {
    5721             :         const wkba *p = P;
    5722           0 :         var_t len = wkba_size(p->itemsNum);
    5723           0 :         assert(len <= GDK_int_max);
    5724           0 :         return (size_t) len;
    5725             : }
    5726             : 
    5727             : static gdk_return
    5728         254 : wkbaHEAP(Heap *heap, size_t capacity)
    5729             : {
    5730         254 :         return HEAP_initialize(heap, capacity, 0, (int) sizeof(var_t));
    5731             : }
    5732             : 
    5733             : geom_export str wkbContains_point_bat(bat *out, wkb **a, bat *point_x, bat *point_y);
    5734             : geom_export str wkbContains_point(bit *out, wkb **a, dbl *point_x, dbl *point_y);
    5735             : 
    5736             : static inline double
    5737             : isLeft(double P0x, double P0y, double P1x, double P1y, double P2x, double P2y)
    5738             : {
    5739           0 :         return ((P1x - P0x) * (P2y - P0y)
    5740           0 :                 - (P2x - P0x) * (P1y - P0y));
    5741             : }
    5742             : 
    5743             : static str
    5744           0 : pnpoly(int *out, int nvert, dbl *vx, dbl *vy, bat *point_x, bat *point_y)
    5745             : {
    5746             :         BAT *bo = NULL, *bpx = NULL, *bpy;
    5747             :         dbl *px = NULL, *py = NULL;
    5748             :         BUN i = 0, cnt;
    5749             :         int j = 0, nv;
    5750             :         bit *cs = NULL;
    5751             : 
    5752             :         /*Get the BATs */
    5753           0 :         if ((bpx = BATdescriptor(*point_x)) == NULL) {
    5754           0 :                 throw(MAL, "geom.point", SQLSTATE(38000) RUNTIME_OBJECT_MISSING);
    5755             :         }
    5756             : 
    5757           0 :         if ((bpy = BATdescriptor(*point_y)) == NULL) {
    5758           0 :                 BBPunfix(bpx->batCacheid);
    5759           0 :                 throw(MAL, "geom.point", SQLSTATE(38000) RUNTIME_OBJECT_MISSING);
    5760             :         }
    5761             : 
    5762             :         /*Check BATs alignment */
    5763           0 :         if (bpx->hseqbase != bpy->hseqbase || BATcount(bpx) != BATcount(bpy)) {
    5764           0 :                 BBPunfix(bpx->batCacheid);
    5765           0 :                 BBPunfix(bpy->batCacheid);
    5766           0 :                 throw(MAL, "geom.point", SQLSTATE(38000) "both point bats must have dense and aligned heads");
    5767             :         }
    5768             : 
    5769             :         /*Create output BAT */
    5770           0 :         if ((bo = COLnew(bpx->hseqbase, TYPE_bit, BATcount(bpx), TRANSIENT)) == NULL) {
    5771           0 :                 BBPunfix(bpx->batCacheid);
    5772           0 :                 BBPunfix(bpy->batCacheid);
    5773           0 :                 throw(MAL, "geom.point", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    5774             :         }
    5775             : 
    5776             :         /*Iterate over the Point BATs and determine if they are in Polygon represented by vertex BATs */
    5777           0 :         BATiter bpxi = bat_iterator(bpx);
    5778           0 :         BATiter bpyi = bat_iterator(bpy);
    5779           0 :         px = (dbl *) bpxi.base;
    5780           0 :         py = (dbl *) bpyi.base;
    5781             : 
    5782           0 :         nv = nvert - 1;
    5783           0 :         cnt = BATcount(bpx);
    5784           0 :         cs = (bit *) Tloc(bo, 0);
    5785           0 :         for (i = 0; i < cnt; i++) {
    5786             :                 int wn = 0;
    5787           0 :                 for (j = 0; j < nv; j++) {
    5788           0 :                         if (vy[j] <= py[i]) {
    5789           0 :                                 if (vy[j + 1] > py[i])
    5790           0 :                                         if (isLeft(vx[j], vy[j], vx[j + 1], vy[j + 1], px[i], py[i]) > 0)
    5791           0 :                                                 ++wn;
    5792             :                         } else {
    5793           0 :                                 if (vy[j + 1] <= py[i])
    5794           0 :                                         if (isLeft(vx[j], vy[j], vx[j + 1], vy[j + 1], px[i], py[i]) < 0)
    5795           0 :                                                 --wn;
    5796             :                         }
    5797             :                 }
    5798           0 :                 *cs++ = wn & 1;
    5799             :         }
    5800           0 :         bat_iterator_end(&bpxi);
    5801           0 :         bat_iterator_end(&bpyi);
    5802             : 
    5803           0 :         bo->tsorted = bo->trevsorted = false;
    5804           0 :         bo->tkey = false;
    5805           0 :         BATsetcount(bo, cnt);
    5806           0 :         BBPunfix(bpx->batCacheid);
    5807           0 :         BBPunfix(bpy->batCacheid);
    5808           0 :         BBPkeepref(*out = bo->batCacheid);
    5809           0 :         return MAL_SUCCEED;
    5810             : }
    5811             : 
    5812             : static str
    5813           0 : pnpolyWithHoles(bat *out, int nvert, dbl *vx, dbl *vy, int nholes, dbl **hx, dbl **hy, int *hn, bat *point_x, bat *point_y)
    5814             : {
    5815             :         BAT *bo = NULL, *bpx = NULL, *bpy;
    5816             :         dbl *px = NULL, *py = NULL;
    5817             :         BUN i = 0, cnt = 0;
    5818             :         int j = 0, h = 0;
    5819             :         bit *cs = NULL;
    5820             : 
    5821             :         /*Get the BATs */
    5822           0 :         if ((bpx = BATdescriptor(*point_x)) == NULL) {
    5823           0 :                 throw(MAL, "geom.point", SQLSTATE(38000) RUNTIME_OBJECT_MISSING);
    5824             :         }
    5825           0 :         if ((bpy = BATdescriptor(*point_y)) == NULL) {
    5826           0 :                 BBPunfix(bpx->batCacheid);
    5827           0 :                 throw(MAL, "geom.point", SQLSTATE(38000) RUNTIME_OBJECT_MISSING);
    5828             :         }
    5829             : 
    5830             :         /*Check BATs alignment */
    5831           0 :         if (bpx->hseqbase != bpy->hseqbase || BATcount(bpx) != BATcount(bpy)) {
    5832           0 :                 BBPunfix(bpx->batCacheid);
    5833           0 :                 BBPunfix(bpy->batCacheid);
    5834           0 :                 throw(MAL, "geom.point", SQLSTATE(38000) "Geos both point bats must have dense and aligned heads");
    5835             :         }
    5836             : 
    5837             :         /*Create output BAT */
    5838           0 :         if ((bo = COLnew(bpx->hseqbase, TYPE_bit, BATcount(bpx), TRANSIENT)) == NULL) {
    5839           0 :                 BBPunfix(bpx->batCacheid);
    5840           0 :                 BBPunfix(bpy->batCacheid);
    5841           0 :                 throw(MAL, "geom.point", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    5842             :         }
    5843             : 
    5844             :         /*Iterate over the Point BATs and determine if they are in Polygon represented by vertex BATs */
    5845           0 :         BATiter bpxi = bat_iterator(bpx);
    5846           0 :         BATiter bpyi = bat_iterator(bpy);
    5847           0 :         px = (dbl *) bpxi.base;
    5848           0 :         py = (dbl *) bpyi.base;
    5849           0 :         cnt = BATcount(bpx);
    5850           0 :         cs = (bit *) Tloc(bo, 0);
    5851           0 :         for (i = 0; i < cnt; i++) {
    5852             :                 int wn = 0;
    5853             : 
    5854             :                 /*First check the holes */
    5855           0 :                 for (h = 0; h < nholes; h++) {
    5856           0 :                         int nv = hn[h] - 1;
    5857             :                         wn = 0;
    5858           0 :                         for (j = 0; j < nv; j++) {
    5859           0 :                                 if (hy[h][j] <= py[i]) {
    5860           0 :                                         if (hy[h][j + 1] > py[i])
    5861           0 :                                                 if (isLeft(hx[h][j], hy[h][j], hx[h][j + 1], hy[h][j + 1], px[i], py[i]) > 0)
    5862           0 :                                                         ++wn;
    5863             :                                 } else {
    5864           0 :                                         if (hy[h][j + 1] <= py[i])
    5865           0 :                                                 if (isLeft(hx[h][j], hy[h][j], hx[h][j + 1], hy[h][j + 1], px[i], py[i]) < 0)
    5866           0 :                                                         --wn;
    5867             :                                 }
    5868             :                         }
    5869             : 
    5870             :                         /*It is in one of the holes */
    5871           0 :                         if (wn) {
    5872             :                                 break;
    5873             :                         }
    5874             :                 }
    5875             : 
    5876           0 :                 if (wn)
    5877           0 :                         continue;
    5878             : 
    5879             :                 /*If not in any of the holes, check inside the Polygon */
    5880           0 :                 for (j = 0; j < nvert - 1; j++) {
    5881           0 :                         if (vy[j] <= py[i]) {
    5882           0 :                                 if (vy[j + 1] > py[i])
    5883           0 :                                         if (isLeft(vx[j], vy[j], vx[j + 1], vy[j + 1], px[i], py[i]) > 0)
    5884           0 :                                                 ++wn;
    5885             :                         } else {
    5886           0 :                                 if (vy[j + 1] <= py[i])
    5887           0 :                                         if (isLeft(vx[j], vy[j], vx[j + 1], vy[j + 1], px[i], py[i]) < 0)
    5888           0 :                                                 --wn;
    5889             :                         }
    5890             :                 }
    5891           0 :                 *cs++ = wn & 1;
    5892             :         }
    5893           0 :         bat_iterator_end(&bpxi);
    5894           0 :         bat_iterator_end(&bpyi);
    5895           0 :         bo->tsorted = bo->trevsorted = false;
    5896           0 :         bo->tkey = false;
    5897           0 :         BATsetcount(bo, cnt);
    5898           0 :         BBPunfix(bpx->batCacheid);
    5899           0 :         BBPunfix(bpy->batCacheid);
    5900           0 :         BBPkeepref(*out = bo->batCacheid);
    5901           0 :         return MAL_SUCCEED;
    5902             : }
    5903             : 
    5904             : #define POLY_NUM_VERT 120
    5905             : #define POLY_NUM_HOLE 10
    5906             : 
    5907             : str
    5908           0 : wkbContains_point_bat(bat *out, wkb **a, bat *point_x, bat *point_y)
    5909             : {
    5910             :         double *vert_x, *vert_y, **holes_x = NULL, **holes_y = NULL;
    5911             :         int *holes_n = NULL, j;
    5912             :         wkb *geom = NULL;
    5913             :         str err = MAL_SUCCEED;
    5914           0 :         str geom_str = NULL;
    5915             :         char *str2, *token, *subtoken;
    5916           0 :         char *saveptr1 = NULL, *saveptr2 = NULL;
    5917             :         int nvert = 0, nholes = 0;
    5918             : 
    5919           0 :         geom = (wkb *) *a;
    5920             : 
    5921           0 :         if ((err = wkbAsText(&geom_str, &geom, NULL)) != MAL_SUCCEED) {
    5922             :                 return err;
    5923             :         }
    5924           0 :         token = strchr(geom_str, '(');
    5925           0 :         token += 2;
    5926             : 
    5927             :         /*Lets get the polygon */
    5928           0 :         token = strtok_r(token, ")", &saveptr1);
    5929           0 :         vert_x = GDKmalloc(POLY_NUM_VERT * sizeof(double));
    5930           0 :         vert_y = GDKmalloc(POLY_NUM_VERT * sizeof(double));
    5931           0 :         if (vert_x == NULL || vert_y == NULL) {
    5932           0 :                 err = createException(MAL, "geom.Contains", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    5933           0 :                 goto bailout;
    5934             :         }
    5935             : 
    5936             :         for (str2 = token;; str2 = NULL) {
    5937           0 :                 subtoken = strtok_r(str2, ",", &saveptr2);
    5938           0 :                 if (subtoken == NULL)
    5939             :                         break;
    5940           0 :                 sscanf(subtoken, "%lf %lf", &vert_x[nvert], &vert_y[nvert]);
    5941           0 :                 nvert++;
    5942           0 :                 if ((nvert % POLY_NUM_VERT) == 0) {
    5943             :                         double *tmp;
    5944           0 :                         tmp = GDKrealloc(vert_x, nvert * 2 * sizeof(double));
    5945           0 :                         if (tmp == NULL) {
    5946           0 :                                 err = createException(MAL, "geom.Contains", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    5947           0 :                                 goto bailout;
    5948             :                         }
    5949             :                         vert_x = tmp;
    5950           0 :                         tmp = GDKrealloc(vert_y, nvert * 2 * sizeof(double));
    5951           0 :                         if (tmp == NULL) {
    5952           0 :                                 err = createException(MAL, "geom.Contains", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    5953           0 :                                 goto bailout;
    5954             :                         }
    5955             :                         vert_y = tmp;
    5956             :                 }
    5957             :         }
    5958             : 
    5959           0 :         token = strtok_r(NULL, ")", &saveptr1);
    5960           0 :         if (token) {
    5961           0 :                 holes_x = GDKzalloc(POLY_NUM_HOLE * sizeof(double *));
    5962           0 :                 holes_y = GDKzalloc(POLY_NUM_HOLE * sizeof(double *));
    5963           0 :                 holes_n = GDKzalloc(POLY_NUM_HOLE * sizeof(int));
    5964           0 :                 if (holes_x == NULL || holes_y == NULL || holes_n == NULL) {
    5965           0 :                         err = createException(MAL, "geom.Contains", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    5966           0 :                         goto bailout;
    5967             :                 }
    5968             :         }
    5969             :         /*Lets get all the holes */
    5970           0 :         while (token) {
    5971             :                 int nhole = 0;
    5972           0 :                 token = strchr(token, '(');
    5973           0 :                 if (!token)
    5974             :                         break;
    5975           0 :                 token++;
    5976             : 
    5977           0 :                 if (holes_x[nholes] == NULL &&
    5978           0 :                     (holes_x[nholes] = GDKzalloc(POLY_NUM_VERT * sizeof(double))) == NULL) {
    5979           0 :                         err = createException(MAL, "geom.Contains", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    5980           0 :                         goto bailout;
    5981             :                 }
    5982           0 :                 if (holes_y[nholes] == NULL &&
    5983           0 :                     (holes_y[nholes] = GDKzalloc(POLY_NUM_VERT * sizeof(double))) == NULL) {
    5984           0 :                         err = createException(MAL, "geom.Contains", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    5985           0 :                         goto bailout;
    5986             :                 }
    5987             : 
    5988             :                 for (str2 = token;; str2 = NULL) {
    5989           0 :                         subtoken = strtok_r(str2, ",", &saveptr2);
    5990           0 :                         if (subtoken == NULL)
    5991             :                                 break;
    5992           0 :                         sscanf(subtoken, "%lf %lf", &holes_x[nholes][nhole], &holes_y[nholes][nhole]);
    5993           0 :                         nhole++;
    5994           0 :                         if ((nhole % POLY_NUM_VERT) == 0) {
    5995             :                                 double *tmp;
    5996           0 :                                 tmp = GDKrealloc(holes_x[nholes], nhole * 2 * sizeof(double));
    5997           0 :                                 if (tmp == NULL) {
    5998           0 :                                         err = createException(MAL, "geom.Contains", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    5999           0 :                                         goto bailout;
    6000             :                                 }
    6001           0 :                                 holes_x[nholes] = tmp;
    6002           0 :                                 tmp = GDKrealloc(holes_y[nholes], nhole * 2 * sizeof(double));
    6003           0 :                                 if (tmp == NULL) {
    6004           0 :                                         err = createException(MAL, "geom.Contains", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    6005           0 :                                         goto bailout;
    6006             :                                 }
    6007           0 :                                 holes_y[nholes] = tmp;
    6008             :                         }
    6009             :                 }
    6010             : 
    6011           0 :                 holes_n[nholes] = nhole;
    6012           0 :                 nholes++;
    6013           0 :                 if ((nholes % POLY_NUM_HOLE) == 0) {
    6014             :                         double **tmp;
    6015             :                         int *itmp;
    6016           0 :                         tmp = GDKrealloc(holes_x, nholes * 2 * sizeof(double *));
    6017           0 :                         if (tmp == NULL) {
    6018           0 :                                 err = createException(MAL, "geom.Contains", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    6019           0 :                                 goto bailout;
    6020             :                         }
    6021             :                         holes_x = tmp;
    6022           0 :                         tmp = GDKrealloc(holes_y, nholes * 2 * sizeof(double *));
    6023           0 :                         if (tmp == NULL) {
    6024           0 :                                 err = createException(MAL, "geom.Contains", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    6025           0 :                                 goto bailout;
    6026             :                         }
    6027             :                         holes_y = tmp;
    6028           0 :                         itmp = GDKrealloc(holes_n, nholes * 2 * sizeof(int));
    6029           0 :                         if (itmp == NULL) {
    6030           0 :                                 err = createException(MAL, "geom.Contains", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    6031           0 :                                 goto bailout;
    6032             :                         }
    6033             :                         holes_n = itmp;
    6034             :                 }
    6035           0 :                 token = strtok_r(NULL, ")", &saveptr1);
    6036             :         }
    6037             : 
    6038           0 :         if (nholes)
    6039           0 :                 err = pnpolyWithHoles(out, nvert, vert_x, vert_y, nholes, holes_x, holes_y, holes_n, point_x, point_y);
    6040             :         else {
    6041           0 :                 err = pnpoly(out, nvert, vert_x, vert_y, point_x, point_y);
    6042             :         }
    6043             : 
    6044           0 :   bailout:
    6045           0 :         GDKfree(geom_str);
    6046           0 :         GDKfree(vert_x);
    6047           0 :         GDKfree(vert_y);
    6048           0 :         if (holes_x && holes_y && holes_n) {
    6049           0 :                 for (j = 0; j < nholes; j++) {
    6050           0 :                         GDKfree(holes_x[j]);
    6051           0 :                         GDKfree(holes_y[j]);
    6052             :                 }
    6053             :         }
    6054           0 :         GDKfree(holes_x);
    6055           0 :         GDKfree(holes_y);
    6056           0 :         GDKfree(holes_n);
    6057             : 
    6058           0 :         return err;
    6059             : }
    6060             : 
    6061             : str
    6062           0 : wkbContains_point(bit *out, wkb **a, dbl *point_x, dbl *point_y)
    6063             : {
    6064             :         (void) a;
    6065             :         (void) point_x;
    6066             :         (void) point_y;
    6067           0 :         *out = TRUE;
    6068           0 :         return MAL_SUCCEED;
    6069             : }
    6070             : 
    6071             : static const unsigned char geom_functions[9054] = {
    6072             : "module geom;\n"
    6073             : "function AsText(w:wkb) :str;\n"
    6074             : "x := ToText(w,0);\n"
    6075             : "return x;\n"
    6076             : "end AsText;\n"
    6077             : "function AsEWKT(w:wkb) :str;\n"
    6078             : "x := ToText(w,1);\n"
    6079             : "return x;\n"
    6080             : "end AsEWKT;\n"
    6081             : "function GeomFromText(wkt:str, srid:int) :wkb;\n"
    6082             : "x := FromText(wkt,srid,0);\n"
    6083             : "return x;\n"
    6084             : "end GeomFromText;\n"
    6085             : "function PointFromText(wkt:str, srid:int) :wkb;\n"
    6086             : "x := FromText(wkt,srid,1);\n"
    6087             : "return x;\n"
    6088             : "end PointFromText;\n"
    6089             : "function LineFromText(wkt:str, srid:int) :wkb;\n"
    6090             : "x := FromText(wkt,srid,2);\n"
    6091             : "return x;\n"
    6092             : "end LineFromText;\n"
    6093             : "function PolygonFromText(wkt:str, srid:int) :wkb;\n"
    6094             : "x := FromText(wkt,srid,4);\n"
    6095             : "return x;\n"
    6096             : "end PolygonFromText;\n"
    6097             : "function MPointFromText(wkt:str, srid:int) :wkb;\n"
    6098             : "x := FromText(wkt,srid,5);\n"
    6099             : "return x;\n"
    6100             : "end MPointFromText;\n"
    6101             : "function MLineFromText(wkt:str, srid:int) :wkb;\n"
    6102             : "x := FromText(wkt,srid,6);\n"
    6103             : "return x;\n"
    6104             : "end MLineFromText;\n"
    6105             : "function MPolyFromText(wkt:str, srid:int) :wkb;\n"
    6106             : "x := FromText(wkt,srid,7);\n"
    6107             : "return x;\n"
    6108             : "end MPolyFromText;\n"
    6109             : "function GeomCollFromText(wkt:str, srid:int) :wkb;\n"
    6110             : "x := FromText(wkt,srid,8);\n"
    6111             : "return x;\n"
    6112             : "end GeomCollFromText;\n"
    6113             : "function GeomFromText(wkt:str) :wkb;\n"
    6114             : "x := FromText(wkt,0,0);\n"
    6115             : "return x;\n"
    6116             : "end GeomFromText;\n"
    6117             : "function PointFromText(wkt:str) :wkb;\n"
    6118             : "x := FromText(wkt,0,1);\n"
    6119             : "return x;\n"
    6120             : "end PointFromText;\n"
    6121             : "function LineFromText(wkt:str) :wkb;\n"
    6122             : "x := FromText(wkt,0,2);\n"
    6123             : "return x;\n"
    6124             : "end LineFromText;\n"
    6125             : "function PolygonFromText(wkt:str) :wkb;\n"
    6126             : "x := FromText(wkt,0,4);\n"
    6127             : "return x;\n"
    6128             : "end PolygonFromText;\n"
    6129             : "function MPointFromText(wkt:str) :wkb;\n"
    6130             : "x := FromText(wkt,0,5);\n"
    6131             : "return x;\n"
    6132             : "end MPointFromText;\n"
    6133             : "function MLineFromText(wkt:str) :wkb;\n"
    6134             : "x := FromText(wkt,0,6);\n"
    6135             : "return x;\n"
    6136             : "end MLineFromText;\n"
    6137             : "function MPolyFromText(wkt:str) :wkb;\n"
    6138             : "x := FromText(wkt,0,7);\n"
    6139             : "return x;\n"
    6140             : "end MPolyFromText;\n"
    6141             : "function GeomCollFromText(wkt:str) :wkb;\n"
    6142             : "x := FromText(wkt,0,8);\n"
    6143             : "return x;\n"
    6144             : "end GeomCollFromText;\n"
    6145             : "function NumInteriorRings(w:wkb) :int;\n"
    6146             : "x := geom.NumRings(w, 0);\n"
    6147             : "return x;\n"
    6148             : "end NumInteriorRings;\n"
    6149             : "function NRings(w:wkb) :int;\n"
    6150             : "x := geom.NumRings(w, 1);\n"
    6151             : "return x;\n"
    6152             : "end NRings;\n"
    6153             : "function BdPolyFromText(wkt:str, srid:int) :wkb;\n"
    6154             : "x := MLineStringToPolygon(wkt,srid,0);\n"
    6155             : "return x;\n"
    6156             : "end BdPolyFromText;\n"
    6157             : "function BdMPolyFromText(wkt:str, srid:int) :wkb;\n"
    6158             : "x := MLineStringToPolygon(wkt,srid,1);\n"
    6159             : "return x;\n"
    6160             : "end BdMPolyFromText;\n"
    6161             : "function MakePoint(x:dbl, y:dbl) :wkb;\n"
    6162             : "p := MakePointXYZM(x, y, 0:dbl, 0:dbl, 0);\n"
    6163             : "return p;\n"
    6164             : "end MakePoint;\n"
    6165             : "function MakePoint(x:dbl, y:dbl, z:dbl) :wkb;\n"
    6166             : "p := MakePointXYZM(x, y, z, 0:dbl, 10);\n"
    6167             : "return p;\n"
    6168             : "end MakePoint;\n"
    6169             : "function MakePointM(x:dbl, y:dbl, m:dbl) :wkb;\n"
    6170             : "p := MakePointXYZM(x, y, 0:dbl, m, 1);\n"
    6171             : "return p;\n"
    6172             : "end MakePointM;\n"
    6173             : "function MakePoint(x:dbl, y:dbl, z:dbl, m:dbl) :wkb;\n"
    6174             : "p := MakePointXYZM(x, y, z, m, 11);\n"
    6175             : "return p;\n"
    6176             : "end MakePoint;\n"
    6177             : "function GeometryType1(w:wkb) :str;\n"
    6178             : "x := GeometryType(w, 0);\n"
    6179             : "return x;\n"
    6180             : "end GeometryType1;\n"
    6181             : "function GeometryType2(w:wkb) :str;\n"
    6182             : "x := GeometryType(w, 1);\n"
    6183             : "return x;\n"
    6184             : "end GeometryType2;\n"
    6185             : "function X(w:wkb) :dbl;\n"
    6186             : "c := GetCoordinate(w, 0);\n"
    6187             : "return c;\n"
    6188             : "end X;\n"
    6189             : "function Y(w:wkb) :dbl;\n"
    6190             : "c := GetCoordinate(w, 1);\n"
    6191             : "return c;\n"
    6192             : "end Y;\n"
    6193             : "function Z(w:wkb) :dbl;\n"
    6194             : "c := GetCoordinate(w, 2);\n"
    6195             : "return c;\n"
    6196             : "end Z;\n"
    6197             : "function Force2D(g:wkb) :wkb;\n"
    6198             : "x := ForceDimensions(g, 2);\n"
    6199             : "return x;\n"
    6200             : "end Force2D;\n"
    6201             : "function Force3D(g:wkb) :wkb;\n"
    6202             : "x := ForceDimensions(g, 3);\n"
    6203             : "return x;\n"
    6204             : "end Force3D;\n"
    6205             : "function Translate(g:wkb, dx:dbl, dy:dbl) :wkb;\n"
    6206             : "x := Translate3D(g,dx,dy,0:dbl);\n"
    6207             : "return x;\n"
    6208             : "end Translate;\n"
    6209             : "function Translate(g:wkb, dx:dbl, dy:dbl, dz:dbl) :wkb;\n"
    6210             : "x := Translate3D(g,dx,dy,dz);\n"
    6211             : "return x;\n"
    6212             : "end Translate;\n"
    6213             : "function NumPoints(w:wkb) :int;\n"
    6214             : "x := PointsNum(w, 1);\n"
    6215             : "return x;\n"
    6216             : "end NumPoints;\n"
    6217             : "function NPoints(w:wkb) :int;\n"
    6218             : "x := PointsNum(w, 0);\n"
    6219             : "return x;\n"
    6220             : "end NPoints;\n"
    6221             : "function MakeEnvelope(xmin:dbl, ymin:dbl, xmax:dbl, ymax:dbl, srid:int) :wkb;\n"
    6222             : "x := EnvelopeFromCoordinates(xmin, ymin, xmax, ymax, srid);\n"
    6223             : "return x;\n"
    6224             : "end MakeEnvelope;\n"
    6225             : "function MakeEnvelope(xmin:dbl, ymin:dbl, xmax:dbl, ymax:dbl) :wkb;\n"
    6226             : "x := EnvelopeFromCoordinates(xmin, ymin, xmax, ymax, 0);\n"
    6227             : "return x;\n"
    6228             : "end MakeEnvelope;\n"
    6229             : "function MakePolygon(external:wkb) :wkb;\n"
    6230             : "x := Polygon(external, nil:bat[:wkb], 0);\n"
    6231             : "return x;\n"
    6232             : "end MakePolygon;\n"
    6233             : "function MakePolygon(external:wkb, srid:int) :wkb;\n"
    6234             : "x := Polygon(external, nil:bat[:wkb], srid);\n"
    6235             : "return x;\n"
    6236             : "end MakePolygon;\n"
    6237             : "function XMinFromWKB(g:wkb) :dbl;\n"
    6238             : "x := coordinateFromWKB(g, 1);\n"
    6239             : "return x;\n"
    6240             : "end XMinFromWKB;\n"
    6241             : "function YMinFromWKB(g:wkb) :dbl;\n"
    6242             : "x := coordinateFromWKB(g, 2);\n"
    6243             : "return x;\n"
    6244             : "end YMinFromWKB;\n"
    6245             : "function XMaxFromWKB(g:wkb) :dbl;\n"
    6246             : "x := coordinateFromWKB(g, 3);\n"
    6247             : "return x;\n"
    6248             : "end XMaxFromWKB;\n"
    6249             : "function YMaxFromWKB(g:wkb) :dbl;\n"
    6250             : "x := coordinateFromWKB(g, 4);\n"
    6251             : "return x;\n"
    6252             : "end YMaxFromWKB;\n"
    6253             : "function XMinFromMBR(b:mbr) :dbl;\n"
    6254             : "x := coordinateFromMBR(b, 1);\n"
    6255             : "return x;\n"
    6256             : "end XMinFromMBR;\n"
    6257             : "function YMinFromMBR(b:mbr) :dbl;\n"
    6258             : "x := coordinateFromMBR(b, 2);\n"
    6259             : "return x;\n"
    6260             : "end YMinFromMBR;\n"
    6261             : "function XMaxFromMBR(b:mbr) :dbl;\n"
    6262             : "x := coordinateFromMBR(b, 3);\n"
    6263             : "return x;\n"
    6264             : "end XMaxFromMBR;\n"
    6265             : "function YMaxFromMBR(b:mbr) :dbl;\n"
    6266             : "x := coordinateFromMBR(b, 4);\n"
    6267             : "return x;\n"
    6268             : "end YMaxFromMBR;\n"
    6269             : "module batgeom;\n"
    6270             : "function GeomFromText(wkt:bat[:str], srid:int) :bat[:wkb];\n"
    6271             : "x := FromText(wkt,srid,0);\n"
    6272             : "return x;\n"
    6273             : "end GeomFromText;\n"
    6274             : "function PointFromText(wkt:bat[:str], srid:int) :bat[ :wkb];\n"
    6275             : "x := FromText(wkt,srid,1);\n"
    6276             : "return x;\n"
    6277             : "end PointFromText;\n"
    6278             : "function LineFromText(wkt:bat[:str], srid:int) :bat[ :wkb];\n"
    6279             : "x := FromText(wkt,srid,2);\n"
    6280             : "return x;\n"
    6281             : "end LineFromText;\n"
    6282             : "function PolygonFromText(wkt:bat[:str], srid:int) :bat[ :wkb];\n"
    6283             : "x := FromText(wkt,srid,4);\n"
    6284             : "return x;\n"
    6285             : "end PolygonFromText;\n"
    6286             : "function MPointFromText(wkt:bat[:str], srid:int) :bat[ :wkb];\n"
    6287             : "x := FromText(wkt,srid,5);\n"
    6288             : "return x;\n"
    6289             : "end MPointFromText;\n"
    6290             : "function MLineFromText(wkt:bat[:str], srid:int) :bat[ :wkb];\n"
    6291             : "x := FromText(wkt,srid,6);\n"
    6292             : "return x;\n"
    6293             : "end MLineFromText;\n"
    6294             : "function MPolyFromText(wkt:bat[:str], srid:int) :bat[ :wkb];\n"
    6295             : "x := FromText(wkt,srid,7);\n"
    6296             : "return x;\n"
    6297             : "end MPolyFromText;\n"
    6298             : "function GeomCollFromText(wkt:bat[:str], srid:int) :bat[ :wkb];\n"
    6299             : "x := FromText(wkt,srid,8);\n"
    6300             : "return x;\n"
    6301             : "end GeomCollFromText;\n"
    6302             : "function GeomFromText(wkt:bat[:str]) :bat[:wkb];\n"
    6303             : "x := FromText(wkt,0,0);\n"
    6304             : "return x;\n"
    6305             : "end GeomFromText;\n"
    6306             : "function PointFromText(wkt:bat[:str]) :bat[ :wkb];\n"
    6307             : "x := FromText(wkt,0,1);\n"
    6308             : "return x;\n"
    6309             : "end PointFromText;\n"
    6310             : "function LineFromText(wkt:bat[:str]) :bat[ :wkb];\n"
    6311             : "x := FromText(wkt,0,2);\n"
    6312             : "return x;\n"
    6313             : "end LineFromText;\n"
    6314             : "function PolygonFromText(wkt:bat[:str]) :bat[ :wkb];\n"
    6315             : "x := FromText(wkt,0,4);\n"
    6316             : "return x;\n"
    6317             : "end PolygonFromText;\n"
    6318             : "function MPointFromText(wkt:bat[:str]) :bat[ :wkb];\n"
    6319             : "x := FromText(wkt,0,5);\n"
    6320             : "return x;\n"
    6321             : "end MPointFromText;\n"
    6322             : "function MLineFromText(wkt:bat[:str]) :bat[ :wkb];\n"
    6323             : "x := FromText(wkt,0,6);\n"
    6324             : "return x;\n"
    6325             : "end MLineFromText;\n"
    6326             : "function MPolyFromText(wkt:bat[:str]) :bat[ :wkb];\n"
    6327             : "x := FromText(wkt,0,7);\n"
    6328             : "return x;\n"
    6329             : "end MPolyFromText;\n"
    6330             : "function GeomCollFromText(wkt:bat[:str]) :bat[ :wkb];\n"
    6331             : "x := FromText(wkt,0,8);\n"
    6332             : "return x;\n"
    6333             : "end GeomCollFromText;\n"
    6334             : "function AsText(w:bat[:wkb]) :bat[:str];\n"
    6335             : "x := ToText(w,0);\n"
    6336             : "return x;\n"
    6337             : "end AsText;\n"
    6338             : "function AsEWKT(w:bat[:wkb]) :bat[:str];\n"
    6339             : "x := ToText(w,1);\n"
    6340             : "return x;\n"
    6341             : "end AsEWKT;\n"
    6342             : "function GeometryType1(w:bat[:wkb]) :bat[:str];\n"
    6343             : "x := GeometryType(w, 0);\n"
    6344             : "return x;\n"
    6345             : "end GeometryType1;\n"
    6346             : "function GeometryType2(w:bat[:wkb]) :bat[:str];\n"
    6347             : "x := GeometryType(w, 1);\n"
    6348             : "return x;\n"
    6349             : "end GeometryType2;\n"
    6350             : "function MakePoint(x:bat[:dbl], y:bat[:dbl]) :bat[:wkb];\n"
    6351             : "p := MakePointXYZM(x, y, nil:bat[:dbl], nil:bat, 0);\n"
    6352             : "return p;\n"
    6353             : "end MakePoint;\n"
    6354             : "function MakePoint(x:bat[:dbl], y:bat[:dbl], z:bat[:dbl]) :bat[:wkb];\n"
    6355             : "p := MakePointXYZM(x, y, z, nil:bat[:dbl], 10);\n"
    6356             : "return p;\n"
    6357             : "end MakePoint;\n"
    6358             : "function MakePointM(x:bat[:dbl], y:bat[:dbl], m:bat[:dbl]) :bat[:wkb];\n"
    6359             : "p := MakePointXYZM(x, y, nil:bat[:dbl], m, 1);\n"
    6360             : "return p;\n"
    6361             : "end MakePointM;\n"
    6362             : "function MakePoint(x:bat[:dbl], y:bat[:dbl], z:bat[:dbl], m:bat[:dbl]) :bat[:wkb];\n"
    6363             : "p := MakePointXYZM(x, y, z, m, 11);\n"
    6364             : "return p;\n"
    6365             : "end MakePoint;\n"
    6366             : "function NumPoints(w:bat[:wkb]) :bat[:int];\n"
    6367             : "x := PointsNum(w, 1);\n"
    6368             : "return x;\n"
    6369             : "end NumPoints;\n"
    6370             : "function NPoints(w:bat[:wkb]) :bat[:int];\n"
    6371             : "x := PointsNum(w, 0);\n"
    6372             : "return x;\n"
    6373             : "end NPoints;\n"
    6374             : "function X(w:bat[:wkb]) :bat[:dbl];\n"
    6375             : "c := GetCoordinate(w, 0);\n"
    6376             : "return c;\n"
    6377             : "end X;\n"
    6378             : "function Y(w:bat[:wkb]) :bat[:dbl];\n"
    6379             : "c := GetCoordinate(w, 1);\n"
    6380             : "return c;\n"
    6381             : "end Y;\n"
    6382             : "function Z(w:bat[:wkb]) :bat[:dbl];\n"
    6383             : "c := GetCoordinate(w, 2);\n"
    6384             : "return c;\n"
    6385             : "end Z;\n"
    6386             : "function NumInteriorRings(w:bat[:wkb]) :bat[:int];\n"
    6387             : "x := batgeom.NumRings(w, 0);\n"
    6388             : "return x;\n"
    6389             : "end NumInteriorRings;\n"
    6390             : "function NRings(w:bat[:wkb]) :bat[:int];\n"
    6391             : "x := batgeom.NumRings(w, 1);\n"
    6392             : "return x;\n"
    6393             : "end NRings;\n"
    6394             : "function XMinFromWKB(g:bat[:wkb]) :bat[:dbl];\n"
    6395             : "x := coordinateFromWKB(g, 1);\n"
    6396             : "return x;\n"
    6397             : "end XMinFromWKB;\n"
    6398             : "function YMinFromWKB(g:bat[:wkb]) :bat[:dbl];\n"
    6399             : "x := coordinateFromWKB(g, 2);\n"
    6400             : "return x;\n"
    6401             : "end YMinFromWKB;\n"
    6402             : "function XMaxFromWKB(g:bat[:wkb]) :bat[:dbl];\n"
    6403             : "x := coordinateFromWKB(g, 3);\n"
    6404             : "return x;\n"
    6405             : "end XMaxFromWKB;\n"
    6406             : "function YMaxFromWKB(g:bat[:wkb]) :bat[:dbl];\n"
    6407             : "x := coordinateFromWKB(g, 4);\n"
    6408             : "return x;\n"
    6409             : "end YMaxFromWKB;\n"
    6410             : "function XMinFromMBR(b:bat[:mbr]) :bat[:dbl];\n"
    6411             : "x := coordinateFromMBR(b, 1);\n"
    6412             : "return x;\n"
    6413             : "end XMinFromMBR;\n"
    6414             : "function YMinFromMBR(b:bat[:mbr]) :bat[:dbl];\n"
    6415             : "x := coordinateFromMBR(b, 2);\n"
    6416             : "return x;\n"
    6417             : "end YMinFromMBR;\n"
    6418             : "function XMaxFromMBR(b:bat[:mbr]) :bat[:dbl];\n"
    6419             : "x := coordinateFromMBR(b, 3);\n"
    6420             : "return x;\n"
    6421             : "end XMaxFromMBR;\n"
    6422             : "function YMaxFromMBR(b:bat[:mbr]) :bat[:dbl];\n"
    6423             : "x := coordinateFromMBR(b, 4);\n"
    6424             : "return x;\n"
    6425             : "end YMaxFromMBR;\n"
    6426             : "function calc.wkb( wkt:str, srid:int, type:int ) :wkb;\n"
    6427             : "x := geom.FromText(wkt,0,0);\n"
    6428             : "return x;\n"
    6429             : "end wkb;\n"
    6430             : };
    6431             : 
    6432             : #include "mel.h"
    6433             : static mel_atom geom_init_atoms[] = {
    6434             :  { .name="mbr", .basetype="lng", .size=sizeof(mbr), .tostr=mbrTOSTR, .fromstr=mbrFROMSTR, .hash=mbrHASH, .null=mbrNULL, .cmp=mbrCOMP, .read=mbrREAD, .write=mbrWRITE, },
    6435             :  { .name="wkb", .tostr=wkbTOSTR, .fromstr=wkbFROMSTR, .hash=wkbHASH, .null=wkbNULL, .cmp=wkbCOMP, .read=wkbREAD, .write=wkbWRITE, .put=wkbPUT, .del=wkbDEL, .length=wkbLENGTH, .heap=wkbHEAP, },
    6436             :  { .name="wkba", .tostr=wkbaTOSTR, .fromstr=wkbaFROMSTR, .null=wkbaNULL, .hash=wkbaHASH, .cmp=wkbaCOMP, .read=wkbaREAD, .write=wkbaWRITE, .put=wkbaPUT, .del=wkbaDEL, .length=wkbaLENGTH, .heap=wkbaHEAP, },  { .cmp=NULL }
    6437             : };
    6438             : static mel_func geom_init_funcs[] = {
    6439             :  command("geom", "hasZ", geoHasZ, false, "returns 1 if the geometry has z coordinate", args(1,2, arg("",int),arg("flags",int))),
    6440             :  command("geom", "hasM", geoHasM, false, "returns 1 if the geometry has m coordinate", args(1,2, arg("",int),arg("flags",int))),
    6441             :  command("geom", "getType", geoGetType, false, "returns the str representation of the geometry type", args(1,3, arg("",str),arg("flags",int),arg("format",int))),
    6442             :  command("geom", "MLineStringToPolygon", wkbMLineStringToPolygon, false, "Creates polygons using the MultiLineString provided as WKT. Depending on the flag creates one (flag=0) or multiple (flag=1) polygons", args(1,4, arg("",wkb),arg("wkt",str),arg("srid",int),arg("flag",int))),
    6443             :  command("geom", "AsBinary", wkbAsBinary, false, "Returns the wkb representation into HEX format", args(1,2, arg("",str),arg("w",wkb))),
    6444             :  command("geom", "FromBinary", wkbFromBinary, false, "Creates a wkb using the HEX representation", args(1,2, arg("",wkb),arg("w",str))),
    6445             :  command("geom", "ToText", wkbAsText, false, "", args(1,3, arg("",str),arg("w",wkb),arg("withSRID",int))),
    6446             :  command("geom", "FromText", wkbFromText, false, "", args(1,4, arg("",wkb),arg("wkt",str),arg("srid",int),arg("type",int))),
    6447             :  command("geom", "NumRings", wkbNumRings, false, "Returns the number of interior rings+exterior on the first polygon of the geometry", args(1,3, arg("",int),arg("w",wkb),arg("exterior",int))),
    6448             :  command("geom", "MakePointXYZM", wkbMakePoint, false, "creates a point using the coordinates", args(1,6, arg("",wkb),arg("x",dbl),arg("y",dbl),arg("z",dbl),arg("m",dbl),arg("zmFlag",int))),
    6449             :  command("geom", "GeometryType", wkbGeometryType, false, "", args(1,3, arg("",str),arg("w",wkb),arg("flag",int))),
    6450             :  command("geom", "GetCoordinate", wkbGetCoordinate, false, "Returns the coordinate at position idx of a point, or NULL if not available. idx=0 -> X, idx=1 -> Y, idx=2 -> Z. Input must be point", args(1,3, arg("",dbl),arg("w",wkb),arg("idx",int))),
    6451             :  command("geom", "Boundary", wkbBoundary, false, "Returns the closure of the combinatorial boundary of the Geometry.", args(1,2, arg("",wkb),arg("w",wkb))),
    6452             :  command("geom", "CoordDim", wkbCoordDim, false, "Return the coordinate dimension of the geometry", args(1,2, arg("",int),arg("w",wkb))),
    6453             :  command("geom", "Dimension", wkbDimension, false, "The inherent dimension of this Geometry object, which must be less than or equal to the coordinate dimension.", args(1,2, arg("",int),arg("w",wkb))),
    6454             :  command("geom", "getSRID", wkbGetSRID, false, "Returns the Spatial Reference System ID for this Geometry.", args(1,2, arg("",int),arg("w",wkb))),
    6455             :  command("geom", "setSRID", wkbSetSRID, false, "Sets the Reference System ID for this Geometry.", args(1,3, arg("",wkb),arg("w",wkb),arg("srid",int))),
    6456             :  command("geom", "StartPoint", wkbStartPoint, false, "Returns the first point of a LINESTRING geometry as a POINT or NULL if the input parameter is not a LINESTRING", args(1,2, arg("",wkb),arg("w",wkb))),
    6457             :  command("geom", "EndPoint", wkbEndPoint, false, "Returns the last point of a LINESTRING geometry as a POINT or NULL if the input parameter is not a LINESTRING.", args(1,2, arg("",wkb),arg("w",wkb))),
    6458             :  command("geom", "PointN", wkbPointN, false, "Returns the n-th point of the Geometry. Argument w should be Linestring.", args(1,3, arg("",wkb),arg("w",wkb),arg("n",int))),
    6459             :  command("geom", "Envelope", wkbEnvelope, false, "The minimum bounding box for this Geometry, returned as a Geometry. The polygon is defined by the corner points of the bounding box ((MINX,MINY),(MAXX,MINY),(MAXX,MAXY),(MINX,MAXY)).", args(1,2, arg("",wkb),arg("w",wkb))),
    6460             :  command("geom", "EnvelopeFromCoordinates", wkbEnvelopeFromCoordinates, false, "A polygon created by the provided coordinates", args(1,6, arg("",wkb),arg("",dbl),arg("",dbl),arg("",dbl),arg("",dbl),arg("",int))),
    6461             :  command("geom", "Polygon", wkbMakePolygon, false, "Returns a Polygon created from the provided LineStrings", args(1,4, arg("",wkb),arg("",wkb),batarg("",wkb),arg("",int))),
    6462             :  command("geom", "ExteriorRing", wkbExteriorRing, false, "Returns a line string representing the exterior ring of the POLYGON geometry. Return NULL if the geometry is not a polygon.", args(1,2, arg("",wkb),arg("w",wkb))),
    6463             :  command("geom", "InteriorRingN", wkbInteriorRingN, false, "Return the Nth interior linestring ring of the polygon geometry. Return NULL if the geometry is not a polygon or the given N is out of range.", args(1,3, arg("",wkb),arg("w",wkb),arg("n",int))),
    6464             :  command("geom", "InteriorRings", wkbInteriorRings, false, "Returns an 'array' with all the interior rings of the polygon", args(1,2, arg("",wkba),arg("w",wkb))),
    6465             :  command("geom", "IsClosed", wkbIsClosed, false, "Returns TRUE if the LINESTRING's start and end points are coincident.", args(1,2, arg("",bit),arg("w",wkb))),
    6466             :  command("geom", "IsEmpty", wkbIsEmpty, false, "Returns true if this Geometry is an empty geometry.", args(1,2, arg("",bit),arg("w",wkb))),
    6467             :  command("geom", "IsRing", wkbIsRing, false, "Returns TRUE if this LINESTRING is both closed and simple.", args(1,2, arg("",bit),arg("w",wkb))),
    6468             :  command("geom", "IsSimple", wkbIsSimple, false, "Returns (TRUE) if this Geometry has no anomalous geometric points, such as self intersection or self tangency.", args(1,2, arg("",bit),arg("w",wkb))),
    6469             :  command("geom", "IsValid", wkbIsValid, false, "Returns true if the ST_Geometry is well formed.", args(1,2, arg("",bit),arg("w",wkb))),
    6470             :  command("geom", "IsValidReason", wkbIsValidReason, false, "Returns text stating if a geometry is valid or not and if not valid, a reason why.", args(1,2, arg("",str),arg("w",wkb))),
    6471             :  command("geom", "IsValidDetail", wkbIsValidDetail, false, "Returns a valid_detail (valid,reason,location) row stating if a geometry is valid or not and if not valid, a reason why and a location where.", args(1,2, arg("",str),arg("w",wkb))),
    6472             :  command("geom", "Area", wkbArea, false, "Returns the area of the surface if it is a polygon or multi-polygon", args(1,2, arg("",dbl),arg("w",wkb))),
    6473             :  command("geom", "Centroid", wkbCentroid, false, "Computes the geometric center of a geometry, or equivalently, the center of mass of the geometry as a POINT.", args(1,2, arg("",wkb),arg("w",wkb))),
    6474             :  command("geom", "Distance", wkbDistance, false, "Returns the 2-dimensional minimum cartesian distance between the two geometries in projected units (spatial ref units.", args(1,3, arg("",dbl),arg("a",wkb),arg("b",wkb))),
    6475             :  command("geom", "Length", wkbLength, false, "Returns the cartesian 2D length of the geometry if it is a linestrin or multilinestring", args(1,2, arg("",dbl),arg("w",wkb))),
    6476             :  command("geom", "ConvexHull", wkbConvexHull, false, "Returns a geometry that represents the convex hull of this geometry. The convex hull of a geometry represents the minimum convex geometry that encloses all geometries within the set.", args(1,2, arg("",wkb),arg("w",wkb))),
    6477             :  command("geom", "Intersection", wkbIntersection, false, "Returns a geometry that represents the point set intersection of the Geometries a, b", args(1,3, arg("",wkb),arg("a",wkb),arg("b",wkb))),
    6478             :  command("geom", "Union", wkbUnion, false, "Returns a geometry that represents the point set union of the Geometries a, b", args(1,3, arg("",wkb),arg("a",wkb),arg("b",wkb))),
    6479             :  command("geom", "Union", wkbUnionAggr, false, "Gets a BAT with geometries and returns their union", args(1,2, arg("",wkb),batarg("a",wkb))),
    6480             :  command("geom", "Difference", wkbDifference, false, "Returns a geometry that represents that part of geometry A that does not intersect with geometry B", args(1,3, arg("",wkb),arg("a",wkb),arg("b",wkb))),
    6481             :  command("geom", "SymDifference", wkbSymDifference, false, "Returns a geometry that represents the portions of A and B that do not intersect", args(1,3, arg("",wkb),arg("a",wkb),arg("b",wkb))),
    6482             :  command("geom", "Buffer", wkbBuffer, false, "Returns a geometry that represents all points whose distance from this geometry is less than or equal to distance. Calculations are in the Spatial Reference System of this Geometry.", args(1,3, arg("",wkb),arg("a",wkb),arg("distance",dbl))),
    6483             :  command("geom", "Contains", wkbContains, false, "Returns true if and only if no points of B lie in the exterior of A, and at least one point of the interior of B lies in the interior of A.", args(1,3, arg("",bit),arg("a",wkb),arg("b",wkb))),
    6484             :  command("geom", "Crosses", wkbCrosses, false, "Returns TRUE if the supplied geometries have some, but not all, interior points in common.", args(1,3, arg("",bit),arg("a",wkb),arg("b",wkb))),
    6485             :  command("geom", "Disjoint", wkbDisjoint, false, "Returns true if these Geometries are 'spatially disjoint'", args(1,3, arg("",bit),arg("a",wkb),arg("b",wkb))),
    6486             :  command("geom", "Equals", wkbEquals, false, "Returns true if the given geometries represent the same geometry. Directionality is ignored.", args(1,3, arg("",bit),arg("a",wkb),arg("b",wkb))),
    6487             :  command("geom", "Intersects", wkbIntersects, false, "Returns true if these Geometries 'spatially intersect in 2D'", args(1,3, arg("",bit),arg("a",wkb),arg("b",wkb))),
    6488             :  command("geom", "Overlaps", wkbOverlaps, false, "Returns TRUE if the Geometries intersect but are not completely contained by each other.", args(1,3, arg("",bit),arg("a",wkb),arg("b",wkb))),
    6489             :  command("geom", "Relate", wkbRelate, false, "Returns true if the Geometry a 'spatially related' to Geometry b, by testing for intersection between the Interior, Boundary and Exterior of the two geometries as specified by the values in the intersectionPatternMatrix.", args(1,4, arg("",bit),arg("a",wkb),arg("b",wkb),arg("intersection_matrix_pattern",str))),
    6490             :  command("geom", "Touches", wkbTouches, false, "Returns TRUE if the geometries have at least one point in common, but their interiors do not intersect.", args(1,3, arg("",bit),arg("a",wkb),arg("b",wkb))),
    6491             :  command("geom", "Within", wkbWithin, false, "Returns TRUE if the geometry A is completely inside geometry B", args(1,3, arg("",bit),arg("a",wkb),arg("b",wkb))),
    6492             :  command("geom", "Covers", wkbCovers, false, "Returns TRUE if no point of geometry B is outside geometry A", args(1,3, arg("",bit),arg("a",wkb),arg("b",wkb))),
    6493             :  command("geom", "CoveredBy", wkbCoveredBy, false, "Returns TRUE if no point of geometry A is outside geometry B", args(1,3, arg("",bit),arg("a",wkb),arg("b",wkb))),
    6494             :  command("geom", "DWithin", wkbDWithin, false, "Returns true if the two geometries are within the specifies distance from each other", args(1,4, arg("",bit),arg("a",wkb),arg("b",wkb),arg("dst",dbl))),
    6495             :  command("geom", "GeometryN", wkbGeometryN, false, "Returns the 1-based Nth geometry if the geometry is a GEOMETRYCOLLECTION, (MULTI)POINT, (MULTI)LINESTRING, MULTICURVE or (MULTI)POLYGON. Otherwise, return NULL", args(1,3, arg("",wkb),arg("g",wkb),arg("n",int))),
    6496             :  command("geom", "NumGeometries", wkbNumGeometries, false, "Returns the number of geometries", args(1,2, arg("",int),arg("g",wkb))),
    6497             :  command("geom", "Transform", wkbTransform, false, "Transforms a geometry from one srid to another", args(1,6, arg("",wkb),arg("g",wkb),arg("srid_src",int),arg("srid_dst",int),arg("proj_src",str),arg("proj_dest",str))),
    6498             :  command("geom", "DelaunayTriangles", wkbDelaunayTriangles, false, "Returns a Delaunay triangulation, flag=0 => collection of polygons, flag=1 => multilinestring", args(1,4, arg("",wkb),arg("a",wkb),arg("tolerance",dbl),arg("flag",int))),
    6499             :  command("geom", "Dump", wkbDump, false, "Gets a MultiPolygon and returns the Polygons in it", args(2,3, batarg("id",str),batarg("geom",wkb),arg("a",wkb))),
    6500             :  command("geom", "DumpPoints", wkbDumpPoints, false, "Gets a Geometry and returns the Points in it", args(2,3, batarg("id",str),batarg("geom",wkb),arg("a",wkb))),
    6501             :  command("geom", "Segmentize", wkbSegmentize, false, "It creates a new geometry with all segments on it smaller or equal to sz", args(1,3, arg("",wkb),arg("g",wkb),arg("sz",dbl))),
    6502             :  command("geom", "ForceDimensions", wkbForceDim, false, "Removes or Adds additional coordinates in the geometry to make it d dimensions", args(1,3, arg("",wkb),arg("g",wkb),arg("d",int))),
    6503             :  command("geom", "Contains", wkbContains_point, false, "Returns true if the Geometry a 'spatially contains' Geometry b", args(1,4, arg("",bit),arg("a",wkb),arg("x",dbl),arg("y",dbl))),
    6504             :  command("geom", "Translate3D", wkbTranslate, false, "Moves all points of the geometry by dx, dy, dz", args(1,5, arg("",wkb),arg("g",wkb),arg("dx",dbl),arg("dy",dbl),arg("dz",dbl))),
    6505             :  command("geom", "Contains", wkbContains_point_bat, false, "Returns true if the Geometry-BAT a 'spatially contains' Geometry-B b", args(1,4, batarg("",bit),arg("a",wkb),batarg("px",dbl),batarg("py",dbl))),
    6506             :  command("geom", "PointsNum", wkbNumPoints, false, "The number of points in the Geometry. If check=1, the geometry should be a linestring", args(1,3, arg("",int),arg("w",wkb),arg("check",int))),
    6507             :  command("geom", "MakeLine", wkbMakeLine, false, "Gets two point or linestring geometries and returns a linestring geometry", args(1,3, arg("",wkb),arg("a",wkb),arg("b",wkb))),
    6508             :  command("geom", "MakeLine", wkbMakeLineAggr, false, "Gets a BAT with point or linestring geometries and returns a single linestring geometry", args(1,2, arg("",wkb),batarg("a",wkb))),
    6509             :  command("geom", "PointOnSurface", wkbPointOnSurface, false, "Returns a point guaranteed to lie on the surface. Similar to postGIS it works for points and lines in addition to surfaces and for 3d geometries.", args(1,2, arg("",wkb),arg("w",wkb))),
    6510             :  command("geom", "mbr", wkbMBR, false, "Creates the mbr for the given wkb.", args(1,2, arg("",mbr),arg("",wkb))),
    6511             :  command("geom", "MakeBox2D", wkbBox2D, false, "Creates an mbr from the two 2D points", args(1,3, arg("",mbr),arg("",wkb),arg("",wkb))),
    6512             :  command("geom", "mbrOverlaps", mbrOverlaps_wkb, false, "Returns true if the mbr of geom1 overlaps the mbr of geom2", args(1,3, arg("",bit),arg("geom1",wkb),arg("geom2",wkb))),
    6513             :  command("geom", "mbrOverlaps", mbrOverlaps, false, "Returns true if box1 overlaps box2", args(1,3, arg("",bit),arg("box1",mbr),arg("box2",mbr))),
    6514             :  command("geom", "mbrOverlapOrLeft", mbrOverlapOrLeft_wkb, false, "Returns true if the mbr of geom1 overlaps or is to the left of thr mbr of geom2", args(1,3, arg("",bit),arg("geom1",wkb),arg("geom2",wkb))),
    6515             :  command("geom", "mbrOverlapOrLeft", mbrOverlapOrLeft, false, "Returns true if box1 overlaps or is to the left of box2", args(1,3, arg("",bit),arg("box1",mbr),arg("box2",mbr))),
    6516             :  command("geom", "mbrOverlapOrBelow", mbrOverlapOrBelow_wkb, false, "Returns true if the mbr of geom1 overlaps or is below the mbr of geom2", args(1,3, arg("",bit),arg("geom1",wkb),arg("geom2",wkb))),
    6517             :  command("geom", "mbrOverlapOrBelow", mbrOverlapOrBelow, false, "Returns true if box1 overlaps or is below box2", args(1,3, arg("",bit),arg("box1",mbr),arg("box2",mbr))),
    6518             :  command("geom", "mbrOverlapOrRight", mbrOverlapOrRight_wkb, false, "Returns true if the mbr of geom1 overlalps or is right of the mbr of geom2", args(1,3, arg("",bit),arg("geom1",wkb),arg("geom2",wkb))),
    6519             :  command("geom", "mbrOverlapOrRight", mbrOverlapOrRight, false, "Returns true if box1 overlalps or is right of box2", args(1,3, arg("",bit),arg("box1",mbr),arg("box2",mbr))),
    6520             :  command("geom", "mbrLeft", mbrLeft_wkb, false, "Returns true if the mbr of geom1 is left of the mbr of geom2", args(1,3, arg("",bit),arg("geom1",wkb),arg("geom2",wkb))),
    6521             :  command("geom", "mbrLeft", mbrLeft, false, "Returns true if box1 is left of box2", args(1,3, arg("",bit),arg("box1",mbr),arg("box2",mbr))),
    6522             :  command("geom", "mbrBelow", mbrBelow_wkb, false, "Returns true if the mbr of geom1 is below the mbr of geom2", args(1,3, arg("",bit),arg("geom1",wkb),arg("geom2",wkb))),
    6523             :  command("geom", "mbrBelow", mbrBelow, false, "Returns true if box1 is below box2", args(1,3, arg("",bit),arg("box1",mbr),arg("box2",mbr))),
    6524             :  command("geom", "mbrEqual", mbrEqual_wkb, false, "Returns true if the mbr of geom1 is the same as the mbr of geom2", args(1,3, arg("",bit),arg("geom1",wkb),arg("geom2",wkb))),
    6525             :  command("geom", "mbrEqual", mbrEqual, false, "Returns true if box1 is the same as box2", args(1,3, arg("",bit),arg("box1",mbr),arg("box2",mbr))),
    6526             :  command("geom", "mbrRight", mbrRight_wkb, false, "Returns true if the mbr of geom1 is right of the mbr of geom2", args(1,3, arg("",bit),arg("geom1",wkb),arg("geom2",wkb))),
    6527             :  command("geom", "mbrRight", mbrRight, false, "Returns true if box1 is right of box2", args(1,3, arg("",bit),arg("box1",mbr),arg("box2",mbr))),
    6528             :  command("geom", "mbrContained", mbrContained_wkb, false, "Returns true if the mbr of geom1 is contained by the mbr of geom2", args(1,3, arg("",bit),arg("geom1",wkb),arg("geom2",wkb))),
    6529             :  command("geom", "mbrContained", mbrContained, false, "Returns true if box1 is contained by box2", args(1,3, arg("",bit),arg("box1",mbr),arg("box2",mbr))),
    6530             :  command("geom", "mbrOverlapOrAbove", mbrOverlapOrAbove_wkb, false, "Returns true if the mbr of geom1 overlaps or is above the mbr of geom2", args(1,3, arg("",bit),arg("geom1",wkb),arg("geom2",wkb))),
    6531             :  command("geom", "mbrOverlapOrAbove", mbrOverlapOrAbove, false, "Returns true if box1 overlaps or is above box2", args(1,3, arg("",bit),arg("box1",mbr),arg("box2",mbr))),
    6532             :  command("geom", "mbrAbove", mbrAbove_wkb, false, "Returns true if the mbr of geom1 is above the mbr of geom2", args(1,3, arg("",bit),arg("geom1",wkb),arg("geom2",wkb))),
    6533             :  command("geom", "mbrAbove", mbrAbove, false, "Returns true if box1 is above box2", args(1,3, arg("",bit),arg("box1",mbr),arg("box2",mbr))),
    6534             :  command("geom", "mbrContains", mbrContains_wkb, false, "Returns true if the mbr of geom1 contains the mbr of geom2", args(1,3, arg("",bit),arg("geom1",wkb),arg("geom2",wkb))),
    6535             :  command("geom", "mbrContains", mbrContains, false, "Returns true if box1 contains box2", args(1,3, arg("",bit),arg("box1",mbr),arg("box2",mbr))),
    6536             :  command("geom", "mbrDistance", mbrDistance_wkb, false, "Returns the distance of the centroids of the mbrs of the two geometries", args(1,3, arg("",dbl),arg("geom1",wkb),arg("geom2",wkb))),
    6537             :  command("geom", "mbrDistance", mbrDistance, false, "Returns the distance of the centroids of the two boxes", args(1,3, arg("",dbl),arg("box1",mbr),arg("box2",mbr))),
    6538             :  command("geom", "coordinateFromWKB", wkbCoordinateFromWKB, false, "returns xmin (=1), ymin (=2), xmax (=3) or ymax(=4) of the provided geometry", args(1,3, arg("",dbl),arg("",wkb),arg("",int))),
    6539             :  command("geom", "coordinateFromMBR", wkbCoordinateFromMBR, false, "returns xmin (=1), ymin (=2), xmax (=3) or ymax(=4) of the provided mbr", args(1,3, arg("",dbl),arg("",mbr),arg("",int))),
    6540             :  command("geom", "prelude", geom_prelude, false, "", args(1,1, arg("",void))),
    6541             :  command("geom", "epilogue", geom_epilogue, false, "", args(1,1, arg("",void))),
    6542             :  command("batgeom", "FromText", wkbFromText_bat, false, "", args(1,4, batarg("",wkb),batarg("wkt",str),arg("srid",int),arg("type",int))),
    6543             :  command("batgeom", "ToText", wkbAsText_bat, false, "", args(1,3, batarg("",str),batarg("w",wkb),arg("withSRID",int))),
    6544             :  command("batgeom", "GeometryType", wkbGeometryType_bat, false, "", args(1,3, batarg("",str),batarg("w",wkb),arg("flag",int))),
    6545             :  command("batgeom", "MakePointXYZM", wkbMakePoint_bat, false, "creates a point using the coordinates", args(1,6, batarg("",wkb),batarg("x",dbl),batarg("y",dbl),batarg("z",dbl),batarg("m",dbl),arg("zmFlag",int))),
    6546             :  command("batgeom", "PointsNum", wkbNumPoints_bat, false, "The number of points in the Geometry. If check=1, the geometry should be a linestring", args(1,3, batarg("",int),batarg("w",wkb),arg("check",int))),
    6547             :  command("batgeom", "GetCoordinate", wkbGetCoordinate_bat, false, "Returns the coordinate at position idx of a point, or NULL if not available. idx=0 -> X, idx=1 -> Y, idx=2 -> Z. Input must be point", args(1,3, batarg("",dbl),batarg("w",wkb),arg("idx",int))),
    6548             :  command("batgeom", "GeometryN", wkbGeometryN_bat, false, "Returns the 1-based Nth geometry if the geometry is a GEOMETRYCOLLECTION, (MULTI)POINT, (MULTI)LINESTRING, MULTICURVE or (MULTI)POLYGON. Otherwise, return NULL", args(1,3, batarg("",wkb),batarg("w",wkb),arg("n",int))),
    6549             :  command("batgeom", "NumGeometries", wkbNumGeometries_bat, false, "Returns the number of geometries", args(1,2, batarg("",int),batarg("w",wkb))),
    6550             :  command("batgeom", "NumRings", wkbNumRings_bat, false, "Returns the number of interior rings+exterior on the first polygon of the geometry", args(1,3, batarg("",int),batarg("w",wkb),arg("exterior",int))),
    6551             :  command("batgeom", "Boundary", wkbBoundary_bat, false, "", args(1,2, batarg("",wkb),batarg("w",wkb))),
    6552             :  command("batgeom", "IsClosed", wkbIsClosed_bat, false, "", args(1,2, batarg("",bit),batarg("w",wkb))),
    6553             :  command("batgeom", "IsEmpty", wkbIsEmpty_bat, false, "", args(1,2, batarg("",bit),batarg("w",wkb))),
    6554             :  command("batgeom", "IsSimple", wkbIsSimple_bat, false, "", args(1,2, batarg("",bit),batarg("w",wkb))),
    6555             :  command("batgeom", "IsRing", wkbIsRing_bat, false, "", args(1,2, batarg("",bit),batarg("w",wkb))),
    6556             :  command("batgeom", "IsValid", wkbIsValid_bat, false, "", args(1,2, batarg("",bit),batarg("w",wkb))),
    6557             :  command("batgeom", "MakeBox2D", wkbBox2D_bat, false, "", args(1,3, batarg("",mbr),batarg("p1",wkb),batarg("p2",wkb))),
    6558             :  command("batgeom", "Dimension", wkbDimension_bat, false, "", args(1,2, batarg("",int),batarg("w",wkb))),
    6559             :  command("batgeom", "Distance", wkbDistance_bat, false, "", args(1,3, batarg("",dbl),batarg("a",wkb),batarg("b",wkb))),
    6560             :  command("batgeom", "Distance", wkbDistance_geom_bat, false, "", args(1,3, batarg("",dbl),arg("a",wkb),batarg("b",wkb))),
    6561             :  command("batgeom", "Distance", wkbDistance_bat_geom, false, "", args(1,3, batarg("",dbl),batarg("a",wkb),arg("b",wkb))),
    6562             :  command("batgeom", "Contains", wkbContains_bat, false, "", args(1,3, batarg("",bit),batarg("a",wkb),batarg("b",wkb))),
    6563             :  command("batgeom", "Contains", wkbContains_geom_bat, false, "", args(1,3, batarg("",bit),arg("a",wkb),batarg("b",wkb))),
    6564             :  command("batgeom", "Contains", wkbContains_bat_geom, false, "", args(1,3, batarg("",bit),batarg("a",wkb),arg("b",wkb))),
    6565             :  command("batgeom", "Filter", wkbFilter_geom_bat, false, "Filters the points in the bats according to the MBR of the other bat.", args(1,3, batarg("",wkb),arg("a",wkb),batarg("b",wkb))),
    6566             :  command("batgeom", "Filter", wkbFilter_bat_geom, false, "", args(1,3, batarg("",wkb),batarg("a",wkb),arg("b",wkb))),
    6567             :  command("batgeom", "setSRID", wkbSetSRID_bat, false, "Sets the Reference System ID for this Geometry.", args(1,3, batarg("",wkb),batarg("w",wkb),arg("srid",int))),
    6568             :  command("batgeom", "MakeLine", wkbMakeLine_bat, false, "Gets two BATS of point or linestring geometries and returns a bat with linestring geometries", args(1,3, batarg("",wkb),batarg("a",wkb),batarg("b",wkb))),
    6569             :  command("batgeom", "Union", wkbUnion_bat, false, "Gets two BATS of geometries and returns the pairwise unions", args(1,3, batarg("",wkb),batarg("a",wkb),batarg("b",wkb))),
    6570             :  command("batgeom", "mbr", wkbMBR_bat, false, "Creates the mbr for the given wkb.", args(1,2, batarg("",mbr),batarg("",wkb))),
    6571             :  command("batgeom", "coordinateFromWKB", wkbCoordinateFromWKB_bat, false, "returns xmin (=1), ymin (=2), xmax (=3) or ymax(=4) of the provided geometry", args(1,3, batarg("",dbl),batarg("",wkb),arg("",int))),
    6572             :  command("batgeom", "coordinateFromMBR", wkbCoordinateFromMBR_bat, false, "returns xmin (=1), ymin (=2), xmax (=3) or ymax(=4) of the provided mbr", args(1,3, batarg("",dbl),batarg("",mbr),arg("",int))),
    6573             :  command("calc", "mbr", mbrFromString, false, "", args(1,2, arg("",mbr),arg("v",str))),
    6574             :  command("calc", "mbr", mbrFromMBR, false, "", args(1,2, arg("",mbr),arg("v",mbr))),
    6575             :  command("calc", "wkb", wkbFromWKB, false, "It is called when adding a new geometry column to an existing table", args(1,2, arg("",wkb),arg("v",wkb))),
    6576             :  command("calc", "wkb", geom_2_geom, false, "Called when inserting values to a table in order to check if the inserted geometries are of the same type and srid required by the column definition", args(1,4, arg("",wkb),arg("geo",wkb),arg("columnType",int),arg("columnSRID",int))),
    6577             :  command("batcalc", "wkb", geom_2_geom_bat, false, "Called when inserting values to a table in order to check if the inserted geometries are of the same type and srid required by the column definition", args(1,5, batarg("",wkb),batarg("geo",wkb),batarg("s",oid),arg("columnType",int),arg("columnSRID",int))),
    6578             :  command("batcalc", "wkb", wkbFromText_bat_cand, false, "", args(1,5, batarg("",wkb),batarg("wkt",str),batarg("s",oid),arg("srid",int),arg("type",int))),
    6579             :  { .imp=NULL }
    6580             : };
    6581             : #include "mal_import.h"
    6582             : #ifdef _MSC_VER
    6583             : #undef read
    6584             : #pragma section(".CRT$XCU",read)
    6585             : #endif
    6586         254 : LIB_STARTUP_FUNC(init_geom_mal)
    6587         254 : { mal_module2("geom", geom_init_atoms, geom_init_funcs, NULL, (const char*)geom_functions); }

Generated by: LCOV version 1.14