diff --git a/src/osgEarth/BuildGeometryFilter.cpp b/src/osgEarth/BuildGeometryFilter.cpp index 8eb8bbb44c..21a401b46e 100644 --- a/src/osgEarth/BuildGeometryFilter.cpp +++ b/src/osgEarth/BuildGeometryFilter.cpp @@ -1012,7 +1012,7 @@ namespace if (auto cropped = geometry->crop(&boundary)) { // Use an iterator since crop could return a multi-polygon - GeometryIterator gi(cropped.get(), false); + GeometryIterator gi(cropped, false); while (gi.hasMore()) { Geometry* geom = gi.next(); @@ -1798,15 +1798,11 @@ BuildGeometryFilter::push( FeatureList& input, FilterContext& context ) // Split features across the dateline if necessary if (context.getOutputSRS() && !context.getOutputSRS()->isGeographic()) { - for(FeatureList::iterator itr = input.begin(); itr != input.end(); ++itr) + for(auto& feature : input) { - osg::ref_ptr f = new Feature(*itr->get()); - f->splitAcrossAntimeridian(); - splitFeatures.emplace_back(f); - //Feature* f = itr->clone(); // itr->get(); - //FeatureList tmpSplit; - //f->splitAcrossDateLine(tmpSplit); - //splitFeatures.insert(splitFeatures.end(), tmpSplit.begin(), tmpSplit.end()); + auto* clone = new Feature(*feature.get()); + clone->splitAcrossAntimeridian(); + splitFeatures.emplace_back(clone); } } else @@ -1815,6 +1811,8 @@ BuildGeometryFilter::push( FeatureList& input, FilterContext& context ) std::copy(input.begin(), input.end(), std::back_inserter(splitFeatures)); } + osg::ref_ptr polysGroup, linesGroup, pointsGroup, meshesGroup; + for(FeatureList::iterator i = splitFeatures.begin(); i != splitFeatures.end(); ++i) { Feature* f = i->get(); @@ -1872,10 +1870,10 @@ BuildGeometryFilter::push( FeatureList& input, FilterContext& context ) if ( has_polysymbol ) { - if (f->getGeometry()->getType() == Geometry::TYPE_TRIMESH) - meshes.push_back(f); - else - polygons.push_back( f ); + //if (f->getGeometry()->getType() == Geometry::TYPE_TRIMESH) + // meshes.push_back(f); + //else + polygons.push_back( f ); } if (has_linesymbol) @@ -1927,7 +1925,10 @@ BuildGeometryFilter::push( FeatureList& input, FilterContext& context ) GenerateNormals gen; geode->accept(gen); - result->addChild( geode.get() ); + if (!polysGroup.valid()) + polysGroup = new osg::Group(); + + polysGroup->addChild( geode.get() ); } } @@ -1937,12 +1938,13 @@ BuildGeometryFilter::push( FeatureList& input, FilterContext& context ) bool twosided = polygons.size() > 0 ? false : true; osg::ref_ptr< osg::Group > lines = processPolygonizedLines(polygonizedLines, twosided, context, false); - if (lines->getNumChildren() > 0) { - result->addChild( lines.get() ); - } + if (!linesGroup.valid()) + linesGroup = new osg::Group(); + linesGroup->addChild( lines.get() ); + } } if ( wireLines.size() > 0 ) @@ -1952,7 +1954,9 @@ BuildGeometryFilter::push( FeatureList& input, FilterContext& context ) if (lines->getNumChildren() > 0) { - result->addChild( lines.get() ); + if (!meshesGroup.valid()) + meshesGroup = new osg::Group(); + meshesGroup->addChild( lines.get() ); } } @@ -1965,7 +1969,9 @@ BuildGeometryFilter::push( FeatureList& input, FilterContext& context ) if ( group->getNumChildren() > 0 ) { - result->addChild(group.get()); + if (!linesGroup.valid()) + linesGroup = new osg::Group(); + linesGroup->addChild(group.get()); } } @@ -1977,7 +1983,9 @@ BuildGeometryFilter::push( FeatureList& input, FilterContext& context ) if ( group->getNumChildren() > 0 ) { - result->addChild(group.get()); + if (!pointsGroup.valid()) + pointsGroup = new osg::Group(); + pointsGroup->addChild(group.get()); } } @@ -1987,10 +1995,22 @@ BuildGeometryFilter::push( FeatureList& input, FilterContext& context ) if (group->getNumChildren() > 0) { - result->addChild(group.get()); + if (!meshesGroup.valid()) + meshesGroup = new osg::Group(); + meshesGroup->addChild(group.get()); } } + if (polysGroup.valid()) + result->addChild(polysGroup.get()); + if (linesGroup.valid()) + result->addChild(linesGroup.get()); + if (pointsGroup.valid()) + result->addChild(pointsGroup.get()); + if (meshesGroup.valid()) + result->addChild(meshesGroup.get()); + + //// indicate that geometry contains clamping attributes if (_style.has() && _style.get()->technique() == AltitudeSymbol::TECHNIQUE_GPU) diff --git a/src/osgEarth/ConvertTypeFilter b/src/osgEarth/ConvertTypeFilter index 7145e91d28..cafcc98512 100644 --- a/src/osgEarth/ConvertTypeFilter +++ b/src/osgEarth/ConvertTypeFilter @@ -39,30 +39,24 @@ namespace osgEarth { namespace Util static bool isSupported() { return true; } public: - ConvertTypeFilter(); - ConvertTypeFilter( const Geometry::Type& toType ); - ConvertTypeFilter( const ConvertTypeFilter& rhs ); + ConvertTypeFilter() = default; + ConvertTypeFilter(const ConvertTypeFilter&) = default; - ConvertTypeFilter( const Config& conf ); + ConvertTypeFilter(const Geometry::Type& toType); + ConvertTypeFilter(const Config& conf); - /** - * Serialize this FeatureFilter - */ - virtual Config getConfig() const; - - virtual ~ConvertTypeFilter() { } + //!Serialize this FeatureFilter + Config getConfig() const override; public: - Geometry::Type& toType() { - return _toType; } const Geometry::Type& toType() const { - return _toType; } + return _toType.value(); } public: virtual FilterContext push( FeatureList& input, FilterContext& context ); protected: - Geometry::Type _toType; + optional _toType = Geometry::TYPE_UNKNOWN; }; } } diff --git a/src/osgEarth/ConvertTypeFilter.cpp b/src/osgEarth/ConvertTypeFilter.cpp index 4be40623cc..e48d24a5df 100644 --- a/src/osgEarth/ConvertTypeFilter.cpp +++ b/src/osgEarth/ConvertTypeFilter.cpp @@ -24,46 +24,28 @@ using namespace osgEarth::Util; OSGEARTH_REGISTER_SIMPLE_FEATUREFILTER(convert, ConvertTypeFilter) -ConvertTypeFilter::ConvertTypeFilter() : -_toType( Geometry::TYPE_UNKNOWN ) -{ - //NOP -} - -ConvertTypeFilter::ConvertTypeFilter( const Geometry::Type& toType ) : -_toType( toType ) +ConvertTypeFilter::ConvertTypeFilter(const Geometry::Type& toType) : + _toType(toType) { // NOP } -ConvertTypeFilter::ConvertTypeFilter( const ConvertTypeFilter& rhs ) : -_toType( rhs._toType ) -{ - //NOP -} - -ConvertTypeFilter::ConvertTypeFilter( const Config& conf): -_toType( Geometry::TYPE_UNKNOWN ) +ConvertTypeFilter::ConvertTypeFilter(const Config& conf) { if (conf.key() == "convert") { - optional type = Geometry::TYPE_POINTSET; - conf.get( "type", "point", type, Geometry::TYPE_POINTSET ); - conf.get( "type", "line", type, Geometry::TYPE_LINESTRING ); - conf.get( "type", "polygon", type, Geometry::TYPE_POLYGON ); - _toType = *type; + conf.get("type", "point", _toType, Geometry::TYPE_POINTSET); + conf.get("type", "line", _toType, Geometry::TYPE_LINESTRING); + conf.get("type", "polygon", _toType, Geometry::TYPE_POLYGON); } } Config ConvertTypeFilter::getConfig() const { Config config("convert"); - optional type(_toType); - type = _toType; - config.set("type", "point", type, Geometry::TYPE_POINTSET); - config.set("type", "line", type, Geometry::TYPE_LINESTRING); - config.set("type", "polygon", type, Geometry::TYPE_POLYGON); - + config.set("type", "point", _toType, Geometry::TYPE_POINTSET); + config.set("type", "line", _toType, Geometry::TYPE_LINESTRING); + config.set("type", "polygon", _toType, Geometry::TYPE_POLYGON); return config; } @@ -76,13 +58,22 @@ ConvertTypeFilter::push( FeatureList& input, FilterContext& context ) return context; } + if (_toType == Geometry::TYPE_UNKNOWN) + { + return context; + } + bool ok = true; - for( FeatureList::iterator i = input.begin(); i != input.end(); ++i ) + for (auto& feature : input) { - Feature* input = i->get(); - if ( input && input->getGeometry() && input->getGeometry()->getComponentType() != _toType ) + if (feature && feature->getGeometry() && feature->getGeometry()->getComponentType() != _toType.value()) { - input->setGeometry( input->getGeometry()->cloneAs(_toType) ); + auto cloned = feature->getGeometry()->cloneAs(_toType.value()); + if (cloned) + { + OE_SOFT_ASSERT(cloned->isValid()); + feature->setGeometry(cloned); + } } } diff --git a/src/osgEarth/CropFilter.cpp b/src/osgEarth/CropFilter.cpp index 073aa5e018..469bc2ab51 100644 --- a/src/osgEarth/CropFilter.cpp +++ b/src/osgEarth/CropFilter.cpp @@ -118,14 +118,12 @@ CropFilter::push( FeatureList& input, FilterContext& context ) // then move on to the cropping operation: else { - if (auto cropped = featureGeom->crop(&poly)) + osg::ref_ptr cropped = featureGeom->crop(&poly); + if (cropped.valid() && cropped->isValid()) { - if (cropped->isValid()) - { - feature->setGeometry(cropped.get()); - keepFeature = true; - newExtent.expandToInclude(GeoExtent(newExtent.getSRS(), cropped->getBounds())); - } + feature->setGeometry(cropped); + keepFeature = true; + newExtent.expandToInclude(GeoExtent(newExtent.getSRS(), cropped->getBounds())); } } } diff --git a/src/osgEarth/Feature b/src/osgEarth/Feature index 948e8c3d96..0769f51122 100644 --- a/src/osgEarth/Feature +++ b/src/osgEarth/Feature @@ -143,7 +143,7 @@ namespace osgEarth Feature(const Feature& rhs); //! Contruct a feature (move) - Feature(Feature&& rhs); + //Feature(Feature&& rhs); public: diff --git a/src/osgEarth/Feature.cpp b/src/osgEarth/Feature.cpp index 27e6782c0a..4519277d64 100644 --- a/src/osgEarth/Feature.cpp +++ b/src/osgEarth/Feature.cpp @@ -207,28 +207,30 @@ Feature::Feature(Geometry* geom, const SpatialReference* srs, const Style& style dirty(); } -Feature::Feature(const Feature& rhs) : //, const osg::CopyOp& copyOp) : +Feature::Feature(const Feature& rhs) : _fid(rhs._fid), _attrs(rhs._attrs), _style(rhs._style), _geoInterp(rhs._geoInterp), _srs(rhs._srs.get()) { + OE_SOFT_ASSERT(rhs._geom.valid()); + if (rhs._geom.valid()) _geom = rhs._geom->clone(); dirty(); } -Feature::Feature(Feature&& rhs) // : osg::Object(rhs) -{ - _fid = std::move(rhs._fid); - _attrs = std::move(rhs._attrs); - _style = std::move(rhs._style); - _geoInterp = std::move(rhs._geoInterp); - _geom = std::move(rhs._geom); - _cachedExtent = std::move(rhs._cachedExtent); -} +//Feature::Feature(Feature&& rhs) // : osg::Object(rhs) +//{ +// _fid = std::move(rhs._fid); +// _attrs = std::move(rhs._attrs); +// _style = std::move(rhs._style); +// _geoInterp = std::move(rhs._geoInterp); +// _geom = std::move(rhs._geom); +// _cachedExtent = std::move(rhs._cachedExtent); +//} Feature::~Feature() { @@ -267,6 +269,7 @@ Feature::setSRS( const SpatialReference* srs ) void Feature::setGeometry( Geometry* geom ) { + OE_HARD_ASSERT(geom != nullptr); _geom = geom; dirty(); } @@ -786,8 +789,8 @@ void Feature::transform( const SpatialReference* srs ) void Feature::splitAcrossAntimeridian() { - // If the feature is geodetic, try to split it across the dateline. - if (getSRS() && getSRS()->isGeodetic()) + // If the feature is geodetic, try to split it across the dateline. + if (getSRS() && getSRS()->isGeodetic() && getGeometry()) { auto* split_geom = getGeometry()->splitAcrossAntimeridian(); setGeometry(split_geom); diff --git a/src/osgEarth/FeatureRasterizer.cpp b/src/osgEarth/FeatureRasterizer.cpp index 9fb5427a00..eb48cda0d5 100644 --- a/src/osgEarth/FeatureRasterizer.cpp +++ b/src/osgEarth/FeatureRasterizer.cpp @@ -1185,7 +1185,8 @@ FeatureRasterizer::render_agglite( { Geometry* geometry = feature->getGeometry(); - if (auto cropped = geometry->crop(&cropPoly)) + osg::ref_ptr cropped = geometry->crop(&cropPoly); + if (cropped.valid()) { if (covValue.isSet()) { @@ -1208,8 +1209,8 @@ FeatureRasterizer::render_agglite( { Geometry* geometry = feature->getGeometry(); - osg::ref_ptr croppedGeometry; - if (auto cropped = geometry->crop(&cropPoly)) + osg::ref_ptr cropped = geometry->crop(&cropPoly); + if (cropped.valid()) { if (covValue.isSet()) { diff --git a/src/osgEarth/Geometry b/src/osgEarth/Geometry index e044b33220..d63d007972 100644 --- a/src/osgEarth/Geometry +++ b/src/osgEarth/Geometry @@ -160,13 +160,13 @@ namespace osgEarth * Crops this geometry to the region represented by the crop polygon, returning * the result in the output parameter. Returns true if the op succeeded. */ - virtual osg::ref_ptr crop(const class Ring* boundary) const; + virtual Geometry* crop(const class Ring* boundary) const; /** * Crops this geometry to the bounds, returning the result in the output parameter. * Returns true if the op succeeded. */ - osg::ref_ptr crop(const Bounds& bounds) const; + Geometry* crop(const Bounds& bounds) const; /** * Creates the union of this geometry with the other geometry, returning @@ -269,7 +269,11 @@ namespace osgEarth virtual Type getComponentType() const { return getType(); } virtual bool isValid() const { return size() >= 1; } - virtual Geometry* clone() const { return cloneAs(getType()); } + //! Deep clone of the object + virtual Geometry* clone() const = 0; + + //! Empty clone of the object (same type, no content) + virtual Geometry* cloneEmpty() const = 0; void push_back(const osg::Vec3d& v ) { osgEarth::InlineVector::push_back(v); } @@ -302,6 +306,8 @@ namespace osgEarth PointSet(int capacity = 0) : Geometry(TYPE_POINTSET, capacity) { } PointSet(const Vec3dVector* toCopy) : Geometry(TYPE_POINTSET, toCopy) { } PointSet(const PointSet& rhs) = default; + Geometry* clone() const override { return new PointSet(*this); } + Geometry* cloneEmpty() const override { return new PointSet(); } // Don't close point sets void close() override { } @@ -320,6 +326,8 @@ namespace osgEarth Point(int capacity = 0) : PointSet(TYPE_POINT, capacity) { } Point(const Vec3dVector* toCopy) : PointSet(TYPE_POINT, toCopy) { } Point(const Point& rhs) = default; + Geometry* clone() const override { return new Point(*this); } + Geometry* cloneEmpty() const override { return new Point(); } void set(const osg::Vec3d& value); @@ -336,6 +344,8 @@ namespace osgEarth LineString(int capacity = 0) : Geometry(TYPE_LINESTRING, capacity) { } LineString(const Vec3dVector* toCopy) : Geometry(TYPE_LINESTRING, toCopy) { } LineString(const LineString& rhs) = default; + Geometry* clone() const override { return new LineString(*this); } + Geometry* cloneEmpty() const override { return new LineString(); } bool getSegment(double length, osg::Vec3d& start, osg::Vec3d& end); @@ -343,6 +353,8 @@ namespace osgEarth double getSignedDistance2D(const osg::Vec3d& point) const override; + Geometry* crop(const class Ring* boundary) const override; + public: bool isValid() const override { return size() >= 2; } }; @@ -358,6 +370,8 @@ namespace osgEarth Ring(int capacity = 0) : Geometry(TYPE_RING, capacity) { } Ring(const Vec3dVector* toCopy) : Ring(TYPE_RING, toCopy) { } Ring(const Ring& ring) = default; + Geometry* clone() const override { return new Ring(*this); } + Geometry* cloneEmpty() const override { return new Ring(); } // override virtual Geometry* cloneAs( const Geometry::Type& newType ) const; @@ -386,7 +400,7 @@ namespace osgEarth double getSignedDistance2D(const osg::Vec3d& a) const override; // crop to a boundary - osg::ref_ptr crop(const class Ring* boundary) const override; + Geometry* crop(const class Ring* boundary) const override; //! split across anitmeridian (geodetic coords only) Geometry* splitAcrossAntimeridian() override; @@ -413,6 +427,8 @@ namespace osgEarth Polygon(int capacity = 0) : Ring(TYPE_POLYGON, capacity) { } Polygon(const Vec3dVector* toCopy) : Ring(TYPE_POLYGON, toCopy) { } Polygon(const Polygon& rhs); + Geometry* clone() const override { return new Polygon(*this); } + Geometry* cloneEmpty() const override { return new Polygon(); } public: virtual Type getType() const { return Geometry::TYPE_POLYGON; } @@ -434,11 +450,13 @@ namespace osgEarth virtual double getSignedDistance2D( const osg::Vec3d& a) const override; - osg::ref_ptr crop(const Ring* boundary) const override; + Geometry* crop(const Ring* boundary) const override; //! split across anitmeridian (geodetic coords only) Geometry* splitAcrossAntimeridian() override; + Geometry* cloneAs( const Geometry::Type& newType ) const override; + public: RingCollection& getHoles() { return _holes; } const RingCollection& getHoles() const { return _holes; } @@ -451,6 +469,9 @@ namespace osgEarth { public: TriMesh() : Geometry(TYPE_TRIMESH) { } + TriMesh(const TriMesh& rhs); + Geometry* clone() const override { return new TriMesh(*this); } + Geometry* cloneEmpty() const override { return new TriMesh(); } unsigned getNumGeometries() const override { return 1u; } @@ -469,6 +490,8 @@ namespace osgEarth MultiGeometry() : Geometry(TYPE_MULTI) { } MultiGeometry(const MultiGeometry& rhs); MultiGeometry(const GeometryCollection& parts); + Geometry* clone() const override { return new MultiGeometry(*this); } + Geometry* cloneEmpty() const override { return new MultiGeometry(); } public: Type getComponentType() const override; @@ -491,7 +514,7 @@ namespace osgEarth void close() override; double getSignedDistance2D(const osg::Vec3d& a) const override; bool contains2D(double x, double y) const override; - osg::ref_ptr crop(const Ring* boundary) const override; + Geometry* crop(const Ring* boundary) const override; //! split across anitmeridian (geodetic coords only) Geometry* splitAcrossAntimeridian() override; diff --git a/src/osgEarth/Geometry.cpp b/src/osgEarth/Geometry.cpp index 289c4593dd..966890d15f 100644 --- a/src/osgEarth/Geometry.cpp +++ b/src/osgEarth/Geometry.cpp @@ -30,6 +30,8 @@ using namespace osgEarth; #define GEOS_OUT OE_DEBUG +//#define CROP_WITH_GEOS + #define LC "[Geometry] " namespace @@ -85,8 +87,12 @@ Geometry::Geometry(Type type, int capacity) : Geometry::Geometry(Type type, const Vec3dVector* data) : _type(type) { - reserve( data->size() ); - insert( begin(), data->begin(), data->end() ); + OE_SOFT_ASSERT(data != nullptr); + if (data) + { + reserve(data->size()); + insert(begin(), data->begin(), data->end()); + } } Geometry::~Geometry() @@ -110,14 +116,16 @@ Geometry::getBounds() const } Geometry* -Geometry::cloneAs( const Geometry::Type& newType ) const +Geometry::cloneAs(const Geometry::Type& newType) const { - switch( newType ) + OE_SOFT_ASSERT_AND_RETURN(newType != TYPE_MULTI && newType != TYPE_UNKNOWN, nullptr); + + switch (newType) { case TYPE_POINT: - return new Point( &this->asVector() ); + return new Point(&this->asVector()); case TYPE_POINTSET: - return new PointSet( &this->asVector() ); + return new PointSet(&this->asVector()); case TYPE_LINESTRING: return new LineString( &this->asVector() ); case TYPE_RING: @@ -127,12 +135,11 @@ Geometry::cloneAs( const Geometry::Type& newType ) const return new Polygon( *static_cast(this) ); else return new Polygon( &this->asVector() ); - case TYPE_UNKNOWN: - return new Geometry(newType, &this->asVector() ); default: break; } - return 0L; + + return nullptr; } osg::Vec3Array* @@ -262,6 +269,11 @@ namespace double determinant = a1 * b2 - a2 * b1; + if (equivalent(determinant, 0.0, 1e-6)) + { + return osg::Vec3d(DBL_MAX, DBL_MAX, 0.0); + } + osg::Vec3d result = { (b2 * c1 - b1 * c2) / determinant, (a1 * c2 - a2 * c1) / determinant, @@ -276,10 +288,10 @@ namespace { std::vector output = input; - for(unsigned i=0; i< boundary.size(); ++i) + for (unsigned b = 0; b < boundary.size(); ++b) { - const osg::Vec3d& edgeStart = boundary[i]; - const osg::Vec3d& edgeEnd = i < boundary.size() - 1 ? boundary[i + 1] : boundary[0]; + const osg::Vec3d& edgeStart = boundary[b]; + const osg::Vec3d& edgeEnd = b < boundary.size() - 1 ? boundary[b + 1] : boundary[0]; std::vector input = output; output.clear(); @@ -306,34 +318,102 @@ namespace return output; } -} -#if 0 -bool -Geometry::crop(const Ring* boundary, osg::ref_ptr& output) const -{ - OE_SOFT_ASSERT_AND_RETURN(boundary, false); - OE_SOFT_ASSERT_AND_RETURN(boundary->size() > 2, false); - std::vector points; - points.reserve(size()); - for (const auto& point : *this) + std::vector> clipLineString(const std::vector& input, const osg::Vec3d& e0, const osg::Vec3d& e1) { - if (boundary->contains2D(point.x(), point.y())) - points.emplace_back(point); + std::vector> outputs; + std::vector* current = nullptr; + + for (size_t i = 1; i < input.size(); ++i) + { + auto& p = input[i]; + auto& prev = input[i - 1]; + + bool p_inside = inside(p, e0, e1); + bool prev_inside = inside(prev, e0, e1); + + if (p_inside && prev_inside) + { + // both inside the zone: + if (!current) + { + outputs.emplace_back(); + current = &outputs.back(); + current->emplace_back(prev); // starting out, so push them oth + } + current->emplace_back(p); + } + else if (p_inside && !prev_inside) + { + // just entered the zone from outside; current should always be nullptr + OE_SOFT_ASSERT(current == nullptr); + if (!current) + { + outputs.emplace_back(); // new linestring + current = &outputs.back(); + } + current->emplace_back(intersection(prev, p, e0, e1)); + current->emplace_back(p); + } + else if (!p_inside && prev_inside) + { + if (!current) + { + outputs.emplace_back(); // new linestring + current = &outputs.back(); + current->emplace_back(prev); // starting out, so push them + } + current->emplace_back(intersection(prev, p, e0, e1)); + current = nullptr; + } + else + { + // both outside, do nothing + } + } + + return outputs; + } + + std::vector> clipLineString(const std::vector& input, const std::vector& boundary) + { + if (input.empty()) + return {}; + + std::vector> working_set; + working_set.emplace_back(input); + + for (size_t b = 0; b < boundary.size() && !working_set.empty(); ++b) + { + std::vector> edge_outputs; + + const osg::Vec3d& e0 = boundary[b]; + const osg::Vec3d& e1 = b < boundary.size() - 1 ? boundary[b + 1] : boundary[0]; + + for (auto& edge_input : working_set) + { + auto clipped = clipLineString(edge_input, e0, e1); + if (!clipped.empty()) + { + for (auto& part : clipped) + edge_outputs.emplace_back(std::move(part)); + } + } + + working_set.swap(edge_outputs); + } + + return working_set; } - output = this->cloneAs(getType()); - output->asVector().swap(points); - return true; } -#endif -osg::ref_ptr +Geometry* Geometry::crop(const Ring* boundary) const { #ifdef OSGEARTH_HAVE_GEOS bool success = false; - osg::ref_ptr output; + Geometry* output = nullptr; if (getType() == TYPE_POINT) { @@ -351,7 +431,7 @@ Geometry::crop(const Ring* boundary) const } if (!points.empty()) { - output = this->cloneAs(getType()); + output = this->cloneAs(getComponentType()); output->asVector().swap(points); } return output; @@ -370,7 +450,7 @@ Geometry::crop(const Ring* boundary) const { output = GEOS::exportGeometry(handle, outGeom); - if (output.valid()) + if (output) { if (output->isValid()) { @@ -379,6 +459,7 @@ Geometry::crop(const Ring* boundary) const else { // GEOS result is invalid + delete output; output = nullptr; } } @@ -388,7 +469,7 @@ Geometry::crop(const Ring* boundary) const // still returning false but allows for check. if (GEOSGeomGetNumPoints_r(handle, outGeom) == 0) { - output = new osgEarth::Geometry(TYPE_UNKNOWN); + output = cloneEmpty(); } } @@ -412,7 +493,7 @@ Geometry::crop(const Ring* boundary) const #endif // OSGEARTH_HAVE_GEOS } -osg::ref_ptr +Geometry* Geometry::crop(const Bounds& bounds) const { Ring boundary; @@ -460,7 +541,7 @@ Geometry::geounion( const Geometry* other, osg::ref_ptr& output ) cons // still returning false but allows for check. if (GEOSGeomGetNumPoints_r(handle, outGeom) == 0) { - output = new osgEarth::Geometry(TYPE_UNKNOWN); + output = cloneEmpty(); } } @@ -859,6 +940,31 @@ LineString::getSignedDistance2D( return sqrt(r); } +Geometry* +LineString::crop(const Ring* boundary) const +{ + auto new_data = clipLineString(this->asVector(), boundary->asVector()); + if (new_data.empty()) + return nullptr; + + if (new_data.size() == 1) + { + auto* geom = new LineString(&new_data[0]); + return geom; + } + else + { + auto* geom = new MultiGeometry(); + for (auto& data : new_data) + { + OE_SOFT_ASSERT(!data.empty()); + auto* part = new LineString(&data); + geom->add(part); + } + return geom; + } +} + //---------------------------------------------------------------------------- Ring::Ring(Type type, const Vec3dVector* data) : @@ -868,8 +974,10 @@ Ring::Ring(Type type, const Vec3dVector* data) : } Geometry* -Ring::cloneAs( const Geometry::Type& newType ) const +Ring::cloneAs(const Geometry::Type& newType) const { + OE_SOFT_ASSERT_AND_RETURN(newType != TYPE_UNKNOWN && newType != TYPE_MULTI, cloneAs(_type)); + if ( newType == TYPE_LINESTRING ) { LineString* line = new LineString( &this->asVector() ); @@ -911,6 +1019,7 @@ Ring::close() // whether the ring is open. bool Ring::isOpen() const + { return size() > 1 && front() != back(); } @@ -982,9 +1091,13 @@ Ring::contains2D( double x, double y ) const return result; } -osg::ref_ptr +Geometry* Ring::crop(const Ring* boundary) const { +#ifdef CROP_WITH_GEOS + return Geometry::crop(boundary); +#else + auto new_points = clipPolygon(this->asVector(), boundary->asVector()); if (!new_points.empty()) { @@ -993,6 +1106,7 @@ Ring::crop(const Ring* boundary) const return new_geom; } return {}; +#endif } namespace @@ -1064,7 +1178,7 @@ Ring::splitAcrossAntimeridian() osg::ref_ptr left, right; split(this, left, right); - if (left->size() > 0 && right->size() > 0) + if (left.valid() && left->size() > 0 && right.valid() && right->size() > 0) { auto mg = new MultiGeometry(); mg->add(left); @@ -1157,9 +1271,12 @@ Polygon::getSignedDistance2D(const osg::Vec3d& a) const return r; } -osg::ref_ptr +Geometry* Polygon::crop(const Ring* boundary) const { +#ifdef CROP_WITH_GEOS + return Geometry::crop(boundary); +#else auto new_points = clipPolygon(this->asVector(), boundary->asVector()); if (!new_points.empty()) { @@ -1179,6 +1296,7 @@ Polygon::crop(const Ring* boundary) const return new_poly; } return {}; +#endif } Geometry* @@ -1190,15 +1308,15 @@ Polygon::splitAcrossAntimeridian() osg::ref_ptr left, right; split(this, left, right); - if (left->size() > 0 && right->size() > 0) + if (left.valid() && left->size() > 0 && right.valid() && right->size() > 0) { for (auto& hole : _holes) { osg::ref_ptr leftHole, rightHole; split(hole.get(), leftHole, rightHole); - if (leftHole->size() > 0) + if (leftHole.valid() && leftHole->size() > 0) left->_holes.push_back(hole); - if (rightHole->size() > 0) + if (rightHole.valid() && rightHole->size() > 0) right->_holes.push_back(hole); } @@ -1213,6 +1331,46 @@ Polygon::splitAcrossAntimeridian() } } +Geometry* +Polygon::cloneAs(const Geometry::Type& newType) const +{ + OE_SOFT_ASSERT_AND_RETURN(newType != TYPE_UNKNOWN && newType != TYPE_MULTI, cloneAs(_type)); + + if (newType == TYPE_LINESTRING) + { + LineString* line = new LineString(&this->asVector()); + + if (line->size() > 1 && line->front() != line->back()) + line->push_back(front()); + + if (_holes.empty()) + { + return line; + } + else + { + auto* mg = new MultiGeometry(); + mg->add(line); + for (auto& hole : _holes) + { + if (hole.valid() && !hole->empty()) + { + LineString* holeLine = new LineString(&hole->asVector()); + if (holeLine->size() > 1 && holeLine->front() != holeLine->back()) + holeLine->push_back(hole->front()); + std::reverse(holeLine->begin(), holeLine->end()); + mg->add(holeLine); + } + } + return mg; + } + } + else + { + return Ring::cloneAs(newType); + } +} + //---------------------------------------------------------------------------- namespace @@ -1221,6 +1379,12 @@ namespace double cross2d(const T& a, const T& b) { return a.x()*b.y() - a.y()*b.x(); } } +TriMesh::TriMesh(const TriMesh& rhs) : + Geometry(rhs) +{ + _indices = rhs._indices; +} + bool TriMesh::contains2D(double x, double y) const { osg::Vec3d P(x, y, 0); @@ -1240,7 +1404,6 @@ bool TriMesh::contains2D(double x, double y) const return false; } - //---------------------------------------------------------------------------- MultiGeometry::MultiGeometry(const MultiGeometry& rhs) : @@ -1308,12 +1471,15 @@ MultiGeometry::getBounds() const } Geometry* -MultiGeometry::cloneAs( const Geometry::Type& newType ) const +MultiGeometry::cloneAs(const Geometry::Type& newType) const { - MultiGeometry* multi = new MultiGeometry(); + OE_SOFT_ASSERT(_parts.size() > 0); + OE_SOFT_ASSERT_AND_RETURN(newType != TYPE_UNKNOWN && newType != TYPE_MULTI, nullptr); + + auto* multi = new MultiGeometry(); for (auto& part : _parts) { - auto cloned_part = part->cloneAs(part->getType()); + auto cloned_part = part->cloneAs(newType); if (cloned_part) multi->getComponents().push_back(cloned_part); } return multi; @@ -1391,54 +1557,68 @@ MultiGeometry::contains2D(double x, double y) const return false; } -osg::ref_ptr +Geometry* MultiGeometry::crop(const Ring* boundary) const { - osg::ref_ptr mg; +#ifdef CROP_WITH_GEOS + return Geometry::crop(boundary); +#else + MultiGeometry* mg = nullptr; for (const auto& part : _parts) { auto cropped = part->crop(boundary); - if (cropped.valid()) + if (cropped) { - if (!mg) mg = new MultiGeometry(); + if (!mg) + mg = new MultiGeometry(); mg->add(cropped); } } - if (mg->_parts.empty()) + if (mg && mg->_parts.empty()) + { + delete mg; return {}; - else if (mg->_parts.size() == 1) - return mg->_parts.front(); + } + else if (mg && mg->_parts.size() == 1) + { + auto* result = mg->_parts.front().release(); + delete mg; + return result; + } else + { return mg; + } +#endif } Geometry* MultiGeometry::splitAcrossAntimeridian() { - osg::ref_ptr mg; + MultiGeometry* mg = nullptr; for (const auto& part : _parts) { auto* split = part->splitAcrossAntimeridian(); if (split != part) { - if (!mg.valid()) - { - mg = new MultiGeometry(); - } - - // breaks up multigeometries (because you cannot next them) + // breaks up multigeometries (because you cannot nest them) GeometryIterator i(split, false); while (i.hasMore()) { auto* part = i.next(); - mg->add(part); + if (part && !part->empty()) + { + if (!mg) + mg = new MultiGeometry(); + mg->add(part); + } } } } - return mg; + return mg ? mg : this; } //---------------------------------------------------------------------------- diff --git a/src/osgEarth/GeometryCompiler.cpp b/src/osgEarth/GeometryCompiler.cpp index 655e19ffcc..ae2819f005 100644 --- a/src/osgEarth/GeometryCompiler.cpp +++ b/src/osgEarth/GeometryCompiler.cpp @@ -490,6 +490,11 @@ GeometryCompiler::compile(FeatureList& workingSet, } } + if (render) + { + render->applyTo(resultGroup.get()); + } + if (Registry::capabilities().supportsGLSL()) { ShaderPolicy shaderPolicy = _options.shaderPolicy().get(); diff --git a/src/osgEarth/MGRSGraticule.cpp b/src/osgEarth/MGRSGraticule.cpp index d01a25991e..7535763f01 100644 --- a/src/osgEarth/MGRSGraticule.cpp +++ b/src/osgEarth/MGRSGraticule.cpp @@ -454,7 +454,8 @@ namespace osg::ref_ptr f = new Feature(polygon.get(), _utm.get()); f->transform(_feature->getSRS()); - if (auto cropped = f->getGeometry()->crop(_extent.bounds())) + osg::ref_ptr cropped = f->getGeometry()->crop(_extent.bounds()); + if (cropped.valid()) { f->setGeometry(cropped.get()); f->set("easting", x); @@ -518,7 +519,7 @@ namespace if (auto cropped = f->getGeometry()->crop(_extent.bounds())) { - f->setGeometry(cropped.get()); + f->setGeometry(cropped); } GeometryCompilerOptions gco; diff --git a/src/osgEarth/SimplifyFilter b/src/osgEarth/SimplifyFilter index 9e27869e14..c6a46a11a8 100644 --- a/src/osgEarth/SimplifyFilter +++ b/src/osgEarth/SimplifyFilter @@ -38,7 +38,7 @@ namespace osgEarth { namespace Util OE_OPTION(double, tolerance, 0.0); OE_OPTION(bool, toleranceIsPercentage, false); - OE_OPTION(bool, preserveTopology, false); + OE_OPTION(bool, preserveTopology, true); OE_OPTION(bool, preserveAllFeatures, false); void fromConfig(const Config& conf) { diff --git a/src/osgEarth/SimplifyFilter.cpp b/src/osgEarth/SimplifyFilter.cpp index 7be0a1c9dc..e107f23f4c 100644 --- a/src/osgEarth/SimplifyFilter.cpp +++ b/src/osgEarth/SimplifyFilter.cpp @@ -73,7 +73,7 @@ SimplifyFilter::push(FeatureList& input, FilterContext& context) continue; } - auto simplifiedGeometry = geometry->simplify(t, options().preserveTopology().value()); + auto simplifiedGeometry = geometry->simplify(t, options().preserveTopology().value()); if (simplifiedGeometry.valid()) { feature->setGeometry(simplifiedGeometry.get()); diff --git a/src/osgEarth/TiledFeatureModelLayer.cpp b/src/osgEarth/TiledFeatureModelLayer.cpp index 25749cd515..df52e26d12 100644 --- a/src/osgEarth/TiledFeatureModelLayer.cpp +++ b/src/osgEarth/TiledFeatureModelLayer.cpp @@ -19,6 +19,7 @@ #include #include #include +#include using namespace osgEarth; @@ -246,26 +247,77 @@ TiledFeatureModelLayer::createTileImplementation(const TileKey& key, ProgressCal NetworkMonitor::ScopedRequestLayer layerRequest(getName()); // Get features for this key - Query query; - query.tileKey() = key; - - GeoExtent dataExtent = key.getExtent(); - // set up for feature indexing if appropriate: - osg::ref_ptr< FeatureSourceIndexNode > index = 0L; - + osg::ref_ptr index = 0L; if (_featureIndex.valid()) { index = new FeatureSourceIndexNode(_featureIndex.get()); } - FilterContext fc(_session.get(), new FeatureProfile(dataExtent), dataExtent, index); + //FilterContext fc(_session.get(), new FeatureProfile(dataExtent), dataExtent, index); GeomFeatureNodeFactory factory(options()); if (progress && progress->isCanceled()) return nullptr; + auto featureProfile = getFeatureSource()->getFeatureProfile(); + OE_SOFT_ASSERT_AND_RETURN(featureProfile, {}); + + FilterContext context(_session.get(), featureProfile, key.getExtent(), index); + Query query(key); + + + osg::ref_ptr group = new osg::Group(); + + auto compile = [&](const Style& style, FeatureList& features, ProgressCallback* progress) + { + if (options().cropFeaturesToTile() == true) + { + FeatureList temp; + temp.swap(features); + + auto extent = key.getExtent().transform(featureProfile->getSRS()); + for (auto& feature : temp) + { + auto cropped = feature->getGeometry()->crop(extent.bounds()); + if (cropped) + { + feature->setGeometry(cropped); + features.emplace_back(feature); + } + } + } + + for (auto& feature : features) + { + feature->set("level", (long long)key.getLOD()); + } + + osg::ref_ptr node; + FeatureListCursor cursor(features); + if (factory.createOrUpdateNode(&cursor, style, context, node, query)) + { + group->addChild(node); + } + }; + + FeatureStyleSorter().sort(key, {}, _session.get(), _filters, compile, progress); + + if (group->getNumChildren() == 0 || group->getBound().valid() == false) + { + return {}; + } + + if (index.valid()) + { + index->addChild(group); + group = index; + } + + return group; + +#if 0 auto cursor = getFeatureSource()->createFeatureCursor(query, _filters, &fc, progress); osg::ref_ptr node = new osg::Group; @@ -279,19 +331,19 @@ TiledFeatureModelLayer::createTileImplementation(const TileKey& key, ProgressCal if (options().cropFeaturesToTile() == true) { - FeatureList tocrop; - tocrop.swap(features); - - for (auto& feature : tocrop) + for (auto& feature : features) { + auto cropped = feature->getGeometry()->crop(dataExtent.bounds()); if (auto cropped = feature->getGeometry()->crop(dataExtent.bounds())) { - feature->setGeometry(cropped.get()); - features.push_back(feature); + feature->setGeometry(cropped); } } } + + //FeatureStyleSorter().sort(features, getStyleSheet(), progress); + if (getStyleSheet() && getStyleSheet()->getSelectors().size() > 0) { osg::Group* group = new osg::Group; @@ -403,6 +455,7 @@ TiledFeatureModelLayer::createTileImplementation(const TileKey& key, ProgressCal } return node; +#endif } const Profile* diff --git a/src/osgEarthImGui/LayersGUI b/src/osgEarthImGui/LayersGUI index 8b85ef6680..306e8f37c1 100644 --- a/src/osgEarthImGui/LayersGUI +++ b/src/osgEarthImGui/LayersGUI @@ -1217,10 +1217,11 @@ namespace osgEarth // Hillshade Shading if (_mapNode->getMap()->getLayer() == nullptr) { - if (ImGui::MenuItem("Hillside shading")) + if (ImGui::MenuItem("Hill shading")) { auto* layer = new GDALDEMLayer(); - layer->setOpacity(0.2); + layer->setName("Hill shading"); + layer->setOpacity(0.25); _mapNode->getMap()->addLayer(layer); } } diff --git a/src/osgEarthProcedural/RoadSurfaceLayer.cpp b/src/osgEarthProcedural/RoadSurfaceLayer.cpp index bc63a11b28..bd7698c430 100644 --- a/src/osgEarthProcedural/RoadSurfaceLayer.cpp +++ b/src/osgEarthProcedural/RoadSurfaceLayer.cpp @@ -357,8 +357,6 @@ RoadSurfaceLayer::createImageImplementation(const TileKey& key, ProgressCallback return GeoImage::INVALID; } -#if 1 - GeoExtent featureExtent = key.getExtent().transform(featureSRS); // Create the output extent: @@ -480,183 +478,4 @@ RoadSurfaceLayer::createImageImplementation(const TileKey& key, ProgressCallback } return GeoImage::INVALID; - -#else - - // Fetch the set of features to render - FeatureList features; - getFeatures(featureSource.get(), key, features, progress); - - if (!features.empty()) - { - GeoExtent featureExtent = key.getExtent().transform(featureSRS); - - // Create the output extent: - GeoExtent outputExtent = key.getExtent(); - - // Establish a local tangent plane near the output extent. This will allow - // the compiler to render the tile in a location cartesian space. - const SpatialReference* keySRS = outputExtent.getSRS(); - osg::Vec3d pos(outputExtent.west(), outputExtent.south(), 0); - osg::ref_ptr srs = keySRS->createTangentPlaneSRS(pos); - outputExtent = outputExtent.transform(srs.get()); - - // Set the LTP as our output SRS. - // The geometry compiler will transform all our features into the - // LTP so we can render using an orthographic camera (TileRasterizer) - FilterContext fc(session.get(), featureProfile.get(), featureExtent); - fc.setOutputSRS(outputExtent.getSRS()); - - // compile the features into a node. - GeometryCompiler compiler; - StyleToFeatures mapping; - - sortFeaturesIntoStyleGroups(styleSheet.get(), features, fc, mapping); - osg::ref_ptr< osg::Group > group; - if (!mapping.empty()) - { - OE_PROFILING_ZONE_NAMED("Style"); - - group = new osg::Group(); - for (unsigned int i = 0; i < mapping.size(); i++) - { - auto& style = mapping[i].first; - osg::ref_ptr node = compiler.compile(mapping[i].second, style, fc); - if (node.valid() && node->getBound().valid()) - { - group->addChild(node); - - auto* render = style.get(); - if (render) - render->applyTo(node.get()); - } - } - } - - if (group && group->getBound().valid()) - { - // Make sure there's actually geometry to render in the output extent - // since rasterization is expensive! - osg::Polytope polytope; - outputExtent.createPolytope(polytope); - - osg::ref_ptr intersector = new osgUtil::PolytopeIntersector(polytope); - osgUtil::IntersectionVisitor visitor(intersector); - group->accept(visitor); - - if (intersector->getIntersections().empty()) - { - //OE_DEBUG << LC << "RSL: skipped an EMPTY bounds without rasterizing :) for " << key.str() << std::endl; - return GeoImage::INVALID; - } - - - OE_PROFILING_ZONE_NAMED("Rasterize"); - - group->setName(key.str()); - - Future> result = rasterizer->render( - group.release(), - outputExtent); - - // Since we join on the rasterizer future immediately, we need to make sure - // the join cancels properly with a custom cancel predicate that checks for - // the existence and status of the host layer. - osg::observer_ptr layer(this); - - osg::ref_ptr local_progress = new ProgressCallback( - progress, - [layer]() { - osg::ref_ptr safe; - return !layer.lock(safe) || !safe->isOpen() || !jobs::alive(); - } - ); - - // Immediately blocks on the result. - // That is OK - we are hopefully in a loading thread. - osg::ref_ptr image = result.join(local_progress.get()); - - // Empty image means the texture did not render anything - if (image.valid() && image->data() != nullptr) - { - if (!ImageUtils::isEmptyImage(image.get())) - { - return GeoImage(image.get(), key.getExtent()); - } - else - { - //OE_DEBUG << LC << "RSL: skipped an EMPTY image result for " << key.str() << std::endl; - return GeoImage::INVALID; - } - } - else - { - return GeoImage::INVALID; - } - } - } - - return GeoImage::INVALID; -#endif } - -void -RoadSurfaceLayer::getFeatures( - FeatureSource* fs, - const TileKey& key, - FeatureList& output, - ProgressCallback* progress) const -{ - OE_PROFILING_ZONE; - - OE_SOFT_ASSERT_AND_RETURN(fs != nullptr, void()); - - // Get the collection of keys accounting for the buffer width - std::unordered_set keys; - fs->getKeys(key, options().featureBufferWidth().get(), keys); - - // Collect all the features, using a small LRU cache and a - // Gate to optimize fetching and sharing with other threads - osg::ref_ptr cursor; - - for (const auto& subkey : keys) - { - FeatureList sublist; - - FeatureListCache::Record r; - if (_lru->get(subkey, r)) - { - sublist = r.value(); - } - else - { - // the Gate prevents 2 threads that requesting the same TileKey - // at the same time from the featuresource. - ScopedGate gatelock(_keygate, subkey); - - // double-check the cache now that we are gate-locked: - if (_lru->get(subkey, r)) - { - sublist = r.value(); - } - else - { - cursor = fs->createFeatureCursor(subkey, _filterChain, nullptr, progress); - if (cursor.valid()) - { - cursor->fill( - sublist, - [](const Feature* f) { return f->getGeometry()->isLinear(); }); - - _lru->insert(subkey, sublist); - } - } - } - - // Clone features onto the end of the output list. - // We must always clone since osgEarth modifies the feature data - // TODO: check whether this is actually true - for (auto& f : sublist) - output.push_back(new Feature(*f)); - } -} \ No newline at end of file