double lwgeom_interpolate_point(const LWGEOM *lwin, const LWPOINT *lwpt) { POINT4D p, p_proj; double ret = 0.0; if ( ! lwin ) lwerror("lwgeom_interpolate_point: null input geometry!"); if ( ! lwgeom_has_m(lwin) ) lwerror("Input geometry does not have a measure dimension"); if ( lwgeom_is_empty(lwin) || lwpoint_is_empty(lwpt) ) lwerror("Input geometry is empty"); switch ( lwin->type ) { case LINETYPE: { LWLINE *lwline = lwgeom_as_lwline(lwin); lwpoint_getPoint4d_p(lwpt, &p); ret = ptarray_locate_point(lwline->points, &p, NULL, &p_proj); ret = p_proj.m; break; } default: lwerror("This function does not accept %s geometries.", lwtype_name(lwin->type)); } return ret; }
LWGEOM* lwgeom_locate_along(const LWGEOM *lwin, double m, double offset) { if ( ! lwin ) return NULL; if ( ! lwgeom_has_m(lwin) ) lwerror("Input geometry does not have a measure dimension"); switch (lwin->type) { case POINTTYPE: return (LWGEOM*)lwpoint_locate_along((LWPOINT*)lwin, m, offset); case MULTIPOINTTYPE: return (LWGEOM*)lwmpoint_locate_along((LWMPOINT*)lwin, m, offset); case LINETYPE: return (LWGEOM*)lwline_locate_along((LWLINE*)lwin, m, offset); case MULTILINETYPE: return (LWGEOM*)lwmline_locate_along((LWMLINE*)lwin, m, offset); /* Only line types supported right now */ /* TO DO: CurveString, CompoundCurve, MultiCurve */ /* TO DO: Point, MultiPoint */ default: lwerror("Only linear geometries are supported, %s provided.",lwtype_name(lwin->type)); return NULL; } return NULL; }
static LWMPOINT* lwmline_locate_along(const LWMLINE *lwmline, double m, double offset) { LWMPOINT *lwmpoint = NULL; LWGEOM *lwg = lwmline_as_lwgeom(lwmline); int i, j; /* Return degenerates upwards */ if ( (!lwmline) || (lwmline->ngeoms < 1) ) return NULL; /* Construct return */ lwmpoint = lwmpoint_construct_empty(lwgeom_get_srid(lwg), lwgeom_has_z(lwg), lwgeom_has_m(lwg)); /* Locate along each sub-line */ for ( i = 0; i < lwmline->ngeoms; i++ ) { LWMPOINT *along = lwline_locate_along(lwmline->geoms[i], m, offset); if ( along != NULL ) { for ( j = 0; j < along->ngeoms; j++ ) { lwmpoint_add_lwpoint(lwmpoint, along->geoms[i]); } /* Free the containing geometry, but leave the sub-geometries around */ if ( along->bbox ) lwfree(along->bbox); lwfree(along); } } return lwmpoint; }
LWCOLLECTION* lwgeom_locate_between(const LWGEOM *lwin, double from, double to, double offset) { if ( ! lwgeom_has_m(lwin) ) lwerror("Input geometry does not have a measure dimension"); return lwgeom_clip_to_ordinate_range(lwin, 'M', from, to, offset); }
LWPOLY* lwpoly_grid(const LWPOLY *poly, const gridspec *grid) { LWPOLY *opoly; int ri; #if 0 /* * TODO: control this assertion * it is assumed that, since the grid size will be a pixel, * a visible ring should show at least a white pixel inside, * thus, for a square, that would be grid_xsize*grid_ysize */ double minvisiblearea = grid->xsize * grid->ysize; #endif LWDEBUGF(3, "lwpoly_grid: applying grid to polygon with %d rings", poly->nrings); opoly = lwpoly_construct_empty(poly->srid, lwgeom_has_z((LWGEOM*)poly), lwgeom_has_m((LWGEOM*)poly)); for (ri=0; ri<poly->nrings; ri++) { POINTARRAY *ring = poly->rings[ri]; POINTARRAY *newring; newring = ptarray_grid(ring, grid); /* Skip ring if not composed by at least 4 pts (3 segments) */ if ( newring->npoints < 4 ) { ptarray_free(newring); LWDEBUGF(3, "grid_polygon3d: ring%d skipped ( <4 pts )", ri); if ( ri ) continue; else break; /* this is the external ring, no need to work on holes */ } if ( ! lwpoly_add_ring(opoly, newring) ) { lwerror("lwpoly_grid, memory error"); return NULL; } } LWDEBUGF(3, "lwpoly_grid: simplified polygon with %d rings", opoly->nrings); if ( ! opoly->nrings ) { lwpoly_free(opoly); return NULL; } return opoly; }
static LWMPOINT* lwpoint_locate_along(const LWPOINT *lwpoint, double m, double offset) { double point_m = lwpoint_get_m(lwpoint); LWGEOM *lwg = lwpoint_as_lwgeom(lwpoint); LWMPOINT *r = lwmpoint_construct_empty(lwgeom_get_srid(lwg), lwgeom_has_z(lwg), lwgeom_has_m(lwg)); if ( FP_EQUALS(m, point_m) ) { lwmpoint_add_lwpoint(r, lwpoint_clone(lwpoint)); } return r; }
/** * Clip an input MULTIPOINT between two values, on any ordinate input. */ LWCOLLECTION* lwmpoint_clip_to_ordinate_range(const LWMPOINT *mpoint, char ordinate, double from, double to) { LWCOLLECTION *lwgeom_out = NULL; char hasz, hasm; int i; /* Nothing to do with NULL */ if ( ! mpoint ) lwerror("Null input geometry."); /* Ensure 'from' is less than 'to'. */ if ( to < from ) { double t = from; from = to; to = t; } /* Read Z/M info */ hasz = lwgeom_has_z(lwmpoint_as_lwgeom(mpoint)); hasm = lwgeom_has_m(lwmpoint_as_lwgeom(mpoint)); /* Prepare return object */ lwgeom_out = lwcollection_construct_empty(MULTIPOINTTYPE, mpoint->srid, hasz, hasm); /* For each point, is its ordinate value between from and to? */ for ( i = 0; i < mpoint->ngeoms; i ++ ) { POINT4D p4d; double ordinate_value; lwpoint_getPoint4d_p(mpoint->geoms[i], &p4d); ordinate_value = lwpoint_get_ordinate(&p4d, ordinate); if ( from <= ordinate_value && to >= ordinate_value ) { LWPOINT *lwp = lwpoint_clone(mpoint->geoms[i]); lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(lwp)); } } /* Set the bbox */ lwgeom_drop_bbox((LWGEOM*)lwgeom_out); lwgeom_add_bbox((LWGEOM*)lwgeom_out); return lwgeom_out; }
LWMPOINT* lwmpoint_from_lwgeom(const LWGEOM *g) { LWPOINTITERATOR* it = lwpointiterator_create(g); int has_z = lwgeom_has_z(g); int has_m = lwgeom_has_m(g); LWMPOINT* result = lwmpoint_construct_empty(g->srid, has_z, has_m); POINT4D p; while(lwpointiterator_next(it, &p)) { LWPOINT* lwp = lwpoint_make(g->srid, has_z, has_m, &p); lwmpoint_add_lwpoint(result, lwp); } lwpointiterator_destroy(it); return result; }
/** * Clip an input POINT between two values, on any ordinate input. */ LWCOLLECTION* lwpoint_clip_to_ordinate_range(const LWPOINT *point, char ordinate, double from, double to) { LWCOLLECTION *lwgeom_out = NULL; char hasz, hasm; POINT4D p4d; double ordinate_value; /* Nothing to do with NULL */ if ( ! point ) lwerror("Null input geometry."); /* Ensure 'from' is less than 'to'. */ if ( to < from ) { double t = from; from = to; to = t; } /* Read Z/M info */ hasz = lwgeom_has_z(lwpoint_as_lwgeom(point)); hasm = lwgeom_has_m(lwpoint_as_lwgeom(point)); /* Prepare return object */ lwgeom_out = lwcollection_construct_empty(MULTIPOINTTYPE, point->srid, hasz, hasm); /* Test if ordinate is in range */ lwpoint_getPoint4d_p(point, &p4d); ordinate_value = lwpoint_get_ordinate(&p4d, ordinate); if ( from <= ordinate_value && to >= ordinate_value ) { LWPOINT *lwp = lwpoint_clone(point); lwcollection_add_lwgeom(lwgeom_out, lwpoint_as_lwgeom(lwp)); } /* Set the bbox */ lwgeom_drop_bbox((LWGEOM*)lwgeom_out); lwgeom_add_bbox((LWGEOM*)lwgeom_out); return lwgeom_out; }
static LWMPOINT* lwmpoint_locate_along(const LWMPOINT *lwin, double m, double offset) { LWGEOM *lwg = lwmpoint_as_lwgeom(lwin); LWMPOINT *lwout = NULL; int i; /* Construct return */ lwout = lwmpoint_construct_empty(lwgeom_get_srid(lwg), lwgeom_has_z(lwg), lwgeom_has_m(lwg)); for ( i = 0; i < lwin->ngeoms; i++ ) { double point_m = lwpoint_get_m(lwin->geoms[i]); if ( FP_EQUALS(m, point_m) ) { lwmpoint_add_lwpoint(lwout, lwpoint_clone(lwin->geoms[i])); } } return lwout; }
static LWMPOINT* lwline_locate_along(const LWLINE *lwline, double m, double offset) { POINTARRAY *opa = NULL; LWMPOINT *mp = NULL; LWGEOM *lwg = lwline_as_lwgeom(lwline); int hasz, hasm, srid; /* Return degenerates upwards */ if ( ! lwline ) return NULL; /* Create empty return shell */ srid = lwgeom_get_srid(lwg); hasz = lwgeom_has_z(lwg); hasm = lwgeom_has_m(lwg); if ( hasm ) { /* Find points along */ opa = ptarray_locate_along(lwline->points, m, offset); } else { LWLINE *lwline_measured = lwline_measured_from_lwline(lwline, 0.0, 1.0); opa = ptarray_locate_along(lwline_measured->points, m, offset); lwline_free(lwline_measured); } /* Return NULL as EMPTY */ if ( ! opa ) return lwmpoint_construct_empty(srid, hasz, hasm); /* Convert pointarray into a multipoint */ mp = lwmpoint_construct(srid, opa); ptarray_free(opa); return mp; }
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; }
static LWGEOM* parse_geojson_multipolygon(json_object *geojson, int *hasz, int root_srid) { LWGEOM *geom = NULL; int i, j, k; json_object* poObjPolys = NULL; if (!root_srid) { geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOLYGONTYPE, root_srid, 1, 0); } else { geom = (LWGEOM *)lwcollection_construct_empty(MULTIPOLYGONTYPE, -1, 1, 0); } poObjPolys = findMemberByName( geojson, "coordinates" ); if ( ! poObjPolys ) { geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4); return NULL; } if( json_type_array == json_object_get_type( poObjPolys ) ) { const int nPolys = json_object_array_length( poObjPolys ); for(i = 0; i < nPolys; ++i) { json_object* poObjPoly = json_object_array_get_idx( poObjPolys, i ); if( json_type_array == json_object_get_type( poObjPoly ) ) { LWPOLY *lwpoly = lwpoly_construct_empty(geom->srid, lwgeom_has_z(geom), lwgeom_has_m(geom)); int nRings = json_object_array_length( poObjPoly ); for(j = 0; j < nRings; ++j) { json_object* points = json_object_array_get_idx( poObjPoly, j ); if( json_type_array == json_object_get_type( points ) ) { POINTARRAY *pa = ptarray_construct_empty(1, 0, 1); int nPoints = json_object_array_length( points ); for ( k=0; k < nPoints; k++ ) { json_object* coords = json_object_array_get_idx( points, k ); parse_geojson_coord(coords, hasz, pa); } lwpoly_add_ring(lwpoly, pa); } } geom = (LWGEOM*)lwmpoly_add_lwpoly((LWMPOLY*)geom, lwpoly); } } } return geom; }
static int lwgeom_write_to_buffer(const LWGEOM *geom, TWKB_GLOBALS *globals, TWKB_STATE *parent_state) { int i, is_empty, has_z, has_m, ndims; size_t bbox_size = 0, optional_precision_byte = 0; uint8_t flag = 0, type_prec = 0; TWKB_STATE child_state; memset(&child_state, 0, sizeof(TWKB_STATE)); child_state.header_buf = bytebuffer_create_with_size(16); child_state.geom_buf = bytebuffer_create_with_size(64); child_state.idlist = parent_state->idlist; /* Read dimensionality from input */ has_z = lwgeom_has_z(geom); has_m = lwgeom_has_m(geom); ndims = lwgeom_ndims(geom); is_empty = lwgeom_is_empty(geom); /* Do we need extended precision? If we have a Z or M we do. */ optional_precision_byte = (has_z || has_m); /* Both X and Y dimension use the same precision */ globals->factor[0] = pow(10, globals->prec_xy); globals->factor[1] = globals->factor[0]; /* Z and M dimensions have their own precisions */ if ( has_z ) globals->factor[2] = pow(10, globals->prec_z); if ( has_m ) globals->factor[2 + has_z] = pow(10, globals->prec_m); /* Reset stats */ for ( i = 0; i < MAX_N_DIMS; i++ ) { /* Reset bbox calculation */ child_state.bbox_max[i] = INT64_MIN; child_state.bbox_min[i] = INT64_MAX; /* Reset acumulated delta values to get absolute values on next point */ child_state.accum_rels[i] = 0; } /* TYPE/PRECISION BYTE */ if ( abs(globals->prec_xy) > 7 ) lwerror("%s: X/Z precision cannot be greater than 7 or less than -7", __func__); /* Read the TWKB type number from the geometry */ TYPE_PREC_SET_TYPE(type_prec, lwgeom_twkb_type(geom)); /* Zig-zag the precision value before encoding it since it is a signed value */ TYPE_PREC_SET_PREC(type_prec, zigzag8(globals->prec_xy)); /* Write the type and precision byte */ bytebuffer_append_byte(child_state.header_buf, type_prec); /* METADATA BYTE */ /* Set first bit if we are going to store bboxes */ FIRST_BYTE_SET_BBOXES(flag, (globals->variant & TWKB_BBOX) && ! is_empty); /* Set second bit if we are going to store resulting size */ FIRST_BYTE_SET_SIZES(flag, globals->variant & TWKB_SIZE); /* There will be no ID-list (for now) */ FIRST_BYTE_SET_IDLIST(flag, parent_state->idlist && ! is_empty); /* Are there higher dimensions */ FIRST_BYTE_SET_EXTENDED(flag, optional_precision_byte); /* Empty? */ FIRST_BYTE_SET_EMPTY(flag, is_empty); /* Write the header byte */ bytebuffer_append_byte(child_state.header_buf, flag); /* EXTENDED PRECISION BYTE (OPTIONAL) */ /* If needed, write the extended dim byte */ if( optional_precision_byte ) { uint8_t flag = 0; if ( has_z && ( globals->prec_z > 7 || globals->prec_z < 0 ) ) lwerror("%s: Z precision cannot be negative or greater than 7", __func__); if ( has_m && ( globals->prec_m > 7 || globals->prec_m < 0 ) ) lwerror("%s: M precision cannot be negative or greater than 7", __func__); HIGHER_DIM_SET_HASZ(flag, has_z); HIGHER_DIM_SET_HASM(flag, has_m); HIGHER_DIM_SET_PRECZ(flag, globals->prec_z); HIGHER_DIM_SET_PRECM(flag, globals->prec_m); bytebuffer_append_byte(child_state.header_buf, flag); } /* It the geometry is empty, we're almost done */ if ( is_empty ) { /* If this output is sized, write the size of */ /* all following content, which is zero because */ /* there is none */ if ( globals->variant & TWKB_SIZE ) bytebuffer_append_byte(child_state.header_buf, 0); bytebuffer_append_bytebuffer(parent_state->geom_buf, child_state.header_buf); bytebuffer_destroy(child_state.header_buf); bytebuffer_destroy(child_state.geom_buf); return 0; } /* Write the TWKB into the output buffer */ lwgeom_to_twkb_buf(geom, globals, &child_state); /*If we have a header_buf, we know that this function is called inside a collection*/ /*and then we have to merge the bboxes of the included geometries*/ /*and put the result to the parent (the collection)*/ if( (globals->variant & TWKB_BBOX) && parent_state->header_buf ) { LWDEBUG(4,"Merge bboxes"); for ( i = 0; i < ndims; i++ ) { if(child_state.bbox_min[i]<parent_state->bbox_min[i]) parent_state->bbox_min[i] = child_state.bbox_min[i]; if(child_state.bbox_max[i]>parent_state->bbox_max[i]) parent_state->bbox_max[i] = child_state.bbox_max[i]; } } /* Did we have a box? If so, how big? */ bbox_size = 0; if( globals->variant & TWKB_BBOX ) { LWDEBUG(4,"We want boxes and will calculate required size"); bbox_size = sizeof_bbox(&child_state, ndims); } /* Write the size if wanted */ if( globals->variant & TWKB_SIZE ) { /* Here we have to add what we know will be written to header */ /* buffer after size value is written */ size_t size_to_register = bytebuffer_getlength(child_state.geom_buf); size_to_register += bbox_size; bytebuffer_append_uvarint(child_state.header_buf, size_to_register); } if( globals->variant & TWKB_BBOX ) write_bbox(&child_state, ndims); bytebuffer_append_bytebuffer(parent_state->geom_buf,child_state.header_buf); bytebuffer_append_bytebuffer(parent_state->geom_buf,child_state.geom_buf); bytebuffer_destroy(child_state.header_buf); bytebuffer_destroy(child_state.geom_buf); return 0; }
/** * 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; }
Datum LWGEOM_exteriorring_polygon(PG_FUNCTION_ARGS) { GSERIALIZED *geom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); GSERIALIZED *result; POINTARRAY *extring; LWGEOM *lwgeom; LWLINE *line; GBOX *bbox=NULL; int type = gserialized_get_type(geom); POSTGIS_DEBUG(2, "LWGEOM_exteriorring_polygon called."); if ( (type != POLYGONTYPE) && (type != CURVEPOLYTYPE) && (type != TRIANGLETYPE)) { elog(ERROR, "ExteriorRing: geom is not a polygon"); PG_RETURN_NULL(); } lwgeom = lwgeom_from_gserialized(geom); if( lwgeom_is_empty(lwgeom) ) { line = lwline_construct_empty(lwgeom->srid, lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom)); result = geometry_serialize(lwline_as_lwgeom(line)); } else if ( lwgeom->type == POLYGONTYPE ) { LWPOLY *poly = lwgeom_as_lwpoly(lwgeom); /* Ok, now we have a polygon. Here is its exterior ring. */ extring = poly->rings[0]; /* * This is a LWLINE constructed by exterior ring POINTARRAY * If the input geom has a bbox, use it for * the output geom, as exterior ring makes it up ! */ if ( poly->bbox ) bbox = gbox_copy(poly->bbox); line = lwline_construct(poly->srid, bbox, extring); result = geometry_serialize((LWGEOM *)line); lwgeom_release((LWGEOM *)line); } else if ( lwgeom->type == TRIANGLETYPE ) { LWTRIANGLE *triangle = lwgeom_as_lwtriangle(lwgeom); /* * This is a LWLINE constructed by exterior ring POINTARRAY * If the input geom has a bbox, use it for * the output geom, as exterior ring makes it up ! */ if ( triangle->bbox ) bbox = gbox_copy(triangle->bbox); line = lwline_construct(triangle->srid, bbox, triangle->points); result = geometry_serialize((LWGEOM *)line); lwgeom_release((LWGEOM *)line); } else { LWCURVEPOLY *curvepoly = lwgeom_as_lwcurvepoly(lwgeom); result = geometry_serialize(curvepoly->rings[0]); } lwgeom_free(lwgeom); PG_FREE_IF_COPY(geom, 0); PG_RETURN_POINTER(result); }
/* ** Given a generic geometry, return the "simplest" form. ** ** eg: ** LINESTRING() => LINESTRING() ** ** MULTILINESTRING(with a single line) => LINESTRING() ** ** GEOMETRYCOLLECTION(MULTILINESTRING()) => MULTILINESTRING() ** ** GEOMETRYCOLLECTION(MULTILINESTRING(), MULTILINESTRING(), POINT()) ** => GEOMETRYCOLLECTION(MULTILINESTRING(), POINT()) */ LWGEOM * lwgeom_homogenize(const LWGEOM *geom) { LWGEOM *hgeom; /* EMPTY Geometry */ if (lwgeom_is_empty(geom)) { if( lwgeom_is_collection(geom) ) { return lwcollection_as_lwgeom(lwcollection_construct_empty(geom->type, geom->srid, lwgeom_has_z(geom), lwgeom_has_m(geom))); } return lwgeom_clone(geom); } switch (geom->type) { /* Return simple geometries untouched */ case POINTTYPE: case LINETYPE: case CIRCSTRINGTYPE: case COMPOUNDTYPE: case TRIANGLETYPE: case CURVEPOLYTYPE: case POLYGONTYPE: return lwgeom_clone(geom); /* Process homogeneous geometries lightly */ case MULTIPOINTTYPE: case MULTILINETYPE: case MULTIPOLYGONTYPE: case MULTICURVETYPE: case MULTISURFACETYPE: case POLYHEDRALSURFACETYPE: case TINTYPE: { LWCOLLECTION *col = (LWCOLLECTION*)geom; /* Strip single-entry multi-geometries down to singletons */ if ( col->ngeoms == 1 ) { hgeom = lwgeom_clone((LWGEOM*)(col->geoms[0])); hgeom->srid = geom->srid; if (geom->bbox) hgeom->bbox = gbox_copy(geom->bbox); return hgeom; } /* Return proper multigeometry untouched */ return lwgeom_clone(geom); } /* Work on anonymous collections separately */ case COLLECTIONTYPE: return lwcollection_homogenize((LWCOLLECTION *) geom); } /* Unknown type */ lwerror("lwgeom_homogenize: Geometry Type not supported (%i)", lwtype_name(geom->type)); return NULL; /* Never get here! */ }
/** * Clip an input MULTILINESTRING between two values, on any ordinate input. */ LWCOLLECTION* lwmline_clip_to_ordinate_range(const LWMLINE *mline, char ordinate, double from, double to) { LWCOLLECTION *lwgeom_out = NULL; if ( ! mline ) { lwerror("Null input geometry."); return NULL; } if ( mline->ngeoms == 1) { lwgeom_out = lwline_clip_to_ordinate_range(mline->geoms[0], ordinate, from, to); } else { LWCOLLECTION *col; char hasz = lwgeom_has_z(lwmline_as_lwgeom(mline)); char hasm = lwgeom_has_m(lwmline_as_lwgeom(mline)); int i, j; char homogeneous = 1; size_t geoms_size = 0; lwgeom_out = lwcollection_construct_empty(MULTILINETYPE, mline->srid, hasz, hasm); FLAGS_SET_Z(lwgeom_out->flags, hasz); FLAGS_SET_M(lwgeom_out->flags, hasm); for ( i = 0; i < mline->ngeoms; i ++ ) { col = lwline_clip_to_ordinate_range(mline->geoms[i], ordinate, from, to); if ( col ) { /* Something was left after the clip. */ if ( lwgeom_out->ngeoms + col->ngeoms > geoms_size ) { geoms_size += 16; if ( lwgeom_out->geoms ) { lwgeom_out->geoms = lwrealloc(lwgeom_out->geoms, geoms_size * sizeof(LWGEOM*)); } else { lwgeom_out->geoms = lwalloc(geoms_size * sizeof(LWGEOM*)); } } for ( j = 0; j < col->ngeoms; j++ ) { lwgeom_out->geoms[lwgeom_out->ngeoms] = col->geoms[j]; lwgeom_out->ngeoms++; } if ( col->type != mline->type ) { homogeneous = 0; } /* Shallow free the struct, leaving the geoms behind. */ if ( col->bbox ) lwfree(col->bbox); lwfree(col->geoms); lwfree(col); } } lwgeom_drop_bbox((LWGEOM*)lwgeom_out); lwgeom_add_bbox((LWGEOM*)lwgeom_out); if ( ! homogeneous ) { lwgeom_out->type = COLLECTIONTYPE; } } if ( ! lwgeom_out || lwgeom_out->ngeoms == 0 ) /* Nothing left after clip. */ { return NULL; } return lwgeom_out; }
Datum LWGEOM_line_interpolate_point(PG_FUNCTION_ARGS) { GSERIALIZED *gser = PG_GETARG_GSERIALIZED_P(0); GSERIALIZED *result; double distance = PG_GETARG_FLOAT8(1); LWLINE *line; LWGEOM *geom; 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 isn't within [0,1]"); PG_RETURN_NULL(); } if ( gserialized_get_type(gser) != LINETYPE ) { elog(ERROR,"line_interpolate_point: 1st arg isn't a line"); PG_RETURN_NULL(); } /* Empty.InterpolatePoint == Point Empty */ if ( gserialized_is_empty(gser) ) { point = lwpoint_construct_empty(gserialized_get_srid(gser), gserialized_has_z(gser), gserialized_has_m(gser)); result = geometry_serialize(lwpoint_as_lwgeom(point)); lwpoint_free(point); PG_RETURN_POINTER(result); } geom = lwgeom_from_gserialized(gser); line = lwgeom_as_lwline(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(lwgeom_has_z(geom), lwgeom_has_m(geom), 1); ptarray_set_point4d(opa, 0, &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(lwgeom_has_z(geom), lwgeom_has_m(geom), 1); ptarray_set_point4d(opa, 0, &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(lwgeom_has_z(geom), lwgeom_has_m(geom), 1); ptarray_set_point4d(opa, 0, &pt); point = lwpoint_construct(line->srid, NULL, opa); PG_FREE_IF_COPY(gser, 0); PG_RETURN_POINTER(geometry_serialize(lwpoint_as_lwgeom(point))); }