/* gg_advanced.c -- Gaia advanced geometric operations version 5.0, 2020 August 1 Author: Sandro Furieri a.furieri@lqt.it ------------------------------------------------------------------------------ Version: MPL 1.1/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is the SpatiaLite library The Initial Developer of the Original Code is Alessandro Furieri Portions created by the Initial Developer are Copyright (C) 2008-2021 the Initial Developer. All Rights Reserved. Contributor(s): Alternatively, the contents of this file may be used under the terms of either the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the LGPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of either the GPL or the LGPL, and not to allow others to use your version of this file under the terms of the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the MPL, the GPL or the LGPL. */ #include #include #include #include #include #include #if defined(_WIN32) && !defined(__MINGW32__) #include "config-msvc.h" #else #include "config.h" #endif #include #include #include #include #include GAIAGEO_DECLARE double gaiaMeasureLength (int dims, double *coords, int vert) { /* computes the total length */ double lung = 0.0; double xx1; double xx2; double yy1; double yy2; double x; double y; double z; double m; double dist; int ind; if (vert <= 0) return lung; if (dims == GAIA_XY_Z) { gaiaGetPointXYZ (coords, 0, &xx1, &yy1, &z); } else if (dims == GAIA_XY_M) { gaiaGetPointXYM (coords, 0, &xx1, &yy1, &m); } else if (dims == GAIA_XY_Z_M) { gaiaGetPointXYZM (coords, 0, &xx1, &yy1, &z, &m); } else { gaiaGetPoint (coords, 0, &xx1, &yy1); } for (ind = 1; ind < vert; ind++) { if (dims == GAIA_XY_Z) { gaiaGetPointXYZ (coords, ind, &xx2, &yy2, &z); } else if (dims == GAIA_XY_M) { gaiaGetPointXYM (coords, ind, &xx2, &yy2, &m); } else if (dims == GAIA_XY_Z_M) { gaiaGetPointXYZM (coords, ind, &xx2, &yy2, &z, &m); } else { gaiaGetPoint (coords, ind, &xx2, &yy2); } x = xx1 - xx2; y = yy1 - yy2; dist = sqrt ((x * x) + (y * y)); lung += dist; xx1 = xx2; yy1 = yy2; } return lung; } GAIAGEO_DECLARE double gaiaMeasureArea (gaiaRingPtr ring) { /* computes the area */ int iv; double xx; double yy; double x; double y; double z; double m; double area = 0.0; if (!ring) return 0.0; if (ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (ring->Coords, 0, &xx, &yy, &z); } else if (ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (ring->Coords, 0, &xx, &yy, &m); } else if (ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (ring->Coords, 0, &xx, &yy, &z, &m); } else { gaiaGetPoint (ring->Coords, 0, &xx, &yy); } for (iv = 1; iv < ring->Points; iv++) { if (ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (ring->Coords, iv, &x, &y, &z); } else if (ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (ring->Coords, iv, &x, &y, &m); } else if (ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (ring->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (ring->Coords, iv, &x, &y); } area += ((xx * y) - (x * yy)); xx = x; yy = y; } area /= 2.0; return fabs (area); } GAIAGEO_DECLARE void gaiaRingCentroid (gaiaRingPtr ring, double *rx, double *ry) { /* computes the simple ring centroid */ double cx = 0.0; double cy = 0.0; double xx; double yy; double x; double y; double z; double m; double coeff; double area; double term; int iv; if (!ring) { *rx = -DBL_MAX; *ry = -DBL_MAX; return; } area = gaiaMeasureArea (ring); coeff = 1.0 / (area * 6.0); if (ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (ring->Coords, 0, &xx, &yy, &z); } else if (ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (ring->Coords, 0, &xx, &yy, &m); } else if (ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (ring->Coords, 0, &xx, &yy, &z, &m); } else { gaiaGetPoint (ring->Coords, 0, &xx, &yy); } for (iv = 1; iv < ring->Points; iv++) { if (ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (ring->Coords, iv, &x, &y, &z); } else if (ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (ring->Coords, iv, &x, &y, &m); } else if (ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (ring->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (ring->Coords, iv, &x, &y); } term = (xx * y) - (x * yy); cx += (xx + x) * term; cy += (yy + y) * term; xx = x; yy = y; } *rx = fabs (cx * coeff); *ry = fabs (cy * coeff); } GAIAGEO_DECLARE void gaiaClockwise (gaiaRingPtr p) { /* determines clockwise or anticlockwise direction */ int ind; int ix; double xx; double yy; double x; double y; double z; double m; double area = 0.0; for (ind = 0; ind < p->Points; ind++) { if (p->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (p->Coords, ind, &xx, &yy, &z); } else if (p->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (p->Coords, ind, &xx, &yy, &m); } else if (p->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (p->Coords, ind, &xx, &yy, &z, &m); } else { gaiaGetPoint (p->Coords, ind, &xx, &yy); } ix = (ind + 1) % p->Points; if (p->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (p->Coords, ix, &x, &y, &z); } else if (p->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (p->Coords, ix, &x, &y, &m); } else if (p->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (p->Coords, ix, &x, &y, &z, &m); } else { gaiaGetPoint (p->Coords, ix, &x, &y); } area += ((xx * y) - (x * yy)); } area /= 2.0; if (area >= 0.0) p->Clockwise = 0; else p->Clockwise = 1; } GAIAGEO_DECLARE double gaiaCurvosityIndex (const void *p_cache, gaiaLinestringPtr ln, int extra_points) { /* calculates the Curvosity Index of some Linestring */ #ifndef OMIT_GEOS /* including GEOS */ double x; double y; double z; double m; int iv; int ov; gaiaGeomCollPtr geo = NULL; gaiaLinestringPtr refln; double refline_length; double line_length = gaiaMeasureLength (ln->DimensionModel, ln->Coords, ln->Points); /* building the Reference Line */ refln = gaiaAllocLinestring (2 + extra_points); /* inserting the first vertex of Line into the Reference Line */ iv = 0; ov = 0; if (ln->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z); } else if (ln->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m); } else if (ln->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (ln->Coords, iv, &x, &y); } gaiaSetPoint (refln->Coords, 0, x, y); ov++; if (extra_points > 0) { /* inserting all interpolated points into the Reference Line */ int i; double perc = 1.0 / (double) (extra_points + 1); double progr = perc; gaiaGeomCollPtr geo; gaiaGeomCollPtr result; gaiaPointPtr pt; if (ln->DimensionModel == GAIA_XY_Z) geo = gaiaAllocGeomCollXYZ (); else if (ln->DimensionModel == GAIA_XY_M) geo = gaiaAllocGeomCollXYM (); else if (ln->DimensionModel == GAIA_XY_Z_M) geo = gaiaAllocGeomCollXYZM (); else geo = gaiaAllocGeomColl (); gaiaInsertLinestringInGeomColl (geo, ln); for (i = 0; i < extra_points; i++) { if (p_cache != NULL) result = gaiaLineInterpolatePoint_r (p_cache, geo, progr); else result = gaiaLineInterpolatePoint (geo, progr); if (result == NULL) goto error; pt = result->FirstPoint; if (pt == NULL) goto error; x = pt->X; y = pt->Y; gaiaFreeGeomColl (result); gaiaSetPoint (refln->Coords, ov, x, y); ov++; progr += perc; } geo->FirstLinestring = NULL; /* releasing ownership on Line */ geo->LastLinestring = NULL; gaiaFreeGeomColl (geo); } /* inserting the last vertex of Line into the Reference Line */ iv = ln->Points - 1; if (ln->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z); } else if (ln->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m); } else if (ln->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (ln->Coords, iv, &x, &y); } gaiaSetPoint (refln->Coords, ov, x, y); refline_length = gaiaMeasureLength (refln->DimensionModel, refln->Coords, refln->Points); return (refline_length / line_length); error: if (geo != NULL) { geo->FirstLinestring = NULL; /* releasing ownership on Line */ geo->LastLinestring = NULL; gaiaFreeGeomColl (geo); } #endif /* end including GEOS */ return -1.0; } GAIAGEO_DECLARE void gaiaUpDownHeight (gaiaLinestringPtr ln, double *up, double *down) { /* calculates the Uphill and Downhill Height of some Linestring */ double x; double y; double z; double m; int iv; double prev_z; double tot_up = 0.0; double tot_down = 0.0; /* inserting the last vertex of Line into the Reference Line */ if (ln->DimensionModel == GAIA_XY || ln->DimensionModel == GAIA_XY_M) { *up = 0.0; *down = 0.0; goto stop; } for (iv = 0; iv < ln->Points; iv++) { if (ln->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z); } else if (ln->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m); } if (iv == 0) { prev_z = z; continue; } if (z > prev_z) tot_up += z - prev_z; else tot_down += prev_z - z; prev_z = z; } stop: *up = tot_up; *down = tot_down; } GAIAGEO_DECLARE int gaiaIsPointOnRingSurface (gaiaRingPtr ring, double pt_x, double pt_y) { /* tests if a POINT falls inside a RING */ int isInternal = 0; int cnt; int i; int j; double x; double y; double z; double m; double *vert_x; double *vert_y; double minx = DBL_MAX; double miny = DBL_MAX; double maxx = -DBL_MAX; double maxy = -DBL_MAX; cnt = ring->Points; cnt--; /* ignoring last vertex because surely identical to the first one */ if (cnt < 2) return 0; /* allocating and loading an array of vertices */ vert_x = malloc (sizeof (double) * (cnt)); vert_y = malloc (sizeof (double) * (cnt)); for (i = 0; i < cnt; i++) { if (ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (ring->Coords, i, &x, &y, &z); } else if (ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (ring->Coords, i, &x, &y, &m); } else if (ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (ring->Coords, i, &x, &y, &z, &m); } else { gaiaGetPoint (ring->Coords, i, &x, &y); } vert_x[i] = x; vert_y[i] = y; if (x < minx) minx = x; if (x > maxx) maxx = x; if (y < miny) miny = y; if (y > maxy) maxy = y; } if (pt_x < minx || pt_x > maxx) goto end; /* outside the bounding box (x axis) */ if (pt_y < miny || pt_y > maxy) goto end; /* outside the bounding box (y axis) */ for (i = 0, j = cnt - 1; i < cnt; j = i++) { /* The definitive reference is "Point in Polyon Strategies" by / Eric Haines [Gems IV] pp. 24-46. / The code in the Sedgewick book Algorithms (2nd Edition, p.354) is / incorrect. */ if ((((vert_y[i] <= pt_y) && (pt_y < vert_y[j])) || ((vert_y[j] <= pt_y) && (pt_y < vert_y[i]))) && (pt_x < (vert_x[j] - vert_x[i]) * (pt_y - vert_y[i]) / (vert_y[j] - vert_y[i]) + vert_x[i])) isInternal = !isInternal; } end: free (vert_x); free (vert_y); return isInternal; } GAIAGEO_DECLARE double gaiaMinDistance (double x0, double y0, int dims, double *coords, int n_vert) { /* computing minimal distance between a POINT and a linestring/ring */ double x; double y; double z; double m; double ox; double oy; double lineMag; double u; double px; double py; double dist; double min_dist = DBL_MAX; int iv; if (n_vert < 2) return min_dist; /* not a valid linestring */ /* computing distance from first vertex */ ox = *(coords + 0); oy = *(coords + 1); min_dist = sqrt (((x0 - ox) * (x0 - ox)) + ((y0 - oy) * (y0 - oy))); for (iv = 1; iv < n_vert; iv++) { /* segment start-end coordinates */ if (dims == GAIA_XY_Z) { gaiaGetPointXYZ (coords, iv - 1, &ox, &oy, &z); gaiaGetPointXYZ (coords, iv, &x, &y, &z); } else if (dims == GAIA_XY_M) { gaiaGetPointXYM (coords, iv - 1, &ox, &oy, &m); gaiaGetPointXYM (coords, iv, &x, &y, &m); } else if (dims == GAIA_XY_Z_M) { gaiaGetPointXYZM (coords, iv - 1, &ox, &oy, &z, &m); gaiaGetPointXYZM (coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (coords, iv - 1, &ox, &oy); gaiaGetPoint (coords, iv, &x, &y); } /* computing distance from vertex */ dist = sqrt (((x0 - x) * (x0 - x)) + ((y0 - y) * (y0 - y))); if (dist < min_dist) min_dist = dist; /* computing a projection */ lineMag = ((x - ox) * (x - ox)) + ((y - oy) * (y - oy)); u = (((x0 - ox) * (x - ox)) + ((y0 - oy) * (y - oy))) / lineMag; if (u < 0.0 || u > 1.0) ; /* closest point does not fall within the line segment */ else { px = ox + u * (x - ox); py = oy + u * (y - oy); dist = sqrt (((x0 - px) * (x0 - px)) + ((y0 - py) * (y0 - py))); if (dist < min_dist) min_dist = dist; } } return min_dist; } GAIAGEO_DECLARE int gaiaIsPointOnPolygonSurface (gaiaPolygonPtr polyg, double x, double y) { /* tests if a POINT falls inside a POLYGON */ int ib; gaiaRingPtr ring = polyg->Exterior; if (gaiaIsPointOnRingSurface (ring, x, y)) { /* ok, the POINT falls inside the polygon */ for (ib = 0; ib < polyg->NumInteriors; ib++) { ring = polyg->Interiors + ib; if (gaiaIsPointOnRingSurface (ring, x, y)) { /* no, the POINT fall inside some hole */ return 0; } } return 1; } return 0; } GAIAGEO_DECLARE int gaiaIntersect (double *x0, double *y0, double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { /* computes intersection [if any] between two line segments / the intersection POINT has coordinates (x0, y0) / first line is identified by(x1, y1) and (x2, y2) / second line is identified by (x3, y3) and (x4, y4) */ double x; double y; double a1; double b1; double c1; double a2; double b2; double c2; double m1; double m2; double p; double det_inv; double minx1; double miny1; double maxx1; double maxy1; double minx2; double miny2; double maxx2; double maxy2; int ok1 = 0; int ok2 = 0; /* building line segment's MBRs */ if (x2 < x1) { minx1 = x2; maxx1 = x1; } else { minx1 = x1; maxx1 = x2; } if (y2 < y1) { miny1 = y2; maxy1 = y1; } else { miny1 = y1; maxy1 = y2; } if (x4 < x3) { minx2 = x4; maxx2 = x3; } else { minx2 = x3; maxx2 = x4; } if (y4 < y3) { miny2 = y4; maxy2 = y3; } else { miny2 = y3; maxy2 = y4; } /* checking MBRs first */ if (minx1 > maxx2) return 0; if (miny1 > maxy2) return 0; if (maxx1 < minx2) return 0; if (maxy1 < miny2) return 0; if (minx2 > maxx1) return 0; if (miny2 > maxy1) return 0; if (maxx2 < minx1) return 0; if (maxy2 < miny1) return 0; /* there is an MBRs intersection - proceeding */ if ((x2 - x1) != 0.0) m1 = (y2 - y1) / (x2 - x1); else m1 = DBL_MAX; if ((x4 - x3) != 0) m2 = (y4 - y3) / (x4 - x3); else m2 = DBL_MAX; if (m1 == m2) /* parallel lines */ return 0; if (m1 == DBL_MAX) c1 = y1; else c1 = (y1 - m1 * x1); if (m2 == DBL_MAX) c2 = y3; else c2 = (y3 - m2 * x3); if (m1 == DBL_MAX) { x = x1; p = m2 * x1; y = p + c2; /* first line is vertical */ goto check_bbox; } if (m2 == DBL_MAX) { x = x3; p = m1 * x3; y = p + c1; /* second line is vertical */ goto check_bbox; } a1 = m1; a2 = m2; b1 = -1; b2 = -1; det_inv = 1 / (a1 * b2 - a2 * b1); x = ((b1 * c2 - b2 * c1) * det_inv); y = ((a2 * c1 - a1 * c2) * det_inv); /* now checking if intersection falls within both segment boundaries */ check_bbox: if (x >= minx1 && x <= maxx1 && y >= miny1 && y <= maxy1) ok1 = 1; if (x >= minx2 && x <= maxx2 && y >= miny2 && y <= maxy2) ok2 = 1; if (ok1 && ok2) { /* intersection point falls within the segments */ *x0 = x; *y0 = y; return 1; } return 0; } GAIAGEO_DECLARE gaiaGeomCollPtr gaiaSanitize (gaiaGeomCollPtr geom) { /* / sanitizes a GEOMETRYCOLLECTION: / - repeated vertices are omitted / - ring closure is enforced anyway */ int iv; int ib; double x = 0.0; double y = 0.0; double z = 0.0; double m = 0.0; double last_x = 0.0; double last_y = 0.0; double last_z = 0.0; int points; gaiaPointPtr point; gaiaLinestringPtr line; gaiaLinestringPtr new_line; gaiaPolygonPtr polyg; gaiaPolygonPtr new_polyg; gaiaGeomCollPtr new_geom; gaiaRingPtr i_ring; gaiaRingPtr o_ring; if (!geom) return NULL; if (geom->DimensionModel == GAIA_XY_Z) new_geom = gaiaAllocGeomCollXYZ (); else if (geom->DimensionModel == GAIA_XY_M) new_geom = gaiaAllocGeomCollXYM (); else if (geom->DimensionModel == GAIA_XY_Z_M) new_geom = gaiaAllocGeomCollXYZM (); else new_geom = gaiaAllocGeomColl (); new_geom->Srid = geom->Srid; new_geom->DeclaredType = geom->DeclaredType; point = geom->FirstPoint; while (point) { /* copying POINTs */ gaiaAddPointToGeomCollXYZM (new_geom, point->X, point->Y, point->Z, point->M); point = point->Next; } line = geom->FirstLinestring; while (line) { /* sanitizing LINESTRINGs */ points = 0; for (iv = 0; iv < line->Points; iv++) { /* PASS I - checking points */ z = 0.0; m = 0.0; if (line->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (line->Coords, iv, &x, &y, &z); } else if (line->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (line->Coords, iv, &x, &y, &m); } else if (line->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (line->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (line->Coords, iv, &x, &y); } if (iv > 0) { if (last_x == x && last_y == y && last_z == z) ; else points++; } else points++; last_x = x; last_y = y; last_z = z; } if (points < 2) { /* illegal LINESTRING - copying the original one */ new_line = gaiaAddLinestringToGeomColl (new_geom, line->Points); gaiaCopyLinestringCoords (new_line, line); } else { /* valid LINESTRING - sanitizing */ new_line = gaiaAddLinestringToGeomColl (new_geom, points); points = 0; for (iv = 0; iv < line->Points; iv++) { /* PASS II - inserting points */ z = 0.0; m = 0.0; if (line->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (line->Coords, iv, &x, &y, &z); } else if (line->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (line->Coords, iv, &x, &y, &m); } else if (line->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (line->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (line->Coords, iv, &x, &y); } if (iv > 0) { if (last_x == x && last_y == y && last_z == z) ; else { if (new_line->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (new_line->Coords, points, x, y, z); } else if (new_line->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (new_line->Coords, points, x, y, m); } else if (new_line->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (new_line->Coords, points, x, y, z, m); } else { gaiaSetPoint (new_line->Coords, points, x, y); } points++; } } else { if (new_line->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (new_line->Coords, points, x, y, z); } else if (new_line->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (new_line->Coords, points, x, y, m); } else if (new_line->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (new_line->Coords, points, x, y, z, m); } else { gaiaSetPoint (new_line->Coords, points, x, y); } points++; } last_x = x; last_y = y; last_z = z; } } line = line->Next; } polyg = geom->FirstPolygon; while (polyg) { /* copying POLYGONs */ i_ring = polyg->Exterior; /* sanitizing EXTERIOR RING */ points = 0; for (iv = 0; iv < i_ring->Points; iv++) { /* PASS I - checking points */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, iv, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, iv, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, iv, &x, &y); } if (iv > 0) { if (last_x == x && last_y == y && last_z == z) ; else points++; } else points++; last_x = x; last_y = y; last_z = z; } if (last_x == x && last_y == y && last_z == z) ; else { /* forcing RING closure */ points++; } if (points < 4) { /* illegal RING - copying the original one */ new_polyg = gaiaAddPolygonToGeomColl (new_geom, i_ring->Points, polyg->NumInteriors); o_ring = new_polyg->Exterior; gaiaCopyRingCoords (o_ring, i_ring); } else { /* valid RING - sanitizing */ new_polyg = gaiaAddPolygonToGeomColl (new_geom, points, polyg->NumInteriors); o_ring = new_polyg->Exterior; points = 0; for (iv = 0; iv < i_ring->Points; iv++) { /* PASS II - inserting points */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, iv, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, iv, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, iv, &x, &y); } if (iv > 0) { if (last_x == x && last_y == y && last_z == z) ; else { if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, points, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, points, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, points, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, points, x, y); } points++; } } else { if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, points, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, points, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, points, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, points, x, y); } points++; } last_x = x; last_y = y; last_z = z; } } /* PASS III - forcing RING closure */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, 0, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, 0, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, 0, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, 0, &x, &y); } points = o_ring->Points - 1; if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, points, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, points, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, points, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, points, x, y); } for (ib = 0; ib < new_polyg->NumInteriors; ib++) { /* copying each INTERIOR RING [if any] */ i_ring = polyg->Interiors + ib; /* sanitizing an INTERIOR RING */ points = 0; for (iv = 0; iv < i_ring->Points; iv++) { /* PASS I - checking points */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, iv, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, iv, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, iv, &x, &y); } if (iv > 0) { if (last_x == x && last_y == y && last_z == z) ; else points++; } else points++; last_x = x; last_y = y; last_z = z; } if (last_x == x && last_y == y && last_z == z) ; else { /* forcing RING closure */ points++; } if (points < 4) { /* illegal RING - copying the original one */ o_ring = gaiaAddInteriorRing (new_polyg, ib, i_ring->Points); gaiaCopyRingCoords (o_ring, i_ring); } else { /* valid RING - sanitizing */ o_ring = gaiaAddInteriorRing (new_polyg, ib, points); points = 0; for (iv = 0; iv < i_ring->Points; iv++) { /* PASS II - inserting points */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, iv, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, iv, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, iv, &x, &y); } if (iv > 0) { if (last_x == x && last_y == y && last_z == z) ; else { if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, points, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, points, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, points, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, points, x, y); } points++; } } else { if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, points, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, points, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, points, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, points, x, y); } points++; } last_x = x; last_y = y; last_z = z; } /* PASS III - forcing RING closure */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, 0, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, 0, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, 0, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, 0, &x, &y); } points = o_ring->Points - 1; if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, points, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, points, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, points, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, points, x, y); } } } polyg = polyg->Next; } return new_geom; } GAIAGEO_DECLARE gaiaGeomCollPtr gaiaEnsureClosedRings (gaiaGeomCollPtr geom) { /* / sanitizes a GEOMETRYCOLLECTION: / - ring closure is enforced anyway */ int iv; int ib; double x = 0.0; double y = 0.0; double z = 0.0; double m = 0.0; double last_x = 0.0; double last_y = 0.0; double last_z = 0.0; int points; int end_point; int enforce; gaiaPointPtr point; gaiaLinestringPtr line; gaiaLinestringPtr new_line; gaiaPolygonPtr polyg; gaiaPolygonPtr new_polyg; gaiaGeomCollPtr new_geom; gaiaRingPtr i_ring; gaiaRingPtr o_ring; if (!geom) return NULL; if (geom->DimensionModel == GAIA_XY_Z) new_geom = gaiaAllocGeomCollXYZ (); else if (geom->DimensionModel == GAIA_XY_M) new_geom = gaiaAllocGeomCollXYM (); else if (geom->DimensionModel == GAIA_XY_Z_M) new_geom = gaiaAllocGeomCollXYZM (); else new_geom = gaiaAllocGeomColl (); new_geom->Srid = geom->Srid; new_geom->DeclaredType = geom->DeclaredType; point = geom->FirstPoint; while (point) { /* copying POINTs */ gaiaAddPointToGeomCollXYZM (new_geom, point->X, point->Y, point->Z, point->M); point = point->Next; } line = geom->FirstLinestring; while (line) { /* copying LINESTRINGs */ new_line = gaiaAddLinestringToGeomColl (new_geom, line->Points); gaiaCopyLinestringCoords (new_line, line); line = line->Next; } polyg = geom->FirstPolygon; while (polyg) { /* copying POLYGONs */ i_ring = polyg->Exterior; /* sanitizing EXTERIOR RING */ points = i_ring->Points; z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, 0, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, 0, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, 0, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, 0, &x, &y); } last_z = 0.0; m = 0.0; end_point = points - 1; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, end_point, &last_x, &last_y, &last_z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, end_point, &last_x, &last_y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, end_point, &last_x, &last_y, &last_z, &m); } else { gaiaGetPoint (i_ring->Coords, end_point, &last_x, &last_y); } if (last_x == x && last_y == y && last_z == z) enforce = 0; else { /* forcing RING closure */ enforce = 1; } if (!enforce) { /* already closed */ new_polyg = gaiaAddPolygonToGeomColl (new_geom, i_ring->Points, polyg->NumInteriors); o_ring = new_polyg->Exterior; gaiaCopyRingCoords (o_ring, i_ring); } else { /* forcing closure */ new_polyg = gaiaAddPolygonToGeomColl (new_geom, i_ring->Points + 1, polyg->NumInteriors); o_ring = new_polyg->Exterior; for (iv = 0; iv < i_ring->Points; iv++) { /* inserting points */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, iv, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, iv, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, iv, &x, &y); } if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, iv, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, iv, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, iv, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, iv, x, y); } } /* adding last vertex so to ensure closure */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, 0, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, 0, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, 0, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, 0, &x, &y); } end_point = o_ring->Points - 1; if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, end_point, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, end_point, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, end_point, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, end_point, x, y); } } for (ib = 0; ib < new_polyg->NumInteriors; ib++) { /* copying each INTERIOR RING [if any] */ i_ring = polyg->Interiors + ib; points = i_ring->Points; z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, 0, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, 0, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, 0, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, 0, &x, &y); } last_z = 0.0; m = 0.0; end_point = points - 1; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, end_point, &last_x, &last_y, &last_z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, end_point, &last_x, &last_y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, end_point, &last_x, &last_y, &last_z, &m); } else { gaiaGetPoint (i_ring->Coords, end_point, &last_x, &last_y); } last_z = z; if (last_x == x && last_y == y && last_z == z) enforce = 0; else { /* forcing RING closure */ enforce = 1; } if (!enforce) { /* already closed */ o_ring = gaiaAddInteriorRing (new_polyg, ib, i_ring->Points); gaiaCopyRingCoords (o_ring, i_ring); } else { /* forcing closure */ o_ring = gaiaAddInteriorRing (new_polyg, ib, i_ring->Points + 1); for (iv = 0; iv < i_ring->Points; iv++) { /* inserting points */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, iv, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, iv, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, iv, &x, &y); } if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, iv, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, iv, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, iv, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, iv, x, y); } } /* adding last vertex so to ensure closure */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, 0, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, 0, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, 0, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, 0, &x, &y); } end_point = o_ring->Points - 1; if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, end_point, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, end_point, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, end_point, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, end_point, x, y); } } } polyg = polyg->Next; } return new_geom; } static double point_point_distance (double x1, double y1, double x2, double y2) { return sqrt (((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2))); } static int repeated_matching_point (gaiaGeomCollPtr geom, double x, double y, double z, double tolerance) { /* checking for duplicate points - multipoint */ gaiaPointPtr point; point = geom->FirstPoint; while (point) { if (tolerance <= 0.0) { if (point->X == x && point->Y == y && point->Z == z) return 1; } else { if (point_point_distance (x, y, point->X, point->Y) <= tolerance) return 1; } point = point->Next; } return 0; } GAIAGEO_DECLARE gaiaGeomCollPtr gaiaRemoveRepeatedPoints (gaiaGeomCollPtr geom, double tolerance) { /* / sanitizes a GEOMETRYCOLLECTION: / - repeated vertices are omitted */ int iv; int ib; double x = 0.0; double y = 0.0; double z = 0.0; double m = 0.0; double last_x = 0.0; double last_y = 0.0; double last_z = 0.0; int points; gaiaPointPtr point; gaiaLinestringPtr line; gaiaLinestringPtr new_line; gaiaPolygonPtr polyg; gaiaPolygonPtr new_polyg; gaiaGeomCollPtr new_geom; gaiaRingPtr i_ring; gaiaRingPtr o_ring; if (!geom) return NULL; if (geom->DimensionModel == GAIA_XY_Z) new_geom = gaiaAllocGeomCollXYZ (); else if (geom->DimensionModel == GAIA_XY_M) new_geom = gaiaAllocGeomCollXYM (); else if (geom->DimensionModel == GAIA_XY_Z_M) new_geom = gaiaAllocGeomCollXYZM (); else new_geom = gaiaAllocGeomColl (); new_geom->Srid = geom->Srid; new_geom->DeclaredType = geom->DeclaredType; point = geom->FirstPoint; while (point) { /* copying POINTs */ if (!repeated_matching_point (new_geom, point->X, point->Y, point->Z, tolerance)) gaiaAddPointToGeomCollXYZM (new_geom, point->X, point->Y, point->Z, point->M); point = point->Next; } line = geom->FirstLinestring; while (line) { /* sanitizing LINESTRINGs */ points = 0; for (iv = 0; iv < line->Points; iv++) { /* PASS I - checking points */ z = 0.0; m = 0.0; if (line->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (line->Coords, iv, &x, &y, &z); } else if (line->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (line->Coords, iv, &x, &y, &m); } else if (line->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (line->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (line->Coords, iv, &x, &y); } if (iv > 0) { if (tolerance <= 0.0) { if (last_x == x && last_y == y && last_z == z) ; else points++; } else { if (point_point_distance (x, y, last_x, last_y) <= tolerance) ; else points++; } } else points++; last_x = x; last_y = y; last_z = z; } if (points < 2) { /* illegal LINESTRING - copying the original one */ new_line = gaiaAddLinestringToGeomColl (new_geom, line->Points); gaiaCopyLinestringCoords (new_line, line); } else { /* valid LINESTRING - sanitizing */ new_line = gaiaAddLinestringToGeomColl (new_geom, points); points = 0; for (iv = 0; iv < line->Points; iv++) { /* PASS II - inserting points */ z = 0.0; m = 0.0; if (line->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (line->Coords, iv, &x, &y, &z); } else if (line->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (line->Coords, iv, &x, &y, &m); } else if (line->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (line->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (line->Coords, iv, &x, &y); } if (iv > 0) { int skip = 0; if (tolerance <= 0.0) { if (last_x == x && last_y == y && last_z == z) skip = 1; } else { if (point_point_distance (x, y, last_x, last_y) <= tolerance) skip = 1; } if (skip) ; else { if (new_line->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (new_line->Coords, points, x, y, z); } else if (new_line->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (new_line->Coords, points, x, y, m); } else if (new_line->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (new_line->Coords, points, x, y, z, m); } else { gaiaSetPoint (new_line->Coords, points, x, y); } points++; } } else { if (new_line->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (new_line->Coords, points, x, y, z); } else if (new_line->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (new_line->Coords, points, x, y, m); } else if (new_line->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (new_line->Coords, points, x, y, z, m); } else { gaiaSetPoint (new_line->Coords, points, x, y); } points++; } last_x = x; last_y = y; last_z = z; } } line = line->Next; } polyg = geom->FirstPolygon; while (polyg) { /* copying POLYGONs */ i_ring = polyg->Exterior; /* sanitizing EXTERIOR RING */ points = 0; for (iv = 0; iv < i_ring->Points; iv++) { /* PASS I - checking points */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, iv, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, iv, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, iv, &x, &y); } if (iv > 0) { if (tolerance <= 0.0) { if (last_x == x && last_y == y && last_z == z) ; else points++; } else { if (point_point_distance (x, y, last_x, last_y) <= tolerance) ; else points++; } } else points++; last_x = x; last_y = y; last_z = z; } if (points < 4) { /* illegal RING - copying the original one */ new_polyg = gaiaAddPolygonToGeomColl (new_geom, i_ring->Points, polyg->NumInteriors); o_ring = new_polyg->Exterior; gaiaCopyRingCoords (o_ring, i_ring); } else { /* valid RING - sanitizing */ new_polyg = gaiaAddPolygonToGeomColl (new_geom, points, polyg->NumInteriors); o_ring = new_polyg->Exterior; points = 0; for (iv = 0; iv < i_ring->Points; iv++) { /* PASS II - inserting points */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, iv, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, iv, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, iv, &x, &y); } if (iv > 0) { int skip = 0; if (tolerance <= 0.0) { if (last_x == x && last_y == y && last_z == z) skip = 1; } else { if (point_point_distance (x, y, last_x, last_y) <= tolerance) skip = 1; } if (skip) ; else { if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, points, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, points, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, points, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, points, x, y); } points++; } } else { if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, points, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, points, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, points, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, points, x, y); } points++; } last_x = x; last_y = y; last_z = z; } } for (ib = 0; ib < new_polyg->NumInteriors; ib++) { /* copying each INTERIOR RING [if any] */ i_ring = polyg->Interiors + ib; /* sanitizing an INTERIOR RING */ points = 0; for (iv = 0; iv < i_ring->Points; iv++) { /* PASS I - checking points */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, iv, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, iv, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, iv, &x, &y); } if (iv > 0) { if (tolerance <= 0.0) { if (last_x == x && last_y == y && last_z == z) ; else points++; } else { if (point_point_distance (x, y, last_x, last_y) <= tolerance) ; else points++; } } else points++; last_x = x; last_y = y; last_z = z; } if (points < 4) { /* illegal RING - copying the original one */ o_ring = gaiaAddInteriorRing (new_polyg, ib, i_ring->Points); gaiaCopyRingCoords (o_ring, i_ring); } else { /* valid RING - sanitizing */ o_ring = gaiaAddInteriorRing (new_polyg, ib, points); points = 0; for (iv = 0; iv < i_ring->Points; iv++) { /* PASS II - inserting points */ z = 0.0; m = 0.0; if (i_ring->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (i_ring->Coords, iv, &x, &y, &z); } else if (i_ring->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (i_ring->Coords, iv, &x, &y, &m); } else if (i_ring->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (i_ring->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (i_ring->Coords, iv, &x, &y); } if (iv > 0) { int skip = 0; if (tolerance <= 0.0) { if (last_x == x && last_y == y && last_z == z) skip = 1; } else { if (point_point_distance (x, y, last_x, last_y) <= tolerance) skip = 1; } if (skip) ; else { if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, points, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, points, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, points, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, points, x, y); } points++; } } else { if (o_ring->DimensionModel == GAIA_XY_Z) { gaiaSetPointXYZ (o_ring->Coords, points, x, y, z); } else if (o_ring->DimensionModel == GAIA_XY_M) { gaiaSetPointXYM (o_ring->Coords, points, x, y, m); } else if (o_ring->DimensionModel == GAIA_XY_Z_M) { gaiaSetPointXYZM (o_ring->Coords, points, x, y, z, m); } else { gaiaSetPoint (o_ring->Coords, points, x, y); } points++; } last_x = x; last_y = y; last_z = z; } } } polyg = polyg->Next; } return new_geom; } static int gaiaIsToxicLinestring (gaiaLinestringPtr line) { /* checking a Linestring */ if (line->Points < 2) return 1; /* not a valid Linestring, simply a degenerated Point */ return 0; } static int gaiaIsToxicRing (gaiaRingPtr ring) { /* checking a Ring */ if (ring->Points < 4) return 1; return 0; } GAIAGEO_DECLARE int gaiaIsToxic (gaiaGeomCollPtr geom) { return gaiaIsToxic_r (NULL, geom); } GAIAGEO_DECLARE int gaiaIsToxic_r (const void *cache, gaiaGeomCollPtr geom) { /* / identifying toxic geometries / i.e. geoms making GEOS to crash !!! */ int ib; gaiaPointPtr point; gaiaLinestringPtr line; gaiaPolygonPtr polyg; gaiaRingPtr ring; if (!geom) return 0; if (gaiaIsEmpty (geom)) return 1; point = geom->FirstPoint; while (point) { /* checking POINTs */ point = point->Next; } line = geom->FirstLinestring; while (line) { /* checking LINESTRINGs */ if (gaiaIsToxicLinestring (line)) { if (cache != NULL) gaiaSetGeosAuxErrorMsg_r (cache, "gaiaIsToxic detected a toxic Linestring: < 2 pts"); else gaiaSetGeosAuxErrorMsg ("gaiaIsToxic detected a toxic Linestring: < 2 pts"); return 1; } line = line->Next; } polyg = geom->FirstPolygon; while (polyg) { /* checking POLYGONs */ ring = polyg->Exterior; if (gaiaIsToxicRing (ring)) { if (cache != NULL) gaiaSetGeosAuxErrorMsg_r (cache, "gaiaIsToxic detected a toxic Ring: < 4 pts"); else gaiaSetGeosAuxErrorMsg ("gaiaIsToxic detected a toxic Ring: < 4 pts"); return 1; } for (ib = 0; ib < polyg->NumInteriors; ib++) { ring = polyg->Interiors + ib; if (gaiaIsToxicRing (ring)) { if (cache != NULL) gaiaSetGeosAuxErrorMsg_r (cache, "gaiaIsToxic detected a toxic Ring: < 4 pts"); else gaiaSetGeosAuxErrorMsg ("gaiaIsToxic detected a toxic Ring: < 4 pts"); return 1; } } polyg = polyg->Next; } return 0; } GAIAGEO_DECLARE int gaiaIsNotClosedRing (gaiaRingPtr ring) { return gaiaIsNotClosedRing_r (NULL, ring); } GAIAGEO_DECLARE int gaiaIsNotClosedRing_r (const void *cache, gaiaRingPtr ring) { /* checking a Ring for closure */ double x0; double y0; double z0; double m0; double x1; double y1; double z1; double m1; gaiaRingGetPoint (ring, 0, &x0, &y0, &z0, &m0); gaiaRingGetPoint (ring, ring->Points - 1, &x1, &y1, &z1, &m1); if (x0 == x1 && y0 == y1 && z0 == z1 && m0 == m1) return 0; else { if (cache != NULL) gaiaSetGeosAuxErrorMsg_r (cache, "gaia detected a not-closed Ring"); else gaiaSetGeosAuxErrorMsg ("gaia detected a not-closed Ring"); return 1; } } GAIAGEO_DECLARE int gaiaIsNotClosedGeomColl (gaiaGeomCollPtr geom) { return gaiaIsNotClosedGeomColl_r (NULL, geom); } GAIAGEO_DECLARE int gaiaIsNotClosedGeomColl_r (const void *cache, gaiaGeomCollPtr geom) { /* / identifying not properly closed Rings / i.e. geoms making GEOS to crash !!! */ int ret; int ib; gaiaPolygonPtr polyg; gaiaRingPtr ring; if (!geom) return 0; polyg = geom->FirstPolygon; while (polyg) { /* checking POLYGONs */ ring = polyg->Exterior; if (cache != NULL) ret = gaiaIsNotClosedRing_r (cache, ring); else ret = gaiaIsNotClosedRing (ring); if (ret) return 1; for (ib = 0; ib < polyg->NumInteriors; ib++) { ring = polyg->Interiors + ib; if (cache != NULL) ret = gaiaIsNotClosedRing_r (cache, ring); else ret = gaiaIsNotClosedRing (ring); if (ret) return 1; } polyg = polyg->Next; } return 0; } GAIAGEO_DECLARE gaiaGeomCollPtr gaiaLinearize (gaiaGeomCollPtr geom, int force_multi) { /* attempts to rearrange a generic Geometry into a (multi)linestring */ int pts = 0; int lns = 0; gaiaGeomCollPtr result; gaiaPointPtr pt; gaiaLinestringPtr ln; gaiaLinestringPtr new_ln; gaiaPolygonPtr pg; gaiaRingPtr rng; int iv; int ib; double x; double y; double m; double z; if (!geom) return NULL; pt = geom->FirstPoint; while (pt) { pts++; pt = pt->Next; } ln = geom->FirstLinestring; while (ln) { lns++; ln = ln->Next; } if (pts || lns) return NULL; if (geom->DimensionModel == GAIA_XY_Z_M) result = gaiaAllocGeomCollXYZM (); else if (geom->DimensionModel == GAIA_XY_Z) result = gaiaAllocGeomCollXYZ (); else if (geom->DimensionModel == GAIA_XY_M) result = gaiaAllocGeomCollXYM (); else result = gaiaAllocGeomColl (); result->Srid = geom->Srid; if (force_multi) result->DeclaredType = GAIA_MULTILINESTRING; pg = geom->FirstPolygon; while (pg) { /* dissolving any POLYGON as simple LINESTRINGs (rings) */ rng = pg->Exterior; new_ln = gaiaAddLinestringToGeomColl (result, rng->Points); for (iv = 0; iv < rng->Points; iv++) { /* copying the EXTERIOR RING as LINESTRING */ if (geom->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (rng->Coords, iv, &x, &y, &z, &m); gaiaSetPointXYZM (new_ln->Coords, iv, x, y, z, m); } else if (geom->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (rng->Coords, iv, &x, &y, &z); gaiaSetPointXYZ (new_ln->Coords, iv, x, y, z); } else if (geom->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (rng->Coords, iv, &x, &y, &m); gaiaSetPointXYM (new_ln->Coords, iv, x, y, m); } else { gaiaGetPoint (rng->Coords, iv, &x, &y); gaiaSetPoint (new_ln->Coords, iv, x, y); } } for (ib = 0; ib < pg->NumInteriors; ib++) { rng = pg->Interiors + ib; new_ln = gaiaAddLinestringToGeomColl (result, rng->Points); for (iv = 0; iv < rng->Points; iv++) { /* copying an INTERIOR RING as LINESTRING */ if (geom->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (rng->Coords, iv, &x, &y, &z, &m); gaiaSetPointXYZM (new_ln->Coords, iv, x, y, z, m); } else if (geom->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (rng->Coords, iv, &x, &y, &z); gaiaSetPointXYZ (new_ln->Coords, iv, x, y, z); } else if (geom->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (rng->Coords, iv, &x, &y, &m); gaiaSetPointXYM (new_ln->Coords, iv, x, y, m); } else { gaiaGetPoint (rng->Coords, iv, &x, &y); gaiaSetPoint (new_ln->Coords, iv, x, y); } } } pg = pg->Next; } if (result->FirstLinestring == NULL) { gaiaFreeGeomColl (result); return NULL; } return result; } GAIAGEO_DECLARE gaiaGeomCollPtr gaiaDissolveSegments (gaiaGeomCollPtr geom) { /* attempts to dissolve a Geometry into segments */ gaiaGeomCollPtr result; gaiaPointPtr pt; gaiaLinestringPtr ln; gaiaLinestringPtr segment; gaiaPolygonPtr pg; gaiaRingPtr rng; int iv; int ib; double x = 0.0; double y = 0.0; double z = 0.0; double m = 0.0; double x0 = 0.0; double y0 = 0.0; double z0 = 0.0; double m0 = 0.0; if (!geom) return NULL; if (geom->DimensionModel == GAIA_XY_Z_M) result = gaiaAllocGeomCollXYZM (); else if (geom->DimensionModel == GAIA_XY_Z) result = gaiaAllocGeomCollXYZ (); else if (geom->DimensionModel == GAIA_XY_M) result = gaiaAllocGeomCollXYM (); else result = gaiaAllocGeomColl (); pt = geom->FirstPoint; while (pt) { if (geom->DimensionModel == GAIA_XY_Z_M) gaiaAddPointToGeomCollXYZM (result, pt->X, pt->Y, pt->Z, pt->M); else if (geom->DimensionModel == GAIA_XY_Z) gaiaAddPointToGeomCollXYZ (result, pt->X, pt->Y, pt->Z); else if (geom->DimensionModel == GAIA_XY_M) gaiaAddPointToGeomCollXYM (result, pt->X, pt->Y, pt->M); else gaiaAddPointToGeomColl (result, pt->X, pt->Y); pt = pt->Next; } ln = geom->FirstLinestring; while (ln) { for (iv = 0; iv < ln->Points; iv++) { if (ln->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m); } else if (ln->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z); } else if (ln->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m); } else { gaiaGetPoint (ln->Coords, iv, &x, &y); } if (iv > 0) { if (geom->DimensionModel == GAIA_XY_Z_M) { if (x != x0 || y != y0 || z != z0 || m != m0) { segment = gaiaAddLinestringToGeomColl (result, 2); gaiaSetPointXYZM (segment->Coords, 0, x0, y0, z0, m0); gaiaSetPointXYZM (segment->Coords, 1, x, y, z, m); } } else if (geom->DimensionModel == GAIA_XY_Z) { if (x != x0 || y != y0 || z != z0) { segment = gaiaAddLinestringToGeomColl (result, 2); gaiaSetPointXYZ (segment->Coords, 0, x0, y0, z0); gaiaSetPointXYZ (segment->Coords, 1, x, y, z); } } else if (geom->DimensionModel == GAIA_XY_M) { if (x != x0 || y != y0 || m != m0) { segment = gaiaAddLinestringToGeomColl (result, 2); gaiaSetPointXYM (segment->Coords, 0, x0, y0, m0); gaiaSetPointXYM (segment->Coords, 1, x, y, m); } } else { if (x != x0 || y != y0) { segment = gaiaAddLinestringToGeomColl (result, 2); gaiaSetPoint (segment->Coords, 0, x0, y0); gaiaSetPoint (segment->Coords, 1, x, y); } } } x0 = x; y0 = y; z0 = z; m0 = m; } ln = ln->Next; } pg = geom->FirstPolygon; while (pg) { rng = pg->Exterior; for (iv = 0; iv < rng->Points; iv++) { /* exterior Ring */ if (rng->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (rng->Coords, iv, &x, &y, &z, &m); } else if (rng->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (rng->Coords, iv, &x, &y, &z); } else if (rng->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (rng->Coords, iv, &x, &y, &m); } else { gaiaGetPoint (rng->Coords, iv, &x, &y); } if (iv > 0) { if (geom->DimensionModel == GAIA_XY_Z_M) { if (x != x0 || y != y0 || z != z0 || m != m0) { segment = gaiaAddLinestringToGeomColl (result, 2); gaiaSetPointXYZM (segment->Coords, 0, x0, y0, z0, m0); gaiaSetPointXYZM (segment->Coords, 1, x, y, z, m); } } else if (geom->DimensionModel == GAIA_XY_Z) { if (x != x0 || y != y0 || z != z0) { segment = gaiaAddLinestringToGeomColl (result, 2); gaiaSetPointXYZ (segment->Coords, 0, x0, y0, z0); gaiaSetPointXYZ (segment->Coords, 1, x, y, z); } } else if (geom->DimensionModel == GAIA_XY_M) { if (x != x0 || y != y0 || m != m0) { segment = gaiaAddLinestringToGeomColl (result, 2); gaiaSetPointXYM (segment->Coords, 0, x0, y0, m0); gaiaSetPointXYM (segment->Coords, 1, x, y, m); } } else { if (x != x0 || y != y0) { segment = gaiaAddLinestringToGeomColl (result, 2); gaiaSetPoint (segment->Coords, 0, x0, y0); gaiaSetPoint (segment->Coords, 1, x, y); } } } x0 = x; y0 = y; z0 = z; m0 = m; } for (ib = 0; ib < pg->NumInteriors; ib++) { rng = pg->Interiors + ib; for (iv = 0; iv < rng->Points; iv++) { /* interior Ring */ if (rng->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (rng->Coords, iv, &x, &y, &z, &m); } else if (rng->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (rng->Coords, iv, &x, &y, &z); } else if (rng->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (rng->Coords, iv, &x, &y, &m); } else { gaiaGetPoint (rng->Coords, iv, &x, &y); } if (iv > 0) { if (geom->DimensionModel == GAIA_XY_Z_M) { if (x != x0 || y != y0 || z != z0 || m != m0) { segment = gaiaAddLinestringToGeomColl (result, 2); gaiaSetPointXYZM (segment->Coords, 0, x0, y0, z0, m0); gaiaSetPointXYZM (segment->Coords, 1, x, y, z, m); } } else if (geom->DimensionModel == GAIA_XY_Z) { if (x != x0 || y != y0 || z != z0) { segment = gaiaAddLinestringToGeomColl (result, 2); gaiaSetPointXYZ (segment->Coords, 0, x0, y0, z0); gaiaSetPointXYZ (segment->Coords, 1, x, y, z); } } else if (geom->DimensionModel == GAIA_XY_M) { if (x != x0 || y != y0 || m != m0) { segment = gaiaAddLinestringToGeomColl (result, 2); gaiaSetPointXYM (segment->Coords, 0, x0, y0, m0); gaiaSetPointXYM (segment->Coords, 1, x, y, m); } } else { if (x != x0 || y != y0) { segment = gaiaAddLinestringToGeomColl (result, 2); gaiaSetPoint (segment->Coords, 0, x0, y0); gaiaSetPoint (segment->Coords, 1, x, y); } } } x0 = x; y0 = y; z0 = z; m0 = m; } } pg = pg->Next; } result->Srid = geom->Srid; return result; } GAIAGEO_DECLARE gaiaGeomCollPtr gaiaDissolvePoints (gaiaGeomCollPtr geom) { /* attempts to dissolve a Geometry into points */ gaiaGeomCollPtr result; gaiaPointPtr pt; gaiaLinestringPtr ln; gaiaPolygonPtr pg; gaiaRingPtr rng; int iv; int ib; double x = 0.0; double y = 0.0; double z = 0.0; double m = 0.0; if (!geom) return NULL; if (geom->DimensionModel == GAIA_XY_Z_M) result = gaiaAllocGeomCollXYZM (); else if (geom->DimensionModel == GAIA_XY_Z) result = gaiaAllocGeomCollXYZ (); else if (geom->DimensionModel == GAIA_XY_M) result = gaiaAllocGeomCollXYM (); else result = gaiaAllocGeomColl (); pt = geom->FirstPoint; while (pt) { if (geom->DimensionModel == GAIA_XY_Z_M) gaiaAddPointToGeomCollXYZM (result, pt->X, pt->Y, pt->Z, pt->M); else if (geom->DimensionModel == GAIA_XY_Z) gaiaAddPointToGeomCollXYZ (result, pt->X, pt->Y, pt->Z); else if (geom->DimensionModel == GAIA_XY_M) gaiaAddPointToGeomCollXYM (result, pt->X, pt->Y, pt->M); else gaiaAddPointToGeomColl (result, pt->X, pt->Y); pt = pt->Next; } ln = geom->FirstLinestring; while (ln) { for (iv = 0; iv < ln->Points; iv++) { if (ln->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m); } else if (ln->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z); } else if (ln->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m); } else { gaiaGetPoint (ln->Coords, iv, &x, &y); } if (geom->DimensionModel == GAIA_XY_Z_M) gaiaAddPointToGeomCollXYZM (result, x, y, z, m); else if (geom->DimensionModel == GAIA_XY_Z) gaiaAddPointToGeomCollXYZ (result, x, y, z); else if (geom->DimensionModel == GAIA_XY_M) gaiaAddPointToGeomCollXYM (result, x, y, m); else gaiaAddPointToGeomColl (result, x, y); } ln = ln->Next; } pg = geom->FirstPolygon; while (pg) { rng = pg->Exterior; for (iv = 0; iv < rng->Points; iv++) { /* exterior Ring */ if (rng->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (rng->Coords, iv, &x, &y, &z, &m); } else if (rng->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (rng->Coords, iv, &x, &y, &z); } else if (rng->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (rng->Coords, iv, &x, &y, &m); } else { gaiaGetPoint (rng->Coords, iv, &x, &y); } if (geom->DimensionModel == GAIA_XY_Z_M) gaiaAddPointToGeomCollXYZM (result, x, y, z, m); else if (geom->DimensionModel == GAIA_XY_Z) gaiaAddPointToGeomCollXYZ (result, x, y, z); else if (geom->DimensionModel == GAIA_XY_M) gaiaAddPointToGeomCollXYM (result, x, y, m); else gaiaAddPointToGeomColl (result, x, y); } for (ib = 0; ib < pg->NumInteriors; ib++) { rng = pg->Interiors + ib; for (iv = 0; iv < rng->Points; iv++) { /* interior Ring */ if (rng->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (rng->Coords, iv, &x, &y, &z, &m); } else if (rng->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (rng->Coords, iv, &x, &y, &z); } else if (rng->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (rng->Coords, iv, &x, &y, &m); } else { gaiaGetPoint (rng->Coords, iv, &x, &y); } if (geom->DimensionModel == GAIA_XY_Z_M) gaiaAddPointToGeomCollXYZM (result, x, y, z, m); else if (geom->DimensionModel == GAIA_XY_Z) gaiaAddPointToGeomCollXYZ (result, x, y, z); else if (geom->DimensionModel == GAIA_XY_M) gaiaAddPointToGeomCollXYM (result, x, y, m); else gaiaAddPointToGeomColl (result, x, y); } } pg = pg->Next; } result->Srid = geom->Srid; return result; } GAIAGEO_DECLARE gaiaGeomCollPtr gaiaExtractPointsFromGeomColl (gaiaGeomCollPtr geom) { /* extracts any POINT from a GeometryCollection */ gaiaGeomCollPtr result; gaiaPointPtr pt; int pts = 0; if (!geom) return NULL; pt = geom->FirstPoint; while (pt) { pts++; pt = pt->Next; } if (!pts) return NULL; if (geom->DimensionModel == GAIA_XY_Z_M) result = gaiaAllocGeomCollXYZM (); else if (geom->DimensionModel == GAIA_XY_Z) result = gaiaAllocGeomCollXYZ (); else if (geom->DimensionModel == GAIA_XY_M) result = gaiaAllocGeomCollXYM (); else result = gaiaAllocGeomColl (); pt = geom->FirstPoint; while (pt) { if (geom->DimensionModel == GAIA_XY_Z_M) gaiaAddPointToGeomCollXYZM (result, pt->X, pt->Y, pt->Z, pt->M); else if (geom->DimensionModel == GAIA_XY_Z) gaiaAddPointToGeomCollXYZ (result, pt->X, pt->Y, pt->Z); else if (geom->DimensionModel == GAIA_XY_M) gaiaAddPointToGeomCollXYM (result, pt->X, pt->Y, pt->M); else gaiaAddPointToGeomColl (result, pt->X, pt->Y); pt = pt->Next; } result->Srid = geom->Srid; if (pts == 1) result->DeclaredType = GAIA_POINT; else result->DeclaredType = GAIA_MULTIPOINT; return result; } GAIAGEO_DECLARE gaiaGeomCollPtr gaiaExtractLinestringsFromGeomColl (gaiaGeomCollPtr geom) { /* extracts any LINESTRING from a GeometryCollection */ gaiaGeomCollPtr result; gaiaLinestringPtr ln; gaiaLinestringPtr new_ln; int lns = 0; int iv; double x; double y; double z; double m; if (!geom) return NULL; ln = geom->FirstLinestring; while (ln) { lns++; ln = ln->Next; } if (!lns) return NULL; if (geom->DimensionModel == GAIA_XY_Z_M) result = gaiaAllocGeomCollXYZM (); else if (geom->DimensionModel == GAIA_XY_Z) result = gaiaAllocGeomCollXYZ (); else if (geom->DimensionModel == GAIA_XY_M) result = gaiaAllocGeomCollXYM (); else result = gaiaAllocGeomColl (); ln = geom->FirstLinestring; while (ln) { new_ln = gaiaAddLinestringToGeomColl (result, ln->Points); for (iv = 0; iv < ln->Points; iv++) { if (ln->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m); gaiaSetPointXYZM (new_ln->Coords, iv, x, y, z, m); } else if (ln->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z); gaiaSetPointXYZ (new_ln->Coords, iv, x, y, z); } else if (ln->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m); gaiaSetPointXYM (new_ln->Coords, iv, x, y, m); } else { gaiaGetPoint (ln->Coords, iv, &x, &y); gaiaSetPoint (new_ln->Coords, iv, x, y); } } ln = ln->Next; } result->Srid = geom->Srid; if (lns == 1) result->DeclaredType = GAIA_LINESTRING; else result->DeclaredType = GAIA_MULTILINESTRING; return result; } GAIAGEO_DECLARE gaiaGeomCollPtr gaiaExtractPolygonsFromGeomColl (gaiaGeomCollPtr geom) { /* extracts any POLYGON from a GeometryCollection */ gaiaGeomCollPtr result; gaiaPolygonPtr pg; gaiaPolygonPtr new_pg; gaiaRingPtr rng; gaiaRingPtr new_rng; int pgs = 0; int iv; int ib; double x; double y; double z; double m; if (!geom) return NULL; pg = geom->FirstPolygon; while (pg) { pgs++; pg = pg->Next; } if (!pgs) return NULL; if (geom->DimensionModel == GAIA_XY_Z_M) result = gaiaAllocGeomCollXYZM (); else if (geom->DimensionModel == GAIA_XY_Z) result = gaiaAllocGeomCollXYZ (); else if (geom->DimensionModel == GAIA_XY_M) result = gaiaAllocGeomCollXYM (); else result = gaiaAllocGeomColl (); pg = geom->FirstPolygon; while (pg) { rng = pg->Exterior; new_pg = gaiaAddPolygonToGeomColl (result, rng->Points, pg->NumInteriors); new_rng = new_pg->Exterior; for (iv = 0; iv < rng->Points; iv++) { if (rng->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (rng->Coords, iv, &x, &y, &z, &m); gaiaSetPointXYZM (new_rng->Coords, iv, x, y, z, m); } else if (rng->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (rng->Coords, iv, &x, &y, &z); gaiaSetPointXYZ (new_rng->Coords, iv, x, y, z); } else if (rng->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (rng->Coords, iv, &x, &y, &m); gaiaSetPointXYM (new_rng->Coords, iv, x, y, m); } else { gaiaGetPoint (rng->Coords, iv, &x, &y); gaiaSetPoint (new_rng->Coords, iv, x, y); } } for (ib = 0; ib < pg->NumInteriors; ib++) { rng = pg->Interiors + ib; new_rng = gaiaAddInteriorRing (new_pg, ib, rng->Points); for (iv = 0; iv < rng->Points; iv++) { if (rng->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (rng->Coords, iv, &x, &y, &z, &m); gaiaSetPointXYZM (new_rng->Coords, iv, x, y, z, m); } else if (rng->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (rng->Coords, iv, &x, &y, &z); gaiaSetPointXYZ (new_rng->Coords, iv, x, y, z); } else if (rng->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (rng->Coords, iv, &x, &y, &m); gaiaSetPointXYM (new_rng->Coords, iv, x, y, m); } else { gaiaGetPoint (rng->Coords, iv, &x, &y); gaiaSetPoint (new_rng->Coords, iv, x, y); } } } pg = pg->Next; } result->Srid = geom->Srid; if (pgs == 1) result->DeclaredType = GAIA_POLYGON; else result->DeclaredType = GAIA_MULTIPOLYGON; return result; } SPATIALITE_PRIVATE int gaia_do_check_linestring (const void *g) { /* testing if the Geometry is a simple Linestring */ gaiaGeomCollPtr geom = (gaiaGeomCollPtr) g; gaiaPointPtr pt; gaiaLinestringPtr ln; gaiaPolygonPtr pg; int pts = 0; int lns = 0; int pgs = 0; pt = geom->FirstPoint; while (pt != NULL) { /* counting how many Points are there */ pts++; pt = pt->Next; } ln = geom->FirstLinestring; while (ln != NULL) { /* counting how many Linestrings are there */ lns++; ln = ln->Next; } pg = geom->FirstPolygon; while (pg != NULL) { /* counting how many Polygons are there */ pgs++; pg = pg->Next; } if (pts == 0 && lns == 1 && pgs == 0) return 1; return 0; } static int do_create_points (sqlite3 * mem_db, const char *table) { /* creating a table for storing Points */ int ret; char *sql; char *err_msg = NULL; /* creating the main table */ if (strcmp (table, "points1") == 0) sql = sqlite3_mprintf ("CREATE TABLE %s " "(id INTEGER PRIMARY KEY AUTOINCREMENT, " "geom BLOB NOT NULL, " "needs_interpolation INTEGER NOT NULL)", table); else sql = sqlite3_mprintf ("CREATE TABLE %s " "(id INTEGER PRIMARY KEY AUTOINCREMENT, " "geom BLOB NOT NULL)", table); ret = sqlite3_exec (mem_db, sql, NULL, NULL, &err_msg); sqlite3_free (sql); if (ret != SQLITE_OK) { spatialite_e ("gaiaDrapeLine: CREATE TABLE \"%s\" error: %s\n", table, err_msg); sqlite3_free (err_msg); return 0; } if (strcmp (table, "points1") == 0) return 1; /* creating the companion R*Tree table */ sql = sqlite3_mprintf ("CREATE VIRTUAL TABLE rtree_%s " "USING rtree(pkid, xmin, xmax, ymin, ymax)", table); ret = sqlite3_exec (mem_db, sql, NULL, NULL, &err_msg); sqlite3_free (sql); if (ret != SQLITE_OK) { spatialite_e ("gaiaDrapeLine: CREATE TABLE \"rtree_%s\" error: %s\n", table, err_msg); sqlite3_free (err_msg); return 0; } return 1; } static int do_insert_point (sqlite3 * mem_db, sqlite3_stmt * stmt_pts, sqlite3_stmt * stmt_rtree_pts, double x, double y, double z, double m) { /* inserting into the Points2 helper table */ int ret; sqlite3_int64 rowid; sqlite3_reset (stmt_pts); sqlite3_clear_bindings (stmt_pts); sqlite3_bind_double (stmt_pts, 1, x); sqlite3_bind_double (stmt_pts, 2, y); sqlite3_bind_double (stmt_pts, 3, z); sqlite3_bind_double (stmt_pts, 4, m); ret = sqlite3_step (stmt_pts); if (ret == SQLITE_DONE || ret == SQLITE_ROW) rowid = sqlite3_last_insert_rowid (mem_db); else { spatialite_e ("INSERT INTO \"Points\" error: \"%s\"\n", sqlite3_errmsg (mem_db)); goto error; } /* inserting into the companion R*Tree */ sqlite3_reset (stmt_rtree_pts); sqlite3_clear_bindings (stmt_rtree_pts); sqlite3_bind_int64 (stmt_rtree_pts, 1, rowid); sqlite3_bind_double (stmt_rtree_pts, 2, x); sqlite3_bind_double (stmt_rtree_pts, 3, x); sqlite3_bind_double (stmt_rtree_pts, 4, y); sqlite3_bind_double (stmt_rtree_pts, 5, y); ret = sqlite3_step (stmt_rtree_pts); if (ret == SQLITE_DONE || ret == SQLITE_ROW) ; else { spatialite_e ("INSERT INTO \"RTree_Points\" error: \"%s\"\n", sqlite3_errmsg (mem_db)); goto error; } return 1; error: return 0; } static int do_populate_points2 (sqlite3 * mem_db, gaiaGeomCollPtr geom) { /* populating Points-2 */ int ret; const char *sql; int iv; gaiaLinestringPtr ln; sqlite3_stmt *stmt_pts = NULL; sqlite3_stmt *stmt_rtree_pts = NULL; double ox; double oy; double oz; double om; double fx; double fy; double fz; double fm; /* creating an SQL statement for inserting rows into the Points2 table */ sql = "INSERT INTO points2 (id, geom) VALUES " "(NULL, MakePointZM(?, ?, ?, ?))"; ret = sqlite3_prepare_v2 (mem_db, sql, strlen (sql), &stmt_pts, NULL); if (ret != SQLITE_OK) { spatialite_e ("INSERT INTO Points2: error %d \"%s\"\n", sqlite3_errcode (mem_db), sqlite3_errmsg (mem_db)); goto error; } /* creating an SQL statement for inserting rows into the R*TREE table */ sql = "INSERT INTO rtree_points2 (pkid, xmin, xmax, ymin, ymax) " "VALUES (?, ?, ?, ?, ?)"; ret = sqlite3_prepare_v2 (mem_db, sql, strlen (sql), &stmt_rtree_pts, NULL); if (ret != SQLITE_OK) { spatialite_e ("INSERT INTO RTree_Points2: error %d \"%s\"\n", sqlite3_errcode (mem_db), sqlite3_errmsg (mem_db)); goto error; } /* starting a Transaction */ sql = "BEGIN"; ret = sqlite3_exec (mem_db, sql, NULL, NULL, NULL); if (ret != SQLITE_OK) { spatialite_e ("BEGIN: error: %d \"%s\"\n", sqlite3_errcode (mem_db), sqlite3_errmsg (mem_db)); goto error; } ln = geom->FirstLinestring; for (iv = 0; iv < ln->Points; iv++) { /* processing all Vertices from the input Linestring */ double x; double y; double z = 0.0; double m = 0.0; int repeated; if (ln->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z); } else if (ln->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m); } else if (ln->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (ln->Coords, iv, &x, &y); } repeated = 0; if (iv != 0) { /* checking for repeated points */ if (x == ox && y == oy && z == oz && m == om) repeated = 1; } if (iv == ln->Points - 1) { /* checkink for a closed Linestring */ if (x == fx && y == fy && z == fz && m == fm) repeated = 1; } if (!repeated) { /* inserting a Point */ if (!do_insert_point (mem_db, stmt_pts, stmt_rtree_pts, x, y, z, m)) goto error; } /* saving the current coords */ ox = x; oy = y; oz = z; om = m; if (iv == 0) { /* saving the Start Point coords */ fx = x; fy = y; fz = z; fm = m; } } /* committing the pending Transaction */ sql = "COMMIT"; ret = sqlite3_exec (mem_db, sql, NULL, NULL, NULL); if (ret != SQLITE_OK) { spatialite_e ("COMMIT: error: %d \"%s\"\n", sqlite3_errcode (mem_db), sqlite3_errmsg (mem_db)); goto error; } sqlite3_finalize (stmt_pts); sqlite3_finalize (stmt_rtree_pts); return 1; error: if (stmt_pts != NULL) sqlite3_finalize (stmt_pts); if (stmt_rtree_pts != NULL) sqlite3_finalize (stmt_rtree_pts); return 0; } static int do_insert_draped_point (sqlite3 * mem_db, sqlite3_stmt * stmt_pts, int needs_interpolation, gaiaGeomCollPtr geom) { /* inserting into the Points helper table */ int ret; gaiaPointPtr pt = geom->FirstPoint; if (pt == NULL) return 0; sqlite3_reset (stmt_pts); sqlite3_clear_bindings (stmt_pts); sqlite3_bind_double (stmt_pts, 1, pt->X); sqlite3_bind_double (stmt_pts, 2, pt->Y); sqlite3_bind_double (stmt_pts, 3, pt->Z); sqlite3_bind_double (stmt_pts, 4, pt->M); sqlite3_bind_int (stmt_pts, 5, needs_interpolation); ret = sqlite3_step (stmt_pts); if (ret == SQLITE_DONE || ret == SQLITE_ROW) return 1; spatialite_e ("INSERT INTO \"Points1\" error: \"%s\"\n", sqlite3_errmsg (mem_db)); return 0; } static gaiaGeomCollPtr do_point_drape_coords (int srid, double x, double y, gaiaGeomCollPtr geom_3d) { /* draping a Point */ gaiaPointPtr pt_3d = geom_3d->FirstPoint; gaiaGeomCollPtr geom = gaiaAllocGeomCollXYZM (); geom->Srid = srid; gaiaAddPointToGeomCollXYZM (geom, x, y, pt_3d->Z, pt_3d->M); return geom; } static gaiaGeomCollPtr do_point_same_coords (int srid, double x, double y, double z, double m) { /* creating a Point (unchanged coords) */ gaiaGeomCollPtr geom; geom = gaiaAllocGeomCollXYZM (); geom->Srid = srid; gaiaAddPointToGeomCollXYZM (geom, x, y, z, m); return geom; } static int do_drape_vertex (sqlite3 * mem_db, sqlite3_stmt * stmt, sqlite3_stmt * stmt_pts, int srid, double tolerance, double x, double y, double z, double m) { /* draping the segment */ double minx = x; double miny = y; double maxx = x; double maxy = y; int ret; int count = 0; gaiaGeomCollPtr g2; /* preparing an extendend BBOX */ minx = x - (tolerance * 2.0); miny = y - (tolerance * 2.0); maxx = x + (tolerance * 2.0); maxy = y + (tolerance * 2.0); /* querying Points-2 by distance from Segment */ sqlite3_reset (stmt); sqlite3_clear_bindings (stmt); sqlite3_bind_double (stmt, 1, minx); /* R*Tree BBOX */ sqlite3_bind_double (stmt, 2, miny); sqlite3_bind_double (stmt, 3, maxx); sqlite3_bind_double (stmt, 4, maxy); sqlite3_bind_double (stmt, 5, x); /* point (distance) */ sqlite3_bind_double (stmt, 6, y); sqlite3_bind_double (stmt, 7, tolerance); while (1) { /* scrolling the result set rows */ ret = sqlite3_step (stmt); if (ret == SQLITE_DONE) break; /* end of result set */ if (ret == SQLITE_ROW) { gaiaGeomCollPtr g2 = NULL; gaiaGeomCollPtr geom = NULL; if (sqlite3_column_type (stmt, 0) == SQLITE_BLOB) { unsigned char *p_blob = (unsigned char *) sqlite3_column_blob (stmt, 0); int n_bytes = sqlite3_column_bytes (stmt, 0); geom = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes); } if (geom != NULL) { /* found a valid Point */ g2 = do_point_drape_coords (srid, x, y, geom); gaiaFreeGeomColl (geom); if (!do_insert_draped_point (mem_db, stmt_pts, 0, g2)) goto error; gaiaFreeGeomColl (g2); count++; } } } if (count == 0) { /* not found - inserting the Point itself */ g2 = do_point_same_coords (srid, x, y, z, m); if (!do_insert_draped_point (mem_db, stmt_pts, 1, g2)) goto error; gaiaFreeGeomColl (g2); } return 1; error: return 0; } static int do_drape_line (sqlite3 * mem_db, gaiaGeomCollPtr geom, double tolerance) { /* draping the line */ int ret; sqlite3_stmt *stmt = NULL; sqlite3_stmt *stmt_pts = NULL; const char *sql; gaiaLinestringPtr ln; int iv; /* creating an SQL statement for querying Points-2 */ sql = "SELECT geom FROM points2 " "WHERE ROWID IN (SELECT pkid FROM rtree_points2 WHERE " "MbrIntersects(geom, BuildMbr(?, ?, ?, ?)) = 1) " "AND ST_Distance(geom, MakePoint(?, ?)) <= ? " "ORDER BY id"; ret = sqlite3_prepare_v2 (mem_db, sql, strlen (sql), &stmt, NULL); if (ret != SQLITE_OK) { spatialite_e ("SELECT Points2: error %d \"%s\"\n", sqlite3_errcode (mem_db), sqlite3_errmsg (mem_db)); goto error; } /* creating an SQL statement for inserting rows into the Points-1 table */ sql = "INSERT INTO points1 (id, geom, needs_interpolation) VALUES " "(NULL, MakePointZM(?, ?, ?, ?), ?)"; ret = sqlite3_prepare_v2 (mem_db, sql, strlen (sql), &stmt_pts, NULL); if (ret != SQLITE_OK) { spatialite_e ("INSERT INTO Points1: error %d \"%s\"\n", sqlite3_errcode (mem_db), sqlite3_errmsg (mem_db)); goto error; } /* starting a Transaction */ sql = "BEGIN"; ret = sqlite3_exec (mem_db, sql, NULL, NULL, NULL); if (ret != SQLITE_OK) { spatialite_e ("BEGIN: error: %d \"%s\"\n", sqlite3_errcode (mem_db), sqlite3_errmsg (mem_db)); goto error; } ln = geom->FirstLinestring; for (iv = 0; iv < ln->Points; iv++) { /* processing all Vertices from the input Linestring */ double x; double y; double z = 0.0; double m = 0.0; if (ln->DimensionModel == GAIA_XY_Z) { gaiaGetPointXYZ (ln->Coords, iv, &x, &y, &z); } else if (ln->DimensionModel == GAIA_XY_M) { gaiaGetPointXYM (ln->Coords, iv, &x, &y, &m); } else if (ln->DimensionModel == GAIA_XY_Z_M) { gaiaGetPointXYZM (ln->Coords, iv, &x, &y, &z, &m); } else { gaiaGetPoint (ln->Coords, iv, &x, &y); } /* processing a Vertex */ if (!do_drape_vertex (mem_db, stmt, stmt_pts, geom->Srid, tolerance, x, y, z, m)) goto error; } /* committing the pending Transaction */ sql = "COMMIT"; ret = sqlite3_exec (mem_db, sql, NULL, NULL, NULL); if (ret != SQLITE_OK) { spatialite_e ("COMMIT: error: %d \"%s\"\n", sqlite3_errcode (mem_db), sqlite3_errmsg (mem_db)); goto error; } sqlite3_finalize (stmt); sqlite3_finalize (stmt_pts); return 1; error: if (stmt != NULL) sqlite3_finalize (stmt); if (stmt_pts != NULL) sqlite3_finalize (stmt_pts); return 0; } static int get_prev_coords (int index, gaiaDynamicLinePtr dyn, double *z, double *m, double *dist) { /* retrieving the previous Point */ double ox; double oy; double x; double y; double zz; double mm; int ok = 0; int count = 0; gaiaPointPtr pt = dyn->First; while (pt != NULL) { if (index - 1 == count) { /* this is the previous point */ ox = pt->X; oy = pt->Y; zz = pt->Z; mm = pt->M; ok = 1; } if (index == count) { /* this is the current point */ x = pt->X; y = pt->Y; if (ok) { *z = zz; *m = mm; *dist = sqrt (((ox - x) * (ox - x)) + ((oy - y) * (oy - y))); return 1; } return 0; } count++; pt = pt->Next; } return 0; } static int get_next_coords (int index, gaiaDynamicLinePtr dyn, const char *interpolate, double *z, double *m, double *dist) { /* retrieving the next Point */ double ox; double oy; double x; double y; double d = 0.0; int ok = 0; int count = 0; gaiaPointPtr pt = dyn->First; while (pt != NULL) { if (index == count) { /* this is the current Point */ ox = pt->X; oy = pt->Y; ok = 1; } if (index < count) { /* this is a following Point */ if (!ok) return 0; x = pt->X; y = pt->Y; d += sqrt (((ox - x) * (ox - x)) + ((oy - y) * (oy - y))); if (*(interpolate + count) == 'N') { /* found a valid Point */ *z = pt->Z; *m = pt->M; *dist = d; return 1; } } count++; pt = pt->Next; } return 0; } static int do_update_coords (int index, gaiaDynamicLinePtr dyn, double z, double m) { /* updating the coords */ int count = 0; gaiaPointPtr pt = dyn->First; while (pt != NULL) { if (index == count) { pt->Z = z; pt->M = m; return 1; } count++; pt = pt->Next; } return 0; } static void do_interpolate_coords (int index, gaiaDynamicLinePtr dyn, char *interpolate) { /* attempting to interpolate coords */ double pz; double pm; double nz; double nm; double z; double m; double pdist; double ndist; double perc; if (!get_prev_coords (index, dyn, &pz, &pm, &pdist)) return; if (!get_next_coords (index, dyn, interpolate, &nz, &nm, &ndist)) return; perc = pdist / (pdist + ndist); z = pz + ((nz - pz) * perc); m = pm + ((nm - pm) * perc); if (do_update_coords (index, dyn, z, m)) *(interpolate + index) = 'I'; } static gaiaGeomCollPtr do_reassemble_line (sqlite3 * mem_db, int dims, int srid) { /* reassembling the Linestring to be returned */ gaiaGeomCollPtr geom = NULL; gaiaPointPtr pt; gaiaLinestringPtr ln; gaiaDynamicLinePtr dyn = gaiaAllocDynamicLine (); const char *sql; int ret; sqlite3_stmt *stmt = NULL; int count = 0; int needs_interpolation = 0; /* creating an SQL statement for querying Points-1 */ sql = "SELECT geom, needs_interpolation FROM points1 ORDER BY id"; ret = sqlite3_prepare_v2 (mem_db, sql, strlen (sql), &stmt, NULL); if (ret != SQLITE_OK) { spatialite_e ("SELECT Points1: error %d \"%s\"\n", sqlite3_errcode (mem_db), sqlite3_errmsg (mem_db)); goto end; } while (1) { /* scrolling the result set rows */ ret = sqlite3_step (stmt); if (ret == SQLITE_DONE) break; /* end of result set */ if (ret == SQLITE_ROW) { gaiaGeomCollPtr g = NULL; if (sqlite3_column_type (stmt, 0) == SQLITE_BLOB) { unsigned char *p_blob = (unsigned char *) sqlite3_column_blob (stmt, 0); int n_bytes = sqlite3_column_bytes (stmt, 0); g = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes); } if (g != NULL) { /* found a valid Point */ pt = g->FirstPoint; if (dims == GAIA_XY_Z_M) gaiaAppendPointZMToDynamicLine (dyn, pt->X, pt->Y, pt->Z, pt->M); else if (dims == GAIA_XY_Z) gaiaAppendPointZToDynamicLine (dyn, pt->X, pt->Y, pt->Z); else if (dims == GAIA_XY_M) gaiaAppendPointMToDynamicLine (dyn, pt->X, pt->Y, pt->M); else gaiaAppendPointToDynamicLine (dyn, pt->X, pt->Y); gaiaFreeGeomColl (g); } if (sqlite3_column_int (stmt, 1) == 1) needs_interpolation = 1; } } pt = dyn->First; while (pt) { /* counting how many points are there */ count++; pt = pt->Next; } if (count < 2) goto end; if (needs_interpolation) { /* attempting to interpolate missing coords */ int i; int max = count; char *interpolate = malloc (max + 1); memset (interpolate, '\0', max + 1); sqlite3_reset (stmt); /* rewinding the resultset */ count = 0; while (1) { /* scrolling the result set rows */ ret = sqlite3_step (stmt); if (ret == SQLITE_DONE) break; /* end of result set */ if (ret == SQLITE_ROW) { if (sqlite3_column_int (stmt, 1) == 0) *(interpolate + count) = 'N'; else *(interpolate + count) = 'Y'; count++; } } for (i = 0; i < max; i++) { if (*(interpolate + i) == 'Y') do_interpolate_coords (i, dyn, interpolate); } free (interpolate); } sqlite3_finalize (stmt); stmt = NULL; /* creating the final Linestring */ if (dims == GAIA_XY_Z_M) geom = gaiaAllocGeomCollXYZM (); else if (dims == GAIA_XY_Z) geom = gaiaAllocGeomCollXYZ (); else if (dims == GAIA_XY_M) geom = gaiaAllocGeomCollXYM (); else geom = gaiaAllocGeomColl (); geom->Srid = srid; ln = gaiaAddLinestringToGeomColl (geom, count); count = 0; pt = dyn->First; while (pt != NULL) { if (dims == GAIA_XY_Z_M) { gaiaSetPointXYZM (ln->Coords, count, pt->X, pt->Y, pt->Z, pt->M); } else if (dims == GAIA_XY_Z) { gaiaSetPointXYZ (ln->Coords, count, pt->X, pt->Y, pt->Z); } else if (dims == GAIA_XY_M) { gaiaSetPointXYM (ln->Coords, count, pt->X, pt->Y, pt->M); } else { gaiaSetPoint (ln->Coords, count, pt->X, pt->Y); } count++; pt = pt->Next; } end: gaiaFreeDynamicLine (dyn); if (stmt != NULL) sqlite3_finalize (stmt); return geom; } static gaiaGeomCollPtr do_reassemble_multi_point (sqlite3 * mem_db, int dims, int srid, int interpolated) { /* reassembling the MultiPoint to be returned */ gaiaGeomCollPtr geom = NULL; gaiaPointPtr pt; gaiaDynamicLinePtr dyn = gaiaAllocDynamicLine (); const char *sql; int ret; sqlite3_stmt *stmt = NULL; int count = 0; int needs_interpolation = 0; char *interpolate = NULL; /* creating an SQL statement for querying Points-1 */ sql = "SELECT geom, needs_interpolation FROM points1 ORDER BY id"; ret = sqlite3_prepare_v2 (mem_db, sql, strlen (sql), &stmt, NULL); if (ret != SQLITE_OK) { spatialite_e ("SELECT Points1: error %d \"%s\"\n", sqlite3_errcode (mem_db), sqlite3_errmsg (mem_db)); goto end; } while (1) { /* scrolling the result set rows */ ret = sqlite3_step (stmt); if (ret == SQLITE_DONE) break; /* end of result set */ if (ret == SQLITE_ROW) { gaiaGeomCollPtr g = NULL; if (sqlite3_column_type (stmt, 0) == SQLITE_BLOB) { unsigned char *p_blob = (unsigned char *) sqlite3_column_blob (stmt, 0); int n_bytes = sqlite3_column_bytes (stmt, 0); g = gaiaFromSpatiaLiteBlobWkb (p_blob, n_bytes); } if (g != NULL) { /* found a valid Point */ pt = g->FirstPoint; if (dims == GAIA_XY_Z_M) gaiaAppendPointZMToDynamicLine (dyn, pt->X, pt->Y, pt->Z, pt->M); else if (dims == GAIA_XY_Z) gaiaAppendPointZToDynamicLine (dyn, pt->X, pt->Y, pt->Z); else if (dims == GAIA_XY_M) gaiaAppendPointMToDynamicLine (dyn, pt->X, pt->Y, pt->M); else gaiaAppendPointToDynamicLine (dyn, pt->X, pt->Y); gaiaFreeGeomColl (g); } if (sqlite3_column_int (stmt, 1) == 1) needs_interpolation = 1; } } pt = dyn->First; while (pt) { /* counting how many points are there */ count++; pt = pt->Next; } if (count < 2) goto end; if (needs_interpolation) { /* attempting to interpolate missing coords */ int i; int max = count; interpolate = malloc (max + 1); memset (interpolate, '\0', max + 1); sqlite3_reset (stmt); /* rewinding the resultset */ count = 0; while (1) { /* scrolling the result set rows */ ret = sqlite3_step (stmt); if (ret == SQLITE_DONE) break; /* end of result set */ if (ret == SQLITE_ROW) { if (sqlite3_column_int (stmt, 1) == 0) *(interpolate + count) = 'N'; else *(interpolate + count) = 'Y'; count++; } } for (i = 0; i < max; i++) { if (*(interpolate + i) == 'Y') do_interpolate_coords (i, dyn, interpolate); } } sqlite3_finalize (stmt); stmt = NULL; /* creating the final MultiPoint */ if (dims == GAIA_XY_Z_M) geom = gaiaAllocGeomCollXYZM (); else if (dims == GAIA_XY_Z) geom = gaiaAllocGeomCollXYZ (); else if (dims == GAIA_XY_M) geom = gaiaAllocGeomCollXYM (); else geom = gaiaAllocGeomColl (); geom->Srid = srid; geom->DeclaredType = GAIA_MULTIPOINT; pt = dyn->First; count = 0; while (pt != NULL) { int ok = 0; if (*(interpolate + count) == 'Y') ok = 1; if (!interpolated && *(interpolate + count) == 'I') ok = 1; if (ok) { if (dims == GAIA_XY_Z_M) gaiaAddPointToGeomCollXYZM (geom, pt->X, pt->Y, pt->Z, pt->M); else if (dims == GAIA_XY_Z) gaiaAddPointToGeomCollXYZ (geom, pt->X, pt->Y, pt->Z); else if (dims == GAIA_XY_M) gaiaAddPointToGeomCollXYM (geom, pt->X, pt->Y, pt->M); else gaiaAddPointToGeomColl (geom, pt->X, pt->Y); } count++; pt = pt->Next; } end: if (interpolate != NULL) free (interpolate); gaiaFreeDynamicLine (dyn); if (stmt != NULL) sqlite3_finalize (stmt); return geom; } GAIAGEO_DECLARE gaiaGeomCollPtr gaiaDrapeLine (sqlite3 * db_handle, gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2, double tolerance) { /* will return a 3D Linestring by draping line-1 (2D) over line-2 (3D) */ int ret; sqlite3 *mem_db = NULL; void *cache; char *sql; char *err_msg = NULL; gaiaGeomCollPtr geom3 = NULL; /* arguments validation */ if (db_handle == NULL) return NULL; if (geom1 == NULL || geom2 == NULL) return NULL; if (tolerance < 0.0) return NULL; if (geom1->Srid != geom2->Srid) return NULL; if (geom1->DimensionModel != GAIA_XY) return NULL; if (geom2->DimensionModel != GAIA_XY_Z) return NULL; if (!gaia_do_check_linestring (geom1)) return NULL; if (!gaia_do_check_linestring (geom2)) return NULL; /* creating a Temporary MemoryDB */ ret = sqlite3_open_v2 (":memory:", &mem_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); if (ret != SQLITE_OK) { spatialite_e ("gaiaDrapeLine: sqlite3_open_v2 error: %s\n", sqlite3_errmsg (mem_db)); sqlite3_close (mem_db); return NULL; } cache = spatialite_alloc_connection (); spatialite_internal_init (mem_db, cache); /* initializing a minimal SpatiaLite DB */ sql = "SELECT InitSpatialMetadata(1, 'NONE')"; ret = sqlite3_exec (mem_db, sql, NULL, NULL, &err_msg); if (ret != SQLITE_OK) { spatialite_e ("gaiaDrapeLine: InitSpatialMetadata() error: %s\n", err_msg); sqlite3_free (err_msg); goto end; } /* creating the helper tables on the Temporary MemoryDB */ if (!do_create_points (mem_db, "points1")) goto end; if (!do_create_points (mem_db, "points2")) goto end; /* populating the Points-2 helper table */ if (!do_populate_points2 (mem_db, geom2)) goto end; /* draping the first line over Points-2 */ if (!do_drape_line (mem_db, geom1, tolerance)) goto end; /* building the final linestring to be returned */ geom3 = do_reassemble_line (mem_db, geom2->DimensionModel, geom2->Srid); end: /* releasing the Temporary MemoryDB */ ret = sqlite3_close (mem_db); if (ret != SQLITE_OK) spatialite_e ("gaiaDrapeLine: sqlite3_close() error: %s\n", sqlite3_errmsg (mem_db)); spatialite_internal_cleanup (cache); return geom3; } GAIAGEO_DECLARE gaiaGeomCollPtr gaiaDrapeLineExceptions (sqlite3 * db_handle, gaiaGeomCollPtr geom1, gaiaGeomCollPtr geom2, double tolerance, int interpolated) { /* * will return a 3D MultiPoint containing all Vertices from geom1 * not being correctly draped over geom2 */ int ret; sqlite3 *mem_db = NULL; void *cache; char *sql; char *err_msg = NULL; gaiaGeomCollPtr geom3 = NULL; /* arguments validation */ if (db_handle == NULL) return NULL; if (geom1 == NULL || geom2 == NULL) return NULL; if (tolerance < 0.0) return NULL; if (geom1->Srid != geom2->Srid) return NULL; if (geom1->DimensionModel != GAIA_XY) return NULL; if (geom2->DimensionModel != GAIA_XY_Z) return NULL; if (!gaia_do_check_linestring (geom1)) return NULL; if (!gaia_do_check_linestring (geom2)) return NULL; /* creating a Temporary MemoryDB */ ret = sqlite3_open_v2 (":memory:", &mem_db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); if (ret != SQLITE_OK) { spatialite_e ("gaiaDrapeLine: sqlite3_open_v2 error: %s\n", sqlite3_errmsg (mem_db)); sqlite3_close (mem_db); return NULL; } cache = spatialite_alloc_connection (); spatialite_internal_init (mem_db, cache); /* initializing a minimal SpatiaLite DB */ sql = "SELECT InitSpatialMetadata(1, 'NONE')"; ret = sqlite3_exec (mem_db, sql, NULL, NULL, &err_msg); if (ret != SQLITE_OK) { spatialite_e ("gaiaDrapeLineExceptions: InitSpatialMetadata() error: %s\n", err_msg); sqlite3_free (err_msg); goto end; } /* creating the helper tables on the Temporary MemoryDB */ if (!do_create_points (mem_db, "points1")) goto end; if (!do_create_points (mem_db, "points2")) goto end; /* populating the Points-2 helper table */ if (!do_populate_points2 (mem_db, geom2)) goto end; /* draping the first line over Points-2 */ if (!do_drape_line (mem_db, geom1, tolerance)) goto end; /* building the final MultiPoint to be returned */ geom3 = do_reassemble_multi_point (mem_db, geom2->DimensionModel, geom2->Srid, interpolated); end: /* releasing the Temporary MemoryDB */ ret = sqlite3_close (mem_db); if (ret != SQLITE_OK) spatialite_e ("gaiaDrapeLineExceptions: sqlite3_close() error: %s\n", sqlite3_errmsg (mem_db)); spatialite_internal_cleanup (cache); return geom3; }