Changeset: 79f196ea8e2c for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB/rev/79f196ea8e2c
Modified Files:
        geom/monetdb5/geom.c
        geom/monetdb5/geom.h
Branch: geo-update
Log Message:

Added GeoPolygon data type to also represent the interior rings of a Polygon. 
Added GeoPolygon to all Polygon functions in geom.c, corrected the 
pointInPolygon error of false positives if the point was within a polygon hole.


diffs (truncated from 516 to 300 lines):

diff --git a/geom/monetdb5/geom.c b/geom/monetdb5/geom.c
--- a/geom/monetdb5/geom.c
+++ b/geom/monetdb5/geom.c
@@ -174,7 +174,7 @@ static GeoPoint geoPointFromGeom(GEOSGeo
 }
 
 /* Converts the a GEOSGeom Line into GeoLines (one or more line segments) 
-   Argument must be a Line, use geoLinesFromGeomPolygon for Polygons. */
+   Argument must be a Line geometry. */
 static GeoLines geoLinesFromGeom(GEOSGeom geom)
 {
        const GEOSCoordSequence *gcs = GEOSGeom_getCoordSeq(geom);
@@ -188,13 +188,33 @@ static GeoLines geoLinesFromGeom(GEOSGeo
                GEOSCoordSeq_getXY(gcs, i, &geo.segments[i].start.lon, 
&geo.segments[i].start.lat);
                GEOSCoordSeq_getXY(gcs, i + 1, &geo.segments[i].end.lon, 
&geo.segments[i].end.lat);
        }
+       //TODO Calculate Boundind Box on initializion?
        geo.bbox = NULL;
        return geo;
 }
 
-static GeoLines geoLinesFromGeomPolygon(GEOSGeom geom)
-{
-       return geoLinesFromGeom((GEOSGeom)GEOSGetExteriorRing(geom));
+/* Converts the a GEOSGeom Line into GeoPolygon (with exterior ring and 
zero-to-multiple interior rings) 
+   Argument must be a Polygon geometry. */
+static GeoPolygon geoPolygonFromGeom(GEOSGeom geom)
+{
+       GeoPolygon geo;
+       //TODO Calculate Boundind Box on initializion?
+       geo.bbox = NULL;
+       //Get exterior ring GeoLines
+       geo.exteriorRing = 
geoLinesFromGeom((GEOSGeom)GEOSGetExteriorRing(geom));
+       geo.interiorRingsCount = GEOSGetNumInteriorRings(geom);
+       //If there are interior rings, allocate space to their GeoLines 
representation
+       if (geo.interiorRingsCount > 0)
+       {
+               //TODO Malloc fail exception?
+               geo.interiorRings = GDKmalloc(sizeof(GeoLines) * 
geo.interiorRingsCount);
+       }
+       //Get interior rings GeoLines
+       for (int i = 0; i < geo.interiorRingsCount; i++)
+       {
+               geo.interiorRings[i] = 
geoLinesFromGeom((GEOSGeom)GEOSGetInteriorRingN(geom, i));
+       }
+       return geo;
 }
 
 static GeoPoint geoPointFromLatLon(double lon, double lat)
@@ -305,7 +325,7 @@ static void boundingBoxAddPoint(Bounding
 }
 
 /* Builds the BoundingBox for a GeoLines geometry */
-static BoundingBox* boundingBoxLines(GeoLines lines)
+static BoundingBox *boundingBoxLines(GeoLines lines)
 {
        CartPoint c;
        BoundingBox *bb = GDKzalloc(sizeof(BoundingBox));
@@ -336,8 +356,9 @@ static int boundingBoxContainsPoint(Boun
        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;
 }
 
-static BoundingBox boundingBoxCopy (BoundingBox bb) {
-       BoundingBox* copy = GDKmalloc(sizeof(BoundingBox));
+static BoundingBox boundingBoxCopy(BoundingBox bb)
+{
+       BoundingBox *copy = GDKmalloc(sizeof(BoundingBox));
        copy->xmin = bb.xmin;
        copy->xmax = bb.xmax;
        copy->ymin = bb.ymin;
@@ -348,14 +369,16 @@ static BoundingBox boundingBoxCopy (Boun
 }
 
 /* Returns a point outside of the polygon's bounding box, for Point-In-Polygon 
calculation */
-static GeoPoint pointOutsidePolygon(GeoLines polygonRing)
+static GeoPoint pointOutsidePolygon(GeoPolygon polygon)
 {
        //If the geometry doesn't have its BoundingBox calculated, calculate it
-       if (polygonRing.bbox == NULL) {
-               polygonRing.bbox = boundingBoxLines(polygonRing);
-       }
-       BoundingBox bb = *polygonRing.bbox;
-       BoundingBox bb2 = boundingBoxCopy(*polygonRing.bbox);
+       //TODO Should we consider the interior rings for bounding box 
calculation?
+       if (polygon.bbox == NULL)
+       {
+               polygon.bbox = boundingBoxLines(polygon.exteriorRing);
+       }
+       BoundingBox bb = *polygon.bbox;
+       BoundingBox bb2 = boundingBoxCopy(*polygon.bbox);
 
        //TODO: From POSTGIS -> CHANGE
        double grow = M_PI / 180.0 / 60.0;
@@ -417,6 +440,7 @@ static GeoPoint pointOutsidePolygon(GeoL
                }
                grow *= 2.0;
        }
+       //TODO: Should this be the return value in case no point is found?
        return geoPointFromLatLon(0, 0);
 }
 
@@ -545,53 +569,39 @@ static double geoDistanceLineLine(GeoLin
 }
 
 //TODO Implement intersection ourselves so we don't use GEOS?
-//TODO This still produces some false positives -> Is it only for Polygons 
where the Point is in a hole?
 /* Checks if a Point is within a Polygon */
-static bool pointWithinPolygon(GeoLines polygonRing,GeoPoint point)
+static bool pointWithinPolygon(GeoPolygon polygon, GeoPoint point)
 {
        int intersectionNum = 0;
        GEOSGeometry *segmentPolygon, *intersectionPoints;
+       GeoLines polygonRing;
 
        //Get an point that's outside the polygon
-       GeoPoint outsidePoint = pointOutsidePolygon(polygonRing);
+       GeoPoint outsidePoint = pointOutsidePolygon(polygon);
 
        //No outside point was found, return false
-       if (outsidePoint.lat == 0 && outsidePoint.lon == 0) {
+       if (outsidePoint.lat == 0 && outsidePoint.lon == 0)
+       {
                return false;
        }
-       
+
        /*printf("Outside point: (%f %f)\n",outsidePoint.lon, outsidePoint.lat);
        fflush(stdout);*/
 
        //Construct a line between the outside point and the input point
        GEOSGeometry *outInLine = cartesianLineFromGeoPoints(point, 
outsidePoint);
 
-       //Count the number of intersections between the polygon and the 
constructed line
+       //Count the number of intersections between the polygon exterior ring 
and the constructed line
+       polygonRing = polygon.exteriorRing;
        for (int i = 0; i < polygonRing.segmentCount; i++)
        {
-               
                segmentPolygon = 
cartesianLineFromGeoPoints(polygonRing.segments[i].start, 
polygonRing.segments[i].end);
                intersectionPoints = GEOSIntersection(segmentPolygon, 
outInLine);
-               //printf("Segment (%d): (%f %f)->(%f 
%f)\n",i,polygonRing.segments[i].start.lon, polygonRing.segments[i].start.lat, 
polygonRing.segments[i].end.lon, polygonRing.segments[i].end.lat);
 
                //If there is an intersection, a point will be returned (line 
when there is none)
                if (GEOSGeomTypeId(intersectionPoints) == GEOS_POINT)
                {
                        intersectionNum++;
-                       /*CartPoint pDegrees;
-                       GeoPoint pRadians;
-                       double x, y, z;
-                       GEOSGeomGetX(intersectionPoints, &x);
-                       GEOSGeomGetY(intersectionPoints, &y);
-                       GEOSGeomGetZ(intersectionPoints, &z);
-                       pDegrees.x = x;
-                       pDegrees.y = y;
-                       pDegrees.z = z;
-                       pRadians = rad2DegPoint(cart2geo(pDegrees));
-                       printf("Intersection Num %d on Segment Num %d\n", 
intersectionNum,i);
-                       printf("Intersection Point (Degrees) (%f %f)\n", 
pRadians.lon, pRadians.lat);
-                       printf("Line (%f %f)->(%f %f)\nLine (%f %f)->(%f 
%f)\n", outsidePoint.lon, outsidePoint.lat, point.lon, point.lat, 
polygonRing.segments[i].start.lon, polygonRing.segments[i].start.lat, 
polygonRing.segments[i].end.lon, polygonRing.segments[i].end.lat);
-                       fflush(stdout);*/
                }
 
                if (intersectionPoints != NULL)
@@ -599,6 +609,29 @@ static bool pointWithinPolygon(GeoLines 
                if (segmentPolygon != NULL)
                        GEOSGeom_destroy(segmentPolygon);
        }
+
+       //Count the number of intersections between the polygon interior rings 
and the constructed line
+       for (int j = 0; j < polygon.interiorRingsCount; j++)
+       {
+               polygonRing = polygon.interiorRings[j];
+               for (int i = 0; i < polygonRing.segmentCount; i++)
+               {
+                       segmentPolygon = 
cartesianLineFromGeoPoints(polygonRing.segments[i].start, 
polygonRing.segments[i].end);
+                       intersectionPoints = GEOSIntersection(segmentPolygon, 
outInLine);
+
+                       //If there is an intersection, a point will be returned 
(line when there is none)
+                       if (GEOSGeomTypeId(intersectionPoints) == GEOS_POINT)
+                       {
+                               intersectionNum++;
+                       }
+
+                       if (intersectionPoints != NULL)
+                               GEOSGeom_destroy(intersectionPoints);
+                       if (segmentPolygon != NULL)
+                               GEOSGeom_destroy(segmentPolygon);
+               }
+       }
+
        if (outInLine != NULL)
                GEOSGeom_destroy(outInLine);
 
@@ -607,27 +640,35 @@ static bool pointWithinPolygon(GeoLines 
 }
 
 /* Distance between Point and Polygon.*/
-static double geoDistancePointPolygon(GeoPoint point, GeoLines polygonRing)
+static double geoDistancePointPolygon(GeoPoint point, GeoPolygon polygon)
 {
        //Check if point is in polygon
-       if (pointWithinPolygon(polygonRing,point))
-       {
+       if (pointWithinPolygon(polygon, point))
                return 0;
-       }
-
-       //Compare Point to the various polygon segments
-       return geoDistancePointLine(point, polygonRing);
+
+       //Calculate distance from Point to the exterior and interior rings of 
the polygon
+       double distance, min_distance = INT_MAX;
+       //First, calculate distance to the exterior ring
+       min_distance = geoDistancePointLine(point, polygon.exteriorRing);
+       //Then, calculate distance to the interior rings
+       for (int i = 0; i < polygon.interiorRingsCount; i++)
+       {
+               distance = geoDistancePointLine(point, 
polygon.interiorRings[i]);
+               if (distance < min_distance)
+                       min_distance = distance;
+       }
+       return min_distance;
 }
 
 /* Distance between Line and Polygon. */
-static double geoDistanceLinePolygon(GeoLines line, GeoLines polygon)
+static double geoDistanceLinePolygon(GeoLines line, GeoPolygon polygon)
 {
        double distance, min_distance = INT_MAX;
        //Calculate distance to all start vertices of the line
        for (int i = 0; i < line.segmentCount; i++)
        {
                distance = geoDistancePointPolygon(line.segments[i].start, 
polygon);
-               
+
                //Short-cut in case the point is within the polygon
                if (distance == 0)
                        return 0;
@@ -637,19 +678,18 @@ static double geoDistanceLinePolygon(Geo
        }
        //Calculate distance to the last vertice (not covered by the previous 
loop)
        distance = geoDistancePointPolygon(line.segments[line.segmentCount - 
1].end, polygon);
-       if (distance < min_distance)
-               min_distance = distance;
-       return min_distance;
+       return distance < min_distance ? distance : min_distance;
 }
 
 /* Distance between two Polygons. */
-static double geoDistancePolygonPolygon(GeoLines polygon1, GeoLines polygon2)
+//TODO Does this calculate the minimum distance between the interior rings as 
well? We are comparing the exterior ring of each with all segments of the other.
+static double geoDistancePolygonPolygon(GeoPolygon polygon1, GeoPolygon 
polygon2)
 {
        double distance1, distance2;
-       //Calculate the distance between all vertices of polygon1 and segments 
of polygon2
-       distance1 = geoDistanceLinePolygon(polygon1, polygon2);
-       //Calculate the distance between all vertices of polygon2 and segments 
of polygon1
-       distance2 = geoDistanceLinePolygon(polygon2, polygon1);
+       //Calculate the distance between the exterior ring of polygon1 and all 
segments of polygon2 (including the interior rings)
+       distance1 = geoDistanceLinePolygon(polygon1.exteriorRing, polygon2);
+       //Other way around
+       distance2 = geoDistanceLinePolygon(polygon2.exteriorRing, polygon1);
        //And return the minimum
        return distance1 < distance2 ? distance1 : distance2;
 }
@@ -683,27 +723,27 @@ static double geoDistanceSingle(GEOSGeom
        else if (dimA == 0 && dimB == 2)
        {
                /* Point and Polygon */
-               return geoDistancePointPolygon(geoPointFromGeom(a), 
geoLinesFromGeomPolygon(b));
+               return geoDistancePointPolygon(geoPointFromGeom(a), 
geoPolygonFromGeom(b));
        }
        else if (dimA == 2 && dimB == 0)
        {
                /* Polygon and Point */
-               return geoDistancePointPolygon(geoPointFromGeom(b), 
geoLinesFromGeomPolygon(a));
+               return geoDistancePointPolygon(geoPointFromGeom(b), 
geoPolygonFromGeom(a));
        }
        else if (dimA == 1 && dimB == 2)
        {
                /* Line/LinearRing and Polygon */
-               return geoDistanceLinePolygon(geoLinesFromGeom(a), 
geoLinesFromGeomPolygon(b));
+               return geoDistanceLinePolygon(geoLinesFromGeom(a), 
geoPolygonFromGeom(b));
        }
        else if (dimA == 2 && dimB == 1)
        {
                /* Polygon and Line/LinearRing */
-               return geoDistanceLinePolygon(geoLinesFromGeom(b), 
geoLinesFromGeomPolygon(a));
+               return geoDistanceLinePolygon(geoLinesFromGeom(b), 
geoPolygonFromGeom(a));
        }
        else if (dimA == 2 && dimB == 2)
        {
                /* Polygon and Polygon */
-               return geoDistancePolygonPolygon(geoLinesFromGeomPolygon(a), 
geoLinesFromGeomPolygon(b));
+               return geoDistancePolygonPolygon(geoPolygonFromGeom(a), 
geoPolygonFromGeom(b));
        }
        return INT_MAX;
 }
@@ -793,7 +833,8 @@ str wkbIntersectsGeographic(bit *out, wk
 }
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to