/** * COLLECTION, MULTIPOINTTYPE, MULTILINETYPE, MULTIPOLYGONTYPE **/ static LWCOLLECTION* lwcollection_from_twkb_state(twkb_parse_state *s) { int ngeoms, i; LWGEOM *geom = NULL; LWCOLLECTION *col = lwcollection_construct_empty(s->lwtype, SRID_UNKNOWN, s->has_z, s->has_m); LWDEBUG(2,"Entering lwcollection_from_twkb_state"); if ( s->is_empty ) return col; /* Read number of geometries */ ngeoms = twkb_parse_state_uvarint(s); LWDEBUGF(4,"Number of geometries %d",ngeoms); /* It has an idlist, we need to skip that */ if ( s->has_idlist ) { for ( i = 0; i < ngeoms; i++ ) twkb_parse_state_varint_skip(s); } for ( i = 0; i < ngeoms; i++ ) { geom = lwgeom_from_twkb_state(s); if ( lwcollection_add_lwgeom(col, geom) == NULL ) { lwerror("Unable to add geometry (%p) to collection (%p)", geom, col); return NULL; } } return col; }
/** Here the geometries are distributed for the new faster distance-calculations */ int lw_dist2d_distribute_fast(LWGEOM *lwg1, LWGEOM *lwg2, DISTPTS *dl) { POINTARRAY *pa1, *pa2; int type1 = lwg1->type; int type2 = lwg2->type; LWDEBUGF(2, "lw_dist2d_distribute_fast is called with typ1=%d, type2=%d", lwg1->type, lwg2->type); switch (type1) { case LINETYPE: pa1 = ((LWLINE *)lwg1)->points; break; case POLYGONTYPE: pa1 = ((LWPOLY *)lwg1)->rings[0]; break; default: lwerror("Unsupported geometry1 type: %s", lwtype_name(type1)); return LW_FALSE; } switch (type2) { case LINETYPE: pa2 = ((LWLINE *)lwg2)->points; break; case POLYGONTYPE: pa2 = ((LWPOLY *)lwg2)->rings[0]; break; default: lwerror("Unsupported geometry2 type: %s", lwtype_name(type1)); return LW_FALSE; } dl->twisted=1; return lw_dist2d_fast_ptarray_ptarray(pa1, pa2, dl, lwg1->bbox, lwg2->bbox); }
static int lwgeom_to_twkb_buf(const LWGEOM *geom, TWKB_GLOBALS *globals, TWKB_STATE *ts) { LWDEBUGF(2, "Entered %s", __func__); switch ( geom->type ) { case POINTTYPE: { LWDEBUGF(4,"Type found is Point, %d", geom->type); return lwpoint_to_twkb_buf((LWPOINT*) geom, globals, ts); } case LINETYPE: { LWDEBUGF(4,"Type found is Linestring, %d", geom->type); return lwline_to_twkb_buf((LWLINE*) geom, globals, ts); } /* Polygon has 'nrings' and 'rings' elements */ case POLYGONTYPE: { LWDEBUGF(4,"Type found is Polygon, %d", geom->type); return lwpoly_to_twkb_buf((LWPOLY*)geom, globals, ts); } /* All these Collection types have 'ngeoms' and 'geoms' elements */ case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: { LWDEBUGF(4,"Type found is Multi, %d", geom->type); return lwmulti_to_twkb_buf((LWCOLLECTION*)geom, globals, ts); } case COLLECTIONTYPE: { LWDEBUGF(4,"Type found is collection, %d", geom->type); return lwcollection_to_twkb_buf((LWCOLLECTION*) geom, globals, ts); } /* Unknown type! */ default: lwerror("Unsupported geometry type: %s [%d]", lwtype_name((geom)->type), (geom)->type); } return 0; }
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; }
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom) { GEOSCoordSeq sq; GEOSGeom g, shell; GEOSGeom *geoms = NULL; /* LWGEOM *tmp; */ uint32_t ngeoms, i; int geostype; #if LWDEBUG_LEVEL >= 4 char *wkt; #endif LWDEBUGF(4, "LWGEOM2GEOS got a %s", lwtype_name(lwgeom->type)); if (lwgeom_has_arc(lwgeom)) { LWDEBUG(3, "LWGEOM2GEOS: arced geometry found."); lwerror("Exception in LWGEOM2GEOS: curved geometry not supported."); return NULL; } switch (lwgeom->type) { LWPOINT *lwp = NULL; LWPOLY *lwpoly = NULL; LWLINE *lwl = NULL; LWCOLLECTION *lwc = NULL; #if POSTGIS_GEOS_VERSION < 33 POINTARRAY *pa = NULL; #endif case POINTTYPE: lwp = (LWPOINT *)lwgeom; if ( lwgeom_is_empty(lwgeom) ) { #if POSTGIS_GEOS_VERSION < 33 pa = ptarray_construct_empty(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2); sq = ptarray_to_GEOSCoordSeq(pa); shell = GEOSGeom_createLinearRing(sq); g = GEOSGeom_createPolygon(shell, NULL, 0); #else g = GEOSGeom_createEmptyPolygon(); #endif } else { sq = ptarray_to_GEOSCoordSeq(lwp->point); g = GEOSGeom_createPoint(sq); } if ( ! g ) { /* lwnotice("Exception in LWGEOM2GEOS"); */ return NULL; } break; case LINETYPE: lwl = (LWLINE *)lwgeom; if ( lwl->points->npoints == 1 ) { /* Duplicate point, to make geos-friendly */ lwl->points = ptarray_addPoint(lwl->points, getPoint_internal(lwl->points, 0), FLAGS_NDIMS(lwl->points->flags), lwl->points->npoints); } sq = ptarray_to_GEOSCoordSeq(lwl->points); g = GEOSGeom_createLineString(sq); if ( ! g ) { /* lwnotice("Exception in LWGEOM2GEOS"); */ return NULL; } break; case POLYGONTYPE: lwpoly = (LWPOLY *)lwgeom; if ( lwgeom_is_empty(lwgeom) ) { #if POSTGIS_GEOS_VERSION < 33 POINTARRAY *pa = ptarray_construct_empty(lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), 2); sq = ptarray_to_GEOSCoordSeq(pa); shell = GEOSGeom_createLinearRing(sq); g = GEOSGeom_createPolygon(shell, NULL, 0); #else g = GEOSGeom_createEmptyPolygon(); #endif } else { sq = ptarray_to_GEOSCoordSeq(lwpoly->rings[0]); /* TODO: check ring for being closed and fix if not */ shell = GEOSGeom_createLinearRing(sq); if ( ! shell ) return NULL; /*lwerror("LWGEOM2GEOS: exception during polygon shell conversion"); */ ngeoms = lwpoly->nrings-1; if ( ngeoms > 0 ) geoms = malloc(sizeof(GEOSGeom)*ngeoms); for (i=1; i<lwpoly->nrings; ++i) { sq = ptarray_to_GEOSCoordSeq(lwpoly->rings[i]); geoms[i-1] = GEOSGeom_createLinearRing(sq); if ( ! geoms[i-1] ) { --i; while (i) GEOSGeom_destroy(geoms[--i]); free(geoms); GEOSGeom_destroy(shell); return NULL; } /*lwerror("LWGEOM2GEOS: exception during polygon hole conversion"); */ } g = GEOSGeom_createPolygon(shell, geoms, ngeoms); if (geoms) free(geoms); } if ( ! g ) return NULL; break; case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: if ( lwgeom->type == MULTIPOINTTYPE ) geostype = GEOS_MULTIPOINT; else if ( lwgeom->type == MULTILINETYPE ) geostype = GEOS_MULTILINESTRING; else if ( lwgeom->type == MULTIPOLYGONTYPE ) geostype = GEOS_MULTIPOLYGON; else geostype = GEOS_GEOMETRYCOLLECTION; lwc = (LWCOLLECTION *)lwgeom; ngeoms = lwc->ngeoms; if ( ngeoms > 0 ) geoms = malloc(sizeof(GEOSGeom)*ngeoms); for (i=0; i<ngeoms; ++i) { GEOSGeometry* g = LWGEOM2GEOS(lwc->geoms[i]); if ( ! g ) { while (i) GEOSGeom_destroy(geoms[--i]); free(geoms); return NULL; } geoms[i] = g; } g = GEOSGeom_createCollection(geostype, geoms, ngeoms); if ( geoms ) free(geoms); if ( ! g ) return NULL; break; default: lwerror("Unknown geometry type: %d - %s", lwgeom->type, lwtype_name(lwgeom->type)); return NULL; } GEOSSetSRID(g, lwgeom->srid); #if LWDEBUG_LEVEL >= 4 wkt = GEOSGeomToWKT(g); LWDEBUGF(4, "LWGEOM2GEOS: GEOSGeom: %s", wkt); free(wkt); #endif return g; }
/** * Find first linestring in serialized geometry and return * the number of points in it. If no linestrings are found * return -1. */ static int32 lwgeom_numpoints_linestring_recursive(const uchar *serialized) { LWGEOM_INSPECTED *inspected = lwgeom_inspect(serialized); int i; LWDEBUG(2, "lwgeom_numpoints_linestring_recursive called."); /* * CURVEPOLY and COMPOUND have no concept of numpoints but look like * collections once inspected. Fast-fail on these here. */ if (lwgeom_getType(inspected->type) == COMPOUNDTYPE || lwgeom_getType(inspected->type) == CURVEPOLYTYPE) { return -1; } for (i=0; i<inspected->ngeometries; i++) { int32 npoints; int type; LWGEOM *geom=NULL; uchar *subgeom; geom = lwgeom_getgeom_inspected(inspected, i); LWDEBUGF(3, "numpoints_recursive: type=%d", lwgeom_getType(geom->type)); if (lwgeom_getType(geom->type) == LINETYPE) { return ((LWLINE *)geom)->points->npoints; } else if (lwgeom_getType(geom->type) == CIRCSTRINGTYPE) { return ((LWCIRCSTRING *)geom)->points->npoints; } subgeom = lwgeom_getsubgeometry_inspected(inspected, i); if ( subgeom == NULL ) { elog(ERROR, "What ? lwgeom_getsubgeometry_inspected returned NULL??"); } type = lwgeom_getType(subgeom[0]); /* MULTILINESTRING && GEOMETRYCOLLECTION are worth checking */ if ( type != MULTILINETYPE && type != COLLECTIONTYPE ) continue; npoints = lwgeom_numpoints_linestring_recursive(subgeom); if ( npoints == -1 ) continue; lwinspected_release(inspected); return npoints; } lwinspected_release(inspected); return -1; }
/** * Force the dimensionality of a geometry to match the dimensionality * of a set of flags (usually derived from a ZM WKT tag). */ static int wkt_parser_set_dims(LWGEOM *geom, uint8_t flags) { int hasz = FLAGS_GET_Z(flags); int hasm = FLAGS_GET_M(flags); int i = 0; /* Error on junk */ if( ! geom ) return LW_FAILURE; FLAGS_SET_Z(geom->flags, hasz); FLAGS_SET_M(geom->flags, hasm); if( ! lwgeom_is_empty(geom) ) { if( geom->type == POINTTYPE ) { LWPOINT *pt = (LWPOINT*)geom; FLAGS_SET_Z(pt->point->flags, hasz); FLAGS_SET_M(pt->point->flags, hasm); return LW_SUCCESS; } else if ( (geom->type == TRIANGLETYPE) || (geom->type == CIRCSTRINGTYPE) || (geom->type == LINETYPE) ) { LWLINE *ln = (LWLINE*)geom; FLAGS_SET_Z(ln->points->flags, hasz); FLAGS_SET_M(ln->points->flags, hasm); return LW_SUCCESS; } else if ( geom->type == POLYGONTYPE ) { LWPOLY *poly = (LWPOLY*)geom; for ( i = 0; i < poly->nrings; i++ ) { FLAGS_SET_Z(poly->rings[i]->flags, hasz); FLAGS_SET_M(poly->rings[i]->flags, hasm); } return LW_SUCCESS; } else if ( geom->type == CURVEPOLYTYPE ) { LWCURVEPOLY *poly = (LWCURVEPOLY*)geom; for ( i = 0; i < poly->nrings; i++ ) wkt_parser_set_dims(poly->rings[i], flags); return LW_SUCCESS; } else if ( lwtype_is_collection(geom->type) ) { LWCOLLECTION *col = (LWCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) wkt_parser_set_dims(col->geoms[i], flags); return LW_SUCCESS; } else { LWDEBUGF(2,"Unknown geometry type: %d", geom->type); return LW_FAILURE; } } return LW_SUCCESS; }
void * lwrealloc(void *mem, size_t size) { LWDEBUGF(5, "lwrealloc: %d@%p", size, mem); return lwrealloc_var(mem, size); }
/** * @brief geom1 same as geom2 * iff * + have same type * + have same # objects * + have same bvol * + each object in geom1 has a corresponding object in geom2 (see above) * @param lwgeom1 * @param lwgeom2 */ char lwgeom_same(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2) { LWDEBUGF(2, "lwgeom_same(%s, %s) called", lwtype_name(lwgeom1->type), lwtype_name(lwgeom2->type)); if ( lwgeom1->type != lwgeom2->type ) { LWDEBUG(3, " type differ"); return LW_FALSE; } if ( FLAGS_GET_ZM(lwgeom1->flags) != FLAGS_GET_ZM(lwgeom2->flags) ) { LWDEBUG(3, " ZM flags differ"); return LW_FALSE; } /* Check boxes if both already computed */ if ( lwgeom1->bbox && lwgeom2->bbox ) { /*lwnotice("bbox1:%p, bbox2:%p", lwgeom1->bbox, lwgeom2->bbox);*/ if ( ! gbox_same(lwgeom1->bbox, lwgeom2->bbox) ) { LWDEBUG(3, " bounding boxes differ"); return LW_FALSE; } } /* geoms have same type, invoke type-specific function */ switch (lwgeom1->type) { case POINTTYPE: return lwpoint_same((LWPOINT *)lwgeom1, (LWPOINT *)lwgeom2); case LINETYPE: return lwline_same((LWLINE *)lwgeom1, (LWLINE *)lwgeom2); case POLYGONTYPE: return lwpoly_same((LWPOLY *)lwgeom1, (LWPOLY *)lwgeom2); case TRIANGLETYPE: return lwtriangle_same((LWTRIANGLE *)lwgeom1, (LWTRIANGLE *)lwgeom2); case CIRCSTRINGTYPE: return lwcircstring_same((LWCIRCSTRING *)lwgeom1, (LWCIRCSTRING *)lwgeom2); case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return lwcollection_same((LWCOLLECTION *)lwgeom1, (LWCOLLECTION *)lwgeom2); default: lwerror("lwgeom_same: unsupported geometry type: %s", lwtype_name(lwgeom1->type)); return LW_FALSE; } }
/** * Take in a LINESTRING and return a MULTILINESTRING of those portions of the * LINESTRING between the from/to range for the specified ordinate (XYZM) */ LWCOLLECTION* lwline_clip_to_ordinate_range(const LWLINE *line, char ordinate, double from, double to) { POINTARRAY *pa_in = NULL; LWCOLLECTION *lwgeom_out = NULL; POINTARRAY *dp = NULL; int i, rv; int added_last_point = 0; POINT4D *p = NULL, *q = NULL, *r = NULL; double ordinate_value_p = 0.0, ordinate_value_q = 0.0; char hasz = lwgeom_has_z(lwline_as_lwgeom(line)); char hasm = lwgeom_has_m(lwline_as_lwgeom(line)); char dims = FLAGS_NDIMS(line->flags); /* Null input, nothing we can do. */ if ( ! line ) { lwerror("Null input geometry."); return NULL; } /* Ensure 'from' is less than 'to'. */ if ( to < from ) { double t = from; from = to; to = t; } LWDEBUGF(4, "from = %g, to = %g, ordinate = %c", from, to, ordinate); LWDEBUGF(4, "%s", lwgeom_to_ewkt((LWGEOM*)line)); /* Asking for an ordinate we don't have. Error. */ if ( (ordinate == 'Z' && ! hasz) || (ordinate == 'M' && ! hasm) ) { lwerror("Cannot clip on ordinate %d in a %d-d geometry.", ordinate, dims); return NULL; } /* Prepare our working point objects. */ p = lwalloc(sizeof(POINT4D)); q = lwalloc(sizeof(POINT4D)); r = lwalloc(sizeof(POINT4D)); /* Construct a collection to hold our outputs. */ lwgeom_out = lwcollection_construct_empty(MULTILINETYPE, line->srid, hasz, hasm); /* Get our input point array */ pa_in = line->points; for ( i = 0; i < pa_in->npoints; i++ ) { LWDEBUGF(4, "Point #%d", i); LWDEBUGF(4, "added_last_point %d", added_last_point); if ( i > 0 ) { *q = *p; ordinate_value_q = ordinate_value_p; } rv = getPoint4d_p(pa_in, i, p); ordinate_value_p = lwpoint_get_ordinate(p, ordinate); LWDEBUGF(4, " ordinate_value_p %g (current)", ordinate_value_p); LWDEBUGF(4, " ordinate_value_q %g (previous)", ordinate_value_q); /* Is this point inside the ordinate range? Yes. */ if ( ordinate_value_p >= from && ordinate_value_p <= to ) { LWDEBUGF(4, " inside ordinate range (%g, %g)", from, to); if ( ! added_last_point ) { LWDEBUG(4," new ptarray required"); /* We didn't add the previous point, so this is a new segment. * Make a new point array. */ dp = ptarray_construct_empty(hasz, hasm, 32); /* We're transiting into the range so add an interpolated * point at the range boundary. * If we're on a boundary and crossing from the far side, * we also need an interpolated point. */ if ( i > 0 && ( /* Don't try to interpolate if this is the first point */ ( ordinate_value_p > from && ordinate_value_p < to ) || /* Inside */ ( ordinate_value_p == from && ordinate_value_q > to ) || /* Hopping from above */ ( ordinate_value_p == to && ordinate_value_q < from ) ) ) /* Hopping from below */ { double interpolation_value; (ordinate_value_q > to) ? (interpolation_value = to) : (interpolation_value = from); rv = point_interpolate(q, p, r, hasz, hasm, ordinate, interpolation_value); rv = ptarray_append_point(dp, r, LW_FALSE); LWDEBUGF(4, "[0] interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value); } } /* Add the current vertex to the point array. */ rv = ptarray_append_point(dp, p, LW_FALSE); if ( ordinate_value_p == from || ordinate_value_p == to ) { added_last_point = 2; /* Added on boundary. */ } else { added_last_point = 1; /* Added inside range. */ } } /* Is this point inside the ordinate range? No. */ else { LWDEBUGF(4, " added_last_point (%d)", added_last_point); if ( added_last_point == 1 ) { /* We're transiting out of the range, so add an interpolated point * to the point array at the range boundary. */ double interpolation_value; (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from); rv = point_interpolate(q, p, r, hasz, hasm, ordinate, interpolation_value); rv = ptarray_append_point(dp, r, LW_FALSE); LWDEBUGF(4, " [1] interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value); } else if ( added_last_point == 2 ) { /* We're out and the last point was on the boundary. * If the last point was the near boundary, nothing to do. * If it was the far boundary, we need an interpolated point. */ if ( from != to && ( (ordinate_value_q == from && ordinate_value_p > from) || (ordinate_value_q == to && ordinate_value_p < to) ) ) { double interpolation_value; (ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from); rv = point_interpolate(q, p, r, hasz, hasm, ordinate, interpolation_value); rv = ptarray_append_point(dp, r, LW_FALSE); LWDEBUGF(4, " [2] interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value); } } else if ( i && ordinate_value_q < from && ordinate_value_p > to ) { /* We just hopped over the whole range, from bottom to top, * so we need to add *two* interpolated points! */ dp = ptarray_construct(hasz, hasm, 2); /* Interpolate lower point. */ rv = point_interpolate(p, q, r, hasz, hasm, ordinate, from); ptarray_set_point4d(dp, 0, r); /* Interpolate upper point. */ rv = point_interpolate(p, q, r, hasz, hasm, ordinate, to); ptarray_set_point4d(dp, 1, r); } else if ( i && ordinate_value_q > to && ordinate_value_p < from ) { /* We just hopped over the whole range, from top to bottom, * so we need to add *two* interpolated points! */ dp = ptarray_construct(hasz, hasm, 2); /* Interpolate upper point. */ rv = point_interpolate(p, q, r, hasz, hasm, ordinate, to); ptarray_set_point4d(dp, 0, r); /* Interpolate lower point. */ rv = point_interpolate(p, q, r, hasz, hasm, ordinate, from); ptarray_set_point4d(dp, 1, r); } /* We have an extant point-array, save it out to a multi-line. */ if ( dp ) { LWDEBUG(4, "saving pointarray to multi-line (1)"); /* Only one point, so we have to make an lwpoint to hold this * and set the overall output type to a generic collection. */ if ( dp->npoints == 1 ) { LWPOINT *opoint = lwpoint_construct(line->srid, NULL, dp); lwgeom_out->type = COLLECTIONTYPE; lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(opoint)); } else { LWLINE *oline = lwline_construct(line->srid, NULL, dp); lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwline_as_lwgeom(oline)); } /* Pointarray is now owned by lwgeom_out, so drop reference to it */ dp = NULL; } added_last_point = 0; } } /* Still some points left to be saved out. */ if ( dp && dp->npoints > 0 ) { LWDEBUG(4, "saving pointarray to multi-line (2)"); LWDEBUGF(4, "dp->npoints == %d", dp->npoints); LWDEBUGF(4, "lwgeom_out->ngeoms == %d", lwgeom_out->ngeoms); if ( dp->npoints == 1 ) { LWPOINT *opoint = lwpoint_construct(line->srid, NULL, dp); lwgeom_out->type = COLLECTIONTYPE; lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(opoint)); } else { LWLINE *oline = lwline_construct(line->srid, NULL, dp); lwgeom_out = lwcollection_add_lwgeom(lwgeom_out, lwline_as_lwgeom(oline)); } /* Pointarray is now owned by lwgeom_out, so drop reference to it */ dp = NULL; } lwfree(p); lwfree(q); lwfree(r); if ( lwgeom_out->ngeoms > 0 ) { lwgeom_drop_bbox((LWGEOM*)lwgeom_out); lwgeom_add_bbox((LWGEOM*)lwgeom_out); } return lwgeom_out; }
LWGEOM* lwgeom_union(const LWGEOM *geom1, const LWGEOM *geom2) { int is3d; int srid; GEOSGeometry *g1, *g2, *g3; LWGEOM *result; LWDEBUG(2, "in geomunion"); /* A.Union(empty) == A */ if ( lwgeom_is_empty(geom1) ) return lwgeom_clone(geom2); /* B.Union(empty) == B */ if ( lwgeom_is_empty(geom2) ) return lwgeom_clone(geom1); /* ensure srids are identical */ srid = (int)(geom1->srid); error_if_srid_mismatch(srid, (int)(geom2->srid)); is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)) ; initGEOS(lwnotice, lwgeom_geos_error); g1 = LWGEOM2GEOS(geom1); if ( 0 == g1 ) /* exception thrown at construction */ { lwerror("First argument geometry could not be converted to GEOS: %s", lwgeom_geos_errmsg); return NULL; } g2 = LWGEOM2GEOS(geom2); if ( 0 == g2 ) /* exception thrown at construction */ { GEOSGeom_destroy(g1); lwerror("Second argument geometry could not be converted to GEOS: %s", lwgeom_geos_errmsg); return NULL; } LWDEBUGF(3, "g1=%s", GEOSGeomToWKT(g1)); LWDEBUGF(3, "g2=%s", GEOSGeomToWKT(g2)); g3 = GEOSUnion(g1,g2); LWDEBUGF(3, "g3=%s", GEOSGeomToWKT(g3)); GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); if (g3 == NULL) { lwerror("GEOSUnion: %s", lwgeom_geos_errmsg); return NULL; /* never get here */ } GEOSSetSRID(g3, srid); result = GEOS2LWGEOM(g3, is3d); GEOSGeom_destroy(g3); if (result == NULL) { lwerror("Error performing union: GEOS2LWGEOM: %s", lwgeom_geos_errmsg); return NULL; /*never get here */ } return result; }
BOX3D * lwcircle_compute_box3d(POINT4D *p1, POINT4D *p2, POINT4D *p3) { double x1, x2, y1, y2, z1, z2; double angle, radius, sweep; /* angles from center */ double a1, a2, a3; /* angles from center once a1 is rotated to zero */ double r2, r3; double xe = 0.0, ye = 0.0; POINT4D *center; int i; BOX3D *box; LWDEBUG(2, "lwcircle_compute_box3d called."); radius = lwcircle_center(p1, p2, p3, ¢er); if (radius < 0.0) { LWDEBUG(3, "lwcircle_compute_box3d: zero radius"); /* * We've got a straight line here. Look to the end points for extents. * It's worth noting that when lwcircle_center returns < 0, center hasn't been allocated. */ x1 = (FP_LT(p1->x, p3->x)) ? p1->x : p3->x; x2 = (FP_GT(p1->x, p3->x)) ? p1->x : p3->x; y1 = (FP_LT(p1->y, p3->y)) ? p1->y : p3->y; y2 = (FP_GT(p1->y, p3->y)) ? p1->y : p3->y; z1 = (FP_LT(p1->z, p2->z)) ? p1->z : p2->z; z1 = (FP_LT(z1, p3->z)) ? z1 : p3->z; z2 = (FP_GT(p1->z, p2->z)) ? p1->z : p2->z; z2 = (FP_GT(z2, p3->z)) ? z2 : p3->z; box = lwalloc(sizeof(BOX3D)); box->xmin = x1; box->xmax = x2; box->ymin = y1; box->ymax = y2; box->zmin = z1; box->zmax = z2; LWDEBUGF(3, "lwcircle_compute_box3d: extents %.16f %.16f %.16f, %.16f %.16f %.16f", x1, y1, z1, x2, y2, z2); return box; } /* top = center->y + radius; left = center->x - radius; LWDEBUGF(3, "lwcircle_compute_box3d: center (%.16f, %.16f)", center->x, center->y); */ x1 = MAXFLOAT; x2 = -1 * MAXFLOAT; y1 = MAXFLOAT; y2 = -1 * MAXFLOAT; a1 = atan2(p1->y - center->y, p1->x - center->x); a2 = atan2(p2->y - center->y, p2->x - center->x); a3 = atan2(p3->y - center->y, p3->x - center->x); /* Rotate a2 and a3 such that a1 = 0 */ r2 = a2 - a1; r3 = a3 - a1; LWDEBUGF(4, "a1 %.16f, a2 %.16f, a3 %.16f", a1, a2, a3); LWDEBUGF(4, "r2 %.16f, r3 %.16f", r2, r3); /* * There are six cases here I'm interested in * Clockwise: * 1. a1-a2 < 180 == r2 < 0 && (r3 > 0 || r3 < r2) * 2. a1-a2 > 180 == r2 > 0 && (r3 > 0 && r3 < r2) * 3. a1-a2 > 180 == r2 > 0 && (r3 > r2 || r3 < 0) * Counter-clockwise: * 4. a1-a2 < 180 == r2 > 0 && (r3 < 0 || r3 > r2) * 5. a1-a2 > 180 == r2 < 0 && (r3 < 0 && r3 > r2) * 6. a1-a2 > 180 == r2 < 0 && (r3 < r2 || r3 > 0) * 3 and 6 are invalid cases where a3 is the midpoint. * BBOX is fundamental, so these cannot error out and will instead * calculate the sweep using a3 as the middle point. */ /* clockwise 1 */ if (FP_LT(r2, 0) && (FP_GT(r3, 0) || FP_LT(r3, r2))) { sweep = (FP_GT(r3, 0)) ? (r3 - 2 * M_PI) : r3; } /* clockwise 2 */ else if (FP_GT(r2, 0) && FP_GT(r3, 0) && FP_LT(r3, r2)) { sweep = (FP_GT(r3, 0)) ? (r3 - 2 * M_PI) : r3; } /* counter-clockwise 4 */ else if (FP_GT(r2, 0) && (FP_LT(r3, 0) || FP_GT(r3, r2))) { sweep = (FP_LT(r3, 0)) ? (r3 + 2 * M_PI) : r3; } /* counter-clockwise 5 */ else if (FP_LT(r2, 0) && FP_LT(r3, 0) && FP_GT(r3, r2)) { sweep = (FP_LT(r3, 0)) ? (r3 + 2 * M_PI) : r3; } /* clockwise invalid 3 */ else if (FP_GT(r2, 0) && (FP_GT(r3, r2) || FP_LT(r3, 0))) { sweep = (FP_GT(r2, 0)) ? (r2 - 2 * M_PI) : r2; } /* clockwise invalid 6 */ else { sweep = (FP_LT(r2, 0)) ? (r2 + 2 * M_PI) : r2; } LWDEBUGF(3, "a1 %.16f, a2 %.16f, a3 %.16f, sweep %.16f", a1, a2, a3, sweep); angle = 0.0; for (i=0; i < 6; i++) { switch (i) { /* right extent */ case 0: angle = 0.0; xe = center->x + radius; ye = center->y; break; /* top extent */ case 1: angle = M_PI_2; xe = center->x; ye = center->y + radius; break; /* left extent */ case 2: angle = M_PI; xe = center->x - radius; ye = center->y; break; /* bottom extent */ case 3: angle = -1 * M_PI_2; xe = center->x; ye = center->y - radius; break; /* first point */ case 4: angle = a1; xe = p1->x; ye = p1->y; break; /* last point */ case 5: angle = a3; xe = p3->x; ye = p3->y; break; } /* determine if the extents are outside the arc */ if (i < 4) { if (FP_GT(sweep, 0.0)) { if (FP_LT(a3, a1)) { if (FP_GT(angle, (a3 + 2 * M_PI)) || FP_LT(angle, a1)) continue; } else { if (FP_GT(angle, a3) || FP_LT(angle, a1)) continue; } } else { if (FP_GT(a3, a1)) { if (FP_LT(angle, (a3 - 2 * M_PI)) || FP_GT(angle, a1)) continue; } else { if (FP_LT(angle, a3) || FP_GT(angle, a1)) continue; } } } LWDEBUGF(3, "lwcircle_compute_box3d: potential extreame %d (%.16f, %.16f)", i, xe, ye); x1 = (FP_LT(x1, xe)) ? x1 : xe; y1 = (FP_LT(y1, ye)) ? y1 : ye; x2 = (FP_GT(x2, xe)) ? x2 : xe; y2 = (FP_GT(y2, ye)) ? y2 : ye; } LWDEBUGF(3, "lwcircle_compute_box3d: extreames found (%.16f %.16f, %.16f %.16f)", x1, y1, x2, y2); /* x1 = center->x + x1 * radius; x2 = center->x + x2 * radius; y1 = center->y + y1 * radius; y2 = center->y + y2 * radius; */ z1 = (FP_LT(p1->z, p2->z)) ? p1->z : p2->z; z1 = (FP_LT(z1, p3->z)) ? z1 : p3->z; z2 = (FP_GT(p1->z, p2->z)) ? p1->z : p2->z; z2 = (FP_GT(z2, p3->z)) ? z2 : p3->z; box = lwalloc(sizeof(BOX3D)); box->xmin = x1; box->xmax = x2; box->ymin = y1; box->ymax = y2; box->zmin = z1; box->zmax = z2; lwfree(center); return box; }
/* * given the LWGEOM serialized form (or a point into a multi* one) * construct a proper LWCIRCSTRING. * serialized_form should point to the 8bit type format (with type = 8) * See serialized form doc */ LWCIRCSTRING * lwcircstring_deserialize(uchar *serialized_form) { uchar type; LWCIRCSTRING *result; uchar *loc=NULL; uint32 npoints; POINTARRAY *pa; type = (uchar)serialized_form[0]; if (lwgeom_getType(type) != CIRCSTRINGTYPE) { lwerror("lwcircstring_deserialize: attempt to deserialize a circularstring which is really a %s", lwgeom_typename(type)); return NULL; } result = (LWCIRCSTRING*) lwalloc(sizeof(LWCIRCSTRING)); result->type = type; loc = serialized_form + 1; if (lwgeom_hasBBOX(type)) { LWDEBUG(3, "lwcircstring_deserialize: input has bbox"); result->bbox = lwalloc(sizeof(BOX2DFLOAT4)); memcpy(result->bbox, loc, sizeof(BOX2DFLOAT4)); loc += sizeof(BOX2DFLOAT4); } else { LWDEBUG(3, "lwcircstring_deserialize: input lacks bbox"); result->bbox = NULL; } if (lwgeom_hasSRID(type)) { LWDEBUG(3, "lwcircstring_deserialize: input has srid"); result->SRID = lw_get_int32(loc); loc += 4; /* type + SRID */ } else { LWDEBUG(3, "lwcircstring_deserialize: input lacks srid"); result->SRID = -1; } /* we've read the type (1 byte) and SRID (4 bytes, if present) */ npoints = lw_get_uint32(loc); LWDEBUGF(3, "circstring npoints = %d", npoints); loc += 4; pa = pointArray_construct(loc, TYPE_HASZ(type), TYPE_HASM(type), npoints); result->points = pa; return result; }
/* * convert this circularstring into its serialized form writing it into * the given buffer, and returning number of bytes written into * the given int pointer. * result's first char will be the 8bit type. See serialized form doc */ void lwcircstring_serialize_buf(LWCIRCSTRING *curve, uchar *buf, size_t *retsize) { char hasSRID; uchar *loc; int ptsize; size_t size; LWDEBUGF(2, "lwcircstring_serialize_buf(%p, %p, %p) called", curve, buf, retsize); if (curve == NULL) { lwerror("lwcircstring_serialize:: given null curve"); return; } if (TYPE_GETZM(curve->type) != TYPE_GETZM(curve->points->dims)) { lwerror("Dimensions mismatch in lwcircstring"); return; } ptsize = pointArray_ptsize(curve->points); hasSRID = (curve->SRID != -1); buf[0] = (uchar)lwgeom_makeType_full( TYPE_HASZ(curve->type), TYPE_HASM(curve->type), hasSRID, CIRCSTRINGTYPE, curve->bbox ? 1 : 0); loc = buf+1; LWDEBUGF(3, "lwcircstring_serialize_buf added type (%d)", curve->type); if (curve->bbox) { memcpy(loc, curve->bbox, sizeof(BOX2DFLOAT4)); loc += sizeof(BOX2DFLOAT4); LWDEBUG(3, "lwcircstring_serialize_buf added BBOX"); } if (hasSRID) { memcpy(loc, &curve->SRID, sizeof(int32)); loc += sizeof(int32); LWDEBUG(3, "lwcircstring_serialize_buf added SRID"); } memcpy(loc, &curve->points->npoints, sizeof(uint32)); loc += sizeof(uint32); LWDEBUGF(3, "lwcircstring_serialize_buf added npoints (%d)", curve->points->npoints); /* copy in points */ size = curve->points->npoints * ptsize; memcpy(loc, getPoint_internal(curve->points, 0), size); loc += size; LWDEBUGF(3, "lwcircstring_serialize_buf copied serialized_pointlist (%d bytes)", ptsize * curve->points->npoints); if (retsize) *retsize = loc-buf; LWDEBUGF(3, "lwcircstring_serialize_buf returning (loc: %p, size: %d)", loc, loc-buf); }
static double ptarray_area_spheroid(const POINTARRAY *pa, const SPHEROID *spheroid) { GEOGRAPHIC_POINT a, b; POINT2D p; int i; double area = 0.0; GBOX gbox2d; int in_south = LW_FALSE; double delta_lon_tolerance; double latitude_min; gbox2d.flags = gflags(0, 0, 0); /* Return zero on non-sensical inputs */ if ( ! pa || pa->npoints < 4 ) return 0.0; /* Get the raw min/max values for the latitudes */ ptarray_calculate_gbox(pa, &gbox2d); if ( signum(gbox2d.ymin) != signum(gbox2d.ymax) ) lwerror("ptarray_area_spheroid: cannot handle ptarray that crosses equator"); /* Geodetic bbox < 0.0 implies geometry is entirely in southern hemisphere */ if ( gbox2d.ymax < 0.0 ) in_south = LW_TRUE; LWDEBUGF(4, "gbox2d.ymax %.12g", gbox2d.ymax); /* Tolerance for strip area calculation */ if ( in_south ) { delta_lon_tolerance = (90.0 / (fabs(gbox2d.ymin) / 8.0) - 2.0) / 10000.0; latitude_min = deg2rad(fabs(gbox2d.ymax)); } else { delta_lon_tolerance = (90.0 / (fabs(gbox2d.ymax) / 8.0) - 2.0) / 10000.0; latitude_min = deg2rad(gbox2d.ymin); } /* Initialize first point */ getPoint2d_p(pa, 0, &p); geographic_point_init(p.x, p.y, &a); for ( i = 1; i < pa->npoints; i++ ) { GEOGRAPHIC_POINT a1, b1; double strip_area = 0.0; double delta_lon = 0.0; LWDEBUGF(4, "edge #%d", i); getPoint2d_p(pa, i, &p); geographic_point_init(p.x, p.y, &b); a1 = a; b1 = b; /* Flip into north if in south */ if ( in_south ) { a1.lat = -1.0 * a1.lat; b1.lat = -1.0 * b1.lat; } LWDEBUGF(4, "in_south %d", in_south); LWDEBUGF(4, "crosses_dateline(a, b) %d", crosses_dateline(&a, &b) ); if ( crosses_dateline(&a, &b) ) { double shift; if ( a1.lon > 0.0 ) shift = (M_PI - a1.lon) + 0.088; /* About 5deg more */ else shift = (M_PI - b1.lon) + 0.088; /* About 5deg more */ LWDEBUGF(4, "shift: %.8g", shift); LWDEBUGF(4, "before shift a1(%.8g %.8g) b1(%.8g %.8g)", a1.lat, a1.lon, b1.lat, b1.lon); point_shift(&a1, shift); point_shift(&b1, shift); LWDEBUGF(4, "after shift a1(%.8g %.8g) b1(%.8g %.8g)", a1.lat, a1.lon, b1.lat, b1.lon); } delta_lon = fabs(b1.lon - a1.lon); LWDEBUGF(4, "a1(%.18g %.18g) b1(%.18g %.18g)", a1.lat, a1.lon, b1.lat, b1.lon); LWDEBUGF(4, "delta_lon %.18g", delta_lon); LWDEBUGF(4, "delta_lon_tolerance %.18g", delta_lon_tolerance); if ( delta_lon > 0.0 ) { if ( delta_lon < delta_lon_tolerance ) { strip_area = spheroid_striparea(&a1, &b1, latitude_min, spheroid); LWDEBUGF(4, "strip_area %.12g", strip_area); area += strip_area; } else { GEOGRAPHIC_POINT p, q; double step = floor(delta_lon / delta_lon_tolerance); double distance = spheroid_distance(&a1, &b1, spheroid); double pDistance = 0.0; int j = 0; LWDEBUGF(4, "step %.18g", step); LWDEBUGF(4, "distance %.18g", distance); step = distance / step; LWDEBUGF(4, "step %.18g", step); p = a1; while (pDistance < (distance - step * 1.01)) { double azimuth = spheroid_direction(&p, &b1, spheroid); j++; LWDEBUGF(4, " iteration %d", j); LWDEBUGF(4, " azimuth %.12g", azimuth); pDistance = pDistance + step; LWDEBUGF(4, " pDistance %.12g", pDistance); spheroid_project(&p, spheroid, step, azimuth, &q); strip_area = spheroid_striparea(&p, &q, latitude_min, spheroid); LWDEBUGF(4, " strip_area %.12g", strip_area); area += strip_area; LWDEBUGF(4, " area %.12g", area); p.lat = q.lat; p.lon = q.lon; } strip_area = spheroid_striparea(&p, &b1, latitude_min, spheroid); area += strip_area; } } /* B gets incremented in the next loop, so we save the value here */ a = b; } return fabs(area); }
/* * 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; }
LWGEOM * lwgeom_intersection(const LWGEOM *geom1, const LWGEOM *geom2) { LWGEOM *result ; GEOSGeometry *g1, *g2, *g3 ; int is3d ; int srid ; /* A.Intersection(Empty) == Empty */ if ( lwgeom_is_empty(geom2) ) return lwgeom_clone(geom2); /* Empty.Intersection(A) == Empty */ if ( lwgeom_is_empty(geom1) ) return lwgeom_clone(geom1); /* ensure srids are identical */ srid = (int)(geom1->srid); error_if_srid_mismatch(srid, (int)(geom2->srid)); is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)) ; initGEOS(lwnotice, lwgeom_geos_error); LWDEBUG(3, "intersection() START"); g1 = LWGEOM2GEOS(geom1); if ( 0 == g1 ) /* exception thrown at construction */ { lwerror("First argument geometry could not be converted to GEOS."); return NULL ; } g2 = LWGEOM2GEOS(geom2); if ( 0 == g2 ) /* exception thrown at construction */ { lwerror("Second argument geometry could not be converted to GEOS."); GEOSGeom_destroy(g1); return NULL ; } LWDEBUG(3, " constructed geometrys - calling geos"); LWDEBUGF(3, " g1 = %s", GEOSGeomToWKT(g1)); LWDEBUGF(3, " g2 = %s", GEOSGeomToWKT(g2)); /*LWDEBUGF(3, "g2 is valid = %i",GEOSisvalid(g2)); */ /*LWDEBUGF(3, "g1 is valid = %i",GEOSisvalid(g1)); */ g3 = GEOSIntersection(g1,g2); LWDEBUG(3, " intersection finished"); if (g3 == NULL) { GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); lwerror("Error performing intersection: %s", lwgeom_geos_errmsg); return NULL; /* never get here */ } LWDEBUGF(3, "result: %s", GEOSGeomToWKT(g3) ) ; GEOSSetSRID(g3, srid); result = GEOS2LWGEOM(g3, is3d); if (result == NULL) { GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); GEOSGeom_destroy(g3); lwerror("Error performing intersection: GEOS2LWGEOM: %s", lwgeom_geos_errmsg); return NULL ; /* never get here */ } GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); GEOSGeom_destroy(g3); return result ; }
/** * pt_in_ring_3d(): crossing number test for a point in a polygon * input: p = a point, * pa = vertex points of a ring V[n+1] with V[n]=V[0] * plane=the plane that the vertex points are lying on * returns: 0 = outside, 1 = inside * * Our polygons have first and last point the same, * * The difference in 3D variant is that we exclude the dimension that faces the plane least. * That is the dimension with the highest number in pv */ int pt_in_ring_3d(const POINT3DZ *p, const POINTARRAY *ring,PLANE3D *plane) { int cn = 0; /* the crossing number counter */ int i; POINT3DZ v1, v2; POINT3DZ first, last; getPoint3dz_p(ring, 0, &first); getPoint3dz_p(ring, ring->npoints-1, &last); if ( memcmp(&first, &last, sizeof(POINT3DZ)) ) { lwerror("pt_in_ring_3d: V[n] != V[0] (%g %g %g!= %g %g %g)", first.x, first.y, first.z, last.x, last.y, last.z); return LW_FALSE; } LWDEBUGF(2, "pt_in_ring_3d called with point: %g %g %g", p->x, p->y, p->z); /* printPA(ring); */ /* loop through all edges of the polygon */ getPoint3dz_p(ring, 0, &v1); if(fabs(plane->pv.z)>=fabs(plane->pv.x)&&fabs(plane->pv.z)>=fabs(plane->pv.y)) /*If the z vector of the normal vector to the plane is larger than x and y vector we project the ring to the xy-plane*/ { for (i=0; i<ring->npoints-1; i++) { double vt; getPoint3dz_p(ring, i+1, &v2); /* 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; } } else if(fabs(plane->pv.y)>=fabs(plane->pv.x)&&fabs(plane->pv.y)>=fabs(plane->pv.z)) /*If the y vector of the normal vector to the plane is larger than x and z vector we project the ring to the xz-plane*/ { for (i=0; i<ring->npoints-1; i++) { double vt; getPoint3dz_p(ring, i+1, &v2); /* edge from vertex i to vertex i+1 */ if ( /* an upward crossing */ ((v1.z <= p->z) && (v2.z > p->z)) /* a downward crossing */ || ((v1.z > p->z) && (v2.z <= p->z)) ) { vt = (double)(p->z - v1.z) / (v2.z - v1.z); /* 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; } } else /*Hopefully we only have the cases where x part of the normal vector is largest left*/ { for (i=0; i<ring->npoints-1; i++) { double vt; getPoint3dz_p(ring, i+1, &v2); /* edge from vertex i to vertex i+1 */ if ( /* an upward crossing */ ((v1.z <= p->z) && (v2.z > p->z)) /* a downward crossing */ || ((v1.z > p->z) && (v2.z <= p->z)) ) { vt = (double)(p->z - v1.z) / (v2.z - v1.z); /* P.x <intersect */ if (p->y < v1.y + vt * (v2.y - v1.y)) { /* a valid crossing of y=p.y right of p.x */ ++cn; } } v1 = v2; } } LWDEBUGF(3, "pt_in_ring_3d returning %d", cn&1); return (cn&1); /* 0 if even (out), and 1 if odd (in) */ }
LWGEOM * lwgeom_symdifference(const LWGEOM* geom1, const LWGEOM* geom2) { GEOSGeometry *g1, *g2, *g3; LWGEOM *result; int is3d; int srid; /* A.SymDifference(Empty) == A */ if ( lwgeom_is_empty(geom2) ) return lwgeom_clone(geom1); /* Empty.DymDifference(B) == Empty */ if ( lwgeom_is_empty(geom1) ) return lwgeom_clone(geom1); /* ensure srids are identical */ srid = (int)(geom1->srid); error_if_srid_mismatch(srid, (int)(geom2->srid)); is3d = (FLAGS_GET_Z(geom1->flags) || FLAGS_GET_Z(geom2->flags)) ; initGEOS(lwnotice, lwgeom_geos_error); g1 = LWGEOM2GEOS(geom1); if ( 0 == g1 ) /* exception thrown at construction */ { lwerror("First argument geometry could not be converted to GEOS: %s", lwgeom_geos_errmsg); return NULL; } g2 = LWGEOM2GEOS(geom2); if ( 0 == g2 ) /* exception thrown at construction */ { lwerror("Second argument geometry could not be converted to GEOS: %s", lwgeom_geos_errmsg); GEOSGeom_destroy(g1); return NULL; } g3 = GEOSSymDifference(g1,g2); if (g3 == NULL) { GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); lwerror("GEOSSymDifference: %s", lwgeom_geos_errmsg); return NULL; /*never get here */ } LWDEBUGF(3, "result: %s", GEOSGeomToWKT(g3)); GEOSSetSRID(g3, srid); result = GEOS2LWGEOM(g3, is3d); if (result == NULL) { GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); GEOSGeom_destroy(g3); lwerror("GEOS symdifference() threw an error (result postgis geometry formation)!"); return NULL ; /*never get here */ } GEOSGeom_destroy(g1); GEOSGeom_destroy(g2); GEOSGeom_destroy(g3); return result; }
/** This is a recursive function delivering every possible combination of subgeometries */ int lw_dist3d_recursive(const LWGEOM *lwg1,const LWGEOM *lwg2, DISTPTS3D *dl) { int i, j; int n1=1; int n2=1; LWGEOM *g1 = NULL; LWGEOM *g2 = NULL; LWCOLLECTION *c1 = NULL; LWCOLLECTION *c2 = NULL; LWDEBUGF(2, "lw_dist3d_recursive is called with type1=%d, type2=%d", lwg1->type, lwg2->type); if (lwgeom_is_collection(lwg1)) { LWDEBUG(3, "First geometry is collection"); c1 = lwgeom_as_lwcollection(lwg1); n1 = c1->ngeoms; } if (lwgeom_is_collection(lwg2)) { LWDEBUG(3, "Second geometry is collection"); c2 = lwgeom_as_lwcollection(lwg2); n2 = c2->ngeoms; } for ( i = 0; i < n1; i++ ) { if (lwgeom_is_collection(lwg1)) { g1 = c1->geoms[i]; } else { g1 = (LWGEOM*)lwg1; } if (lwgeom_is_empty(g1)) return LW_TRUE; if (lwgeom_is_collection(g1)) { LWDEBUG(3, "Found collection inside first geometry collection, recursing"); if (!lw_dist3d_recursive(g1, lwg2, dl)) return LW_FALSE; continue; } for ( j = 0; j < n2; j++ ) { if (lwgeom_is_collection(lwg2)) { g2 = c2->geoms[j]; } else { g2 = (LWGEOM*)lwg2; } if (lwgeom_is_collection(g2)) { LWDEBUG(3, "Found collection inside second geometry collection, recursing"); if (!lw_dist3d_recursive(g1, g2, dl)) return LW_FALSE; continue; } /*If one of geometries is empty, return. True here only means continue searching. False would have stoped the process*/ if (lwgeom_is_empty(g1)||lwgeom_is_empty(g2)) return LW_TRUE; if (!lw_dist3d_distribute_bruteforce(g1, g2, dl)) return LW_FALSE; if (dl->distance<=dl->tolerance && dl->mode == DIST_MIN) return LW_TRUE; /*just a check if the answer is already given*/ } } return LW_TRUE; }
GEOSGeometry* LWGEOM_GEOS_buildArea(const GEOSGeometry* geom_in) { GEOSGeometry *tmp; GEOSGeometry *geos_result, *shp; GEOSGeometry const *vgeoms[1]; uint32_t i, ngeoms; int srid = GEOSGetSRID(geom_in); vgeoms[0] = geom_in; geos_result = GEOSPolygonize(vgeoms, 1); LWDEBUGF(3, "GEOSpolygonize returned @ %p", geos_result); /* Null return from GEOSpolygonize (an exception) */ if ( ! geos_result ) return 0; /* * We should now have a collection */ #if PARANOIA_LEVEL > 0 if ( GEOSGeometryTypeId(geos_result) != COLLECTIONTYPE ) { GEOSGeom_destroy(geos_result); lwerror("Unexpected return from GEOSpolygonize"); return 0; } #endif ngeoms = GEOSGetNumGeometries(geos_result); LWDEBUGF(3, "GEOSpolygonize: ngeoms in polygonize output: %d", ngeoms); LWDEBUGF(3, "GEOSpolygonize: polygonized:%s", lwgeom_to_ewkt(GEOS2LWGEOM(geos_result, 0))); /* * No geometries in collection, early out */ if ( ngeoms == 0 ) { GEOSSetSRID(geos_result, srid); return geos_result; } /* * Return first geometry if we only have one in collection, * to avoid the unnecessary Geometry clone below. */ if ( ngeoms == 1 ) { tmp = (GEOSGeometry *)GEOSGetGeometryN(geos_result, 0); if ( ! tmp ) { GEOSGeom_destroy(geos_result); return 0; /* exception */ } shp = GEOSGeom_clone(tmp); GEOSGeom_destroy(geos_result); /* only safe after the clone above */ GEOSSetSRID(shp, srid); return shp; } /* * Iteratively invoke symdifference on outer rings * as suggested by Carl Anderson: * postgis-devel/2005-December/001805.html */ shp = NULL; for (i=0; i<ngeoms; ++i) { GEOSGeom extring; GEOSCoordSeq sq; /* * Construct a Polygon from geometry i exterior ring * We don't use GEOSGeom_clone on the ExteriorRing * due to a bug in CAPI contained in GEOS 2.2 branch * failing to properly return a LinearRing from * a LinearRing clone. */ sq=GEOSCoordSeq_clone(GEOSGeom_getCoordSeq( GEOSGetExteriorRing(GEOSGetGeometryN( geos_result, i)) )); extring = GEOSGeom_createPolygon( GEOSGeom_createLinearRing(sq), NULL, 0 ); if ( extring == NULL ) /* exception */ { lwerror("GEOSCreatePolygon threw an exception"); return 0; } if ( shp == NULL ) { shp = extring; LWDEBUGF(3, "GEOSpolygonize: shp:%s", lwgeom_to_ewkt(GEOS2LWGEOM(shp, 0))); } else { tmp = GEOSSymDifference(shp, extring); LWDEBUGF(3, "GEOSpolygonize: SymDifference(%s, %s):%s", lwgeom_to_ewkt(GEOS2LWGEOM(shp, 0)), lwgeom_to_ewkt(GEOS2LWGEOM(extring, 0)), lwgeom_to_ewkt(GEOS2LWGEOM(tmp, 0)) ); GEOSGeom_destroy(shp); GEOSGeom_destroy(extring); shp = tmp; } } GEOSGeom_destroy(geos_result); GEOSSetSRID(shp, srid); return shp; }
/** This function distributes the brute-force for 3D so far the only type, tasks depending on type */ int lw_dist3d_distribute_bruteforce(const LWGEOM *lwg1, const LWGEOM *lwg2, DISTPTS3D *dl) { int t1 = lwg1->type; int t2 = lwg2->type; LWDEBUGF(2, "lw_dist3d_distribute_bruteforce is called with typ1=%d, type2=%d", lwg1->type, lwg2->type); if ( t1 == POINTTYPE ) { if ( t2 == POINTTYPE ) { dl->twisted=1; return lw_dist3d_point_point((LWPOINT *)lwg1, (LWPOINT *)lwg2, dl); } else if ( t2 == LINETYPE ) { dl->twisted=1; return lw_dist3d_point_line((LWPOINT *)lwg1, (LWLINE *)lwg2, dl); } else if ( t2 == POLYGONTYPE ) { dl->twisted=1; return lw_dist3d_point_poly((LWPOINT *)lwg1, (LWPOLY *)lwg2,dl); } else { lwerror("Unsupported geometry type: %s", lwtype_name(t2)); return LW_FALSE; } } else if ( t1 == LINETYPE ) { if ( t2 == POINTTYPE ) { dl->twisted=(-1); return lw_dist3d_point_line((LWPOINT *)lwg2,(LWLINE *)lwg1,dl); } else if ( t2 == LINETYPE ) { dl->twisted=1; return lw_dist3d_line_line((LWLINE *)lwg1,(LWLINE *)lwg2,dl); } else if ( t2 == POLYGONTYPE ) { dl->twisted=1; return lw_dist3d_line_poly((LWLINE *)lwg1,(LWPOLY *)lwg2,dl); } else { lwerror("Unsupported geometry type: %s", lwtype_name(t2)); return LW_FALSE; } } else if ( t1 == POLYGONTYPE ) { if ( t2 == POLYGONTYPE ) { dl->twisted=1; return lw_dist3d_poly_poly((LWPOLY *)lwg1, (LWPOLY *)lwg2,dl); } else if ( t2 == POINTTYPE ) { dl->twisted=-1; return lw_dist3d_point_poly((LWPOINT *)lwg2, (LWPOLY *)lwg1,dl); } else if ( t2 == LINETYPE ) { dl->twisted=-1; return lw_dist3d_line_poly((LWLINE *)lwg2,(LWPOLY *)lwg1,dl); } else { lwerror("Unsupported geometry type: %s", lwtype_name(t2)); return LW_FALSE; } } else { lwerror("Unsupported geometry type: %s", lwtype_name(t1)); return LW_FALSE; } /*You shouldn't being able to get here*/ lwerror("unspecified error in function lw_dist3d_distribute_bruteforce"); return LW_FALSE; }
LWGEOM* lwgeom_flip_coordinates(LWGEOM *in) { LWCOLLECTION *col; LWPOLY *poly; int i; if ( (!in) || lwgeom_is_empty(in) ) return in; LWDEBUGF(4, "lwgeom_flip_coordinates, got type: %s", lwtype_name(in->type)); switch (in->type) { case POINTTYPE: ptarray_flip_coordinates(lwgeom_as_lwpoint(in)->point); break; case LINETYPE: ptarray_flip_coordinates(lwgeom_as_lwline(in)->points); break; case CIRCSTRINGTYPE: ptarray_flip_coordinates(lwgeom_as_lwcircstring(in)->points); break; case POLYGONTYPE: poly = (LWPOLY *) in; for (i=0; i<poly->nrings; i++) { ptarray_flip_coordinates(poly->rings[i]); } break; case TRIANGLETYPE: ptarray_flip_coordinates(lwgeom_as_lwtriangle(in)->points); break; case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COLLECTIONTYPE: case COMPOUNDTYPE: case CURVEPOLYTYPE: case MULTISURFACETYPE: case MULTICURVETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: col = (LWCOLLECTION *) in; for (i=0; i<col->ngeoms; i++) { lwgeom_flip_coordinates(col->geoms[i]); } break; default: lwerror("lwgeom_flip_coordinates: unsupported geometry type: %s", lwtype_name(in->type)); return NULL; } lwgeom_drop_bbox(in); lwgeom_add_bbox(in); return in; }
/** * Stores a pointarray as varints in the buffer * @register_npoints, controls whether an npoints entry is added to the buffer (used to skip npoints for point types) * @dimension, states the dimensionality of object this array is part of (0 = point, 1 = linear, 2 = areal) */ static int ptarray_to_twkb_buf(const POINTARRAY *pa, TWKB_GLOBALS *globals, TWKB_STATE *ts, int register_npoints, int minpoints) { int ndims = FLAGS_NDIMS(pa->flags); int i, j; bytebuffer_t b; bytebuffer_t *b_p; int64_t nextdelta[MAX_N_DIMS]; int npoints = 0; size_t npoints_offset = 0; LWDEBUGF(2, "Entered %s", __func__); /* Dispense with the empty case right away */ if ( pa->npoints == 0 && register_npoints ) { LWDEBUGF(4, "Register npoints:%d", pa->npoints); bytebuffer_append_uvarint(ts->geom_buf, pa->npoints); return 0; } /* If npoints is more than 127 it is unpredictable how many bytes npoints will need */ /* Then we have to store the deltas in a temp buffer to later add them after npoints */ /* If noints is below 128 we know 1 byte will be needed */ /* Then we can make room for that 1 byte at once and write to */ /* ordinary buffer */ if( pa->npoints > 127 ) { /* Independent buffer to hold the coordinates, so we can put the npoints */ /* into the stream once we know how many points we actually have */ bytebuffer_init_with_size(&b, 3 * ndims * pa->npoints); b_p = &b; } else { /* We give an alias to our ordinary buffer */ b_p = ts->geom_buf; if ( register_npoints ) { /* We do not store a pointer to the place where we want the npoints value */ /* Instead we store how far from the beginning of the buffer we want the value */ /* That is because we otherwise will get in trouble if the buffer is reallocated */ npoints_offset = b_p->writecursor - b_p->buf_start; /* We just move the cursor 1 step to make room for npoints byte */ /* We use the function append_byte even if we have no value yet, */ /* since that gives us the check for big enough buffer and moves the cursor */ bytebuffer_append_byte(b_p, 0); } } for ( i = 0; i < pa->npoints; i++ ) { double *dbl_ptr = (double*)getPoint_internal(pa, i); int diff = 0; /* Write this coordinate to the buffer as a varint */ for ( j = 0; j < ndims; j++ ) { /* To get the relative coordinate we don't get the distance */ /* from the last point but instead the distance from our */ /* last accumulated point. This is important to not build up an */ /* accumulated error when rounding the coordinates */ nextdelta[j] = (int64_t) llround(globals->factor[j] * dbl_ptr[j]) - ts->accum_rels[j]; LWDEBUGF(4, "deltavalue: %d, ", nextdelta[j]); diff += llabs(nextdelta[j]); } /* Skipping the first point is not allowed */ /* If the sum(abs()) of all the deltas was zero, */ /* then this was a duplicate point, so we can ignore it */ if ( i > minpoints && diff == 0 ) continue; /* We really added a point, so... */ npoints++; /* Write this vertex to the temporary buffer as varints */ for ( j = 0; j < ndims; j++ ) { ts->accum_rels[j] += nextdelta[j]; bytebuffer_append_varint(b_p, nextdelta[j]); } /* See if this coordinate expands the bounding box */ if( globals->variant & TWKB_BBOX ) { for ( j = 0; j < ndims; j++ ) { if( ts->accum_rels[j] > ts->bbox_max[j] ) ts->bbox_max[j] = ts->accum_rels[j]; if( ts->accum_rels[j] < ts->bbox_min[j] ) ts->bbox_min[j] = ts->accum_rels[j]; } } } if ( pa->npoints > 127 ) { /* Now write the temporary results into the main buffer */ /* First the npoints */ if ( register_npoints ) bytebuffer_append_uvarint(ts->geom_buf, npoints); /* Now the coordinates */ bytebuffer_append_bytebuffer(ts->geom_buf, b_p); /* Clear our temporary buffer */ lwfree(b.buf_start); } else { /* If we didn't use a temp buffer, we just write that npoints value */ /* to where it belongs*/ if ( register_npoints ) varint_u64_encode_buf(npoints, b_p->buf_start + npoints_offset); } return 0; }
/** To get the effective area, we have to check what area a point results in when all smaller areas are eliminated */ static void tune_areas(EFFECTIVE_AREAS *ea, int avoid_collaps, int set_area, double trshld) { LWDEBUG(2, "Entered tune_areas"); const double *P1; const double *P2; const double *P3; double area; int go_on=1; double check_order_min_area = 0; int npoints=ea->inpts->npoints; int i; int current, before_current, after_current; MINHEAP tree = initiate_minheap(npoints); int is3d = FLAGS_GET_Z(ea->inpts->flags); /*Add all keys (index in initial_arealist) into minheap array*/ for (i=0;i<npoints;i++) { tree.key_array[i]=ea->initial_arealist+i; LWDEBUGF(2, "add nr %d, with area %lf, and %lf",i,ea->initial_arealist[i].area, tree.key_array[i]->area ); } tree.usedSize=npoints; /*order the keys by area, small to big*/ qsort(tree.key_array, npoints, sizeof(void*), cmpfunc); /*We have to put references to our tree in our point-list*/ for (i=0;i<npoints;i++) { ((areanode*) tree.key_array[i])->treeindex=i; LWDEBUGF(4,"Check ordering qsort gives, area=%lf and belong to point %d",((areanode*) tree.key_array[i])->area, tree.key_array[i]-ea->initial_arealist); } /*Ok, now we have a minHeap, just need to keep it*/ /*for (i=0;i<npoints-1;i++)*/ i=0; while (go_on) { /*Get a reference to the point with the currently smallest effective area*/ current=minheap_pop(&tree, ea->initial_arealist)-ea->initial_arealist; /*We have found the smallest area. That is the resulting effective area for the "current" point*/ if (i<npoints-avoid_collaps) ea->res_arealist[current]=ea->initial_arealist[current].area; else ea->res_arealist[current]=FLT_MAX; if(ea->res_arealist[current]<check_order_min_area) lwerror("Oh no, this is a bug. For some reason the minHeap returned our points in the wrong order. Please file a ticket in PostGIS ticket system, or send a mial at the mailing list.Returned area = %lf, and last area = %lf",ea->res_arealist[current],check_order_min_area); check_order_min_area=ea->res_arealist[current]; /*The found smallest area point is now regarded as elimnated and we have to recalculate the area the adjacent (ignoring earlier elimnated points) points gives*/ /*FInd point before and after*/ before_current=ea->initial_arealist[current].prev; after_current=ea->initial_arealist[current].next; P2= (double*)getPoint_internal(ea->inpts, before_current); P3= (double*)getPoint_internal(ea->inpts, after_current); /*Check if point before current point is the first in the point array. */ if(before_current>0) { P1= (double*)getPoint_internal(ea->inpts, ea->initial_arealist[before_current].prev); if(is3d) area=triarea3d(P1, P2, P3); else area=triarea2d(P1, P2, P3); ea->initial_arealist[before_current].area = FP_MAX(area,ea->res_arealist[current]); minheap_update(&tree, ea->initial_arealist, ea->initial_arealist[before_current].treeindex); } if(after_current<npoints-1)/*Check if point after current point is the last in the point array. */ { P1=P2; P2=P3; P3= (double*)getPoint_internal(ea->inpts, ea->initial_arealist[after_current].next); if(is3d) area=triarea3d(P1, P2, P3); else area=triarea2d(P1, P2, P3); ea->initial_arealist[after_current].area = FP_MAX(area,ea->res_arealist[current]); minheap_update(&tree, ea->initial_arealist, ea->initial_arealist[after_current].treeindex); } /*rearrange the nodes so the eliminated point will be ingored on the next run*/ ea->initial_arealist[before_current].next = ea->initial_arealist[current].next; ea->initial_arealist[after_current].prev = ea->initial_arealist[current].prev; /*Check if we are finnished*/ if((!set_area && ea->res_arealist[current]>trshld) || (ea->initial_arealist[0].next==(npoints-1))) go_on=0; i++; }; destroy_minheap(tree); return; }
/** * GEOMETRY * Generic handling for WKB geometries. The front of every WKB geometry * (including those embedded in collections) is an endian byte, a type * number and an optional srid number. We handle all those here, then pass * to the appropriate handler for the specific type. */ LWGEOM* lwgeom_from_wkb_state(wkb_parse_state *s) { char wkb_little_endian; uint32_t wkb_type; LWDEBUG(4,"Entered function"); /* Fail when handed incorrect starting byte */ wkb_little_endian = byte_from_wkb_state(s); if( wkb_little_endian != 1 && wkb_little_endian != 0 ) { LWDEBUG(4,"Leaving due to bad first byte!"); lwerror("Invalid endian flag value encountered."); return NULL; } /* Check the endianness of our input */ s->swap_bytes = LW_FALSE; if( getMachineEndian() == NDR ) /* Machine arch is little */ { if ( ! wkb_little_endian ) /* Data is big! */ s->swap_bytes = LW_TRUE; } else /* Machine arch is big */ { if ( wkb_little_endian ) /* Data is little! */ s->swap_bytes = LW_TRUE; } /* Read the type number */ wkb_type = integer_from_wkb_state(s); LWDEBUGF(4,"Got WKB type number: 0x%X", wkb_type); lwtype_from_wkb_state(s, wkb_type); /* Read the SRID, if necessary */ if( s->has_srid ) { s->srid = clamp_srid(integer_from_wkb_state(s)); /* TODO: warn on explicit UNKNOWN srid ? */ LWDEBUGF(4,"Got SRID: %u", s->srid); } /* Do the right thing */ switch( s->lwtype ) { case POINTTYPE: return (LWGEOM*)lwpoint_from_wkb_state(s); break; case LINETYPE: return (LWGEOM*)lwline_from_wkb_state(s); break; case CIRCSTRINGTYPE: return (LWGEOM*)lwcircstring_from_wkb_state(s); break; case POLYGONTYPE: return (LWGEOM*)lwpoly_from_wkb_state(s); break; case TRIANGLETYPE: return (LWGEOM*)lwtriangle_from_wkb_state(s); break; case CURVEPOLYTYPE: return (LWGEOM*)lwcurvepoly_from_wkb_state(s); break; case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case COMPOUNDTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: case COLLECTIONTYPE: return (LWGEOM*)lwcollection_from_wkb_state(s); break; /* Unknown type! */ default: lwerror("Unsupported geometry type: %s [%d]", lwtype_name(s->lwtype), s->lwtype); } /* Return value to keep compiler happy. */ return NULL; }
/** * @brief returns 0 for points, 1 for lines, 2 for polygons. * returns max dimension for a collection. * returns -1 for the empty geometry (TODO) * returns -2 on error */ static int32 lwgeom_dimension_recursive(const uchar *serialized) { LWGEOM_INSPECTED *inspected; int ret = -1; int i; /** @todo * This has the unfortunate habit of traversing the CURVEPOLYTYPe * geoms and returning 1, as all contained geoms are linear. * Here we preempt this problem. * TODO: This should handle things a bit better. Only * GEOMETRYCOLLECTION should ever need to be traversed. */ if (lwgeom_getType(serialized[0]) == CURVEPOLYTYPE) return 2; inspected = lwgeom_inspect(serialized); for (i=0; i<inspected->ngeometries; i++) { uchar *subgeom; char typeflags = lwgeom_getsubtype_inspected(inspected, i); int type = lwgeom_getType(typeflags); int dims=-1; LWDEBUGF(3, "lwgeom_dimension_recursive: type %d", type); if ( type == POINTTYPE ) dims = 0; else if ( type == MULTIPOINTTYPE ) dims=0; else if ( type == LINETYPE ) dims=1; else if ( type == CIRCSTRINGTYPE ) dims=1; else if ( type == COMPOUNDTYPE ) dims=1; else if ( type == MULTILINETYPE ) dims=1; else if ( type == MULTICURVETYPE ) dims=1; else if ( type == POLYGONTYPE ) dims=2; else if ( type == CURVEPOLYTYPE ) dims=2; else if ( type == MULTIPOLYGONTYPE ) dims=2; else if ( type == MULTISURFACETYPE ) dims=2; else if ( type == COLLECTIONTYPE ) { subgeom = lwgeom_getsubgeometry_inspected(inspected, i); if ( subgeom == NULL ) { lwinspected_release(inspected); return -2; } dims = lwgeom_dimension_recursive(subgeom); } if ( dims == 2 ) { /* nothing can be higher */ lwinspected_release(inspected); return 2; } if ( dims > ret ) ret = dims; } lwinspected_release(inspected); return ret; }
LWGEOM* pta_desegmentize(POINTARRAY *points, int type, int srid) { int i = 0, j, k; POINT4D a1, a2, a3, b; char *edges_in_arcs; int found_arc = LW_FALSE; int current_arc = 1; int num_edges; int edge_type = -1; int start, end; LWCOLLECTION *outcol; /* Die on null input */ if ( ! points ) lwerror("pta_desegmentize called with null pointarray"); /* Null on empty input? */ if ( points->npoints == 0 ) return NULL; /* We can't desegmentize anything shorter than four points */ if ( points->npoints < 4 ) { /* Return a linestring here*/ lwerror("pta_desegmentize needs implementation for npoints < 4"); } /* Allocate our result array of vertices that are part of arcs */ num_edges = points->npoints - 1; edges_in_arcs = lwalloc(num_edges); memset(edges_in_arcs, 0, num_edges); /* We make a candidate arc of the first two edges, */ /* And then see if the next edge follows it */ while( i < num_edges-2 ) { found_arc = LW_FALSE; /* Make candidate arc */ getPoint4d_p(points, i , &a1); getPoint4d_p(points, i+1, &a2); getPoint4d_p(points, i+2, &a3); for( j = i+3; j < num_edges+1; j++ ) { LWDEBUGF(4, "i=%d, j=%d", i, j); getPoint4d_p(points, j, &b); /* Does this point fall on our candidate arc? */ if ( pt_continues_arc(&a1, &a2, &a3, &b) ) { /* Yes. Mark this edge and the two preceding it as arc components */ LWDEBUGF(4, "pt_continues_arc #%d", current_arc); found_arc = LW_TRUE; for ( k = j-1; k > j-4; k-- ) edges_in_arcs[k] = current_arc; } else { /* No. So we're done with this candidate arc */ LWDEBUG(4, "pt_continues_arc = false"); current_arc++; break; } } /* Jump past all the edges that were added to the arc */ if ( found_arc ) { i = j-1; } else { /* Mark this edge as a linear edge */ edges_in_arcs[i] = 0; i = i+1; } } #if POSTGIS_DEBUG_LEVEL > 3 { char *edgestr = lwalloc(num_edges+1); for ( i = 0; i < num_edges; i++ ) { if ( edges_in_arcs[i] ) edgestr[i] = 48 + edges_in_arcs[i]; else edgestr[i] = '.'; } edgestr[num_edges] = 0; LWDEBUGF(3, "edge pattern %s", edgestr); lwfree(edgestr); } #endif start = 0; edge_type = edges_in_arcs[0]; outcol = lwcollection_construct_empty(COMPOUNDTYPE, srid, ptarray_has_z(points), ptarray_has_m(points)); for( i = 1; i < num_edges; i++ ) { if( edge_type != edges_in_arcs[i] ) { end = i - 1; lwcollection_add_lwgeom(outcol, geom_from_pa(points, srid, edge_type, start, end)); start = i; edge_type = edges_in_arcs[i]; } } /* Roll out last item */ end = num_edges - 1; lwcollection_add_lwgeom(outcol, geom_from_pa(points, srid, edge_type, start, end)); /* Strip down to singleton if only one entry */ if ( outcol->ngeoms == 1 ) { LWGEOM *outgeom = outcol->geoms[0]; lwfree(outcol); return outgeom; } return lwcollection_as_lwgeom(outcol); }
int lwline_split_by_point_to(const LWLINE* lwline_in, const LWPOINT* blade_in, LWMLINE* v) { double mindist = -1; POINT4D pt, pt_projected; POINT4D p1, p2; POINTARRAY *ipa = lwline_in->points; POINTARRAY* pa1; POINTARRAY* pa2; int i, nsegs, seg = -1; /* 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 * -> Leave collection untouched, 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); /* Find closest segment */ getPoint4d_p(ipa, 0, &p1); nsegs = ipa->npoints - 1; for ( i = 0; i < nsegs; i++ ) { getPoint4d_p(ipa, i+1, &p2); double dist; dist = distance2d_pt_seg((POINT2D*)&pt, (POINT2D*)&p1, (POINT2D*)&p2); LWDEBUGF(4, " Distance of point %g %g to segment %g %g, %g %g: %g", pt.x, pt.y, p1.x, p1.y, p2.x, p2.y, dist); if (i==0 || dist < mindist ) { mindist = dist; seg=i; if ( mindist == 0.0 ) break; /* can't be closer than ON line */ } p1 = p2; } LWDEBUGF(3, "Closest segment: %d", seg); LWDEBUGF(3, "mindist: %g", mindist); /* No intersection */ if ( mindist > 0 ) return 0; /* empty or single-point line, intersection on boundary */ if ( seg < 0 ) return 1; /* * We need to project the * point on the closest segment, * to interpolate Z and M if needed */ getPoint4d_p(ipa, seg, &p1); getPoint4d_p(ipa, seg+1, &p2); closest_point_on_segment(&pt, &p1, &p2, &pt_projected); /* But X and Y we want the ones of the input point, * as on some architectures the interpolation math moves the * coordinates (see #3422) */ pt_projected.x = pt.x; pt_projected.y = pt.y; LWDEBUGF(3, "Projected point:(%g %g), seg:%d, p1:(%g %g), p2:(%g %g)", pt_projected.x, pt_projected.y, seg, p1.x, p1.y, p2.x, p2.y); /* When closest point == an endpoint, this is a boundary intersection */ if ( ( (seg == nsegs-1) && p4d_same(&pt_projected, &p2) ) || ( (seg == 0) && p4d_same(&pt_projected, &p1) ) ) { return 1; } /* This is an internal intersection, let's build the two new pointarrays */ pa1 = ptarray_construct_empty(FLAGS_GET_Z(ipa->flags), FLAGS_GET_M(ipa->flags), seg+2); /* TODO: replace with a memcpy ? */ for (i=0; i<=seg; ++i) { getPoint4d_p(ipa, i, &p1); ptarray_append_point(pa1, &p1, LW_FALSE); } ptarray_append_point(pa1, &pt_projected, LW_FALSE); pa2 = ptarray_construct_empty(FLAGS_GET_Z(ipa->flags), FLAGS_GET_M(ipa->flags), ipa->npoints-seg); ptarray_append_point(pa2, &pt_projected, LW_FALSE); /* TODO: replace with a memcpy (if so need to check for duplicated point) ? */ for (i=seg+1; i<ipa->npoints; ++i) { getPoint4d_p(ipa, i, &p1); ptarray_append_point(pa2, &p1, LW_FALSE); } /* 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; }
/* * 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; }