/** * Re-write the measure ordinate (or add one, if it isn't already there) interpolating * the measure between the supplied start and end values. */ LWMLINE* lwmline_measured_from_lwmline(const LWMLINE *lwmline, double m_start, double m_end) { int i = 0; int hasm = 0, hasz = 0; double length = 0.0, length_so_far = 0.0; double m_range = m_end - m_start; LWGEOM **geoms = NULL; if ( lwmline->type != MULTILINETYPE ) { lwerror("lwmline_measured_from_lmwline: only multiline types supported"); return NULL; } hasz = FLAGS_GET_Z(lwmline->flags); hasm = 1; /* Calculate the total length of the mline */ for ( i = 0; i < lwmline->ngeoms; i++ ) { LWLINE *lwline = (LWLINE*)lwmline->geoms[i]; if ( lwline->points && lwline->points->npoints > 1 ) { length += ptarray_length_2d(lwline->points); } } if ( lwgeom_is_empty((LWGEOM*)lwmline) ) { return (LWMLINE*)lwcollection_construct_empty(MULTILINETYPE, lwmline->srid, hasz, hasm); } geoms = lwalloc(sizeof(LWGEOM*) * lwmline->ngeoms); for ( i = 0; i < lwmline->ngeoms; i++ ) { double sub_m_start, sub_m_end; double sub_length = 0.0; LWLINE *lwline = (LWLINE*)lwmline->geoms[i]; if ( lwline->points && lwline->points->npoints > 1 ) { sub_length = ptarray_length_2d(lwline->points); } sub_m_start = (m_start + m_range * length_so_far / length); sub_m_end = (m_start + m_range * (length_so_far + sub_length) / length); geoms[i] = (LWGEOM*)lwline_measured_from_lwline(lwline, sub_m_start, sub_m_end); length_so_far += sub_length; } return (LWMLINE*)lwcollection_construct(lwmline->type, lwmline->srid, NULL, lwmline->ngeoms, geoms); }
double lwtriangle_perimeter_2d(const LWTRIANGLE *triangle) { if( triangle->points ) return ptarray_length_2d(triangle->points); else return 0.0; }
/** * Compute the sum of polygon rings length (forcing 2d computation). * Could use a more numerically stable calculator... */ double lwpoly_perimeter_2d(const LWPOLY *poly) { double result=0.0; int i; LWDEBUGF(2, "in lwgeom_polygon_perimeter (%d rings)", poly->nrings); for (i=0; i<poly->nrings; i++) result += ptarray_length_2d(poly->rings[i]); return result; }
/** * Find the 3d/2d length of the given #POINTARRAY * (depending on its dimensionality) */ double ptarray_length(const POINTARRAY *pts) { double dist = 0.0; int i; POINT3DZ frm; POINT3DZ to; if ( pts->npoints < 2 ) return 0.0; /* compute 2d length if 3d is not available */ if ( ! FLAGS_GET_Z(pts->flags) ) return ptarray_length_2d(pts); for (i=0; i<pts->npoints-1; i++) { getPoint3dz_p(pts, i, &frm); getPoint3dz_p(pts, i+1, &to); dist += sqrt( ( (frm.x - to.x)*(frm.x - to.x) ) + ((frm.y - to.y)*(frm.y - to.y) ) + ((frm.z - to.z)*(frm.z - to.z) ) ); } return dist; }
int lwline_split_by_point_to(const LWLINE* lwline_in, const LWPOINT* blade_in, LWMLINE* v) { double loc, dist; POINT4D pt, pt_projected; POINTARRAY* pa1; POINTARRAY* pa2; double vstol; /* vertex snap tolerance */ /* Possible outcomes: * * 1. The point is not on the line or on the boundary * -> Leave collection untouched, return 0 * 2. The point is on the boundary * -> Push 1 element on the collection: * o the original line * -> Return 1 * 3. The point is in the line * -> Push 2 elements on the collection: * o start_point - cut_point * o cut_point - last_point * -> Return 2 */ getPoint4d_p(blade_in->point, 0, &pt); loc = ptarray_locate_point(lwline_in->points, &pt, &dist, &pt_projected); /* lwnotice("Location: %g -- Distance: %g", loc, dist); */ if ( dist > 0 ) /* TODO: accept a tolerance ? */ { /* No intersection */ return 0; } if ( loc == 0 || loc == 1 ) { /* Intersection is on the boundary */ return 1; } /* There is a real intersection, let's get two substrings */ /* Compute vertex snap tolerance based on line length * TODO: take as parameter ? */ vstol = ptarray_length_2d(lwline_in->points) / 1e14; pa1 = ptarray_substring(lwline_in->points, 0, loc, vstol); pa2 = ptarray_substring(lwline_in->points, loc, 1, vstol); /* NOTE: I've seen empty pointarrays with loc != 0 and loc != 1 */ if ( pa1->npoints == 0 || pa2->npoints == 0 ) { ptarray_free(pa1); ptarray_free(pa2); /* Intersection is on the boundary */ return 1; } lwmline_add_lwline(v, lwline_construct(SRID_UNKNOWN, NULL, pa1)); lwmline_add_lwline(v, lwline_construct(SRID_UNKNOWN, NULL, pa2)); return 2; }
Datum LWGEOM_line_interpolate_point(PG_FUNCTION_ARGS) { GSERIALIZED *geom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); double distance = PG_GETARG_FLOAT8(1); LWLINE *line; LWPOINT *point; POINTARRAY *ipa, *opa; POINT4D pt; int nsegs, i; double length, slength, tlength; if ( distance < 0 || distance > 1 ) { elog(ERROR,"line_interpolate_point: 2nd arg isnt within [0,1]"); PG_RETURN_NULL(); } if ( gserialized_get_type(geom) != LINETYPE ) { elog(ERROR,"line_interpolate_point: 1st arg isnt a line"); PG_RETURN_NULL(); } line = lwgeom_as_lwline(lwgeom_from_gserialized(geom)); ipa = line->points; /* If distance is one of the two extremes, return the point on that * end rather than doing any expensive computations */ if ( distance == 0.0 || distance == 1.0 ) { if ( distance == 0.0 ) getPoint4d_p(ipa, 0, &pt); else getPoint4d_p(ipa, ipa->npoints-1, &pt); opa = ptarray_construct_reference_data(FLAGS_GET_Z(line->flags), FLAGS_GET_M(line->flags), 1, (uint8_t*)&pt); point = lwpoint_construct(line->srid, NULL, opa); PG_RETURN_POINTER(geometry_serialize(lwpoint_as_lwgeom(point))); } /* Interpolate a point on the line */ nsegs = ipa->npoints - 1; length = ptarray_length_2d(ipa); tlength = 0; for ( i = 0; i < nsegs; i++ ) { POINT4D p1, p2; POINT4D *p1ptr=&p1, *p2ptr=&p2; /* don't break * strict-aliasing rules */ getPoint4d_p(ipa, i, &p1); getPoint4d_p(ipa, i+1, &p2); /* Find the relative length of this segment */ slength = distance2d_pt_pt((POINT2D*)p1ptr, (POINT2D*)p2ptr)/length; /* If our target distance is before the total length we've seen * so far. create a new point some distance down the current * segment. */ if ( distance < tlength + slength ) { double dseg = (distance - tlength) / slength; interpolate_point4d(&p1, &p2, &pt, dseg); opa = ptarray_construct_reference_data(FLAGS_GET_Z(line->flags), FLAGS_GET_M(line->flags), 1, (uint8_t*)&pt); point = lwpoint_construct(line->srid, NULL, opa); PG_RETURN_POINTER(geometry_serialize(lwpoint_as_lwgeom(point))); } tlength += slength; } /* Return the last point on the line. This shouldn't happen, but * could if there's some floating point rounding errors. */ getPoint4d_p(ipa, ipa->npoints-1, &pt); opa = ptarray_construct_reference_data(FLAGS_GET_Z(line->flags), FLAGS_GET_M(line->flags), 1, (uint8_t*)&pt); point = lwpoint_construct(line->srid, NULL, opa); PG_RETURN_POINTER(geometry_serialize(lwpoint_as_lwgeom(point))); }
Datum LWGEOM_line_substring(PG_FUNCTION_ARGS) { GSERIALIZED *geom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); double from = PG_GETARG_FLOAT8(1); double to = PG_GETARG_FLOAT8(2); LWGEOM *olwgeom; POINTARRAY *ipa, *opa; GSERIALIZED *ret; int type = gserialized_get_type(geom); if ( from < 0 || from > 1 ) { elog(ERROR,"line_interpolate_point: 2nd arg isnt within [0,1]"); PG_RETURN_NULL(); } if ( to < 0 || to > 1 ) { elog(ERROR,"line_interpolate_point: 3rd arg isnt within [0,1]"); PG_RETURN_NULL(); } if ( from > to ) { elog(ERROR, "2nd arg must be smaller then 3rd arg"); PG_RETURN_NULL(); } if ( type == LINETYPE ) { LWLINE *iline = lwgeom_as_lwline(lwgeom_from_gserialized(geom)); if ( lwgeom_is_empty((LWGEOM*)iline) ) { /* TODO return empty line */ lwline_release(iline); PG_FREE_IF_COPY(geom, 0); PG_RETURN_NULL(); } ipa = iline->points; opa = ptarray_substring(ipa, from, to, 0); if ( opa->npoints == 1 ) /* Point returned */ olwgeom = (LWGEOM *)lwpoint_construct(iline->srid, NULL, opa); else olwgeom = (LWGEOM *)lwline_construct(iline->srid, NULL, opa); } else if ( type == MULTILINETYPE ) { LWMLINE *iline; int i = 0, g = 0; int homogeneous = LW_TRUE; LWGEOM **geoms = NULL; double length = 0.0, sublength = 0.0, minprop = 0.0, maxprop = 0.0; iline = lwgeom_as_lwmline(lwgeom_from_gserialized(geom)); if ( lwgeom_is_empty((LWGEOM*)iline) ) { /* TODO return empty collection */ lwmline_release(iline); PG_FREE_IF_COPY(geom, 0); PG_RETURN_NULL(); } /* Calculate the total length of the mline */ for ( i = 0; i < iline->ngeoms; i++ ) { LWLINE *subline = (LWLINE*)iline->geoms[i]; if ( subline->points && subline->points->npoints > 1 ) length += ptarray_length_2d(subline->points); } geoms = lwalloc(sizeof(LWGEOM*) * iline->ngeoms); /* Slice each sub-geometry of the multiline */ for ( i = 0; i < iline->ngeoms; i++ ) { LWLINE *subline = (LWLINE*)iline->geoms[i]; double subfrom = 0.0, subto = 0.0; if ( subline->points && subline->points->npoints > 1 ) sublength += ptarray_length_2d(subline->points); /* Calculate proportions for this subline */ minprop = maxprop; maxprop = sublength / length; /* This subline doesn't reach the lowest proportion requested or is beyond the highest proporton */ if ( from > maxprop || to < minprop ) continue; if ( from <= minprop ) subfrom = 0.0; if ( to >= maxprop ) subto = 1.0; if ( from > minprop && from <= maxprop ) subfrom = (from - minprop) / (maxprop - minprop); if ( to < maxprop && to >= minprop ) subto = (to - minprop) / (maxprop - minprop); opa = ptarray_substring(subline->points, subfrom, subto, 0); if ( opa && opa->npoints > 0 ) { if ( opa->npoints == 1 ) /* Point returned */ { geoms[g] = (LWGEOM *)lwpoint_construct(iline->srid, NULL, opa); homogeneous = LW_FALSE; } else { geoms[g] = (LWGEOM *)lwline_construct(iline->srid, NULL, opa); } g++; } } /* If we got any points, we need to return a GEOMETRYCOLLECTION */ if ( ! homogeneous ) type = COLLECTIONTYPE; olwgeom = (LWGEOM*)lwcollection_construct(type, iline->srid, NULL, g, geoms); } else { elog(ERROR,"line_interpolate_point: 1st arg isnt a line"); PG_RETURN_NULL(); } ret = geometry_serialize(olwgeom); lwgeom_free(olwgeom); PG_FREE_IF_COPY(geom, 0); PG_RETURN_POINTER(ret); }
/* * 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 start, end, proj, p; /* Initialize our 2D copy of the input parameter */ p.x = p4d->x; p.y = p4d->y; if ( ! proj4d ) proj4d = &projtmp; getPoint2d_p(pa, 0, &start); for (t=1; t<pa->npoints; t++) { double dist; getPoint2d_p(pa, t, &end); 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; getPoint2d_p(pa, 0, &start); for (t=0; t<seg; t++, start=end) { getPoint2d_p(pa, t+1, &end); 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; }
POINTARRAY * ptarray_substring(POINTARRAY *ipa, double from, double to, double tolerance) { POINTARRAY *dpa; POINT4D pt; POINT4D p1, p2; POINT4D *p1ptr=&p1; /* don't break strict-aliasing rule */ POINT4D *p2ptr=&p2; int nsegs, i; double length, slength, tlength; int state = 0; /* 0=before, 1=inside */ /* * Create a dynamic pointarray with an initial capacity * equal to full copy of input points */ dpa = ptarray_construct_empty(FLAGS_GET_Z(ipa->flags), FLAGS_GET_M(ipa->flags), ipa->npoints); /* Compute total line length */ length = ptarray_length_2d(ipa); LWDEBUGF(3, "Total length: %g", length); /* Get 'from' and 'to' lengths */ from = length*from; to = length*to; LWDEBUGF(3, "From/To: %g/%g", from, to); tlength = 0; getPoint4d_p(ipa, 0, &p1); nsegs = ipa->npoints - 1; for ( i = 0; i < nsegs; i++ ) { double dseg; getPoint4d_p(ipa, i+1, &p2); LWDEBUGF(3 ,"Segment %d: (%g,%g,%g,%g)-(%g,%g,%g,%g)", i, p1.x, p1.y, p1.z, p1.m, p2.x, p2.y, p2.z, p2.m); /* Find the length of this segment */ slength = distance2d_pt_pt((POINT2D *)p1ptr, (POINT2D *)p2ptr); /* * We are before requested start. */ if ( state == 0 ) /* before */ { LWDEBUG(3, " Before start"); if ( fabs ( from - ( tlength + slength ) ) <= tolerance ) { LWDEBUG(3, " Second point is our start"); /* * Second point is our start */ ptarray_append_point(dpa, &p2, LW_FALSE); state=1; /* we're inside now */ goto END; } else if ( fabs(from - tlength) <= tolerance ) { LWDEBUG(3, " First point is our start"); /* * First point is our start */ ptarray_append_point(dpa, &p1, LW_FALSE); /* * We're inside now, but will check * 'to' point as well */ state=1; } /* * Didn't reach the 'from' point, * nothing to do */ else if ( from > tlength + slength ) goto END; else /* tlength < from < tlength+slength */ { LWDEBUG(3, " Seg contains first point"); /* * Our start is between first and * second point */ dseg = (from - tlength) / slength; interpolate_point4d(&p1, &p2, &pt, dseg); ptarray_append_point(dpa, &pt, LW_FALSE); /* * We're inside now, but will check * 'to' point as well */ state=1; } } if ( state == 1 ) /* inside */ { LWDEBUG(3, " Inside"); /* * 'to' point is our second point. */ if ( fabs(to - ( tlength + slength ) ) <= tolerance ) { LWDEBUG(3, " Second point is our end"); ptarray_append_point(dpa, &p2, LW_FALSE); break; /* substring complete */ } /* * 'to' point is our first point. * (should only happen if 'to' is 0) */ else if ( fabs(to - tlength) <= tolerance ) { LWDEBUG(3, " First point is our end"); ptarray_append_point(dpa, &p1, LW_FALSE); break; /* substring complete */ } /* * Didn't reach the 'end' point, * just copy second point */ else if ( to > tlength + slength ) { ptarray_append_point(dpa, &p2, LW_FALSE); goto END; } /* * 'to' point falls on this segment * Interpolate and break. */ else if ( to < tlength + slength ) { LWDEBUG(3, " Seg contains our end"); dseg = (to - tlength) / slength; interpolate_point4d(&p1, &p2, &pt, dseg); ptarray_append_point(dpa, &pt, LW_FALSE); break; } else { LWDEBUG(3, "Unhandled case"); } } END: tlength += slength; memcpy(&p1, &p2, sizeof(POINT4D)); } LWDEBUGF(3, "Out of loop, ptarray has %d points", dpa->npoints); return dpa; }