LCOV - code coverage report
Current view: top level - geom/monetdb5 - geod.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 7 716 1.0 %
Date: 2024-10-04 20:04:04 Functions: 1 48 2.1 %

          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             : *  Conversions
      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;
     353             : 
     354             :         //If there are no segments, return NULL
     355           0 :         if (lines.pointCount == 0)
     356             :                 return NULL;
     357             : 
     358           0 :         bb = GDKzalloc(sizeof(BoundingBox));
     359           0 :         if (bb == NULL)
     360             :                 return NULL;
     361             : 
     362           0 :         c = geo2cartFromDegrees(lines.points[0]);
     363             : 
     364             :         //Initialize the bounding box with the first point
     365           0 :         bb->xmin = bb->xmax = c.x;
     366           0 :         bb->ymin = bb->ymax = c.y;
     367           0 :         bb->zmin = bb->zmax = c.z;
     368             : 
     369           0 :         for (int i = 1; i < lines.pointCount; i++) {
     370           0 :                 c = geo2cartFromDegrees(lines.points[i]);
     371           0 :                 boundingBoxAddPoint(bb, c);
     372             :         }
     373             :         return bb;
     374             : }
     375             : 
     376             : static int
     377           0 : boundingBoxContainsPoint(BoundingBox bb, CartPoint3D pt)
     378             : {
     379           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;
     380             : }
     381             : 
     382             : /* Returns a point outside of the polygon's bounding box, for Point-In-Polygon calculation */
     383             : static GeoPoint
     384           0 : pointOutsidePolygon(GeoPolygon polygon)
     385             : {
     386           0 :         BoundingBox bb = *polygon.bbox;
     387           0 :         BoundingBox bb2 = *polygon.bbox;
     388             : 
     389             :         //TODO: From POSTGIS -> CHANGE
     390           0 :         double grow = M_PI / 180.0 / 60.0;
     391           0 :         CartPoint3D corners[8];
     392           0 :         while (grow < M_PI) {
     393           0 :                 if (bb.xmin > -1)
     394           0 :                         bb.xmin -= grow;
     395           0 :                 if (bb.ymin > -1)
     396           0 :                         bb.ymin -= grow;
     397           0 :                 if (bb.zmin > -1)
     398           0 :                         bb.zmin -= grow;
     399           0 :                 if (bb.xmax < 1)
     400           0 :                         bb.xmax += grow;
     401           0 :                 if (bb.ymax < 1)
     402           0 :                         bb.ymax += grow;
     403           0 :                 if (bb.zmax < 1)
     404           0 :                         bb.zmax += grow;
     405             : 
     406           0 :                 corners[0].x = bb.xmin;
     407           0 :                 corners[0].y = bb.ymin;
     408           0 :                 corners[0].z = bb.zmin;
     409             : 
     410           0 :                 corners[1].x = bb.xmin;
     411           0 :                 corners[1].y = bb.ymax;
     412           0 :                 corners[1].z = bb.zmin;
     413             : 
     414           0 :                 corners[2].x = bb.xmin;
     415           0 :                 corners[2].y = bb.ymin;
     416           0 :                 corners[2].z = bb.zmax;
     417             : 
     418           0 :                 corners[3].x = bb.xmax;
     419           0 :                 corners[3].y = bb.ymin;
     420           0 :                 corners[3].z = bb.zmin;
     421             : 
     422           0 :                 corners[4].x = bb.xmax;
     423           0 :                 corners[4].y = bb.ymax;
     424           0 :                 corners[4].z = bb.zmin;
     425             : 
     426           0 :                 corners[5].x = bb.xmax;
     427           0 :                 corners[5].y = bb.ymin;
     428           0 :                 corners[5].z = bb.zmax;
     429             : 
     430           0 :                 corners[6].x = bb.xmin;
     431           0 :                 corners[6].y = bb.ymax;
     432           0 :                 corners[6].z = bb.zmax;
     433             : 
     434           0 :                 corners[7].x = bb.xmax;
     435           0 :                 corners[7].y = bb.ymax;
     436           0 :                 corners[7].z = bb.zmax;
     437             : 
     438           0 :                 for (int i = 0; i < 8; i++)
     439           0 :                         if (!boundingBoxContainsPoint(bb2, corners[i])) {
     440           0 :                                 CartPoint3D pt_cart = corners[i];
     441           0 :                                 return rad2DegPoint(cart2geo(pt_cart));
     442             :                         }
     443           0 :                 grow *= 2.0;
     444             :         }
     445             :         //TODO: Should this be the return value in case no point is found?
     446           0 :         return geoPointFromLatLon(0, 0);
     447             : }
     448             : 
     449             : /**
     450             : * Distance functions
     451             : *
     452             : **/
     453             : /**
     454             : * The haversine formula calculate the distance in meters between two lat/lon points
     455             : * The points must be measured in radians.
     456             : * This formula assumes a spherical model of the earth, which can lead to an error of about 0.3% compared to a ellipsoidal model.
     457             : */
     458             : static double
     459           0 : haversine(GeoPoint a, GeoPoint b)
     460             : {
     461           0 :         double d_lon = b.lon - a.lon;
     462           0 :         double d_lat = b.lat - a.lat;
     463           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);
     464           0 :         double c = 2 * atan2(sqrt(d), sqrt(1 - d));
     465             :         //TODO: Same as the previous line (which one is best?) -> 2 * asin(sqrt(d));
     466           0 :         return earth_radius_meters * c;
     467             : }
     468             : 
     469             : /* Distance between two Points */
     470             : static double
     471           0 : geoDistancePointPoint(GeoPoint a, GeoPoint b)
     472             : {
     473           0 :         return haversine(deg2RadPoint(a), deg2RadPoint(b));
     474             : }
     475             : 
     476             : /* Calculates the distance between the perpendicular projection of a point in the Line */
     477             : static double
     478           0 : calculatePerpendicularDistance(GeoPoint p_geo, GeoPoint l1_geo, GeoPoint l2_geo)
     479             : {
     480           0 :         CartPoint3D l1, l2, p, projection;
     481           0 :         GeoPoint projection_geo;
     482             : 
     483             :         //First, convert the points to 3D cartesian coordinates
     484           0 :         l1 = geo2cartFromDegrees(l1_geo);
     485           0 :         l2 = geo2cartFromDegrees(l2_geo);
     486           0 :         p = geo2cartFromDegrees(p_geo);
     487             : 
     488             :         //Calculate the projection of point into the line
     489           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);
     490           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;
     491             : 
     492             :         //If t is not between 0 and 1, the projected point is not in the line, so there is no perpendicular, return huge number
     493           0 :         if (t < 0 || t > 1)
     494             :                 return INT_MAX;
     495             : 
     496             :         //If the projection is in the line segment, build the point -> projection = l1 + t * (l2-l1)
     497           0 :         projection = cartPointFromXYZ(l1.x + t * (l2.x - l1.x), l1.y + t * (l2.y - l1.y), l1.z + t * (l2.z - l1.z));
     498             : 
     499             :         //Convert into geographic coordinates (radians)
     500           0 :         projection_geo = cart2geo(projection);
     501             : 
     502             :         //Calculate distance from original point to the projection
     503           0 :         return haversine(deg2RadPoint(p_geo), projection_geo);
     504             : }
     505             : 
     506             : /* Distance between Point and Line
     507             :    The returned distance is the minimum distance between the point and the line vertices
     508             :    and the perpendicular projection of the point in each line segment.  */
     509             : static double
     510           0 : geoDistancePointLine(GeoPoint point, GeoLines lines, double distance_min_limit)
     511             : {
     512           0 :         double distancePoint, distancePerpendicular, min_distance = INT_MAX;
     513           0 :         for (int i = 0; i < lines.pointCount-1; i++) {
     514           0 :                 distancePoint = geoDistancePointPoint(point, lines.points[i]);
     515           0 :                 distancePerpendicular = calculatePerpendicularDistance(point,lines.points[i],lines.points[i+1]);
     516           0 :                 if (distancePoint < min_distance)
     517           0 :                         min_distance = distancePoint;
     518           0 :                 if (distancePerpendicular < min_distance)
     519           0 :                         min_distance = distancePerpendicular;
     520             :                 //Shortcut, if the geometries are already at their minimum distance
     521           0 :                 if (min_distance <= distance_min_limit)
     522           0 :                         return min_distance;
     523             :         }
     524           0 :         distancePoint = geoDistancePointPoint(point, lines.points[lines.pointCount-1]);
     525           0 :         return distancePoint < min_distance ? distancePoint : min_distance;
     526             : }
     527             : 
     528             : /* Distance between two Lines. */
     529             : static double
     530           0 : geoDistanceLineLine(GeoLines line1, GeoLines line2, double distance_min_limit)
     531             : {
     532           0 :         double distance, min_distance = INT_MAX;
     533           0 :         for (int i = 0; i < line1.pointCount; i++) {
     534           0 :                 distance = geoDistancePointLine(line1.points[i], line2, distance_min_limit);
     535           0 :                 if (distance < min_distance)
     536           0 :                         min_distance = distance;
     537             :                 //Shortcut, if the geometries are already at their minimum distance
     538           0 :                 if (min_distance <= distance_min_limit)
     539           0 :                         return min_distance;
     540             :         }
     541           0 :         for (int i = 0; i < line2.pointCount; i++) {
     542           0 :                 for (int j = 0; j < line1.pointCount - 1; j++) {
     543           0 :                         distance = calculatePerpendicularDistance(line2.points[i],line1.points[j],line1.points[j+1]);
     544           0 :                         if (distance < min_distance)
     545           0 :                                 min_distance = distance;
     546             :                         //Shortcut, if the geometries are already at their minimum distance
     547           0 :                         if (min_distance <= distance_min_limit)
     548           0 :                                 return min_distance;
     549             :                 }
     550             :         }
     551             :         return min_distance;
     552             : }
     553             : 
     554             : /* Checks if a Point is within a Polygon */
     555             : static bool
     556           0 : pointWithinPolygon(GeoPolygon polygon, GeoPoint point)
     557             : {
     558           0 :         int intersectionNum = 0;
     559           0 :         GEOSGeometry *segmentPolygon, *intersectionPoints;
     560           0 :         GeoLines polygonRing;
     561             : 
     562             :         //Get an point that's outside the polygon
     563           0 :         GeoPoint outsidePoint = pointOutsidePolygon(polygon);
     564             : 
     565             :         //No outside point was found, return false
     566           0 :         if (outsidePoint.lat == 0 && outsidePoint.lon == 0)
     567             :                 return false;
     568             : 
     569             :         /*printf("Outside point: (%f %f)\n",outsidePoint.lon, outsidePoint.lat);
     570             :         fflush(stdout);*/
     571             : 
     572             :         //Construct a line between the outside point and the input point
     573           0 :         GEOSGeometry *outInLine = cartesianLineFromGeoPoints(point, outsidePoint);
     574             : 
     575             :         //TODO This is producing wrong results, review the intersection conditional
     576             :         //Count the number of intersections between the polygon exterior ring and the constructed line
     577           0 :         polygonRing = polygon.exteriorRing;
     578           0 :         for (int i = 0; i < polygonRing.pointCount-1; i++) {
     579           0 :                 segmentPolygon = cartesianLineFromGeoPoints(polygonRing.points[i], polygonRing.points[i+1]);
     580           0 :                 intersectionPoints = GEOSIntersection_r(geoshandle, segmentPolygon, outInLine);
     581             : 
     582             :                 //If there is an intersection, a point will be returned (line when there is none)
     583           0 :                 if (GEOSGeomTypeId_r(geoshandle, intersectionPoints) == GEOS_POINT)
     584           0 :                         intersectionNum++;
     585             : 
     586           0 :                 if (intersectionPoints != NULL)
     587           0 :                         GEOSGeom_destroy_r(geoshandle, intersectionPoints);
     588           0 :                 if (segmentPolygon != NULL)
     589           0 :                         GEOSGeom_destroy_r(geoshandle, segmentPolygon);
     590             :         }
     591             : 
     592             :         //Count the number of intersections between the polygon interior rings and the constructed line
     593           0 :         for (int j = 0; j < polygon.interiorRingsCount; j++) {
     594           0 :                 polygonRing = polygon.interiorRings[j];
     595           0 :                 for (int i = 0; i < polygonRing.pointCount-1; i++) {
     596           0 :                         segmentPolygon = cartesianLineFromGeoPoints(polygonRing.points[i], polygonRing.points[i+1]);
     597           0 :                         intersectionPoints = GEOSIntersection_r(geoshandle, segmentPolygon, outInLine);
     598             : 
     599             :                         //If there is an intersection, a point will be returned (line when there is none)
     600           0 :                         if (GEOSGeomTypeId_r(geoshandle, intersectionPoints) == GEOS_POINT)
     601           0 :                                 intersectionNum++;
     602             : 
     603           0 :                         if (intersectionPoints != NULL)
     604           0 :                                 GEOSGeom_destroy_r(geoshandle, intersectionPoints);
     605           0 :                         if (segmentPolygon != NULL)
     606           0 :                                 GEOSGeom_destroy_r(geoshandle, segmentPolygon);
     607             :                 }
     608             :         }
     609             : 
     610           0 :         if (outInLine != NULL)
     611           0 :                 GEOSGeom_destroy_r(geoshandle, outInLine);
     612             : 
     613             :         //If even, the point is not within the polygon. If odd, it is within
     614           0 :         return intersectionNum % 2 == 1;
     615             : }
     616             : 
     617             : /* Distance between Point and Polygon.*/
     618             : static double
     619           0 : geoDistancePointPolygon(GeoPoint point, GeoPolygon polygon, double distance_min_limit)
     620             : {
     621             :         //Check if point is in polygon
     622           0 :         if (pointWithinPolygon(polygon, point))
     623             :                 return 0;
     624             : 
     625             :         //Calculate distance from Point to the exterior and interior rings of the polygon
     626           0 :         double distance, min_distance = INT_MAX;
     627             :         //First, calculate distance to the exterior ring
     628           0 :         min_distance = geoDistancePointLine(point, polygon.exteriorRing, distance_min_limit);
     629             :         //Then, calculate distance to the interior rings
     630           0 :         for (int i = 0; i < polygon.interiorRingsCount; i++) {
     631             :                 //Shortcut, if the geometries are already at their minimum distance
     632           0 :                 if (min_distance <= distance_min_limit)
     633           0 :                         return min_distance;
     634           0 :                 distance = geoDistancePointLine(point, polygon.interiorRings[i], distance_min_limit);
     635           0 :                 if (distance < min_distance)
     636           0 :                         min_distance = distance;
     637             :         }
     638             :         return min_distance;
     639             : }
     640             : 
     641             : /* Distance between Line and Polygon. */
     642             : static double
     643           0 : geoDistanceLinePolygon(GeoLines line, GeoPolygon polygon, double distance_min_limit)
     644             : {
     645           0 :         double distance, min_distance = INT_MAX;
     646             :         //Calculate distance to all start vertices of the line
     647           0 :         for (int i = 0; i < line.pointCount; i++) {
     648           0 :                 distance = geoDistancePointPolygon(line.points[i], polygon, distance_min_limit);
     649             : 
     650             :                 //Short-cut in case the point is within the polygon
     651           0 :                 if (distance <= distance_min_limit)
     652           0 :                         return distance;
     653             : 
     654           0 :                 if (distance < min_distance)
     655           0 :                         min_distance = distance;
     656             :         }
     657             :         return min_distance;
     658             : }
     659             : 
     660             : /* Distance between two Polygons. */
     661             : static double
     662           0 : geoDistancePolygonPolygon(GeoPolygon polygon1, GeoPolygon polygon2, double distance_min_limit)
     663             : {
     664           0 :         double distance1, distance2;
     665             :         //Calculate the distance between the exterior ring of polygon1 and all segments of polygon2 (including the interior rings)
     666           0 :         distance1 = geoDistanceLinePolygon(polygon1.exteriorRing, polygon2, distance_min_limit);
     667             :         //Shortcut, if the geometries are already at their minimum distance
     668           0 :         if (distance1 <= distance_min_limit)
     669             :                 return distance1;
     670           0 :         distance2 = geoDistanceLinePolygon(polygon2.exteriorRing, polygon1, distance_min_limit);
     671           0 :         return distance1 < distance2 ? distance1 : distance2;
     672             : }
     673             : 
     674             : /* Distance between two (non-collection) geometries. */
     675             : static double
     676           0 : geoDistanceSingle(GEOSGeom aGeom, GEOSGeom bGeom, double distance_min_limit)
     677             : {
     678           0 :         int dimA, dimB;
     679           0 :         double distance = INT_MAX;
     680           0 :         dimA = GEOSGeom_getDimensions_r(geoshandle, aGeom);
     681           0 :         dimB = GEOSGeom_getDimensions_r(geoshandle, bGeom);
     682           0 :         if (dimA == 0 && dimB == 0) {
     683             :                 /* Point and Point */
     684           0 :                 GeoPoint a = geoPointFromGeom(aGeom);
     685           0 :                 GeoPoint b = geoPointFromGeom(bGeom);
     686           0 :                 distance = geoDistancePointPoint(a, b);
     687           0 :         } else if (dimA == 0 && dimB == 1) {
     688             :                 /* Point and Line/LinearRing */
     689           0 :                 GeoPoint a = geoPointFromGeom(aGeom);
     690           0 :                 GeoLines b = geoLinesFromGeom(bGeom);
     691           0 :                 distance = geoDistancePointLine(a, b, distance_min_limit);
     692           0 :                 freeGeoLines(b);
     693           0 :         } else if (dimA == 1 && dimB == 0) {
     694             :                 /* Line/LinearRing and Point */
     695           0 :                 GeoLines a = geoLinesFromGeom(aGeom);
     696           0 :                 GeoPoint b = geoPointFromGeom(bGeom);
     697           0 :                 distance = geoDistancePointLine(b, a, distance_min_limit);
     698           0 :                 freeGeoLines(a);
     699           0 :         } else if (dimA == 1 && dimB == 1) {
     700             :                 /* Line/LinearRing and Line/LinearRing */
     701           0 :                 GeoLines a = geoLinesFromGeom(aGeom);
     702           0 :                 GeoLines b = geoLinesFromGeom(bGeom);
     703           0 :                 distance = geoDistanceLineLine(a, b, distance_min_limit);
     704           0 :                 freeGeoLines(a);
     705           0 :                 freeGeoLines(b);
     706           0 :         } else if (dimA == 0 && dimB == 2) {
     707             :                 /* Point and Polygon */
     708           0 :                 GeoPoint a = geoPointFromGeom(aGeom);
     709           0 :                 GeoPolygon b = geoPolygonFromGeom(bGeom);
     710           0 :                 distance = geoDistancePointPolygon(a, b, distance_min_limit);
     711           0 :                 freeGeoPolygon(b);
     712           0 :         } else if (dimA == 2 && dimB == 0) {
     713             :                 /* Polygon and Point */
     714           0 :                 GeoPolygon a = geoPolygonFromGeom(aGeom);
     715           0 :                 GeoPoint b = geoPointFromGeom(bGeom);
     716           0 :                 distance = geoDistancePointPolygon(b, a, distance_min_limit);
     717           0 :                 freeGeoPolygon(a);
     718           0 :         } else if (dimA == 1 && dimB == 2) {
     719             :                 /* Line/LinearRing and Polygon */
     720           0 :                 GeoLines a = geoLinesFromGeom(aGeom);
     721           0 :                 GeoPolygon b = geoPolygonFromGeom(bGeom);
     722           0 :                 distance = geoDistanceLinePolygon(a, b, distance_min_limit);
     723           0 :                 freeGeoLines(a);
     724           0 :                 freeGeoPolygon(b);
     725           0 :         } else if (dimA == 2 && dimB == 1) {
     726             :                 /* Polygon and Line/LinearRing */
     727           0 :                 GeoPolygon a = geoPolygonFromGeom(aGeom);
     728           0 :                 GeoLines b = geoLinesFromGeom(bGeom);
     729           0 :                 distance = geoDistanceLinePolygon(b, a, distance_min_limit);
     730           0 :                 freeGeoPolygon(a);
     731           0 :                 freeGeoLines(b);
     732           0 :         } else if (dimA == 2 && dimB == 2) {
     733             :                 /* Polygon and Polygon */
     734           0 :                 GeoPolygon a = geoPolygonFromGeom(aGeom);
     735           0 :                 GeoPolygon b = geoPolygonFromGeom(bGeom);
     736           0 :                 distance = geoDistancePolygonPolygon(a, b, distance_min_limit);
     737           0 :                 freeGeoPolygon(a);
     738           0 :                 freeGeoPolygon(b);
     739             :         }
     740           0 :         return distance;
     741             : }
     742             : 
     743             : //The distance_min_limit argument is used for DWithin and Intersects.
     744             : //If we get to the minimum distance for the predicate, return immediately
     745             : //It is equal to 0 if the operation is Distance
     746             : static double
     747           0 : geoDistanceInternal(GEOSGeom a, GEOSGeom b, double distance_min_limit)
     748             : {
     749           0 :         int numGeomsA = GEOSGetNumGeometries_r(geoshandle, a), numGeomsB = GEOSGetNumGeometries_r(geoshandle, b);
     750           0 :         double distance, min_distance = INT_MAX;
     751           0 :         GEOSGeometry *geo1, *geo2;
     752           0 :         for (int i = 0; i < numGeomsA; i++) {
     753           0 :                 geo1 = (GEOSGeometry *)GEOSGetGeometryN_r(geoshandle, (const GEOSGeometry *)a, i);
     754           0 :                 for (int j = 0; j < numGeomsB; j++) {
     755           0 :                         geo2 = (GEOSGeometry *)GEOSGetGeometryN_r(geoshandle, (const GEOSGeometry *)b, j);
     756           0 :                         distance = geoDistanceSingle(geo1, geo2, distance_min_limit);
     757             :                         //Shortcut, if the geometries are already at their minimum distance (0 in the case of normal Distance)
     758           0 :                         if (distance <= distance_min_limit)
     759           0 :                                 return distance;
     760           0 :                         if (distance < min_distance)
     761           0 :                                 min_distance = distance;
     762             :                 }
     763             :         }
     764             :         return min_distance;
     765             : }
     766             : 
     767             : /**
     768             : * Distance
     769             : *
     770             : **/
     771             : /* Calculates the distance, in meters, between two geographic geometries with latitude/longitude coordinates */
     772             : str
     773           0 : wkbDistanceGeographic(dbl *out, wkb * const *a, wkb * const *b)
     774             : {
     775           0 :         str err = MAL_SUCCEED;
     776           0 :         GEOSGeom ga, gb;
     777           0 :         err = wkbGetCompatibleGeometries(a, b, &ga, &gb);
     778           0 :         if (ga && gb) {
     779           0 :                 (*out) = geoDistanceInternal(ga, gb, 0);
     780             :         }
     781           0 :         GEOSGeom_destroy_r(geoshandle, ga);
     782           0 :         GEOSGeom_destroy_r(geoshandle, gb);
     783           0 :         return err;
     784             : }
     785             : 
     786             : /**
     787             : * Distance Within
     788             : *
     789             : **/
     790             : /* Checks if two geographic geometries are within d meters of one another */
     791             : str
     792           0 : wkbDWithinGeographic(bit *out, wkb * const *a, wkb * const *b, const dbl *d)
     793             : {
     794           0 :         str err = MAL_SUCCEED;
     795           0 :         GEOSGeom ga, gb;
     796           0 :         double distance;
     797           0 :         err = wkbGetCompatibleGeometries(a, b, &ga, &gb);
     798           0 :         if (ga && gb) {
     799           0 :                 distance = geoDistanceInternal(ga, gb, *d);
     800           0 :                 (*out) = (distance <= (*d));
     801             :         }
     802           0 :         GEOSGeom_destroy_r(geoshandle, ga);
     803           0 :         GEOSGeom_destroy_r(geoshandle, gb);
     804           0 :         return err;
     805             : }
     806             : 
     807             : /**
     808             : * Intersects
     809             : *
     810             : **/
     811             : /* Checks if two geographic geometries intersect at any point */
     812             : str
     813           0 : wkbIntersectsGeographic(bit *out, wkb * const *a, wkb * const *b)
     814             : {
     815           0 :         str err = MAL_SUCCEED;
     816           0 :         GEOSGeom ga, gb;
     817           0 :         double distance;
     818           0 :         err = wkbGetCompatibleGeometries(a, b, &ga, &gb);
     819           0 :         if (ga && gb) {
     820           0 :                 distance = geoDistanceInternal(ga, gb, 0);
     821           0 :                 (*out) = (distance == 0);
     822             :         }
     823           0 :         GEOSGeom_destroy_r(geoshandle, ga);
     824           0 :         GEOSGeom_destroy_r(geoshandle, gb);
     825           0 :         return err;
     826             : }
     827             : 
     828             : /* Checks if a Polygon covers a Line geometry */
     829             : static bool
     830           0 : geoPolygonCoversLine(GeoPolygon polygon, GeoLines lines)
     831             : {
     832           0 :         for (int i = 0; i < lines.pointCount; i++) {
     833           0 :                 if (pointWithinPolygon(polygon, lines.points[i]) == false)
     834             :                         return false;
     835             :         }
     836             :         return true;
     837             : }
     838             : 
     839             : /* Compares two GeoPoints, returns true if they're equal */
     840             : static bool
     841           0 : geoPointEquals(GeoPoint pointA, GeoPoint pointB)
     842             : {
     843           0 :         return (pointA.lat == pointB.lat) && (pointA.lon = pointB.lon);
     844             : }
     845             : 
     846             : //TODO Check if this works correctly
     847             : static bool
     848           0 : geoCoversSingle(GEOSGeom a, GEOSGeom b)
     849             : {
     850           0 :         int dimA = GEOSGeom_getDimensions_r(geoshandle, a), dimB = GEOSGeom_getDimensions_r(geoshandle, b);
     851           0 :         if (dimA < dimB)
     852             :                 //If the dimension of A is smaller than B, then it must not cover it
     853             :                 return false;
     854             : 
     855           0 :         if (dimA == 0){
     856             :                 //A and B are Points
     857           0 :                 GeoPoint pointA = geoPointFromGeom(a);
     858           0 :                 GeoPoint pointB = geoPointFromGeom(b);
     859           0 :                 return geoPointEquals(pointA, pointB);
     860           0 :         } else if (dimA == 1) {
     861             :                 //A is Line
     862             :                 //GeoLines lineA = geoLinesFromGeom(a);
     863             :                 if (dimB == 0) {
     864             :                         //B is Point
     865             :                         //GeoPoint pointB = geoPointFromGeom(b);
     866             :                         //return geoLineCoversPoint(lineA,pointB);
     867             :                         return false;
     868             :                 } else {
     869             :                         //B is Line
     870             :                         //GeoLines lineB = geoLinesFromGeom(b);
     871             :                         //return geoLineCoversLine(lineA, lineB);
     872             :                         return false;
     873             :                 }
     874           0 :         } else if (dimA == 2) {
     875             :                 //A is Polygon
     876           0 :                 GeoPolygon polygonA = geoPolygonFromGeom(a);
     877           0 :                 if (dimB == 0){
     878             :                         //B is Point
     879           0 :                         GeoPoint pointB = geoPointFromGeom(b);
     880           0 :                         return pointWithinPolygon(polygonA, pointB);
     881           0 :                 } else if (dimB == 1) {
     882             :                         //B is Line
     883           0 :                         GeoLines lineB = geoLinesFromGeom(b);
     884           0 :                         return geoPolygonCoversLine(polygonA, lineB);
     885             :                 } else {
     886             :                         //B is Polygon
     887           0 :                         GeoPolygon polygonB = geoPolygonFromGeom(b);
     888             :                         //If every point in the exterior ring of B is covered, polygon B is covered by polygon A
     889           0 :                         return geoPolygonCoversLine(polygonA, polygonB.exteriorRing);
     890             :                 }
     891             :         } else
     892             :                 return false;
     893             : }
     894             : 
     895             : static bool
     896           0 : geoCoversInternal(GEOSGeom a, GEOSGeom b)
     897             : {
     898           0 :         int numGeomsA = GEOSGetNumGeometries_r(geoshandle, a), numGeomsB = GEOSGetNumGeometries_r(geoshandle, b);
     899           0 :         GEOSGeometry *geo1, *geo2;
     900           0 :         for (int i = 0; i < numGeomsA; i++) {
     901           0 :                 geo1 = (GEOSGeometry *)GEOSGetGeometryN_r(geoshandle, (const GEOSGeometry *)a, i);
     902           0 :                 for (int j = 0; j < numGeomsB; j++) {
     903           0 :                         geo2 = (GEOSGeometry *)GEOSGetGeometryN_r(geoshandle, (const GEOSGeometry *)b, j);
     904           0 :                         if (geoCoversSingle(geo1, geo2) == 0)
     905             :                                 return 0;
     906             :                 }
     907             :         }
     908             :         return 1;
     909             : }
     910             : 
     911             : /**
     912             : * Covers
     913             : *
     914             : **/
     915             : /* Checks if no point of Geometry B is outside Geometry A */
     916             : str
     917           0 : wkbCoversGeographic(bit *out, wkb * const *a, wkb * const *b)
     918             : {
     919           0 :         str err = MAL_SUCCEED;
     920           0 :         GEOSGeom ga, gb;
     921           0 :         err = wkbGetCompatibleGeometries(a, b, &ga, &gb);
     922           0 :         if (ga && gb)
     923           0 :                 (*out) = geoCoversInternal(ga, gb);
     924             : 
     925           0 :         GEOSGeom_destroy_r(geoshandle, ga);
     926           0 :         GEOSGeom_destroy_r(geoshandle, gb);
     927             : 
     928           0 :         return err;
     929             : }
     930             : 
     931             : /**
     932             :  * FILTER FUNCTIONS
     933             :  **/
     934             : 
     935             : static inline bit
     936           0 : geosDistanceWithin (GEOSGeom geom1, GEOSGeom geom2, dbl distance_within) {
     937           0 :         dbl distance = geoDistanceInternal(geom1, geom2, distance_within);
     938           0 :         return distance <= distance_within;
     939             : }
     940             : 
     941             : //TODO Change BUNappend with manual insertion into the result BAT
     942             : static str
     943           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)
     944             : {
     945           0 :         BAT *out = NULL, *b = NULL, *s = NULL;
     946           0 :         BATiter b_iter;
     947           0 :         struct canditer ci;
     948           0 :         GEOSGeom col_geom, const_geom;
     949             : 
     950             :         //Check if the geometry is null and convert to GEOS
     951           0 :         if ((const_geom = wkb2geos(wkb_const)) == NULL) {
     952           0 :                 if ((out = BATdense(0, 0, 0)) == NULL)
     953           0 :                         throw(MAL, name, GDK_EXCEPTION);
     954           0 :                 *outid = out->batCacheid;
     955           0 :                 BBPkeepref(out);
     956           0 :                 return MAL_SUCCEED;
     957             :         }
     958             :         //get the BATs
     959           0 :         if ((b = BATdescriptor(*bid)) == NULL)
     960           0 :                 throw(MAL, name, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
     961             :         //get the candidate lists
     962           0 :         if (sid && !is_bat_nil(*sid) && !(s = BATdescriptor(*sid))) {
     963           0 :                 BBPunfix(b->batCacheid);
     964           0 :                 throw(MAL, name, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
     965             :         }
     966           0 :         canditer_init(&ci, b, s);
     967             :         //create a new BAT for the output
     968           0 :         if ((out = COLnew(0, ATOMindex("oid"), ci.ncand, TRANSIENT)) == NULL) {
     969           0 :                 BBPunfix(b->batCacheid);
     970           0 :                 if (s)
     971           0 :                         BBPunfix(s->batCacheid);
     972           0 :                 throw(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
     973             :         }
     974           0 :         b_iter = bat_iterator(b);
     975             :         //Loop through column and compare with constant
     976           0 :         for (BUN i = 0; i < ci.ncand; i++) {
     977           0 :                 oid c_oid = canditer_next(&ci);
     978           0 :                 const wkb *col_wkb = BUNtvar(b_iter, c_oid - b->hseqbase);
     979           0 :                 if ((col_geom = wkb2geos(col_wkb)) == NULL)
     980           0 :                         continue;
     981           0 :                 if (GEOSGetSRID_r(geoshandle, col_geom) != GEOSGetSRID_r(geoshandle, const_geom)) {
     982           0 :                         GEOSGeom_destroy_r(geoshandle, col_geom);
     983           0 :                         GEOSGeom_destroy_r(geoshandle, const_geom);
     984           0 :                         bat_iterator_end(&b_iter);
     985           0 :                         BBPunfix(b->batCacheid);
     986           0 :                         if (s)
     987           0 :                                 BBPunfix(s->batCacheid);
     988           0 :                         BBPreclaim(out);
     989           0 :                         throw(MAL, name, SQLSTATE(38000) "Geometries of different SRID");
     990             :                 }
     991             :                 //Apply the (Geom, Geom, double) -> bit function
     992           0 :                 bit cond = (*func)(col_geom, const_geom, double_flag);
     993           0 :                 if (cond != anti) {
     994           0 :                         if (BUNappend(out, &c_oid, false) != GDK_SUCCEED) {
     995           0 :                                 if (col_geom)
     996           0 :                                         GEOSGeom_destroy_r(geoshandle, col_geom);
     997           0 :                                 if (const_geom)
     998           0 :                                         GEOSGeom_destroy_r(geoshandle, const_geom);
     999           0 :                                 bat_iterator_end(&b_iter);
    1000           0 :                                 BBPunfix(b->batCacheid);
    1001           0 :                                 if (s)
    1002           0 :                                         BBPunfix(s->batCacheid);
    1003           0 :                                 BBPreclaim(out);
    1004           0 :                                 throw(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1005             :                         }
    1006             :                 }
    1007           0 :                 GEOSGeom_destroy_r(geoshandle, col_geom);
    1008             :         }
    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 :         *outid = out->batCacheid;
    1015           0 :         BBPkeepref(out);
    1016           0 :         return MAL_SUCCEED;
    1017             : }
    1018             : 
    1019             : static str
    1020           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)
    1021             : {
    1022           0 :         BAT *lres = NULL, *rres = NULL, *l = NULL, *r = NULL, *ls = NULL, *rs = NULL;
    1023           0 :         BATiter l_iter, r_iter;
    1024           0 :         str msg = MAL_SUCCEED;
    1025           0 :         struct canditer l_ci, r_ci;
    1026           0 :         GEOSGeom l_geom, r_geom;
    1027           0 :         GEOSGeom *l_geoms = NULL, *r_geoms = NULL;
    1028           0 :         BUN est;
    1029             : 
    1030             :         //get the input BATs
    1031           0 :         l = BATdescriptor(*l_id);
    1032           0 :         r = BATdescriptor(*r_id);
    1033           0 :         if (l == NULL || r == NULL) {
    1034           0 :                 if (l)
    1035           0 :                         BBPunfix(l->batCacheid);
    1036           0 :                 if (r)
    1037           0 :                         BBPunfix(r->batCacheid);
    1038           0 :                 throw(MAL, name, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
    1039             :         }
    1040             :         //get the candidate lists
    1041           0 :         if (ls_id && !is_bat_nil(*ls_id) && !(ls = BATdescriptor(*ls_id)) && rs_id && !is_bat_nil(*rs_id) && !(rs = BATdescriptor(*rs_id))) {
    1042           0 :                 msg = createException(MAL, name, SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
    1043           0 :                 goto free;
    1044             :         }
    1045           0 :         canditer_init(&l_ci, l, ls);
    1046           0 :         canditer_init(&r_ci, r, rs);
    1047             :         //create new BATs for the output
    1048           0 :         est = is_lng_nil(*estimate) || *estimate == 0 ? l_ci.ncand : (BUN) *estimate;
    1049           0 :         if ((lres = COLnew(0, ATOMindex("oid"), est, TRANSIENT)) == NULL) {
    1050           0 :                 msg = createException(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1051           0 :                 goto free;
    1052             :         }
    1053           0 :         if ((rres = COLnew(0, ATOMindex("oid"), est, TRANSIENT)) == NULL) {
    1054           0 :                 msg = createException(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1055           0 :                 goto free;
    1056             :         }
    1057             : 
    1058             :         //Allocate arrays for reutilizing GEOS type conversion
    1059           0 :         if ((l_geoms = GDKmalloc(l_ci.ncand * sizeof(GEOSGeometry *))) == NULL || (r_geoms = GDKmalloc(r_ci.ncand * sizeof(GEOSGeometry *))) == NULL) {
    1060           0 :                 msg = createException(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1061           0 :                 goto free;
    1062             :         }
    1063             : 
    1064           0 :         l_iter = bat_iterator(l);
    1065           0 :         r_iter = bat_iterator(r);
    1066             : 
    1067             :         //Convert wkb to GEOS only once
    1068           0 :         for (BUN i = 0; i < l_ci.ncand; i++) {
    1069           0 :                 oid l_oid = canditer_next(&l_ci);
    1070           0 :                 l_geoms[i] = wkb2geos((const wkb*) BUNtvar(l_iter, l_oid - l->hseqbase));
    1071             :         }
    1072           0 :         for (BUN j = 0; j < r_ci.ncand; j++) {
    1073           0 :                 oid r_oid = canditer_next(&r_ci);
    1074           0 :                 r_geoms[j] = wkb2geos((const wkb*)BUNtvar(r_iter, r_oid - r->hseqbase));
    1075             :         }
    1076             : 
    1077           0 :         canditer_reset(&l_ci);
    1078           0 :         for (BUN i = 0; i < l_ci.ncand; i++) {
    1079           0 :                 oid l_oid = canditer_next(&l_ci);
    1080           0 :                 l_geom = l_geoms[i];
    1081           0 :                 if (!nil_matches && l_geom == NULL)
    1082           0 :                         continue;
    1083           0 :                 canditer_reset(&r_ci);
    1084           0 :                 for (BUN j = 0; j < r_ci.ncand; j++) {
    1085           0 :                         oid r_oid = canditer_next(&r_ci);
    1086           0 :                         r_geom = r_geoms[j];
    1087             :                         //Null handling
    1088           0 :                         if (r_geom == NULL) {
    1089           0 :                                 if (nil_matches && l_geom == NULL) {
    1090           0 :                                         if (BUNappend(lres, &l_oid, false) != GDK_SUCCEED || BUNappend(rres, &r_oid, false) != GDK_SUCCEED) {
    1091           0 :                                                 msg = createException(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1092           0 :                                                 bat_iterator_end(&l_iter);
    1093           0 :                                                 bat_iterator_end(&r_iter);
    1094           0 :                                                 goto free;
    1095             :                                         }
    1096             :                                 }
    1097             :                                 else
    1098           0 :                                         continue;
    1099             :                         }
    1100             :                         //TODO Do we need to do this check for every element?
    1101           0 :                         if (GEOSGetSRID_r(geoshandle, l_geom) != GEOSGetSRID_r(geoshandle, r_geom)) {
    1102           0 :                                 msg = createException(MAL, name, SQLSTATE(38000) "Geometries of different SRID");
    1103           0 :                                 bat_iterator_end(&l_iter);
    1104           0 :                                 bat_iterator_end(&r_iter);
    1105           0 :                                 goto free;
    1106             :                         }
    1107             :                         //Apply the (Geom, Geom) -> bit function
    1108           0 :                         bit cond = (*func)(l_geom, r_geom, double_flag);
    1109           0 :                         if (cond != anti) {
    1110           0 :                                 if (BUNappend(lres, &l_oid, false) != GDK_SUCCEED || BUNappend(rres, &r_oid, false) != GDK_SUCCEED) {
    1111           0 :                                         msg = createException(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1112           0 :                                         bat_iterator_end(&l_iter);
    1113           0 :                                         bat_iterator_end(&r_iter);
    1114           0 :                                         goto free;
    1115             :                                 }
    1116             :                         }
    1117             :                 }
    1118             :         }
    1119             :         if (l_geoms) {
    1120           0 :                 for (BUN i = 0; i < l_ci.ncand; i++) {
    1121           0 :                         GEOSGeom_destroy_r(geoshandle, l_geoms[i]);
    1122             :                 }
    1123           0 :                 GDKfree(l_geoms);
    1124             :         }
    1125           0 :         if (r_geoms) {
    1126           0 :                 for (BUN i = 0; i < r_ci.ncand; i++) {
    1127           0 :                         GEOSGeom_destroy_r(geoshandle, r_geoms[i]);
    1128             :                 }
    1129           0 :                 GDKfree(r_geoms);
    1130             :         }
    1131           0 :         bat_iterator_end(&l_iter);
    1132           0 :         bat_iterator_end(&r_iter);
    1133           0 :         BBPunfix(l->batCacheid);
    1134           0 :         BBPunfix(r->batCacheid);
    1135           0 :         if (ls)
    1136           0 :                 BBPunfix(ls->batCacheid);
    1137           0 :         if (rs)
    1138           0 :                 BBPunfix(rs->batCacheid);
    1139           0 :         *lres_id = lres->batCacheid;
    1140           0 :         BBPkeepref(lres);
    1141           0 :         *rres_id = rres->batCacheid;
    1142           0 :         BBPkeepref(rres);
    1143           0 :         return MAL_SUCCEED;
    1144           0 : free:
    1145           0 :         if (l_geoms) {
    1146           0 :                 for (BUN i = 0; i < l_ci.ncand; i++) {
    1147           0 :                         GEOSGeom_destroy_r(geoshandle, l_geoms[i]);
    1148             :                 }
    1149           0 :                 GDKfree(l_geoms);
    1150             :         }
    1151           0 :         if (r_geoms) {
    1152           0 :                 for (BUN i = 0; i < r_ci.ncand; i++) {
    1153           0 :                         GEOSGeom_destroy_r(geoshandle, r_geoms[i]);
    1154             :                 }
    1155           0 :                 GDKfree(r_geoms);
    1156             :         }
    1157           0 :         BBPunfix(l->batCacheid);
    1158           0 :         BBPunfix(r->batCacheid);
    1159           0 :         if (ls)
    1160           0 :                 BBPunfix(ls->batCacheid);
    1161           0 :         if (rs)
    1162           0 :                 BBPunfix(rs->batCacheid);
    1163           0 :         if (lres)
    1164           0 :                 BBPreclaim(lres);
    1165           0 :         if (rres)
    1166           0 :                 BBPreclaim(rres);
    1167             :         return msg;
    1168             : }
    1169             : 
    1170             : str
    1171           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) {
    1172           0 :         double distance_within = 0;
    1173           0 :         BAT *d = NULL;
    1174             :         //Get the distance BAT and get the double value
    1175           0 :         if ((d = BATdescriptor(*d_id)) == NULL) {
    1176           0 :                 throw(MAL, "geom.wkbDWithinGeographicJoin", SQLSTATE(HY002) RUNTIME_OBJECT_MISSING);
    1177             :         }
    1178           0 :         if (BATcount(d) == 1) {
    1179           0 :                 distance_within = *((double*) Tloc(d, 0));
    1180             :         }
    1181           0 :         BBPunfix(d->batCacheid);
    1182           0 :         return filterJoinGeomGeomDoubleToBit(lres_id,rres_id,l_id,r_id,distance_within,ls_id,rs_id,*nil_matches,estimate,*anti,geosDistanceWithin,"geom.wkbDWithinGeographicJoin");
    1183             : }
    1184             : 
    1185             : str
    1186           0 : wkbDWithinGeographicSelect(bat* outid, const bat *bid , const bat *sid, wkb * const *wkb_const, const dbl *distance_within, const bit *anti) {
    1187           0 :         return filterSelectGeomGeomDoubleToBit(outid,bid,sid,*wkb_const,*distance_within,*anti,geosDistanceWithin,"geom.wkbDWithinGeographicSelect");
    1188             : }
    1189             : 
    1190             : str
    1191           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) {
    1192           0 :         return filterJoinGeomGeomDoubleToBit(lres_id,rres_id,l_id,r_id,0,ls_id,rs_id,*nil_matches,estimate,*anti,geosDistanceWithin,"geom.wkbIntersectsGeographicJoin");
    1193             : }
    1194             : 
    1195             : str
    1196           0 : wkbIntersectsGeographicSelect(bat* outid, const bat *bid , const bat *sid, wkb * const *wkb_const, const bit *anti) {
    1197           0 :         return filterSelectGeomGeomDoubleToBit(outid,bid,sid,*wkb_const,0,*anti,geosDistanceWithin,"geom.wkbIntersectsGeographicSelect");
    1198             : }
    1199             : 
    1200             : static inline CartPoint3D
    1201           0 : crossProduct (const CartPoint3D *p1, const CartPoint3D *p2)
    1202             : {
    1203           0 :         CartPoint3D p3;
    1204           0 :         p3.x = p1->y * p2->z - p1->z * p2->y;
    1205           0 :         p3.y = p1->z * p2->x - p1->x * p2->z;
    1206           0 :         p3.z = p1->x * p2->y - p1->y * p2->x;
    1207           0 :         return p3;
    1208             : }
    1209             : 
    1210             : static inline double
    1211           0 : dotProduct (const CartPoint3D *p1, const CartPoint3D *p2)
    1212             : {
    1213           0 :         return (p1->x * p2->x) + (p1->y * p2->y) + (p1->z * p2->z);
    1214             : }
    1215             : 
    1216             : //
    1217             : static inline int
    1218           0 : angleRotation (const CartPoint2D p1, const CartPoint2D p2, const CartPoint2D p3)
    1219             : {
    1220             :         // vector P = P1P2
    1221             :         // vector Q = P1P3
    1222             :         // side = || Q x P ||
    1223             :         // Q and P are 2D vectors, so z component is 0
    1224           0 :         double side = ((p3.x - p1.x) * (p2.y - p1.y) - (p2.x - p1.x) * (p3.y - p1.y));
    1225             :         //
    1226           0 :         if (side < 0)
    1227             :                 return -1;
    1228           0 :         else if (side > 0)
    1229             :                 return 1;
    1230             :         else
    1231           0 :                 return 0;
    1232             : }
    1233             : 
    1234             : static void
    1235           0 : normalize2D (CartPoint2D *p) {
    1236           0 :         double c = sqrt(p->x * p->x + p->y * p->y);
    1237           0 :         if (c == 0) {
    1238           0 :                 p->x = p->y = 0;
    1239           0 :                 return;
    1240             :         }
    1241           0 :         p->x = p->x / c;
    1242           0 :         p->y = p->y / c;
    1243             : }
    1244             : 
    1245             : static void
    1246           0 : normalize3D (CartPoint3D *p) {
    1247           0 :         double c = sqrt(p->x * p->x + p->y * p->y + p->z * p->z);
    1248           0 :         if (c == 0) {
    1249           0 :                 p->x = p->y = p->z = 0;
    1250           0 :                 return;
    1251             :         }
    1252           0 :         p->x = p->x / c;
    1253           0 :         p->y = p->y / c;
    1254           0 :         p->z = p->z / c;
    1255             : }
    1256             : 
    1257             : static inline bool
    1258           0 : FP_EQUALS (double x, double y)
    1259             : {
    1260           0 :         return fabs(x-y) < 1e-12;
    1261             : }
    1262             : 
    1263             : str
    1264           0 : geodeticEdgeBoundingBox(const CartPoint3D* p1, const CartPoint3D* p2, BoundingBox* mbox)
    1265             : {
    1266           0 :         CartPoint3D p3, pn, ep3d;
    1267           0 :         CartPoint3D e[6];
    1268           0 :         CartPoint2D s1, s2, ep, o;
    1269           0 :         int rotation_to_origin, rotation_to_ep;
    1270             : 
    1271             :     // check coinciding points
    1272           0 :         if (FP_EQUALS(p1->x,p2->x) && FP_EQUALS(p1->y,p2->y) && FP_EQUALS(p1->z,p2->z))
    1273             :                 return MAL_SUCCEED;
    1274             :     // check antipodal points
    1275           0 :         if (FP_EQUALS(p1->x,-p2->x) && FP_EQUALS(p1->y,-p2->y) && FP_EQUALS(p1->z,-p2->z))
    1276           0 :                 throw(MAL, "geom.geodeticEdgeBoundingBox", SQLSTATE(38000) "Antipodal edge");
    1277             : 
    1278             :     // create the great circle plane coord system (p1, p3)
    1279             :     // pn = p1 x p2
    1280             :     // p3 = pn x p1
    1281             :         // TODO handle the narrow and wide angle cases
    1282           0 :         pn = crossProduct(p1,p2);
    1283           0 :         normalize3D(&pn);
    1284           0 :         p3 = crossProduct(&pn,p1);
    1285           0 :         normalize3D(&p3);
    1286             : 
    1287             :     // represent p1, p2 with (s1, s2) 2-D space
    1288             :     // s1.x = 1, s1.y = 0
    1289             :     // s2.x = p2 * p1, s2.y = p2 * p3
    1290           0 :         s1.x = 1;
    1291           0 :         s1.y = 0;
    1292           0 :         s2.x = dotProduct(p2, p1);
    1293           0 :         s2.y = dotProduct(p2, &p3);
    1294             :     // 2-D space origin
    1295             :     // O.x = 0, O.y = 0
    1296           0 :         o.x = 0;
    1297           0 :         o.y = 0;
    1298             : 
    1299             :     // create 3D endpoints E.x, E.-x, ...
    1300             :     // E.x = (1, 0, 0), E.-x = (-1, 0, 0) ...
    1301           0 :         memset(e, 0, sizeof(CartPoint2D) * 6);
    1302           0 :         e[0].x = e[1].y = e[2].z = 1;
    1303           0 :         e[3].x = e[4].y = e[5].z = -1;
    1304             : 
    1305             :     // find the rotation between s1->s2 and s1->O
    1306             :     // rot = norm( vec(s1,s2) x vec(s1,0))
    1307           0 :         rotation_to_origin = angleRotation(s1,s2,o);
    1308             : 
    1309             :     // for every endpoint E
    1310           0 :         for (int i = 0; i < 6; i++) {
    1311             :                 // project the endpoint in the 2-D space
    1312           0 :                 ep.x = dotProduct(&e[i],p1);
    1313           0 :                 ep.y = dotProduct(&e[i],&p3);
    1314             :         // re-normalize it e.g. EP (for endpoint_projection)
    1315           0 :                 normalize2D(&ep);
    1316             :         // ep_rot = norm( vec(s1,s2) x vec(s1,EP_end) )
    1317           0 :                 rotation_to_ep = angleRotation(s1,s2,ep);
    1318           0 :                 if (rotation_to_origin != rotation_to_ep) {
    1319             :                         // convert the 2-D EP into 3-D space
    1320           0 :                         ep3d.x = ep.x * p1->x + ep.y * p3.x;
    1321           0 :                         ep3d.y = ep.x * p1->y + ep.y * p3.y;
    1322           0 :                         ep3d.z = ep.x * p1->z + ep.y * p3.z;
    1323             :             // expand the mbox in order to include 3-D representation of EP
    1324           0 :                         boundingBoxAddPoint(mbox,ep3d);
    1325             :                 }
    1326             :         }
    1327             :         return MAL_SUCCEED;
    1328             : }

Generated by: LCOV version 1.14