LCOV - code coverage report
Current view: top level - geom/monetdb5 - geom.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1343 3123 43.0 %
Date: 2024-10-03 20:03:20 Functions: 156 223 70.0 %

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

Generated by: LCOV version 1.14