Skip to content

Exception occurred while drawing and filling polygons #2964

Description

@wangningyu

osgEarth Version (required): 3.8.0

Description of the problem: Polygon filling anomaly

What you have tried: I have tried three methods to fill the semi transparent space

Under the 2D plan view (-180/90 in the lower left and right corners, 180/90 in the upper right corner), the following effect should be drawn:

Image

I have encountered an issue where the semi transparent color filling is abnormal when drawing polygons.

I have tried the following three methods:

  • 1.the default method
void OSGWidget::addLargeConcavePolygonBase(osgEarth::MapNode* mapNode)
{
    QPolygonF               qPoly;
    std::vector<osg::Vec3d> ctrlPoints;

    ctrlPoints.push_back(osg::Vec3d(90.0,  30.0, 0));
    ctrlPoints.push_back(osg::Vec3d(120.0,  13.0, 0));
    ctrlPoints.push_back(osg::Vec3d(140.0,  1.0, 0));
    ctrlPoints.push_back(osg::Vec3d(95.0,  -4.0, 0));
    ctrlPoints.push_back(osg::Vec3d(85.0,  -5.0, 0));
    ctrlPoints.push_back(osg::Vec3d(75.0,  -6.0, 0));
    ctrlPoints.push_back(osg::Vec3d(45.0,  -7.0, 0));
    ctrlPoints.push_back(osg::Vec3d(15.0,  -10.0, 0));
    ctrlPoints.push_back(osg::Vec3d(0.0,  -8.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-8.0,  -20.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-15.0,  -45.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-90.0,  -70.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-135.0,  -45.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-160.0,  -0.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-90.0,  28.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-5.0,  6.0, 0));

    for (size_t i = 0; i < ctrlPoints.size() - 1; ++i)
        qPoly.push_back(QPointF(ctrlPoints.at(i).x(), ctrlPoints.at(i).y()));

    static osg::observer_ptr<osg::Group> m_polygonGroup;
    if (m_polygonGroup.valid()) m_root->removeChild(m_polygonGroup.get());

    osg::ref_ptr<osg::Group> newGroup = new osg::Group();
    m_root->addChild(newGroup.get());
    m_polygonGroup = newGroup.get();

    const osgEarth::SpatialReference    *srs = mapNode->getMap()->getSRS();
    osgEarth::Style                     style;

    style.getOrCreate<osgEarth::PolygonSymbol>()->fill()->color() = osgEarth::Color(osg::Vec4(1.0, 0.65, 0.0, 0.4));
    //style.getOrCreate<osgEarth::LineSymbol>()->stroke()->color() = osgEarth::Color(osg::Vec4(1.0, 0.65, 0.0, 0.4));
    //style.getOrCreate<osgEarth::LineSymbol>()->stroke()->width() = osgEarth::Distance(0.001, osgEarth::Units::PIXELS);
    //style.getOrCreate<osgEarth::LineSymbol>()->tessellationSize() = osgEarth::Distance(1000, osgEarth::Units::METERS);

    osgEarth::AltitudeSymbol* as = style.getOrCreate<osgEarth::AltitudeSymbol>();
    as->clamping() = osgEarth::AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN;
    as->technique() = osgEarth::AltitudeSymbol::TECHNIQUE_DRAPE;
    as->verticalOffset() = osgEarth::Distance(0.5, osgEarth::Units::METERS);

    osg::ref_ptr<osgEarth::Polygon> poly = new osgEarth::Polygon();
    for (const QPointF& pt : qPoly)
    {
        poly->push_back(osg::Vec3d(pt.x(), pt.y(), 0.0));
    }

    if (poly->front() != poly->back())
        poly->push_back(poly->front());

    poly->rewind(osgEarth::Geometry::ORIENTATION_CCW);

    osg::ref_ptr<osgEarth::FeatureNode> fn = new osgEarth::FeatureNode(new osgEarth::Feature(poly.get(), srs, style));
    fn->getOrCreateStateSet()->setRenderBinDetails(10, "RenderBin");
    newGroup->addChild(fn.get());
}
Image

this type can't show any polygon and fill color

  • 2. use TessellateOperator::tessellateGeo
// Using TessellateOperator to segment and draw functions
void OSGWidget::addLargeConcavePolygonTess(osgEarth::MapNode* mapNode)
{
    std::vector<osg::Vec3d> ctrlPoints;

    ctrlPoints.push_back(osg::Vec3d(90.0,  30.0, 0));
    ctrlPoints.push_back(osg::Vec3d(120.0,  13.0, 0));
    ctrlPoints.push_back(osg::Vec3d(140.0,  1.0, 0));
    ctrlPoints.push_back(osg::Vec3d(95.0,  -4.0, 0));
    ctrlPoints.push_back(osg::Vec3d(85.0,  -5.0, 0));
    ctrlPoints.push_back(osg::Vec3d(75.0,  -6.0, 0));
    ctrlPoints.push_back(osg::Vec3d(45.0,  -7.0, 0));
    ctrlPoints.push_back(osg::Vec3d(15.0,  -10.0, 0));
    ctrlPoints.push_back(osg::Vec3d(0.0,  -8.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-8.0,  -20.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-15.0,  -45.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-90.0,  -70.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-135.0,  -45.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-160.0,  -0.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-90.0,  28.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-5.0,  6.0, 0));

    osg::ref_ptr<osgEarth::Polygon> finalPoly = new osgEarth::Polygon();
    int numPoints = ctrlPoints.size();

    for (int i = 0; i < numPoints; ++i)
    {
        std::vector<osg::Vec3d> segmentPoints;

        // Calculate the index of the next point, and if it reaches the end, take the remainder to return to 0
        int nextIdx = (i + 1) % numPoints;

        // Split the long side into multiple segments
        osgEarth::Util::TessellateOperator::tessellateGeo(
            ctrlPoints[i],
            ctrlPoints[nextIdx],
            100,
            osgEarth::GEOINTERP_GREAT_CIRCLE, // Interpolation of large circular flight path
            segmentPoints
            );

        // Discard the last point of each paragraph
        // This not only avoids repeating the beginning and end, but also perfectly closes the entire polygon
        for (size_t j = 0; j < segmentPoints.size() - 1; ++j) {
            finalPoly->push_back(segmentPoints[j]);
        }
    }

    osg::ref_ptr<osgEarth::Feature> feature = new osgEarth::Feature(finalPoly.get(), osgEarth::SpatialReference::get("wgs84"));
    osgEarth::Style style;

    osgEarth::PolygonSymbol* fill = style.getOrCreate<osgEarth::PolygonSymbol>();
    fill->fill()->color() = osgEarth::Color(osgEarth::Color::Red, 0.5);

    osgEarth::LineSymbol* ls = style.getOrCreate<osgEarth::LineSymbol>();
    ls->stroke()->color() = osgEarth::Color::Green;
    ls->stroke()->width() = osgEarth::Distance(1.0, osgEarth::Units::PIXELS);

    osgEarth::AltitudeSymbol* alt = style.getOrCreate<osgEarth::AltitudeSymbol>();
    alt->clamping() = osgEarth::AltitudeSymbol::CLAMP_TO_TERRAIN;
    alt->technique() = osgEarth::AltitudeSymbol::TECHNIQUE_DRAPE;//TECHNIQUE_GPU;
    feature->setStyle(style);

    osg::ref_ptr<osgEarth::FeatureNode> node = new osgEarth::FeatureNode(feature.get());
    node->setMapNode(mapNode);

    mapNode->addChild(node.get());
}
Image

TessellateOperator's type can show the green bordline, but fill failed.

  • 3. split the East and West Hemispheres
// Forcefully insert a necessary point passing through the Y-axis in a polygon
QPolygonF insertYAxisIntersections(const QPolygonF& poly)
{
    QPolygonF newPoly;
    int n = poly.size();
    if (n < 2) return poly;

    for (int i = 0; i < n; ++i)
    {
        const QPointF& p1 = poly[i];
        const QPointF& p2 = poly[(i+1) % n]; // Next points, wrap around the polygon

        newPoly.push_back(p1); // First add the current point

        // Determine whether two points span both sides (x < 0 and x > 0)
        if ((p1.x() < 0 && p2.x() > 0) || (p1.x() > 0 && p2.x() < 0))
        {
            double t = (0.0 - p1.x()) / (p2.x() - p1.x()); // linear interpolation
            double yIntersect = p1.y() + t * (p2.y() - p1.y());
            QPointF interPoint(0.0, yIntersect);

            newPoly.push_back(interPoint); // Insert Intersection Point
        }
    }

    return newPoly;
}

void OSGWidget::addLargeConcavePolygonEastWest(osgEarth::MapNode* mapNode)
{
    QPolygonF               qPoly;
    std::vector<osg::Vec3d> ctrlPoints;

    ctrlPoints.push_back(osg::Vec3d(90.0,  30.0, 0));
    ctrlPoints.push_back(osg::Vec3d(120.0,  13.0, 0));
    ctrlPoints.push_back(osg::Vec3d(140.0,  1.0, 0));
    ctrlPoints.push_back(osg::Vec3d(95.0,  -4.0, 0));
    ctrlPoints.push_back(osg::Vec3d(85.0,  -5.0, 0));
    ctrlPoints.push_back(osg::Vec3d(75.0,  -6.0, 0));
    ctrlPoints.push_back(osg::Vec3d(45.0,  -7.0, 0));
    ctrlPoints.push_back(osg::Vec3d(15.0,  -10.0, 0));
    ctrlPoints.push_back(osg::Vec3d(0.0,  -8.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-8.0,  -20.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-15.0,  -45.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-90.0,  -70.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-135.0,  -45.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-160.0,  -0.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-90.0,  28.0, 0));
    ctrlPoints.push_back(osg::Vec3d(-5.0,  6.0, 0));

    for (size_t i = 0; i < ctrlPoints.size() - 1; ++i)
        qPoly.push_back(QPointF(ctrlPoints.at(i).x(), ctrlPoints.at(i).y()));

    // If two points of the original shape pass through the Y-axis, insert a point that passes through the Y-axis
    QPolygonF                           modifiedPoly = insertYAxisIntersections(qPoly);
    osg::ref_ptr<osg::Group>            newGroup = new osg::Group();
    mapNode->addChild(newGroup.get());

    const osgEarth::SpatialReference* srs = mapNode->getMap()->getSRS();

    osgEarth::Style style;
    style.getOrCreate<osgEarth::PolygonSymbol>()->fill()->color() = osgEarth::Color(osg::Vec4(1.0, 0.65, 0.0, 0.4));

    style.getOrCreate<osgEarth::LineSymbol>()->tessellationSize() = osgEarth::Distance(500, osgEarth::Units::METERS);
    style.getOrCreate<osgEarth::LineSymbol>()->stroke()->color() = osgEarth::Color(osg::Vec4(1.0, 0.65, 0.0, 0.4));
    style.getOrCreate<osgEarth::LineSymbol>()->stroke()->width() = osgEarth::Distance(0.01, osgEarth::Units::METERS);

    osgEarth::AltitudeSymbol* as = style.getOrCreate<osgEarth::AltitudeSymbol>();
    as->clamping() = osgEarth::AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN;
    as->technique() = osgEarth::AltitudeSymbol::TECHNIQUE_DRAPE;
    as->verticalOffset() = osgEarth::Distance(0.5, osgEarth::Units::METERS);

    // Draw separately from the east and west hemispheres
    osg::ref_ptr<osgEarth::Polygon> westPoly = new osgEarth::Polygon();
    osg::ref_ptr<osgEarth::Polygon> eastPoly = new osgEarth::Polygon();

    for (const QPointF& pt : modifiedPoly) {
        double lon = pt.x();
        if (lon <= 0) westPoly->push_back(osg::Vec3d(lon, pt.y(), 0));
        if (lon >= 0) eastPoly->push_back(osg::Vec3d(lon, pt.y(), 0));
    }

    // Process polygons of two hemispheres separately
    auto processSubPoly = [&](osg::ref_ptr<osgEarth::Polygon> sub) {
        if (sub->size() < 3) return;
        sub->rewind(osgEarth::Geometry::ORIENTATION_CCW);
        osg::ref_ptr<osgEarth::FeatureNode> fn = new osgEarth::FeatureNode(new osgEarth::Feature(sub.get(), srs, style));
        fn->getOrCreateStateSet()->setRenderBinDetails(10, "RenderBin");
        newGroup->addChild(fn.get());
    };

    if(westPoly->size()) processSubPoly(westPoly);
    if(eastPoly->size()) processSubPoly(eastPoly);
}
Image Image

If the border lines of the segmented polygon are colored, there will be a vertical line in the middle.

At present, I am not sure if this is due to an error in my usage method or a rendering error myself?

Best Regards!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions