bool Polygon::covers(PointRef& ref) const { GEOSCoordSequence* coords = GEOSCoordSeq_create_r(m_ctx, 1, 3); if (!coords) throw pdal_error("Unable to allocate coordinate sequence"); const double x = ref.getFieldAs<double>(Dimension::Id::X); const double y = ref.getFieldAs<double>(Dimension::Id::Y); const double z = ref.getFieldAs<double>(Dimension::Id::Z); if (!GEOSCoordSeq_setX_r(m_ctx, coords, 0, x)) throw pdal_error("unable to set x for coordinate sequence"); if (!GEOSCoordSeq_setY_r(m_ctx, coords, 0, y)) throw pdal_error("unable to set y for coordinate sequence"); if (!GEOSCoordSeq_setZ_r(m_ctx, coords, 0, z)) throw pdal_error("unable to set z for coordinate sequence"); GEOSGeometry* p = GEOSGeom_createPoint_r(m_ctx, coords); if (!p) throw pdal_error("unable to allocate candidate test point"); bool covers = (bool)(GEOSPreparedCovers_r(m_ctx, m_prepGeom, p)); GEOSGeom_destroy_r(m_ctx, p); return covers; }
State get_state(const Point &point, const GEOSPreparedGeometry *gp_domain, GEOSContextHandle_t handle) { State state; if (isfinite(point.x) && isfinite(point.y)) { // TODO: Avoid create-destroy GEOSCoordSequence *coords = GEOSCoordSeq_create_r(handle, 1, 2); GEOSCoordSeq_setX_r(handle, coords, 0, point.x); GEOSCoordSeq_setY_r(handle, coords, 0, point.y); GEOSGeometry *g_point = GEOSGeom_createPoint_r(handle, coords); state = GEOSPreparedCovers_r(handle, gp_domain, g_point) ? POINT_IN : POINT_OUT; GEOSGeom_destroy_r(handle, g_point); } else { state = POINT_NAN; } return state; }
/* * Return whether the given line segment is suitable as an * approximation of the projection of the source line. * * t_start: Interpolation parameter for the start point. * p_start: Projected start point. * t_end: Interpolation parameter for the end point. * p_start: Projected end point. * interpolator: Interpolator for current source line. * threshold: Lateral tolerance in target projection coordinates. * handle: Thread-local context handle for GEOS. * gp_domain: Prepared polygon of target map domain. * inside: Whether the start point is within the map domain. */ bool straightAndDomain(double t_start, const Point &p_start, double t_end, const Point &p_end, Interpolator *interpolator, double threshold, GEOSContextHandle_t handle, const GEOSPreparedGeometry *gp_domain, bool inside) { // Straight and in-domain (de9im[7] == 'F') bool valid; // This could be optimised out of the loop. if (!(isfinite(p_start.x) && isfinite(p_start.y))) { valid = false; } else if (!(isfinite(p_end.x) && isfinite(p_end.y))) { valid = false; } else { // TODO: Re-use geometries, instead of create-destroy! // Create a LineString for the current end-point. GEOSCoordSequence *coords = GEOSCoordSeq_create_r(handle, 2, 2); GEOSCoordSeq_setX_r(handle, coords, 0, p_start.x); GEOSCoordSeq_setY_r(handle, coords, 0, p_start.y); GEOSCoordSeq_setX_r(handle, coords, 1, p_end.x); GEOSCoordSeq_setY_r(handle, coords, 1, p_end.y); GEOSGeometry *g_segment = GEOSGeom_createLineString_r(handle, coords); // Find the projected mid-point double t_mid = (t_start + t_end) * 0.5; Point p_mid = interpolator->interpolate(t_mid); // Make it into a GEOS geometry coords = GEOSCoordSeq_create_r(handle, 1, 2); GEOSCoordSeq_setX_r(handle, coords, 0, p_mid.x); GEOSCoordSeq_setY_r(handle, coords, 0, p_mid.y); GEOSGeometry *g_mid = GEOSGeom_createPoint_r(handle, coords); double along = GEOSProjectNormalized_r(handle, g_segment, g_mid); if(isnan(along)) { valid = true; } else { valid = 0.0 < along && along < 1.0; if (valid) { double separation; GEOSDistance_r(handle, g_segment, g_mid, &separation); if (inside) { // Scale the lateral threshold by the distance from // the nearest end. I.e. Near the ends the lateral // threshold is much smaller; it only has its full // value in the middle. valid = separation <= threshold * 2.0 * (0.5 - fabs(0.5 - along)); } else { // Check if the mid-point makes less than ~11 degree // angle with the straight line. // sin(11') => 0.2 // To save the square-root we just use the square of // the lengths, hence: // 0.2 ^ 2 => 0.04 double hypot_dx = p_mid.x - p_start.x; double hypot_dy = p_mid.y - p_start.y; double hypot = hypot_dx * hypot_dx + hypot_dy * hypot_dy; valid = ((separation * separation) / hypot) < 0.04; } } } if (valid) { if(inside) valid = GEOSPreparedCovers_r(handle, gp_domain, g_segment); else valid = GEOSPreparedDisjoint_r(handle, gp_domain, g_segment); } GEOSGeom_destroy_r(handle, g_segment); GEOSGeom_destroy_r(handle, g_mid); } return valid; }