/** * Returns the area in cartesian units. Area is negative if ring is oriented CCW, * positive if it is oriented CW and zero if the ring is degenerate or flat. * http://en.wikipedia.org/wiki/Shoelace_formula */ double ptarray_signed_area(const POINTARRAY *pa) { const POINT2D *P1; const POINT2D *P2; const POINT2D *P3; double sum = 0.0; double x0, x, y1, y2; int i; if (! pa || pa->npoints < 3 ) return 0.0; P1 = getPoint2d_cp(pa, 0); P2 = getPoint2d_cp(pa, 1); x0 = P1->x; for ( i = 1; i < pa->npoints - 1; i++ ) { P3 = getPoint2d_cp(pa, i+1); x = P2->x - x0; y1 = P3->y; y2 = P1->y; sum += x * (y2-y1); /* Move forwards! */ P1 = P2; P2 = P3; } return sum / 2.0; }
int pt_in_ring_2d(const POINT2D *p, const POINTARRAY *ring) { int cn = 0; /* the crossing number counter */ int i; const POINT2D *v1, *v2; const POINT2D *first, *last; first = getPoint2d_cp(ring, 0); last = getPoint2d_cp(ring, ring->npoints-1); if ( memcmp(first, last, sizeof(POINT2D)) ) { lwerror("pt_in_ring_2d: V[n] != V[0] (%g %g != %g %g)", first->x, first->y, last->x, last->y); return LW_FALSE; } LWDEBUGF(2, "pt_in_ring_2d called with point: %g %g", p->x, p->y); /* printPA(ring); */ /* loop through all edges of the polygon */ v1 = getPoint2d_cp(ring, 0); for (i=0; i<ring->npoints-1; i++) { double vt; v2 = getPoint2d_cp(ring, i+1); /* edge from vertex i to vertex i+1 */ if ( /* an upward crossing */ ((v1->y <= p->y) && (v2->y > p->y)) /* a downward crossing */ || ((v1->y > p->y) && (v2->y <= p->y)) ) { vt = (double)(p->y - v1->y) / (v2->y - v1->y); /* P->x <intersect */ if (p->x < v1->x + vt * (v2->x - v1->x)) { /* a valid crossing of y=p->y right of p->x */ ++cn; } } v1 = v2; } LWDEBUGF(3, "pt_in_ring_2d returning %d", cn&1); return (cn&1); /* 0 if even (out), and 1 if odd (in) */ }
static void ptarray_dp_findsplit(POINTARRAY *pts, int p1, int p2, int *split, double *dist) { int k; const POINT2D *pk, *pa, *pb; double tmp, d; LWDEBUG(4, "function called"); *split = p1; d = -1; if (p1 + 1 < p2) { pa = getPoint2d_cp(pts, p1); pb = getPoint2d_cp(pts, p2); LWDEBUGF(4, "P%d(%f,%f) to P%d(%f,%f)", p1, pa->x, pa->y, p2, pb->x, pb->y); for (k=p1+1; k<p2; k++) { pk = getPoint2d_cp(pts, k); LWDEBUGF(4, "P%d(%f,%f)", k, pk->x, pk->y); /* distance computation */ tmp = distance2d_sqr_pt_seg(pk, pa, pb); if (tmp > d) { d = tmp; /* record the maximum */ *split = k; LWDEBUGF(4, "P%d is farthest (%g)", k, d); } } *dist = d; } /* length---should be redone if can == 0 */ else { LWDEBUG(3, "segment too short, no split/no dist"); *dist = -1; } }
static size_t pointArray_to_geojson(POINTARRAY *pa, char *output, int precision) { int i; char *ptr; #define BUFSIZE OUT_MAX_DIGS_DOUBLE+OUT_MAX_DOUBLE_PRECISION char x[BUFSIZE+1]; char y[BUFSIZE+1]; char z[BUFSIZE+1]; assert ( precision <= OUT_MAX_DOUBLE_PRECISION ); /* Ensure a terminating NULL at the end of buffers * so that we don't need to check for truncation * inprint_double */ x[BUFSIZE] = '\0'; y[BUFSIZE] = '\0'; z[BUFSIZE] = '\0'; ptr = output; /* TODO: rewrite this loop to be simpler and possibly quicker */ if (!FLAGS_GET_Z(pa->flags)) { for (i=0; i<pa->npoints; i++) { const POINT2D *pt; pt = getPoint2d_cp(pa, i); lwprint_double(pt->x, precision, x, BUFSIZE); trim_trailing_zeros(x); lwprint_double(pt->y, precision, y, BUFSIZE); trim_trailing_zeros(y); if ( i ) ptr += sprintf(ptr, ","); ptr += sprintf(ptr, "[%s,%s]", x, y); } } else { for (i=0; i<pa->npoints; i++) { const POINT3DZ *pt; pt = getPoint3dz_cp(pa, i); lwprint_double(pt->x, precision, x, BUFSIZE); trim_trailing_zeros(x); lwprint_double(pt->y, precision, y, BUFSIZE); trim_trailing_zeros(y); lwprint_double(pt->z, precision, z, BUFSIZE); trim_trailing_zeros(z); if ( i ) ptr += sprintf(ptr, ","); ptr += sprintf(ptr, "[%s,%s,%s]", x, y, z); } } return (ptr-output); }
int ptarray_npoints_in_rect(const POINTARRAY *pa, const GBOX *gbox) { const POINT2D *pt; int n = 0; int i; for ( i = 0; i < pa->npoints; i++ ) { pt = getPoint2d_cp(pa, i); if ( gbox_contains_point2d(gbox, pt) ) n++; } return n; }
/** * Find the 2d length of the given #POINTARRAY (even if it's 3d) */ double ptarray_length_2d(const POINTARRAY *pts) { double dist = 0.0; int i; const POINT2D *frm; const POINT2D *to; if ( pts->npoints < 2 ) return 0.0; frm = getPoint2d_cp(pts, 0); for ( i=1; i < pts->npoints; i++ ) { to = getPoint2d_cp(pts, i); dist += sqrt( ((frm->x - to->x)*(frm->x - to->x)) + ((frm->y - to->y)*(frm->y - to->y)) ); frm = to; } return dist; }
/** * Find the 2d length of the given #POINTARRAY, using circular * arc interpolation between each coordinate triple. * Length(A1, A2, A3, A4, A5) = Length(A1, A2, A3)+Length(A3, A4, A5) */ double ptarray_arc_length_2d(const POINTARRAY *pts) { double dist = 0.0; int i; const POINT2D *a1; const POINT2D *a2; const POINT2D *a3; if ( pts->npoints % 2 != 1 ) lwerror("arc point array with even number of points"); a1 = getPoint2d_cp(pts, 0); for ( i=2; i < pts->npoints; i += 2 ) { a2 = getPoint2d_cp(pts, i-1); a3 = getPoint2d_cp(pts, i); dist += lw_arc_length(a1, a2, a3); a1 = a3; } return dist; }
/** * POINT * Read a WKB point, starting just after the endian byte, * type number and optional srid number. * Advance the parse state forward appropriately. * WKB point has just a set of doubles, with the quantity depending on the * dimension of the point, so this looks like a special case of the above * with only one point. */ static LWPOINT* lwpoint_from_wkb_state(wkb_parse_state *s) { static uint32_t npoints = 1; POINTARRAY *pa = NULL; size_t pa_size; uint32_t ndims = 2; const POINT2D *pt; /* Count the dimensions. */ if( s->has_z ) ndims++; if( s->has_m ) ndims++; pa_size = ndims * WKB_DOUBLE_SIZE; /* Does the data we want to read exist? */ wkb_parse_state_check(s, pa_size); /* If we're in a native endianness, we can just copy the data directly! */ if( ! s->swap_bytes ) { pa = ptarray_construct_copy_data(s->has_z, s->has_m, npoints, (uint8_t*)s->pos); s->pos += pa_size; } /* Otherwise we have to read each double, separately */ else { int i = 0; double *dlist; pa = ptarray_construct(s->has_z, s->has_m, npoints); dlist = (double*)(pa->serialized_pointlist); for( i = 0; i < ndims; i++ ) { dlist[i] = double_from_wkb_state(s); } } /* Check for POINT(NaN NaN) ==> POINT EMPTY */ pt = getPoint2d_cp(pa, 0); if ( isnan(pt->x) && isnan(pt->y) ) { ptarray_free(pa); return lwpoint_construct_empty(s->srid, s->has_z, s->has_m); } else { return lwpoint_construct(s->srid, NULL, pa); } }
int ptarrayarc_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number) { int wn = 0; int i, side; const POINT2D *seg1; const POINT2D *seg2; const POINT2D *seg3; GBOX gbox; /* Check for not an arc ring (always have odd # of points) */ if ( (pa->npoints % 2) == 0 ) { lwerror("ptarrayarc_contains_point called with even number of points"); return LW_OUTSIDE; } /* Check for not an arc ring (always have >= 3 points) */ if ( pa->npoints < 3 ) { lwerror("ptarrayarc_contains_point called too-short pointarray"); return LW_OUTSIDE; } /* Check for unclosed case */ seg1 = getPoint2d_cp(pa, 0); seg3 = getPoint2d_cp(pa, pa->npoints-1); if ( check_closed && ! p2d_same(seg1, seg3) ) { lwerror("ptarrayarc_contains_point called on unclosed ring"); return LW_OUTSIDE; } /* OK, it's closed. Is it just one circle? */ else if ( p2d_same(seg1, seg3) && pa->npoints == 3 ) { double radius, d; POINT2D c; seg2 = getPoint2d_cp(pa, 1); /* Wait, it's just a point, so it can't contain anything */ if ( lw_arc_is_pt(seg1, seg2, seg3) ) return LW_OUTSIDE; /* See if the point is within the circle radius */ radius = lw_arc_center(seg1, seg2, seg3, &c); d = distance2d_pt_pt(pt, &c); if ( FP_EQUALS(d, radius) ) return LW_BOUNDARY; /* Boundary of circle */ else if ( d < radius ) return LW_INSIDE; /* Inside circle */ else return LW_OUTSIDE; /* Outside circle */ } else if ( p2d_same(seg1, pt) || p2d_same(seg3, pt) ) { return LW_BOUNDARY; /* Boundary case */ } /* Start on the ring */ seg1 = getPoint2d_cp(pa, 0); for ( i=1; i < pa->npoints; i += 2 ) { seg2 = getPoint2d_cp(pa, i); seg3 = getPoint2d_cp(pa, i+1); /* Catch an easy boundary case */ if( p2d_same(seg3, pt) ) return LW_BOUNDARY; /* Skip arcs that have no size */ if ( lw_arc_is_pt(seg1, seg2, seg3) ) { seg1 = seg3; continue; } /* Only test segments in our vertical range */ lw_arc_calculate_gbox_cartesian_2d(seg1, seg2, seg3, &gbox); if ( pt->y > gbox.ymax || pt->y < gbox.ymin ) { seg1 = seg3; continue; } /* Outside of horizontal range, and not between end points we also skip */ if ( (pt->x > gbox.xmax || pt->x < gbox.xmin) && (pt->y > FP_MAX(seg1->y, seg3->y) || pt->y < FP_MIN(seg1->y, seg3->y)) ) { seg1 = seg3; continue; } side = lw_arc_side(seg1, seg2, seg3, pt); /* On the boundary */ if ( (side == 0) && lw_pt_in_arc(pt, seg1, seg2, seg3) ) { return LW_BOUNDARY; } /* Going "up"! Point to left of arc. */ if ( side < 0 && (seg1->y <= pt->y) && (pt->y < seg3->y) ) { wn++; } /* Going "down"! */ if ( side > 0 && (seg2->y <= pt->y) && (pt->y < seg1->y) ) { wn--; } /* Inside the arc! */ if ( pt->x <= gbox.xmax && pt->x >= gbox.xmin ) { POINT2D C; double radius = lw_arc_center(seg1, seg2, seg3, &C); double d = distance2d_pt_pt(pt, &C); /* On the boundary! */ if ( d == radius ) return LW_BOUNDARY; /* Within the arc! */ if ( d < radius ) { /* Left side, increment winding number */ if ( side < 0 ) wn++; /* Right side, decrement winding number */ if ( side > 0 ) wn--; } } seg1 = seg3; } /* Sent out the winding number for calls that are building on this as a primitive */ if ( winding_number ) *winding_number = wn; /* Outside */ if (wn == 0) { return LW_OUTSIDE; } /* Inside */ return LW_INSIDE; }
int ptarray_contains_point_partial(const POINTARRAY *pa, const POINT2D *pt, int check_closed, int *winding_number) { int wn = 0; int i; double side; const POINT2D *seg1; const POINT2D *seg2; double ymin, ymax; seg1 = getPoint2d_cp(pa, 0); seg2 = getPoint2d_cp(pa, pa->npoints-1); if ( check_closed && ! p2d_same(seg1, seg2) ) lwerror("ptarray_contains_point called on unclosed ring"); for ( i=1; i < pa->npoints; i++ ) { seg2 = getPoint2d_cp(pa, i); /* Zero length segments are ignored. */ if ( seg1->x == seg2->x && seg1->y == seg2->y ) { seg1 = seg2; continue; } ymin = FP_MIN(seg1->y, seg2->y); ymax = FP_MAX(seg1->y, seg2->y); /* Only test segments in our vertical range */ if ( pt->y > ymax || pt->y < ymin ) { seg1 = seg2; continue; } side = lw_segment_side(seg1, seg2, pt); /* * A point on the boundary of a ring is not contained. * WAS: if (fabs(side) < 1e-12), see #852 */ if ( (side == 0) && lw_pt_in_seg(pt, seg1, seg2) ) { return LW_BOUNDARY; } /* * If the point is to the left of the line, and it's rising, * then the line is to the right of the point and * circling counter-clockwise, so incremement. */ if ( (side < 0) && (seg1->y <= pt->y) && (pt->y < seg2->y) ) { wn++; } /* * If the point is to the right of the line, and it's falling, * then the line is to the right of the point and circling * clockwise, so decrement. */ else if ( (side > 0) && (seg2->y <= pt->y) && (pt->y < seg1->y) ) { wn--; } seg1 = seg2; } /* Sent out the winding number for calls that are building on this as a primitive */ if ( winding_number ) *winding_number = wn; /* Outside */ if (wn == 0) { return LW_OUTSIDE; } /* Inside */ return LW_INSIDE; }
/* * Returns a POINTARRAY with consecutive equal points * removed. Equality test on all dimensions of input. * * Always returns a newly allocated object. * */ POINTARRAY * ptarray_remove_repeated_points_minpoints(const POINTARRAY *in, double tolerance, int minpoints) { POINTARRAY* out; size_t ptsize; size_t ipn, opn; const POINT2D *last_point, *this_point; double tolsq = tolerance * tolerance; if ( minpoints < 1 ) minpoints = 1; LWDEBUGF(3, "%s called", __func__); /* Single or zero point arrays can't have duplicates */ if ( in->npoints < 3 ) return ptarray_clone_deep(in); ptsize = ptarray_point_size(in); LWDEBUGF(3, " ptsize: %d", ptsize); /* Allocate enough space for all points */ out = ptarray_construct(FLAGS_GET_Z(in->flags), FLAGS_GET_M(in->flags), in->npoints); /* Now fill up the actual points (NOTE: could be optimized) */ opn=1; /* Keep the first point */ memcpy(getPoint_internal(out, 0), getPoint_internal(in, 0), ptsize); last_point = getPoint2d_cp(in, 0); LWDEBUGF(3, " first point copied, out points: %d", opn); for ( ipn = 1; ipn < in->npoints; ++ipn) { this_point = getPoint2d_cp(in, ipn); if ( ipn < in->npoints-minpoints+1 || opn >= minpoints ) /* need extra points to hit minponts */ { if ( (tolerance == 0 && memcmp(getPoint_internal(in, ipn-1), getPoint_internal(in, ipn), ptsize) == 0) || /* exact dupe */ (tolerance > 0.0 && distance2d_sqr_pt_pt(last_point, this_point) <= tolsq) /* within the removal tolerance */ ) continue; } /* * The point is different (see above) from the previous, * so we add it to output */ memcpy(getPoint_internal(out, opn++), getPoint_internal(in, ipn), ptsize); last_point = this_point; LWDEBUGF(3, " Point %d differs from point %d. Out points: %d", ipn, ipn-1, opn); } /* Keep the last point */ if ( memcmp(last_point, getPoint_internal(in, ipn-1), ptsize) != 0 ) { memcpy(getPoint_internal(out, opn-1), getPoint_internal(in, ipn-1), ptsize); } LWDEBUGF(3, " in:%d out:%d", out->npoints, opn); out->npoints = opn; return out; }
/* * Given a point, returns the location of closest point on pointarray * and, optionally, it's actual distance from the point array. */ double ptarray_locate_point(const POINTARRAY *pa, const POINT4D *p4d, double *mindistout, POINT4D *proj4d) { double mindist=-1; double tlen, plen; int t, seg=-1; POINT4D start4d, end4d, projtmp; POINT2D proj, p; const POINT2D *start = NULL, *end = NULL; /* Initialize our 2D copy of the input parameter */ p.x = p4d->x; p.y = p4d->y; if ( ! proj4d ) proj4d = &projtmp; start = getPoint2d_cp(pa, 0); /* If the pointarray has only one point, the nearest point is */ /* just that point */ if ( pa->npoints == 1 ) { getPoint4d_p(pa, 0, proj4d); if ( mindistout ) *mindistout = distance2d_pt_pt(&p, start); return 0.0; } /* Loop through pointarray looking for nearest segment */ for (t=1; t<pa->npoints; t++) { double dist; end = getPoint2d_cp(pa, t); dist = distance2d_pt_seg(&p, start, end); if (t==1 || dist < mindist ) { mindist = dist; seg=t-1; } if ( mindist == 0 ) { LWDEBUG(3, "Breaking on mindist=0"); break; } start = end; } if ( mindistout ) *mindistout = mindist; LWDEBUGF(3, "Closest segment: %d", seg); LWDEBUGF(3, "mindist: %g", mindist); /* * We need to project the * point on the closest segment. */ getPoint4d_p(pa, seg, &start4d); getPoint4d_p(pa, seg+1, &end4d); closest_point_on_segment(p4d, &start4d, &end4d, proj4d); /* Copy 4D values into 2D holder */ proj.x = proj4d->x; proj.y = proj4d->y; LWDEBUGF(3, "Closest segment:%d, npoints:%d", seg, pa->npoints); /* For robustness, force 1 when closest point == endpoint */ if ( (seg >= (pa->npoints-2)) && p2d_same(&proj, end) ) { return 1.0; } LWDEBUGF(3, "Closest point on segment: %g,%g", proj.x, proj.y); tlen = ptarray_length_2d(pa); LWDEBUGF(3, "tlen %g", tlen); /* Location of any point on a zero-length line is 0 */ /* See http://trac.osgeo.org/postgis/ticket/1772#comment:2 */ if ( tlen == 0 ) return 0; plen=0; start = getPoint2d_cp(pa, 0); for (t=0; t<seg; t++, start=end) { end = getPoint2d_cp(pa, t+1); plen += distance2d_pt_pt(start, end); LWDEBUGF(4, "Segment %d made plen %g", t, plen); } plen+=distance2d_pt_pt(&proj, start); LWDEBUGF(3, "plen %g, tlen %g", plen, tlen); return plen/tlen; }
int * lwgeom_cluster_2d_kmeans(const LWGEOM **geoms, int ngeoms, int k) { int i; int num_centroids = 0; LWGEOM **centroids; POINT2D *centers_raw; const POINT2D *cp; POINT2D min = { DBL_MAX, DBL_MAX }; POINT2D max = { -DBL_MAX, -DBL_MAX }; double dx, dy; kmeans_config config; kmeans_result result; int *seen; int sidx = 0; assert(k>0); assert(ngeoms>0); assert(geoms); /* Initialize our static structs */ memset(&config, 0, sizeof(kmeans_config)); memset(&result, 0, sizeof(kmeans_result)); if (ngeoms<k) { lwerror("%s: number of geometries is less than the number of clusters requested", __func__); } /* We'll hold the temporary centroid objects here */ centroids = lwalloc(sizeof(LWGEOM*) * ngeoms); memset(centroids, 0, sizeof(LWGEOM*) * ngeoms); /* The vector of cluster means. We have to allocate a */ /* chunk of memory for these because we'll be mutating them */ /* in the kmeans algorithm */ centers_raw = lwalloc(sizeof(POINT2D) * k); memset(centers_raw, 0, sizeof(POINT2D) * k); /* K-means configuration setup */ config.objs = lwalloc(sizeof(Pointer) * ngeoms); config.num_objs = ngeoms; config.clusters = lwalloc(sizeof(int) * ngeoms); config.centers = lwalloc(sizeof(Pointer) * k); config.k = k; config.max_iterations = 0; config.distance_method = lwkmeans_pt_distance; config.centroid_method = lwkmeans_pt_centroid; /* Clean the memory */ memset(config.objs, 0, sizeof(Pointer) * ngeoms); memset(config.clusters, 0, sizeof(int) * ngeoms); memset(config.centers, 0, sizeof(Pointer) * k); /* Prepare the list of object pointers for K-means */ for (i = 0; i < ngeoms; i++) { const LWGEOM *geom = geoms[i]; LWPOINT *lwpoint; /* Null/empty geometries get a NULL pointer */ if ((!geom) || lwgeom_is_empty(geom)) { config.objs[i] = NULL; continue; } /* If the input is a point, use its coordinates */ /* If its not a point, convert it to one via centroid */ if (lwgeom_get_type(geom) != POINTTYPE) { LWGEOM *centroid = lwgeom_centroid(geom); if ((!centroid) || lwgeom_is_empty(centroid)) { config.objs[i] = NULL; continue; } centroids[num_centroids++] = centroid; lwpoint = lwgeom_as_lwpoint(centroid); } else { lwpoint = lwgeom_as_lwpoint(geom); } /* Store a pointer to the POINT2D we are interested in */ cp = getPoint2d_cp(lwpoint->point, 0); config.objs[i] = (Pointer)cp; /* Since we're already here, let's calculate the extrema of the set */ if (cp->x < min.x) min.x = cp->x; if (cp->y < min.y) min.y = cp->y; if (cp->x > max.x) max.x = cp->x; if (cp->y > max.y) max.y = cp->y; } /* * We map a uniform assignment of points in the area covered by the set * onto actual points in the set */ dx = (max.x - min.x)/k; dy = (max.y - min.y)/k; seen = lwalloc(sizeof(int)*config.k); memset(seen, 0, sizeof(int)*config.k); for (i = 0; i < k; i++) { int closest; POINT2D p; int j; /* Calculate a point in the range */ p.x = min.x + dx * (i + 0.5); p.y = min.y + dy * (i + 0.5); /* Find the data point closest to the calculated point */ closest = lwkmeans_pt_closest(config.objs, config.num_objs, &p); /* If something is terrible wrong w/ data, cannot find a closest */ if (closest < 0) lwerror("unable to calculate cluster seed points, too many NULLs or empties?"); /* Ensure we aren't already using that point as a seed */ j = 0; while(j < sidx) { if (seen[j] == closest) { closest = (closest + 1) % config.num_objs; j = 0; } else { j++; } } seen[sidx++] = closest; /* Copy the point coordinates into the initial centers array */ /* This is ugly, but the centers array is an array of */ /* pointers to points, not an array of points */ centers_raw[i] = *((POINT2D*)config.objs[closest]); config.centers[i] = &(centers_raw[i]); } result = kmeans(&config); /* Before error handling, might as well clean up all the inputs */ lwfree(config.objs); lwfree(config.centers); lwfree(centers_raw); lwfree(centroids); lwfree(seen); /* Good result */ if (result == KMEANS_OK) return config.clusters; /* Bad result, not going to need the answer */ lwfree(config.clusters); if (result == KMEANS_EXCEEDED_MAX_ITERATIONS) { lwerror("%s did not converge after %d iterations", __func__, config.max_iterations); return NULL; } /* Unknown error */ return NULL; }
/** ** @brief lwline_crossing_direction: returns the kind of #CG_LINE_CROSS_TYPE behavior of 2 linestrings ** @param l1 first line string ** @param l2 second line string ** @return a #CG_LINE_CROSS_TYPE ** LINE_NO_CROSS = 0 ** LINE_CROSS_LEFT = -1 ** LINE_CROSS_RIGHT = 1 ** LINE_MULTICROSS_END_LEFT = -2 ** LINE_MULTICROSS_END_RIGHT = 2 ** LINE_MULTICROSS_END_SAME_FIRST_LEFT = -3 ** LINE_MULTICROSS_END_SAME_FIRST_RIGHT = 3 ** */ int lwline_crossing_direction(const LWLINE *l1, const LWLINE *l2) { int i = 0, j = 0; const POINT2D *p1, *p2, *q1, *q2; POINTARRAY *pa1 = NULL, *pa2 = NULL; int cross_left = 0; int cross_right = 0; int first_cross = 0; int this_cross = 0; pa1 = (POINTARRAY*)l1->points; pa2 = (POINTARRAY*)l2->points; /* One-point lines can't intersect (and shouldn't exist). */ if ( pa1->npoints < 2 || pa2->npoints < 2 ) return LINE_NO_CROSS; LWDEBUGF(4, "l1 = %s", lwgeom_to_ewkt((LWGEOM*)l1)); LWDEBUGF(4, "l2 = %s", lwgeom_to_ewkt((LWGEOM*)l2)); /* Initialize first point of q */ q1 = getPoint2d_cp(pa2, 0); for ( i = 1; i < pa2->npoints; i++ ) { /* Update second point of q to next value */ q2 = getPoint2d_cp(pa2, i); /* Initialize first point of p */ p1 = getPoint2d_cp(pa1, 0); for ( j = 1; j < pa1->npoints; j++ ) { /* Update second point of p to next value */ p2 = getPoint2d_cp(pa1, j); this_cross = lw_segment_intersects(p1, p2, q1, q2); LWDEBUGF(4, "i=%d, j=%d (%.8g %.8g, %.8g %.8g)", this_cross, i, j, p1->x, p1->y, p2->x, p2->y); if ( this_cross == SEG_CROSS_LEFT ) { LWDEBUG(4,"this_cross == SEG_CROSS_LEFT"); cross_left++; if ( ! first_cross ) first_cross = SEG_CROSS_LEFT; } if ( this_cross == SEG_CROSS_RIGHT ) { LWDEBUG(4,"this_cross == SEG_CROSS_RIGHT"); cross_right++; if ( ! first_cross ) first_cross = SEG_CROSS_LEFT; } /* ** Crossing at a co-linearity can be turned handled by extending ** segment to next vertext and seeing if the end points straddle ** the co-linear segment. */ if ( this_cross == SEG_COLINEAR ) { LWDEBUG(4,"this_cross == SEG_COLINEAR"); /* TODO: Add logic here and in segment_intersects() continue; */ } LWDEBUG(4,"this_cross == SEG_NO_INTERSECTION"); /* Turn second point of p into first point */ p1 = p2; } /* Turn second point of q into first point */ q1 = q2; } LWDEBUGF(4, "first_cross=%d, cross_left=%d, cross_right=%d", first_cross, cross_left, cross_right); if ( !cross_left && !cross_right ) return LINE_NO_CROSS; if ( !cross_left && cross_right == 1 ) return LINE_CROSS_RIGHT; if ( !cross_right && cross_left == 1 ) return LINE_CROSS_LEFT; if ( cross_left - cross_right == 1 ) return LINE_MULTICROSS_END_LEFT; if ( cross_left - cross_right == -1 ) return LINE_MULTICROSS_END_RIGHT; if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_LEFT ) return LINE_MULTICROSS_END_SAME_FIRST_LEFT; if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_RIGHT ) return LINE_MULTICROSS_END_SAME_FIRST_RIGHT; return LINE_NO_CROSS; }