LCOV - code coverage report
Current view: top level - geom/monetdb5 - geod.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 7 724 1.0 %
Date: 2024-04-25 20:03:45 Functions: 1 49 2.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             : #include "geod.h"
      14             : 
      15             : /**
      16             : *  Convertions
      17             : *
      18             : **/
      19             : 
      20             : const double earth_radius = 6371.009;
      21             : const double earth_radius_meters = 6371009;
      22             : #ifndef M_PI
      23             : #define M_PI    ((double) 3.14159265358979323846)       /* pi */
      24             : #endif
      25             : #ifndef M_PI_2
      26             : #define M_PI_2      1.57079632679489661923132169163975144   /* pi/2           */
      27             : #endif
      28             : 
      29             : /* Converts a longitude value in degrees to radians */
      30             : static double
      31           0 : deg2RadLongitude(double lon_degrees)
      32             : {
      33             :         //Convert
      34           0 :         double lon = M_PI * lon_degrees / 180.0;
      35             :         //Normalize
      36           0 :         if (lon == -1.0 * M_PI)
      37             :                 return M_PI;
      38           0 :         if (lon == -2.0 * M_PI)
      39             :                 return 0.0;
      40           0 :         if (lon > 2.0 * M_PI)
      41           0 :                 lon = remainder(lon, 2.0 * M_PI);
      42             : 
      43           0 :         if (lon < -2.0 * M_PI)
      44           0 :                 lon = remainder(lon, -2.0 * M_PI);
      45             : 
      46           0 :         if (lon > M_PI)
      47           0 :                 lon = -2.0 * M_PI + lon;
      48             : 
      49           0 :         if (lon < -1.0 * M_PI)
      50           0 :                 lon = 2.0 * M_PI + lon;
      51             : 
      52           0 :         if (lon == -2.0 * M_PI)
      53           0 :                 lon *= -1.0;
      54             : 
      55             :         return lon;
      56             : }
      57             : 
      58             : /* Converts a latitude value in degrees to radians */
      59             : static double
      60           0 : deg2RadLatitude(double lat_degrees)
      61             : {
      62             :         //Convert
      63           0 :         double lat = M_PI * lat_degrees / 180.0;
      64             :         //Normalize
      65           0 :         if (lat > 2.0 * M_PI)
      66           0 :                 lat = remainder(lat, 2.0 * M_PI);
      67             : 
      68           0 :         if (lat < -2.0 * M_PI)
      69           0 :                 lat = remainder(lat, -2.0 * M_PI);
      70             : 
      71           0 :         if (lat > M_PI)
      72           0 :                 lat = M_PI - lat;
      73             : 
      74           0 :         if (lat < -1.0 * M_PI)
      75           0 :                 lat = -1.0 * M_PI - lat;
      76             : 
      77           0 :         if (lat > M_PI_2)
      78           0 :                 lat = M_PI - lat;
      79             : 
      80           0 :         if (lat < -1.0 * M_PI_2)
      81           0 :                 lat = -1.0 * M_PI - lat;
      82             : 
      83           0 :         return lat;
      84             : }
      85             : 
      86             : /* Converts the GeoPoint from degrees to radians latitude and longitude*/
      87             : static GeoPoint
      88           0 : deg2RadPoint(GeoPoint geo)
      89             : {
      90           0 :         geo.lon = deg2RadLongitude(geo.lon);
      91           0 :         geo.lat = deg2RadLatitude(geo.lat);
      92           0 :         return geo;
      93             : }
      94             : 
      95             : /**
      96             :  *  Converts a longitude value in radians to degrees
      97             :  */
      98             : static double
      99           0 : rad2DegLongitude(double lon_radians)
     100             : {
     101             :         //Convert
     102           0 :         double lon = lon_radians * 180.0 / M_PI;
     103             :         //Normalize
     104           0 :         if (lon > 360.0)
     105           0 :                 lon = remainder(lon, 360.0);
     106             : 
     107           0 :         if (lon < -360.0)
     108           0 :                 lon = remainder(lon, -360.0);
     109             : 
     110           0 :         if (lon > 180.0)
     111           0 :                 lon = -360.0 + lon;
     112             : 
     113           0 :         if (lon < -180.0)
     114           0 :                 lon = 360 + lon;
     115             : 
     116           0 :         if (lon == -180.0)
     117             :                 return 180.0;
     118             : 
     119           0 :         if (lon == -360.0)
     120           0 :                 return 0.0;
     121             : 
     122             :         return lon;
     123             : }
     124             : 
     125             : /**
     126             :  *  Converts a latitude value in radians to degrees
     127             :  */
     128             : static double
     129           0 : rad2DegLatitude(double lat_radians)
     130             : {
     131             :         //Convert
     132           0 :         double lat = lat_radians * 180.0 / M_PI;
     133             :         //Normalize
     134           0 :         if (lat > 360.0)
     135           0 :                 lat = remainder(lat, 360.0);
     136             : 
     137           0 :         if (lat < -360.0)
     138           0 :                 lat = remainder(lat, -360.0);
     139             : 
     140           0 :         if (lat > 180.0)
     141           0 :                 lat = 180.0 - lat;
     142             : 
     143           0 :         if (lat < -180.0)
     144           0 :                 lat = -180.0 - lat;
     145             : 
     146           0 :         if (lat > 90.0)
     147           0 :                 lat = 180.0 - lat;
     148             : 
     149           0 :         if (lat < -90.0)
     150           0 :                 lat = -180.0 - lat;
     151             : 
     152           0 :         return lat;
     153             : }
     154             : 
     155             : /* Converts the GeoPoint from degrees to radians latitude and longitude*/
     156             : static GeoPoint
     157           0 : rad2DegPoint(GeoPoint geo)
     158             : {
     159           0 :         geo.lon = rad2DegLongitude(geo.lon);
     160           0 :         geo.lat = rad2DegLatitude(geo.lat);
     161           0 :         return geo;
     162             : }
     163             : 
     164             : /* Converts the a GEOSGeom Point into a GeoPoint */
     165             : static GeoPoint
     166           0 : geoPointFromGeom(GEOSGeom geom)
     167             : {
     168           0 :         GeoPoint geo;
     169           0 :         GEOSGeomGetX_r(geoshandle, geom, &(geo.lon));
     170           0 :         GEOSGeomGetY_r(geoshandle, geom, &(geo.lat));
     171           0 :         return geo;
     172             : }
     173             : 
     174             : /* Converts the a GEOSGeom Line into GeoLines
     175             :    Argument must be a Line geometry. */
     176             : static GeoLines
     177           0 : geoLinesFromGeom(GEOSGeom geom)
     178             : {
     179           0 :         const GEOSCoordSequence *gcs = GEOSGeom_getCoordSeq_r(geoshandle, geom);
     180           0 :         GeoLines geo;
     181           0 :         geo.pointCount = GEOSGeomGetNumPoints_r(geoshandle, geom);
     182           0 :         geo.points = GDKmalloc(sizeof(GeoPoint) * geo.pointCount);
     183           0 :         for (int i = 0; i < geo.pointCount; i++)
     184           0 :                 GEOSCoordSeq_getXY_r(geoshandle, gcs, i, &geo.points[i].lon, &geo.points[i].lat);
     185           0 :         geo.bbox = NULL;
     186           0 :         return geo;
     187             : }
     188             : 
     189             : static BoundingBox * boundingBoxLines(GeoLines lines);
     190             : 
     191             : /* Converts the a GEOSGeom Line into GeoPolygon (with exterior ring and zero-to-multiple interior rings)
     192             :    Argument must be a Polygon geometry. */
     193             : static GeoPolygon
     194           0 : geoPolygonFromGeom(GEOSGeom geom)
     195             : {
     196           0 :         GeoPolygon geo;
     197             :         //Get exterior ring GeoLines
     198           0 :         geo.exteriorRing = geoLinesFromGeom((GEOSGeom)GEOSGetExteriorRing_r(geoshandle, geom));
     199           0 :         geo.interiorRingsCount = GEOSGetNumInteriorRings_r(geoshandle, geom);
     200             :         //If there are interior rings, allocate space to their GeoLines representation
     201           0 :         if (geo.interiorRingsCount > 0)
     202             :                 //TODO Malloc fail exception?
     203           0 :                 geo.interiorRings = GDKmalloc(sizeof(GeoLines) * geo.interiorRingsCount);
     204             :         else
     205             :                 geo.interiorRings = NULL;
     206             :         //Get interior rings GeoLines
     207           0 :         for (int i = 0; i < geo.interiorRingsCount; i++)
     208           0 :                 geo.interiorRings[i] = geoLinesFromGeom((GEOSGeom)GEOSGetInteriorRingN_r(geoshandle, geom, i));
     209             :         // If the geometry doesn't have BoundingBoxe, calculate it
     210           0 :         geo.bbox = boundingBoxLines(geo.exteriorRing);
     211           0 :         return geo;
     212             : }
     213             : 
     214             : static GeoPoint
     215           0 : geoPointFromLatLon(double lon, double lat)
     216             : {
     217           0 :         GeoPoint geo;
     218           0 :         geo.lon = lon;
     219           0 :         geo.lat = lat;
     220           0 :         return geo;
     221             : }
     222             : 
     223             : static str
     224           0 : freeGeoLines(GeoLines lines) {
     225           0 :         str msg = MAL_SUCCEED;
     226           0 :         GDKfree(lines.points);
     227           0 :         if (lines.bbox)
     228           0 :                 GDKfree(lines.bbox);
     229           0 :         return msg;
     230             : }
     231             : 
     232             : static str
     233           0 : freeGeoPolygon(GeoPolygon polygon) {
     234           0 :         str msg = MAL_SUCCEED;
     235           0 :         msg = freeGeoLines(polygon.exteriorRing);
     236           0 :         if (polygon.bbox)
     237           0 :                 GDKfree(polygon.bbox);
     238           0 :         for (int i = 0; i < polygon.interiorRingsCount; i++)
     239           0 :                 msg = freeGeoLines(polygon.interiorRings[i]);
     240           0 :         if (polygon.interiorRings)
     241           0 :                 GDKfree(polygon.interiorRings);
     242           0 :         return msg;
     243             : }
     244             : 
     245             : static CartPoint3D
     246           0 : cartPointFromXYZ(double x, double y, double z)
     247             : {
     248           0 :         CartPoint3D cart;
     249           0 :         cart.x = x;
     250           0 :         cart.y = y;
     251           0 :         cart.z = z;
     252           0 :         return cart;
     253             : }
     254             : 
     255             : /* Converts Well-Known Bytes into Geos Geometries, if they are not NULL and have the same SRID (used for geographic functions) */
     256             : str
     257          13 : wkbGetCompatibleGeometries(wkb * const *a, wkb * const *b, GEOSGeom *ga, GEOSGeom *gb)
     258             : {
     259          13 :         str err = MAL_SUCCEED;
     260             : 
     261          13 :         if (is_wkb_nil(*a) || is_wkb_nil(*b)) {
     262           0 :                 (*ga) = NULL;
     263           0 :                 (*gb) = NULL;
     264           0 :                 return MAL_SUCCEED;
     265             :         }
     266          13 :         (*ga) = wkb2geos(*a);
     267          13 :         (*gb) = wkb2geos(*b);
     268          13 :         if ((*ga) == NULL || (*gb) == NULL)
     269           0 :                 err = createException(MAL, "geom.wkbGetComplatibleGeometries", SQLSTATE(38000) "Geos operation wkb2geos failed");
     270          13 :         else if (GEOSGetSRID_r(geoshandle, (*ga)) != GEOSGetSRID_r(geoshandle, *gb)) {
     271           0 :                 GEOSGeom_destroy_r(geoshandle, *ga);
     272           0 :                 GEOSGeom_destroy_r(geoshandle, *gb);
     273           0 :                 err = createException(MAL, "geom.wkbGetComplatibleGeometries", SQLSTATE(38000) "Geometries of different SRID");
     274             :         }
     275             :         return err;
     276             : }
     277             : 
     278             : /**
     279             : * Convert spherical coordinates to cartesian coordinates on unit sphere.
     280             : * The inputs have to be in radians.
     281             : */
     282             : static CartPoint3D
     283           0 : geo2cart(GeoPoint geo)
     284             : {
     285           0 :         CartPoint3D cart;
     286           0 :         cart.x = cos(geo.lat) * cos(geo.lon);
     287           0 :         cart.y = cos(geo.lat) * sin(geo.lon);
     288           0 :         cart.z = sin(geo.lat);
     289           0 :         return cart;
     290             : }
     291             : 
     292             : /**
     293             : * Convert spherical coordinates to cartesian coordinates on unit sphere.
     294             : * The inputs have to be in degrees.
     295             : */
     296             : static CartPoint3D
     297           0 : geo2cartFromDegrees(GeoPoint geo)
     298             : {
     299           0 :         return geo2cart(deg2RadPoint(geo));
     300             : }
     301             : 
     302             : /* Convert cartesian coordinates to spherical coordinates on unit sphere */
     303             : static GeoPoint
     304           0 : cart2geo(CartPoint3D cart)
     305             : {
     306           0 :         GeoPoint geo;
     307           0 :         geo.lon = atan2(cart.y, cart.x);
     308           0 :         geo.lat = asin(cart.z);
     309           0 :         return geo;
     310             : }
     311             : 
     312             : /* Converts two lat/lon points into cartesian coordinates and creates a Line geometry */
     313             : static GEOSGeom
     314           0 : cartesianLineFromGeoPoints(GeoPoint p1, GeoPoint p2)
     315             : {
     316           0 :         CartPoint3D p1_cart, p2_cart;
     317           0 :         p1_cart = geo2cartFromDegrees(p1);
     318           0 :         p2_cart = geo2cartFromDegrees(p2);
     319           0 :         GEOSCoordSequence *lineSeq = GEOSCoordSeq_create_r(geoshandle, 2, 3);
     320           0 :         GEOSCoordSeq_setXYZ_r(geoshandle, lineSeq, 0, p1_cart.x, p1_cart.y, p1_cart.z);
     321           0 :         GEOSCoordSeq_setXYZ_r(geoshandle, lineSeq, 1, p2_cart.x, p2_cart.y, p2_cart.z);
     322           0 :         return GEOSGeom_createLineString_r(geoshandle, lineSeq);
     323             : }
     324             : 
     325             : /**
     326             : *  Bounding Box functions
     327             : *
     328             : **/
     329             : /* Adds a Cartesian Point to the BoundingBox */
     330             : static void
     331           0 : boundingBoxAddPoint(BoundingBox *bb, CartPoint3D p)
     332             : {
     333           0 :         if (bb->xmin > p.x)
     334           0 :                 bb->xmin = p.x;
     335           0 :         if (bb->xmax < p.x)
     336           0 :                 bb->xmax = p.x;
     337           0 :         if (bb->ymin > p.y)
     338           0 :                 bb->ymin = p.y;
     339           0 :         if (bb->ymax < p.y)
     340           0 :                 bb->ymax = p.y;
     341           0 :         if (bb->zmin > p.z)
     342           0 :                 bb->zmin = p.z;
     343           0 :         if (bb->zmax < p.z)
     344           0 :                 bb->zmax = p.z;
     345           0 : }
     346             : 
     347             : /* Builds the BoundingBox for a GeoLines geometry */
     348             : static BoundingBox *
     349           0 : boundingBoxLines(GeoLines lines)
     350             : {
     351           0 :         CartPoint3D c;
     352           0 :         BoundingBox *bb = GDKzalloc(sizeof(BoundingBox));
     353             : 
     354             :         //If there are no segments, return NULL
     355           0 :         if (lines.pointCount == 0)
     356             :                 return NULL;
     357             : 
     358           0 :         c = geo2cartFromDegrees(lines.points[0]);
     359             : 
     360             :         //Initialize the bounding box with the first point
     361           0 :         bb->xmin = bb->xmax = c.x;
     362           0 :         bb->ymin = bb->ymax = c.y;
     363           0 :         bb->zmin = bb->zmax = c.z;
     364             : 
     365           0 :         for (int i = 1; i < lines.pointCount; i++) {
     366           0 :                 c = geo2cartFromDegrees(lines.points[i]);
     367           0 :                 boundingBoxAddPoint(bb, c);
     368             :         }
     369             :         return bb;
     370             : }
     371             : 
     372             : static int
     373           0 : boundingBoxContainsPoint(BoundingBox bb, CartPoint3D pt)
     374             : {
     375           0 :         return bb.xmin <= pt.x && bb.xmax >= pt.x && bb.ymin <= pt.y && bb.ymax >= pt.y && bb.zmin <= pt.z && bb.zmax >= pt.z;
     376             : }
     377             : 
     378             : static BoundingBox*
     379           0 : boundingBoxCopy(BoundingBox bb)
     380             : {
     381             :         //TODO Malloc fail?
     382           0 :         BoundingBox *copy = GDKmalloc(sizeof(BoundingBox));
     383           0 :         copy->xmin = bb.xmin;
     384           0 :         copy->xmax = bb.xmax;
     385           0 :         copy->ymin = bb.ymin;
     386           0 :         copy->ymax = bb.ymax;
     387           0 :         copy->zmin = bb.zmin;
     388           0 :         copy->zmax = bb.zmax;
     389           0 :         return copy;
     390             : }
     391             : 
     392             : /* Returns a point outside of the polygon's bounding box, for Point-In-Polygon calculation */
     393             : static GeoPoint
     394           0 : pointOutsidePolygon(GeoPolygon polygon)
     395             : {
     396           0 :         BoundingBox bb = *(polygon.bbox);
     397           0 :         BoundingBox *bb2 = boundingBoxCopy(*(polygon.bbox));
     398             : 
     399             :         //TODO: From POSTGIS -> CHANGE
     400           0 :         double grow = M_PI / 180.0 / 60.0;
     401           0 :         CartPoint3D corners[8];
     402           0 :         while (grow < M_PI) {
     403           0 :                 if (bb.xmin > -1)
     404           0 :                         bb.xmin -= grow;
     405           0 :                 if (bb.ymin > -1)
     406           0 :                         bb.ymin -= grow;
     407           0 :                 if (bb.zmin > -1)
     408           0 :                         bb.zmin -= grow;
     409           0 :                 if (bb.xmax < 1)
     410           0 :                         bb.xmax += grow;
     411           0 :                 if (bb.ymax < 1)
     412           0 :                         bb.ymax += grow;
     413           0 :                 if (bb.zmax < 1)
     414           0 :                         bb.zmax += grow;
     415             : 
     416           0 :                 corners[0].x = bb.xmin;
     417           0 :                 corners[0].y = bb.ymin;
     418           0 :                 corners[0].z = bb.zmin;
     419             : 
     420           0 :                 corners[1].x = bb.xmin;
     421           0 :                 corners[1].y = bb.ymax;
     422           0 :                 corners[1].z = bb.zmin;
     423             : 
     424           0 :                 corners[2].x = bb.xmin;
     425           0 :                 corners[2].y = bb.ymin;
     426           0 :                 corners[2].z = bb.zmax;
     427             : 
     428           0 :                 corners[3].x = bb.xmax;
     429           0 :                 corners[3].y = bb.ymin;
     430           0 :                 corners[3].z = bb.zmin;
     431             : 
     432           0 :                 corners[4].x = bb.xmax;
     433           0 :                 corners[4].y = bb.ymax;
     434           0 :                 corners[4].z = bb.zmin;
     435             : 
     436           0 :                 corners[5].x = bb.xmax;
     437           0 :                 corners[5].y = bb.ymin;
     438           0 :                 corners[5].z = bb.zmax;
     439             : 
     440           0 :                 corners[6].x = bb.xmin;
     441           0 :                 corners[6].y = bb.ymax;
     442           0 :                 corners[6].z = bb.zmax;
     443             : 
     444           0 :                 corners[7].x = bb.xmax;
     445           0 :                 corners[7].y = bb.ymax;
     446           0 :                 corners[7].z = bb.zmax;
     447             : 
     448           0 :                 for (int i = 0; i < 8; i++)
     449           0 :                         if (!boundingBoxContainsPoint(*bb2, corners[i])) {
     450           0 :                                 CartPoint3D pt_cart = corners[i];
     451           0 :                                 GDKfree(bb2);
     452           0 :                                 return rad2DegPoint(cart2geo(pt_cart));
     453             :                         }
     454           0 :                 grow *= 2.0;
     455             :         }
     456             :         //TODO: Should this be the return value in case no point is found?
     457           0 :         return geoPointFromLatLon(0, 0);
     458             : }
     459             : 
     460             : /**
     461             : * Distance functions
     462             : *
     463             : **/
     464             : /**
     465             : * The haversine formula calculate the distance in meters between two lat/lon points
     466             : * The points must be measured in radians.
     467             : * This formula assumes a spherical model of the earth, which can lead to an error of about 0.3% compared to a ellipsoidal model.
     468             : */
     469             : static double
     470           0 : haversine(GeoPoint a, GeoPoint b)
     471             : {
     472           0 :         double d_lon = b.lon - a.lon;
     473           0 :         double d_lat = b.lat - a.lat;
     474           0 :         double d = sin(d_lat / 2) * sin(d_lat / 2) + sin(d_lon / 2) * sin(d_lon / 2) * cos(b.lat) * cos(a.lat);
     475           0 :         double c = 2 * atan2(sqrt(d), sqrt(1 - d));
     476             :         //TODO: Same as the previous line (which one is best?) -> 2 * asin(sqrt(d));
     477           0 :         return earth_radius_meters * c;
     478             : }
     479             : 
     480             : /* Distance between two Points */
     481             : static double
     482           0 : geoDistancePointPoint(GeoPoint a, GeoPoint b)
     483             : {
     484           0 :         return haversine(deg2RadPoint(a), deg2RadPoint(b));
     485             : }
     486             : 
     487             : /* Calculates the distance between the perpendicular projection of a point in the Line */
     488             : static double
     489           0 : calculatePerpendicularDistance(GeoPoint p_geo, GeoPoint l1_geo, GeoPoint l2_geo)
     490             : {
     491           0 :         CartPoint3D l1, l2, p, projection;
     492           0 :         GeoPoint projection_geo;
     493             : 
     494             :         //First, convert the points to 3D cartesian coordinates
     495           0 :         l1 = geo2cartFromDegrees(l1_geo);
     496           0 :         l2 = geo2cartFromDegrees(l2_geo);
     497           0 :         p = geo2cartFromDegrees(p_geo);
     498             : 
     499             :         //Calculate the projection of point into the line
     500           0 :         double d_ab = (l2.z - l1.z) * (l2.z - l1.z) + (l2.y - l1.y) * (l2.y - l1.y) + (l2.x - l1.x) * (l2.x - l1.x);
     501           0 :         double t = (((p.x - l1.x) * (l2.x - l1.x)) + ((p.y - l1.y) * (l2.y - l1.y)) + ((p.z - l1.z) * (l2.z - l1.z))) / d_ab;
     502             : 
     503             :         //If t is not between 0 and 1, the projected point is not in the line, so there is no perpendicular, return huge number
     504           0 :         if (t < 0 || t > 1)
     505             :                 return INT_MAX;
     506             : 
     507             :         //If the projection is in the line segment, build the point -> projection = l1 + t * (l2-l1)
     508           0 :         projection = cartPointFromXYZ(l1.x + t * (l2.x - l1.x), l1.y + t * (l2.y - l1.y), l1.z + t * (l2.z - l1.z));
     509             : 
     510             :         //Convert into geographic coordinates (radians)
     511           0 :         projection_geo = cart2geo(projection);
     512             : 
     513             :         //Calculate distance from original point to the projection
     514           0 :         return haversine(deg2RadPoint(p_geo), projection_geo);
     515             : }
     516             : 
     517             : /* Distance between Point and Line
     518             :    The returned distance is the minimum distance between the point and the line vertices
     519             :    and the perpendicular projection of the point in each line segment.  */
     520             : static double
     521           0 : geoDistancePointLine(GeoPoint point, GeoLines lines, double distance_min_limit)
     522             : {
     523           0 :         double distancePoint, distancePerpendicular, min_distance = INT_MAX;
     524           0 :         for (int i = 0; i < lines.pointCount-1; i++) {
     525           0 :                 distancePoint = geoDistancePointPoint(point, lines.points[i]);
     526           0 :                 distancePerpendicular = calculatePerpendicularDistance(point,lines.points[i],lines.points[i+1]);
     527           0 :                 if (distancePoint < min_distance)
     528           0 :                         min_distance = distancePoint;
     529           0 :                 if (distancePerpendicular < min_distance)
     530           0 :                         min_distance = distancePerpendicular;
     531             :                 //Shortcut, if the geometries are already at their minimum distance
     532           0 :                 if (min_distance <= distance_min_limit)
     533           0 :                         return min_distance;
     534             :         }
     535           0 :         distancePoint = geoDistancePointPoint(point, lines.points[lines.pointCount-1]);
     536           0 :         return distancePoint < min_distance ? distancePoint : min_distance;
     537             : }
     538             : 
     539             : /* Distance between two Lines. */
     540             : static double
     541           0 : geoDistanceLineLine(GeoLines line1, GeoLines line2, double distance_min_limit)
     542             : {
     543           0 :         double distance, min_distance = INT_MAX;
     544           0 :         for (int i = 0; i < line1.pointCount; i++) {
     545           0 :                 distance = geoDistancePointLine(line1.points[i], line2, distance_min_limit);
     546           0 :                 if (distance < min_distance)
     547           0 :                         min_distance = distance;
     548             :                 //Shortcut, if the geometries are already at their minimum distance
     549           0 :                 if (min_distance <= distance_min_limit)
     550           0 :                         return min_distance;
     551             :         }
     552           0 :         for (int i = 0; i < line2.pointCount; i++) {
     553           0 :                 for (int j = 0; j < line1.pointCount - 1; j++) {
     554           0 :                         distance = calculatePerpendicularDistance(line2.points[i],line1.points[j],line1.points[j+1]);
     555           0 :                         if (distance < min_distance)
     556           0 :                                 min_distance = distance;
     557             :                         //Shortcut, if the geometries are already at their minimum distance
     558           0 :                         if (min_distance <= distance_min_limit)
     559           0 :                                 return min_distance;
     560             :                 }
     561             :         }
     562             :         return min_distance;
     563             : }
     564             : 
     565             : /* Checks if a Point is within a Polygon */
     566             : static bool
     567           0 : pointWithinPolygon(GeoPolygon polygon, GeoPoint point)
     568             : {
     569           0 :         int intersectionNum = 0;
     570           0 :         GEOSGeometry *segmentPolygon, *intersectionPoints;
     571           0 :         GeoLines polygonRing;
     572             : 
     573             :         //Get an point that's outside the polygon
     574           0 :         GeoPoint outsidePoint = pointOutsidePolygon(polygon);
     575             : 
     576             :         //No outside point was found, return false
     577           0 :         if (outsidePoint.lat == 0 && outsidePoint.lon == 0)
     578             :                 return false;
     579             : 
     580             :         /*printf("Outside point: (%f %f)\n",outsidePoint.lon, outsidePoint.lat);
     581             :         fflush(stdout);*/
     582             : 
     583             :         //Construct a line between the outside point and the input point
     584           0 :         GEOSGeometry *outInLine = cartesianLineFromGeoPoints(point, outsidePoint);
     585             : 
     586             :         //TODO This is producing wrong results, review the intersection conditional
     587             :         //Count the number of intersections between the polygon exterior ring and the constructed line
     588           0 :         polygonRing = polygon.exteriorRing;
     589           0 :         for (int i = 0; i < polygonRing.pointCount-1; i++) {
     590           0 :                 segmentPolygon = cartesianLineFromGeoPoints(polygonRing.points[i], polygonRing.points[i+1]);
     591           0 :                 intersectionPoints = GEOSIntersection_r(geoshandle, segmentPolygon, outInLine);
     592             : 
     593             :                 //If there is an intersection, a point will be returned (line when there is none)
     594           0 :                 if (GEOSGeomTypeId_r(geoshandle, intersectionPoints) == GEOS_POINT)
     595           0 :                         intersectionNum++;
     596             : 
     597           0 :                 if (intersectionPoints != NULL)
     598           0 :                         GEOSGeom_destroy_r(geoshandle, intersectionPoints);
     599           0 :                 if (segmentPolygon != NULL)
     600           0 :                         GEOSGeom_destroy_r(geoshandle, segmentPolygon);
     601             :         }
     602             : 
     603             :         //Count the number of intersections between the polygon interior rings and the constructed line
     604           0 :         for (int j = 0; j < polygon.interiorRingsCount; j++) {
     605           0 :                 polygonRing = polygon.interiorRings[j];
     606           0 :                 for (int i = 0; i < polygonRing.pointCount-1; i++) {
     607           0 :                         segmentPolygon = cartesianLineFromGeoPoints(polygonRing.points[i], polygonRing.points[i+1]);
     608           0 :                         intersectionPoints = GEOSIntersection_r(geoshandle, segmentPolygon, outInLine);
     609             : 
     610             :                         //If there is an intersection, a point will be returned (line when there is none)
     611           0 :                         if (GEOSGeomTypeId_r(geoshandle, intersectionPoints) == GEOS_POINT)
     612           0 :                                 intersectionNum++;
     613             : 
     614           0 :                         if (intersectionPoints != NULL)
     615           0 :                                 GEOSGeom_destroy_r(geoshandle, intersectionPoints);
     616           0 :                         if (segmentPolygon != NULL)
     617           0 :                                 GEOSGeom_destroy_r(geoshandle, segmentPolygon);
     618             :                 }
     619             :         }
     620             : 
     621           0 :         if (outInLine != NULL)
     622           0 :                 GEOSGeom_destroy_r(geoshandle, outInLine);
     623             : 
     624             :         //If even, the point is not within the polygon. If odd, it is within
     625           0 :         return intersectionNum % 2 == 1;
     626             : }
     627             : 
     628             : /* Distance between Point and Polygon.*/
     629             : static double
     630           0 : geoDistancePointPolygon(GeoPoint point, GeoPolygon polygon, double distance_min_limit)
     631             : {
     632             :         //Check if point is in polygon
     633           0 :         if (pointWithinPolygon(polygon, point))
     634             :                 return 0;
     635             : 
     636             :         //Calculate distance from Point to the exterior and interior rings of the polygon
     637           0 :         double distance, min_distance = INT_MAX;
     638             :         //First, calculate distance to the exterior ring
     639           0 :         min_distance = geoDistancePointLine(point, polygon.exteriorRing, distance_min_limit);
     640             :         //Then, calculate distance to the interior rings
     641           0 :         for (int i = 0; i < polygon.interiorRingsCount; i++) {
     642             :                 //Shortcut, if the geometries are already at their minimum distance
     643           0 :                 if (min_distance <= distance_min_limit)
     644           0 :                         return min_distance;
     645           0 :                 distance = geoDistancePointLine(point, polygon.interiorRings[i], distance_min_limit);
     646           0 :                 if (distance < min_distance)
     647           0 :                         min_distance = distance;
     648             :         }
     649             :         return min_distance;
     650             : }
     651             : 
     652             : /* Distance between Line and Polygon. */
     653             : static double
     654           0 : geoDistanceLinePolygon(GeoLines line, GeoPolygon polygon, double distance_min_limit)
     655             : {
     656           0 :         double distance, min_distance = INT_MAX;
     657             :         //Calculate distance to all start vertices of the line
     658           0 :         for (int i = 0; i < line.pointCount; i++) {
     659           0 :                 distance = geoDistancePointPolygon(line.points[i], polygon, distance_min_limit);
     660             : 
     661             :                 //Short-cut in case the point is within the polygon
     662           0 :                 if (distance <= distance_min_limit)
     663           0 :                         return distance;
     664             : 
     665           0 :                 if (distance < min_distance)
     666           0 :                         min_distance = distance;
     667             :         }
     668             :         return min_distance;
     669             : }
     670             : 
     671             : /* Distance between two Polygons. */
     672             : static double
     673           0 : geoDistancePolygonPolygon(GeoPolygon polygon1, GeoPolygon polygon2, double distance_min_limit)
     674             : {
     675           0 :         double distance1, distance2;
     676             :         //Calculate the distance between the exterior ring of polygon1 and all segments of polygon2 (including the interior rings)
     677           0 :         distance1 = geoDistanceLinePolygon(polygon1.exteriorRing, polygon2, distance_min_limit);
     678             :         //Shortcut, if the geometries are already at their minimum distance
     679           0 :         if (distance1 <= distance_min_limit)
     680             :                 return distance1;
     681           0 :         distance2 = geoDistanceLinePolygon(polygon2.exteriorRing, polygon1, distance_min_limit);
     682           0 :         return distance1 < distance2 ? distance1 : distance2;
     683             : }
     684             : 
     685             : /* Distance between two (non-collection) geometries. */
     686             : static double
     687           0 : geoDistanceSingle(GEOSGeom aGeom, GEOSGeom bGeom, double distance_min_limit)
     688             : {
     689           0 :         int dimA, dimB;
     690           0 :         double distance = INT_MAX;
     691           0 :         dimA = GEOSGeom_getDimensions_r(geoshandle, aGeom);
     692           0 :         dimB = GEOSGeom_getDimensions_r(geoshandle, bGeom);
     693           0 :         if (dimA == 0 && dimB == 0) {
     694             :                 /* Point and Point */
     695           0 :                 GeoPoint a = geoPointFromGeom(aGeom);
     696           0 :                 GeoPoint b = geoPointFromGeom(bGeom);
     697           0 :                 distance = geoDistancePointPoint(a, b);
     698           0 :         } else if (dimA == 0 && dimB == 1) {
     699             :                 /* Point and Line/LinearRing */
     700           0 :                 GeoPoint a = geoPointFromGeom(aGeom);
     701           0 :                 GeoLines b = geoLinesFromGeom(bGeom);
     702           0 :                 distance = geoDistancePointLine(a, b, distance_min_limit);
     703           0 :                 freeGeoLines(b);
     704           0 :         } else if (dimA == 1 && dimB == 0) {
     705             :                 /* Line/LinearRing and Point */
     706           0 :                 GeoLines a = geoLinesFromGeom(aGeom);
     707           0 :                 GeoPoint b = geoPointFromGeom(bGeom);
     708           0 :                 distance = geoDistancePointLine(b, a, distance_min_limit);
     709           0 :                 freeGeoLines(a);
     710           0 :         } else if (dimA == 1 && dimB == 1) {
     711             :                 /* Line/LinearRing and Line/LinearRing */
     712           0 :                 GeoLines a = geoLinesFromGeom(aGeom);
     713           0 :                 GeoLines b = geoLinesFromGeom(bGeom);
     714           0 :                 distance = geoDistanceLineLine(a, b, distance_min_limit);
     715           0 :                 freeGeoLines(a);
     716           0 :                 freeGeoLines(b);
     717           0 :         } else if (dimA == 0 && dimB == 2) {
     718             :                 /* Point and Polygon */
     719           0 :                 GeoPoint a = geoPointFromGeom(aGeom);
     720           0 :                 GeoPolygon b = geoPolygonFromGeom(bGeom);
     721           0 :                 distance = geoDistancePointPolygon(a, b, distance_min_limit);
     722           0 :                 freeGeoPolygon(b);
     723           0 :         } else if (dimA == 2 && dimB == 0) {
     724             :                 /* Polygon and Point */
     725           0 :                 GeoPolygon a = geoPolygonFromGeom(aGeom);
     726           0 :                 GeoPoint b = geoPointFromGeom(bGeom);
     727           0 :                 distance = geoDistancePointPolygon(b, a, distance_min_limit);
     728           0 :                 freeGeoPolygon(a);
     729           0 :         } else if (dimA == 1 && dimB == 2) {
     730             :                 /* Line/LinearRing and Polygon */
     731           0 :                 GeoLines a = geoLinesFromGeom(aGeom);
     732           0 :                 GeoPolygon b = geoPolygonFromGeom(bGeom);
     733           0 :                 distance = geoDistanceLinePolygon(a, b, distance_min_limit);
     734           0 :                 freeGeoLines(a);
     735           0 :                 freeGeoPolygon(b);
     736           0 :         } else if (dimA == 2 && dimB == 1) {
     737             :                 /* Polygon and Line/LinearRing */
     738           0 :                 GeoPolygon a = geoPolygonFromGeom(aGeom);
     739           0 :                 GeoLines b = geoLinesFromGeom(bGeom);
     740           0 :                 distance = geoDistanceLinePolygon(b, a, distance_min_limit);
     741           0 :                 freeGeoPolygon(a);
     742           0 :                 freeGeoLines(b);
     743           0 :         } else if (dimA == 2 && dimB == 2) {
     744             :                 /* Polygon and Polygon */
     745           0 :                 GeoPolygon a = geoPolygonFromGeom(aGeom);
     746           0 :                 GeoPolygon b = geoPolygonFromGeom(bGeom);
     747           0 :                 distance = geoDistancePolygonPolygon(a, b, distance_min_limit);
     748           0 :                 freeGeoPolygon(a);
     749           0 :                 freeGeoPolygon(b);
     750             :         }
     751           0 :         return distance;
     752             : }
     753             : 
     754             : //The distance_min_limit argument is used for DWithin and Intersects.
     755             : //If we get to the minimum distance for the predicate, return immediatly
     756             : //It is equal to 0 if the operation is Distance
     757             : static double
     758           0 : geoDistanceInternal(GEOSGeom a, GEOSGeom b, double distance_min_limit)
     759             : {
     760           0 :         int numGeomsA = GEOSGetNumGeometries_r(geoshandle, a), numGeomsB = GEOSGetNumGeometries_r(geoshandle, b);
     761           0 :         double distance, min_distance = INT_MAX;
     762           0 :         GEOSGeometry *geo1, *geo2;
     763           0 :         for (int i = 0; i < numGeomsA; i++) {
     764           0 :                 geo1 = (GEOSGeometry *)GEOSGetGeometryN_r(geoshandle, (const GEOSGeometry *)a, i);
     765           0 :                 for (int j = 0; j < numGeomsB; j++) {
     766           0 :                         geo2 = (GEOSGeometry *)GEOSGetGeometryN_r(geoshandle, (const GEOSGeometry *)b, j);
     767           0 :                         distance = geoDistanceSingle(geo1, geo2, distance_min_limit);
     768             :                         //Shortcut, if the geometries are already at their minimum distance (0 in the case of normal Distance)
     769           0 :                         if (distance <= distance_min_limit)
     770           0 :                                 return distance;
     771           0 :                         if (distance < min_distance)
     772           0 :                                 min_distance = distance;
     773             :                 }
     774             :         }
     775             :         return min_distance;
     776             : }
     777             : 
     778             : /**
     779             : * Distance
     780             : *
     781             : **/
     782             : /* Calculates the distance, in meters, between two geographic geometries with latitude/longitude coordinates */
     783             : str
     784           0 : wkbDistanceGeographic(dbl *out, wkb * const *a, wkb * const *b)
     785             : {
     786           0 :         str err = MAL_SUCCEED;
     787           0 :         GEOSGeom ga, gb;
     788           0 :         err = wkbGetCompatibleGeometries(a, b, &ga, &gb);
     789           0 :         if (ga && gb) {
     790           0 :                 (*out) = geoDistanceInternal(ga, gb, 0);
     791             :         }
     792           0 :         GEOSGeom_destroy_r(geoshandle, ga);
     793           0 :         GEOSGeom_destroy_r(geoshandle, gb);
     794           0 :         return err;
     795             : }
     796             : 
     797             : /**
     798             : * Distance Within
     799             : *
     800             : **/
     801             : /* Checks if two geographic geometries are within d meters of one another */
     802             : str
     803           0 : wkbDWithinGeographic(bit *out, wkb * const *a, wkb * const *b, const dbl *d)
     804             : {
     805           0 :         str err = MAL_SUCCEED;
     806           0 :         GEOSGeom ga, gb;
     807           0 :         double distance;
     808           0 :         err = wkbGetCompatibleGeometries(a, b, &ga, &gb);
     809           0 :         if (ga && gb) {
     810           0 :                 distance = geoDistanceInternal(ga, gb, *d);
     811           0 :                 (*out) = (distance <= (*d));
     812             :         }
     813           0 :         GEOSGeom_destroy_r(geoshandle, ga);
     814           0 :         GEOSGeom_destroy_r(geoshandle, gb);
     815           0 :         return err;
     816             : }
     817             : 
     818             : /**
     819             : * Intersects
     820             : *
     821             : **/
     822             : /* Checks if two geographic geometries intersect at any point */
     823             : str
     824           0 : wkbIntersectsGeographic(bit *out, wkb * const *a, wkb * const *b)
     825             : {
     826           0 :         str err = MAL_SUCCEED;
     827           0 :         GEOSGeom ga, gb;
     828           0 :         double distance;
     829           0 :         err = wkbGetCompatibleGeometries(a, b, &ga, &gb);
     830           0 :         if (ga && gb) {
     831           0 :                 distance = geoDistanceInternal(ga, gb, 0);
     832           0 :                 (*out) = (distance == 0);
     833             :         }
     834           0 :         GEOSGeom_destroy_r(geoshandle, ga);
     835           0 :         GEOSGeom_destroy_r(geoshandle, gb);
     836           0 :         return err;
     837             : }
     838             : 
     839             : /* Checks if a Polygon covers a Line geometry */
     840             : static bool
     841           0 : geoPolygonCoversLine(GeoPolygon polygon, GeoLines lines)
     842             : {
     843           0 :         for (int i = 0; i < lines.pointCount; i++) {
     844           0 :                 if (pointWithinPolygon(polygon, lines.points[i]) == false)
     845             :                         return false;
     846             :         }
     847             :         return true;
     848             : }
     849             : 
     850             : /* Compares two GeoPoints, returns true if they're equal */
     851             : static bool
     852           0 : geoPointEquals(GeoPoint pointA, GeoPoint pointB)
     853             : {
     854           0 :         return (pointA.lat == pointB.lat) && (pointA.lon = pointB.lon);
     855             : }
     856             : 
     857             : //TODO Check if this works correctly
     858             : static bool
     859           0 : geoCoversSingle(GEOSGeom a, GEOSGeom b)
     860             : {
     861           0 :         int dimA = GEOSGeom_getDimensions_r(geoshandle, a), dimB = GEOSGeom_getDimensions_r(geoshandle, b);
     862           0 :         if (dimA < dimB)
     863             :                 //If the dimension of A is smaller than B, then it must not cover it
     864             :                 return false;
     865             : 
     866           0 :         if (dimA == 0){
     867             :                 //A and B are Points
     868           0 :                 GeoPoint pointA = geoPointFromGeom(a);
     869           0 :                 GeoPoint pointB = geoPointFromGeom(b);
     870           0 :                 return geoPointEquals(pointA, pointB);
     871           0 :         } else if (dimA == 1) {
     872             :                 //A is Line
     873             :                 //GeoLines lineA = geoLinesFromGeom(a);
     874             :                 if (dimB == 0) {
     875             :                         //B is Point
     876             :                         //GeoPoint pointB = geoPointFromGeom(b);
     877             :                         //return geoLineCoversPoint(lineA,pointB);
     878             :                         return false;
     879             :                 } else {
     880             :                         //B is Line
     881             :                         //GeoLines lineB = geoLinesFromGeom(b);
     882             :                         //return geoLineCoversLine(lineA, lineB);
     883             :                         return false;
     884             :                 }
     885           0 :         } else if (dimA == 2) {
     886             :                 //A is Polygon
     887           0 :                 GeoPolygon polygonA = geoPolygonFromGeom(a);
     888           0 :                 if (dimB == 0){
     889             :                         //B is Point
     890           0 :                         GeoPoint pointB = geoPointFromGeom(b);
     891           0 :                         return pointWithinPolygon(polygonA, pointB);
     892           0 :                 } else if (dimB == 1) {
     893             :                         //B is Line
     894           0 :                         GeoLines lineB = geoLinesFromGeom(b);
     895           0 :                         return geoPolygonCoversLine(polygonA, lineB);
     896             :                 } else {
     897             :                         //B is Polygon
     898           0 :                         GeoPolygon polygonB = geoPolygonFromGeom(b);
     899             :                         //If every point in the exterior ring of B is covered, polygon B is covered by polygon A
     900           0 :                         return geoPolygonCoversLine(polygonA, polygonB.exteriorRing);
     901             :                 }
     902             :         } else
     903             :                 return false;
     904             : }
     905             : 
     906             : static bool
     907           0 : geoCoversInternal(GEOSGeom a, GEOSGeom b)
     908             : {
     909           0 :         int numGeomsA = GEOSGetNumGeometries_r(geoshandle, a), numGeomsB = GEOSGetNumGeometries_r(geoshandle, b);
     910           0 :         GEOSGeometry *geo1, *geo2;
     911           0 :         for (int i = 0; i < numGeomsA; i++) {
     912           0 :                 geo1 = (GEOSGeometry *)GEOSGetGeometryN_r(geoshandle, (const GEOSGeometry *)a, i);
     913           0 :                 for (int j = 0; j < numGeomsB; j++) {
     914           0 :                         geo2 = (GEOSGeometry *)GEOSGetGeometryN_r(geoshandle, (const GEOSGeometry *)b, j);
     915           0 :                         if (geoCoversSingle(geo1, geo2) == 0)
     916             :                                 return 0;
     917             :                 }
     918             :         }
     919             :         return 1;
     920             : }
     921             : 
     922             : /**
     923             : * Covers
     924             : *
     925             : **/
     926             : /* Checks if no point of Geometry B is outside Geometry A */
     927             : str
     928           0 : wkbCoversGeographic(bit *out, wkb * const *a, wkb * const *b)
     929             : {
     930           0 :         str err = MAL_SUCCEED;
     931           0 :         GEOSGeom ga, gb;
     932           0 :         err = wkbGetCompatibleGeometries(a, b, &ga, &gb);
     933           0 :         if (ga && gb)
     934           0 :                 (*out) = geoCoversInternal(ga, gb);
     935             : 
     936           0 :         GEOSGeom_destroy_r(geoshandle, ga);
     937           0 :         GEOSGeom_destroy_r(geoshandle, gb);
     938             : 
     939           0 :         return err;
     940             : }
     941             : 
     942             : /**
     943             :  * FILTER FUNCTIONS
     944             :  **/
     945             : 
     946             : static inline bit
     947           0 : geosDistanceWithin (GEOSGeom geom1, GEOSGeom geom2, dbl distance_within) {
     948           0 :         dbl distance = geoDistanceInternal(geom1, geom2, distance_within);
     949           0 :         return distance <= distance_within;
     950             : }
     951             : 
     952             : //TODO Change BUNappend with manual insertion into the result BAT
     953             : static str
     954           0 : filterSelectGeomGeomDoubleToBit(bat* outid, const bat *bid , const bat *sid, const wkb *wkb_const, dbl double_flag, bit anti, bit (*func) (GEOSGeom, GEOSGeom, dbl), const char *name)
     955             : {
     956           0 :         BAT *out = NULL, *b = NULL, *s = NULL;
     957           0 :         BATiter b_iter;
     958           0 :         struct canditer ci;
     959           0 :         GEOSGeom col_geom, const_geom;
     960             : 
     961             :         //Check if the geometry is null and convert to GEOS
     962           0 :         if ((const_geom = wkb2geos(wkb_const)) == NULL) {
     963           0 :                 if ((out = BATdense(0, 0, 0)) == NULL)
     964           0 :                         throw(MAL, name, GDK_EXCEPTION);
     965           0 :                 *outid = out->batCacheid;
     966           0 :                 BBPkeepref(out);
     967           0 :                 return MAL_SUCCEED;
     968             :         }
     969             :         //get the BATs
     970           0 :         if ((b = BATdescriptor(*bid)) == NULL)
     971           0 :                 throw(MAL, name, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
     972             :         //get the candidate lists
     973           0 :         if (sid && !is_bat_nil(*sid) && !(s = BATdescriptor(*sid))) {
     974           0 :                 BBPunfix(b->batCacheid);
     975           0 :                 throw(MAL, name, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
     976             :         }
     977           0 :         canditer_init(&ci, b, s);
     978             :         //create a new BAT for the output
     979           0 :         if ((out = COLnew(0, ATOMindex("oid"), ci.ncand, TRANSIENT)) == NULL) {
     980           0 :                 BBPunfix(b->batCacheid);
     981           0 :                 if (s)
     982           0 :                         BBPunfix(s->batCacheid);
     983           0 :                 throw(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
     984             :         }
     985           0 :         b_iter = bat_iterator(b);
     986             :         //Loop through column and compare with constant
     987           0 :         for (BUN i = 0; i < ci.ncand; i++) {
     988           0 :                 oid c_oid = canditer_next(&ci);
     989           0 :                 const wkb *col_wkb = BUNtvar(b_iter, c_oid - b->hseqbase);
     990           0 :                 if ((col_geom = wkb2geos(col_wkb)) == NULL)
     991           0 :                         continue;
     992           0 :                 if (GEOSGetSRID_r(geoshandle, col_geom) != GEOSGetSRID_r(geoshandle, const_geom)) {
     993           0 :                         GEOSGeom_destroy_r(geoshandle, col_geom);
     994           0 :                         GEOSGeom_destroy_r(geoshandle, const_geom);
     995           0 :                         bat_iterator_end(&b_iter);
     996           0 :                         BBPunfix(b->batCacheid);
     997           0 :                         if (s)
     998           0 :                                 BBPunfix(s->batCacheid);
     999           0 :                         BBPreclaim(out);
    1000           0 :                         throw(MAL, name, SQLSTATE(38000) "Geometries of different SRID");
    1001             :                 }
    1002             :                 //Apply the (Geom, Geom, double) -> bit function
    1003           0 :                 bit cond = (*func)(col_geom, const_geom, double_flag);
    1004           0 :                 if (cond != anti) {
    1005           0 :                         if (BUNappend(out, &c_oid, false) != GDK_SUCCEED) {
    1006           0 :                                 if (col_geom)
    1007           0 :                                         GEOSGeom_destroy_r(geoshandle, col_geom);
    1008           0 :                                 if (const_geom)
    1009           0 :                                         GEOSGeom_destroy_r(geoshandle, const_geom);
    1010           0 :                                 bat_iterator_end(&b_iter);
    1011           0 :                                 BBPunfix(b->batCacheid);
    1012           0 :                                 if (s)
    1013           0 :                                         BBPunfix(s->batCacheid);
    1014           0 :                                 BBPreclaim(out);
    1015           0 :                                 throw(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1016             :                         }
    1017             :                 }
    1018           0 :                 GEOSGeom_destroy_r(geoshandle, col_geom);
    1019             :         }
    1020           0 :         GEOSGeom_destroy_r(geoshandle, const_geom);
    1021           0 :         bat_iterator_end(&b_iter);
    1022           0 :         BBPunfix(b->batCacheid);
    1023           0 :         if (s)
    1024           0 :                 BBPunfix(s->batCacheid);
    1025           0 :         *outid = out->batCacheid;
    1026           0 :         BBPkeepref(out);
    1027           0 :         return MAL_SUCCEED;
    1028             : }
    1029             : 
    1030             : static str
    1031           0 : filterJoinGeomGeomDoubleToBit(bat *lres_id, bat *rres_id, const bat *l_id, const bat *r_id, double double_flag, const bat *ls_id, const bat *rs_id, bit nil_matches, const lng *estimate, bit anti, bit (*func) (GEOSGeom, GEOSGeom, dbl), const char *name)
    1032             : {
    1033           0 :         BAT *lres = NULL, *rres = NULL, *l = NULL, *r = NULL, *ls = NULL, *rs = NULL;
    1034           0 :         BATiter l_iter, r_iter;
    1035           0 :         str msg = MAL_SUCCEED;
    1036           0 :         struct canditer l_ci, r_ci;
    1037           0 :         GEOSGeom l_geom, r_geom;
    1038           0 :         GEOSGeom *l_geoms = NULL, *r_geoms = NULL;
    1039           0 :         BUN est;
    1040             : 
    1041             :         //get the input BATs
    1042           0 :         l = BATdescriptor(*l_id);
    1043           0 :         r = BATdescriptor(*r_id);
    1044           0 :         if (l == NULL || r == NULL) {
    1045           0 :                 if (l)
    1046           0 :                         BBPunfix(l->batCacheid);
    1047           0 :                 if (r)
    1048           0 :                         BBPunfix(r->batCacheid);
    1049           0 :                 throw(MAL, name, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
    1050             :         }
    1051             :         //get the candidate lists
    1052           0 :         if (ls_id && !is_bat_nil(*ls_id) && !(ls = BATdescriptor(*ls_id)) && rs_id && !is_bat_nil(*rs_id) && !(rs = BATdescriptor(*rs_id))) {
    1053           0 :                 msg = createException(MAL, name, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
    1054           0 :                 goto free;
    1055             :         }
    1056           0 :         canditer_init(&l_ci, l, ls);
    1057           0 :         canditer_init(&r_ci, r, rs);
    1058             :         //create new BATs for the output
    1059           0 :         est = is_lng_nil(*estimate) || *estimate == 0 ? l_ci.ncand : (BUN) *estimate;
    1060           0 :         if ((lres = COLnew(0, ATOMindex("oid"), est, TRANSIENT)) == NULL) {
    1061           0 :                 msg = createException(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1062           0 :                 goto free;
    1063             :         }
    1064           0 :         if ((rres = COLnew(0, ATOMindex("oid"), est, TRANSIENT)) == NULL) {
    1065           0 :                 msg = createException(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1066           0 :                 goto free;
    1067             :         }
    1068             : 
    1069             :         //Allocate arrays for reutilizing GEOS type conversion
    1070           0 :         if ((l_geoms = GDKmalloc(l_ci.ncand * sizeof(GEOSGeometry *))) == NULL || (r_geoms = GDKmalloc(r_ci.ncand * sizeof(GEOSGeometry *))) == NULL) {
    1071           0 :                 msg = createException(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1072           0 :                 goto free;
    1073             :         }
    1074             : 
    1075           0 :         l_iter = bat_iterator(l);
    1076           0 :         r_iter = bat_iterator(r);
    1077             : 
    1078             :         //Convert wkb to GEOS only once
    1079           0 :         for (BUN i = 0; i < l_ci.ncand; i++) {
    1080           0 :                 oid l_oid = canditer_next(&l_ci);
    1081           0 :                 l_geoms[i] = wkb2geos((const wkb*) BUNtvar(l_iter, l_oid - l->hseqbase));
    1082             :         }
    1083           0 :         for (BUN j = 0; j < r_ci.ncand; j++) {
    1084           0 :                 oid r_oid = canditer_next(&r_ci);
    1085           0 :                 r_geoms[j] = wkb2geos((const wkb*)BUNtvar(r_iter, r_oid - r->hseqbase));
    1086             :         }
    1087             : 
    1088           0 :         canditer_reset(&l_ci);
    1089           0 :         for (BUN i = 0; i < l_ci.ncand; i++) {
    1090           0 :                 oid l_oid = canditer_next(&l_ci);
    1091           0 :                 l_geom = l_geoms[i];
    1092           0 :                 if (!nil_matches && l_geom == NULL)
    1093           0 :                         continue;
    1094           0 :                 canditer_reset(&r_ci);
    1095           0 :                 for (BUN j = 0; j < r_ci.ncand; j++) {
    1096           0 :                         oid r_oid = canditer_next(&r_ci);
    1097           0 :                         r_geom = r_geoms[j];
    1098             :                         //Null handling
    1099           0 :                         if (r_geom == NULL) {
    1100           0 :                                 if (nil_matches && l_geom == NULL) {
    1101           0 :                                         if (BUNappend(lres, &l_oid, false) != GDK_SUCCEED || BUNappend(rres, &r_oid, false) != GDK_SUCCEED) {
    1102           0 :                                                 msg = createException(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1103           0 :                                                 bat_iterator_end(&l_iter);
    1104           0 :                                                 bat_iterator_end(&r_iter);
    1105           0 :                                                 goto free;
    1106             :                                         }
    1107             :                                 }
    1108             :                                 else
    1109           0 :                                         continue;
    1110             :                         }
    1111             :                         //TODO Do we need to do this check for every element?
    1112           0 :                         if (GEOSGetSRID_r(geoshandle, l_geom) != GEOSGetSRID_r(geoshandle, r_geom)) {
    1113           0 :                                 msg = createException(MAL, name, SQLSTATE(38000) "Geometries of different SRID");
    1114           0 :                                 bat_iterator_end(&l_iter);
    1115           0 :                                 bat_iterator_end(&r_iter);
    1116           0 :                                 goto free;
    1117             :                         }
    1118             :                         //Apply the (Geom, Geom) -> bit function
    1119           0 :                         bit cond = (*func)(l_geom, r_geom, double_flag);
    1120           0 :                         if (cond != anti) {
    1121           0 :                                 if (BUNappend(lres, &l_oid, false) != GDK_SUCCEED || BUNappend(rres, &r_oid, false) != GDK_SUCCEED) {
    1122           0 :                                         msg = createException(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1123           0 :                                         bat_iterator_end(&l_iter);
    1124           0 :                                         bat_iterator_end(&r_iter);
    1125           0 :                                         goto free;
    1126             :                                 }
    1127             :                         }
    1128             :                 }
    1129             :         }
    1130             :         if (l_geoms) {
    1131           0 :                 for (BUN i = 0; i < l_ci.ncand; i++) {
    1132           0 :                         GEOSGeom_destroy_r(geoshandle, l_geoms[i]);
    1133             :                 }
    1134           0 :                 GDKfree(l_geoms);
    1135             :         }
    1136           0 :         if (r_geoms) {
    1137           0 :                 for (BUN i = 0; i < r_ci.ncand; i++) {
    1138           0 :                         GEOSGeom_destroy_r(geoshandle, r_geoms[i]);
    1139             :                 }
    1140           0 :                 GDKfree(r_geoms);
    1141             :         }
    1142           0 :         bat_iterator_end(&l_iter);
    1143           0 :         bat_iterator_end(&r_iter);
    1144           0 :         BBPunfix(l->batCacheid);
    1145           0 :         BBPunfix(r->batCacheid);
    1146           0 :         if (ls)
    1147           0 :                 BBPunfix(ls->batCacheid);
    1148           0 :         if (rs)
    1149           0 :                 BBPunfix(rs->batCacheid);
    1150           0 :         *lres_id = lres->batCacheid;
    1151           0 :         BBPkeepref(lres);
    1152           0 :         *rres_id = rres->batCacheid;
    1153           0 :         BBPkeepref(rres);
    1154           0 :         return MAL_SUCCEED;
    1155           0 : free:
    1156           0 :         if (l_geoms) {
    1157           0 :                 for (BUN i = 0; i < l_ci.ncand; i++) {
    1158           0 :                         GEOSGeom_destroy_r(geoshandle, l_geoms[i]);
    1159             :                 }
    1160           0 :                 GDKfree(l_geoms);
    1161             :         }
    1162           0 :         if (r_geoms) {
    1163           0 :                 for (BUN i = 0; i < r_ci.ncand; i++) {
    1164           0 :                         GEOSGeom_destroy_r(geoshandle, r_geoms[i]);
    1165             :                 }
    1166           0 :                 GDKfree(r_geoms);
    1167             :         }
    1168           0 :         BBPunfix(l->batCacheid);
    1169           0 :         BBPunfix(r->batCacheid);
    1170           0 :         if (ls)
    1171           0 :                 BBPunfix(ls->batCacheid);
    1172           0 :         if (rs)
    1173           0 :                 BBPunfix(rs->batCacheid);
    1174           0 :         if (lres)
    1175           0 :                 BBPreclaim(lres);
    1176           0 :         if (rres)
    1177           0 :                 BBPreclaim(rres);
    1178             :         return msg;
    1179             : }
    1180             : 
    1181             : str
    1182           0 : wkbDWithinGeographicJoin(bat *lres_id, bat *rres_id, const bat *l_id, const bat *r_id, const bat *d_id, const bat *ls_id, const bat *rs_id, const bit *nil_matches, const lng *estimate, const bit *anti) {
    1183           0 :         double distance_within = 0;
    1184           0 :         BAT *d = NULL;
    1185             :         //Get the distance BAT and get the double value
    1186           0 :         if ((d = BATdescriptor(*d_id)) == NULL) {
    1187           0 :                 throw(MAL, "geom.wkbDWithinGeographicJoin", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
    1188             :         }
    1189           0 :         if (BATcount(d) == 1) {
    1190           0 :                 distance_within = *((double*) Tloc(d, 0));
    1191             :         }
    1192           0 :         BBPunfix(d->batCacheid);
    1193           0 :         return filterJoinGeomGeomDoubleToBit(lres_id,rres_id,l_id,r_id,distance_within,ls_id,rs_id,*nil_matches,estimate,*anti,geosDistanceWithin,"geom.wkbDWithinGeographicJoin");
    1194             : }
    1195             : 
    1196             : str
    1197           0 : wkbDWithinGeographicSelect(bat* outid, const bat *bid , const bat *sid, wkb * const *wkb_const, const dbl *distance_within, const bit *anti) {
    1198           0 :         return filterSelectGeomGeomDoubleToBit(outid,bid,sid,*wkb_const,*distance_within,*anti,geosDistanceWithin,"geom.wkbDWithinGeographicSelect");
    1199             : }
    1200             : 
    1201             : str
    1202           0 : wkbIntersectsGeographicJoin(bat *lres_id, bat *rres_id, const bat *l_id, const bat *r_id, const bat *ls_id, const bat *rs_id, const bit *nil_matches, const lng *estimate, const bit *anti) {
    1203           0 :         return filterJoinGeomGeomDoubleToBit(lres_id,rres_id,l_id,r_id,0,ls_id,rs_id,*nil_matches,estimate,*anti,geosDistanceWithin,"geom.wkbIntersectsGeographicJoin");
    1204             : }
    1205             : 
    1206             : str
    1207           0 : wkbIntersectsGeographicSelect(bat* outid, const bat *bid , const bat *sid, wkb * const *wkb_const, const bit *anti) {
    1208           0 :         return filterSelectGeomGeomDoubleToBit(outid,bid,sid,*wkb_const,0,*anti,geosDistanceWithin,"geom.wkbIntersectsGeographicSelect");
    1209             : }
    1210             : 
    1211             : static inline CartPoint3D
    1212           0 : crossProduct (const CartPoint3D *p1, const CartPoint3D *p2)
    1213             : {
    1214           0 :         CartPoint3D p3;
    1215           0 :         p3.x = p1->y * p2->z - p1->z * p2->y;
    1216           0 :         p3.y = p1->z * p2->x - p1->x * p2->z;
    1217           0 :         p3.z = p1->x * p2->y - p1->y * p2->x;
    1218           0 :         return p3;
    1219             : }
    1220             : 
    1221             : static inline double
    1222           0 : dotProduct (const CartPoint3D *p1, const CartPoint3D *p2)
    1223             : {
    1224           0 :         return (p1->x * p2->x) + (p1->y * p2->y) + (p1->z * p2->z);
    1225             : }
    1226             : 
    1227             : //
    1228             : static inline int
    1229           0 : angleRotation (const CartPoint2D p1, const CartPoint2D p2, const CartPoint2D p3)
    1230             : {
    1231             :         // vector P = P1P2
    1232             :         // vector Q = P1P3
    1233             :         // side = || Q x P ||
    1234             :         // Q and P are 2D vectors, so z component is 0
    1235           0 :         double side = ((p3.x - p1.x) * (p2.y - p1.y) - (p2.x - p1.x) * (p3.y - p1.y));
    1236             :         //
    1237           0 :         if (side < 0)
    1238             :                 return -1;
    1239           0 :         else if (side > 0)
    1240             :                 return 1;
    1241             :         else
    1242           0 :                 return 0;
    1243             : }
    1244             : 
    1245             : static void
    1246           0 : normalize2D (CartPoint2D *p) {
    1247           0 :         double c = sqrt(p->x * p->x + p->y * p->y);
    1248           0 :         if (c == 0) {
    1249           0 :                 p->x = p->y = 0;
    1250           0 :                 return;
    1251             :         }
    1252           0 :         p->x = p->x / c;
    1253           0 :         p->y = p->y / c;
    1254             : }
    1255             : 
    1256             : static void
    1257           0 : normalize3D (CartPoint3D *p) {
    1258           0 :         double c = sqrt(p->x * p->x + p->y * p->y + p->z * p->z);
    1259           0 :         if (c == 0) {
    1260           0 :                 p->x = p->y = p->z = 0;
    1261           0 :                 return;
    1262             :         }
    1263           0 :         p->x = p->x / c;
    1264           0 :         p->y = p->y / c;
    1265           0 :         p->z = p->z / c;
    1266             : }
    1267             : 
    1268             : static inline bool
    1269           0 : FP_EQUALS (double x, double y)
    1270             : {
    1271           0 :         return fabs(x-y) < 1e-12;
    1272             : }
    1273             : 
    1274             : str
    1275           0 : geodeticEdgeBoundingBox(const CartPoint3D* p1, const CartPoint3D* p2, BoundingBox* mbox)
    1276             : {
    1277           0 :         CartPoint3D p3, pn, ep3d;
    1278           0 :         CartPoint3D e[6];
    1279           0 :         CartPoint2D s1, s2, ep, o;
    1280           0 :         int rotation_to_origin, rotation_to_ep;
    1281             : 
    1282             :     // check coinciding points
    1283           0 :         if (FP_EQUALS(p1->x,p2->x) && FP_EQUALS(p1->y,p2->y) && FP_EQUALS(p1->z,p2->z))
    1284             :                 return MAL_SUCCEED;
    1285             :     // check antipodal points
    1286           0 :         if (FP_EQUALS(p1->x,-p2->x) && FP_EQUALS(p1->y,-p2->y) && FP_EQUALS(p1->z,-p2->z))
    1287           0 :                 throw(MAL, "geom.geodeticEdgeBoundingBox", SQLSTATE(38000) "Antipodal edge");
    1288             : 
    1289             :     // create the great circle plane coord system (p1, p3)
    1290             :     // pn = p1 x p2
    1291             :     // p3 = pn x p1
    1292             :         // TODO handle the narrow and wide angle cases
    1293           0 :         pn = crossProduct(p1,p2);
    1294           0 :         normalize3D(&pn);
    1295           0 :         p3 = crossProduct(&pn,p1);
    1296           0 :         normalize3D(&p3);
    1297             : 
    1298             :     // represent p1, p2 with (s1, s2) 2-D space
    1299             :     // s1.x = 1, s1.y = 0
    1300             :     // s2.x = p2 * p1, s2.y = p2 * p3
    1301           0 :         s1.x = 1;
    1302           0 :         s1.y = 0;
    1303           0 :         s2.x = dotProduct(p2, p1);
    1304           0 :         s2.y = dotProduct(p2, &p3);
    1305             :     // 2-D space origin
    1306             :     // O.x = 0, O.y = 0
    1307           0 :         o.x = 0;
    1308           0 :         o.y = 0;
    1309             : 
    1310             :     // create 3D endpoints E.x, E.-x, ...
    1311             :     // E.x = (1, 0, 0), E.-x = (-1, 0, 0) ...
    1312           0 :         memset(e, 0, sizeof(CartPoint2D) * 6);
    1313           0 :         e[0].x = e[1].y = e[2].z = 1;
    1314           0 :         e[3].x = e[4].y = e[5].z = -1;
    1315             : 
    1316             :     // find the rotation between s1->s2 and s1->O
    1317             :     // rot = norm( vec(s1,s2) x vec(s1,0))
    1318           0 :         rotation_to_origin = angleRotation(s1,s2,o);
    1319             : 
    1320             :     // for every endpoint E
    1321           0 :         for (int i = 0; i < 6; i++) {
    1322             :                 // project the endpoint in the 2-D space
    1323           0 :                 ep.x = dotProduct(&e[i],p1);
    1324           0 :                 ep.y = dotProduct(&e[i],&p3);
    1325             :         // re-normalize it e.g. EP (for endpoint_projection)
    1326           0 :                 normalize2D(&ep);
    1327             :         // ep_rot = norm( vec(s1,s2) x vec(s1,EP_end) )
    1328           0 :                 rotation_to_ep = angleRotation(s1,s2,ep);
    1329           0 :                 if (rotation_to_origin != rotation_to_ep) {
    1330             :                         // convert the 2-D EP into 3-D space
    1331           0 :                         ep3d.x = ep.x * p1->x + ep.y * p3.x;
    1332           0 :                         ep3d.y = ep.x * p1->y + ep.y * p3.y;
    1333           0 :                         ep3d.z = ep.x * p1->z + ep.y * p3.z;
    1334             :             // expand the mbox in order to include 3-D representation of EP
    1335           0 :                         boundingBoxAddPoint(mbox,ep3d);
    1336             :                 }
    1337             :         }
    1338             :         return MAL_SUCCEED;
    1339             : }

Generated by: LCOV version 1.14