void compute() { //Tell the calculator about the new start/end points _profileCalculator->setStartEnd( GeoPoint(_mapNode->getMapSRS(), _start.x(), _start.y(), 0), GeoPoint(_mapNode->getMapSRS(), _end.x(), _end.y(), 0)); if (_featureNode.valid()) { _root->removeChild( _featureNode.get() ); _featureNode = 0; } LineString* line = new LineString(); line->push_back( _start ); line->push_back( _end ); Feature* feature = new Feature(line, _mapNode->getMapSRS()); feature->geoInterp() = GEOINTERP_GREAT_CIRCLE; //Define a style for the line Style style; LineSymbol* ls = style.getOrCreateSymbol<LineSymbol>(); ls->stroke()->color() = Color::Yellow; ls->stroke()->width() = 2.0f; ls->tessellation() = 20; style.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_TO_TERRAIN; feature->style() = style; _featureNode = new FeatureNode( _mapNode, feature ); //Disable lighting _featureNode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); _root->addChild( _featureNode.get() ); }
TEST(TileCover, DISABLED_FuzzLine) { while(1) { std::srand (time(NULL)); std::size_t len = std::rand() % 10000 + 3; MultiLineString<double> mls; std::size_t num_lines = 1; num_lines += std::rand() % 5; while (num_lines > 0) { LineString<double> line; for (std::size_t i = 0; i < len; ++i) { double x = std::rand() % 180; double y = std::rand() % 90; line.push_back({x,y}); } mls.emplace_back(line); --num_lines; } std::clog << "."; util::TileCover tc(mls, 5); while(tc.next()) { }; } }
Geometry* Ring::cloneAs( const Geometry::Type& newType ) const { if ( newType == TYPE_LINESTRING ) { LineString* line = new LineString( &this->asVector() ); if ( line->size() > 1 && line->front() != line->back() ) line->push_back( front() ); return line; } else return Geometry::cloneAs( newType ); }
std::unordered_map<std::string, std::vector<Feature>> Source::Impl::queryRenderedFeatures(const QueryParameters& parameters) const { std::unordered_map<std::string, std::vector<Feature>> result; if (renderTiles.empty() || parameters.geometry.empty()) { return result; } LineString<double> queryGeometry; for (const auto& p : parameters.geometry) { queryGeometry.push_back(TileCoordinate::fromScreenCoordinate( parameters.transformState, 0, { p.x, parameters.transformState.getSize().height - p.y }).p); } mapbox::geometry::box<double> box = mapbox::geometry::envelope(queryGeometry); auto sortRenderTiles = [](const RenderTile& a, const RenderTile& b) { return a.id.canonical.z != b.id.canonical.z ? a.id.canonical.z < b.id.canonical.z : a.id.canonical.y != b.id.canonical.y ? a.id.canonical.y < b.id.canonical.y : a.id.wrap != b.id.wrap ? a.id.wrap < b.id.wrap : a.id.canonical.x < b.id.canonical.x; }; std::vector<std::reference_wrapper<const RenderTile>> sortedTiles; std::transform(renderTiles.cbegin(), renderTiles.cend(), std::back_inserter(sortedTiles), [](const auto& pair) { return std::ref(pair.second); }); std::sort(sortedTiles.begin(), sortedTiles.end(), sortRenderTiles); for (const auto& renderTileRef : sortedTiles) { const RenderTile& renderTile = renderTileRef.get(); GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min); if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT) { continue; } GeometryCoordinate tileSpaceBoundsMax = TileCoordinate::toGeometryCoordinate(renderTile.id, box.max); if (tileSpaceBoundsMax.x < 0 || tileSpaceBoundsMax.y < 0) { continue; } GeometryCoordinates tileSpaceQueryGeometry; tileSpaceQueryGeometry.reserve(queryGeometry.size()); for (const auto& c : queryGeometry) { tileSpaceQueryGeometry.push_back(TileCoordinate::toGeometryCoordinate(renderTile.id, c)); } renderTile.tile.queryRenderedFeatures(result, tileSpaceQueryGeometry, parameters.transformState, parameters.layerIDs); } return result; }
osg::Group* UTMData::buildGZDTile(const std::string& name, const GeoExtent& extent, const Style& style, const FeatureProfile* featureProfile, const Map* map) { osg::Group* group = new osg::Group(); Style lineStyle; lineStyle.add( const_cast<LineSymbol*>(style.get<LineSymbol>()) ); lineStyle.add( const_cast<AltitudeSymbol*>(style.get<AltitudeSymbol>()) ); bool hasText = style.get<TextSymbol>() != 0L; GeometryCompiler compiler; osg::ref_ptr<Session> session = new Session(map); FilterContext context( session.get(), featureProfile, extent ); // make sure we get sufficient tessellation: compiler.options().maxGranularity() = 1.0; FeatureList features; // longitudinal line: LineString* lon = new LineString(2); lon->push_back( osg::Vec3d(extent.xMin(), extent.yMax(), 0) ); lon->push_back( osg::Vec3d(extent.xMin(), extent.yMin(), 0) ); Feature* lonFeature = new Feature(lon, extent.getSRS()); lonFeature->geoInterp() = GEOINTERP_GREAT_CIRCLE; features.push_back( lonFeature ); // latitudinal line: LineString* lat = new LineString(2); lat->push_back( osg::Vec3d(extent.xMin(), extent.yMin(), 0) ); lat->push_back( osg::Vec3d(extent.xMax(), extent.yMin(), 0) ); Feature* latFeature = new Feature(lat, extent.getSRS()); latFeature->geoInterp() = GEOINTERP_RHUMB_LINE; features.push_back( latFeature ); // top lat line at 84N if ( extent.yMax() == 84.0 ) { LineString* lat = new LineString(2); lat->push_back( osg::Vec3d(extent.xMin(), extent.yMax(), 0) ); lat->push_back( osg::Vec3d(extent.xMax(), extent.yMax(), 0) ); Feature* latFeature = new Feature(lat, extent.getSRS()); latFeature->geoInterp() = GEOINTERP_RHUMB_LINE; features.push_back( latFeature ); } osg::Node* geomNode = compiler.compile(features, lineStyle, context); if ( geomNode ) group->addChild( geomNode ); // get the geocentric tile center: osg::Vec3d tileCenter; extent.getCentroid( tileCenter.x(), tileCenter.y() ); const SpatialReference* ecefSRS = extent.getSRS()->getGeocentricSRS(); osg::Vec3d centerECEF; extent.getSRS()->transform( tileCenter, ecefSRS, centerECEF ); if ( hasText ) { osg::Vec3d west, east; extent.getSRS()->transform( osg::Vec3d(extent.xMin(),tileCenter.y(),0), ecefSRS, west ); extent.getSRS()->transform( osg::Vec3d(extent.xMax(),tileCenter.y(),0), ecefSRS, east ); const TextSymbol* textSym_in = style.get<TextSymbol>(); osg::ref_ptr<TextSymbol> textSym = textSym_in ? new TextSymbol(*textSym_in) : new TextSymbol(); textSym->size() = (west-east).length() / 3.0; TextSymbolizer ts(textSym.get()); osg::Geode* textGeode = new osg::Geode(); osg::Drawable* d = ts.create(name); d->getOrCreateStateSet()->setRenderBinToInherit(); textGeode->addDrawable(d); Registry::shaderGenerator().run(textGeode, Registry::stateSetCache()); osg::Matrixd centerL2W; ecefSRS->createLocalToWorld( centerECEF, centerL2W ); osg::MatrixTransform* mt = new osg::MatrixTransform(centerL2W); mt->addChild(textGeode); group->addChild(mt); } //group = buildGZDChildren( group, name ); group = ClusterCullingFactory::createAndInstall( group, centerECEF )->asGroup(); return group; }
osg::Node* UTMGraticule::buildGZDTile( const std::string& name, const GeoExtent& extent ) { osg::Group* group = new osg::Group(); Style lineStyle; lineStyle.add( _options->primaryStyle()->get<LineSymbol>() ); lineStyle.add( _options->primaryStyle()->get<AltitudeSymbol>() ); //const Style& lineStyle = *_options->lineStyle(); //Style textStyle = *_options->textStyle(); bool hasText = _options->primaryStyle()->get<TextSymbol>() != 0L; GeometryCompiler compiler; osg::ref_ptr<Session> session = new Session( _mapNode->getMap() ); FilterContext context( session.get(), _featureProfile.get(), extent ); // make sure we get sufficient tessellation: compiler.options().maxGranularity() = 1.0; FeatureList features; // longitudinal line: LineString* lon = new LineString(2); lon->push_back( osg::Vec3d(extent.xMin(), extent.yMax(), 0) ); lon->push_back( osg::Vec3d(extent.xMin(), extent.yMin(), 0) ); Feature* lonFeature = new Feature(lon, extent.getSRS()); lonFeature->geoInterp() = GEOINTERP_GREAT_CIRCLE; features.push_back( lonFeature ); // latitudinal line: LineString* lat = new LineString(2); lat->push_back( osg::Vec3d(extent.xMin(), extent.yMin(), 0) ); lat->push_back( osg::Vec3d(extent.xMax(), extent.yMin(), 0) ); Feature* latFeature = new Feature(lat, extent.getSRS()); latFeature->geoInterp() = GEOINTERP_RHUMB_LINE; features.push_back( latFeature ); // top lat line at 84N if ( extent.yMax() == 84.0 ) { LineString* lat = new LineString(2); lat->push_back( osg::Vec3d(extent.xMin(), extent.yMax(), 0) ); lat->push_back( osg::Vec3d(extent.xMax(), extent.yMax(), 0) ); Feature* latFeature = new Feature(lat, extent.getSRS()); latFeature->geoInterp() = GEOINTERP_RHUMB_LINE; features.push_back( latFeature ); } osg::Node* geomNode = compiler.compile(features, lineStyle, context); if ( geomNode ) group->addChild( geomNode ); // get the geocentric tile center: osg::Vec3d tileCenter; extent.getCentroid( tileCenter.x(), tileCenter.y() ); osg::Vec3d centerECEF; extent.getSRS()->transformToECEF( tileCenter, centerECEF ); if ( hasText ) { osg::Vec3d west, east; extent.getSRS()->transformToECEF(osg::Vec3d(extent.xMin(),tileCenter.y(),0), west ); extent.getSRS()->transformToECEF(osg::Vec3d(extent.xMax(),tileCenter.y(),0), east ); TextSymbol* textSym = _options->primaryStyle()->getOrCreate<TextSymbol>(); textSym->size() = (west-east).length() / 3.0; TextSymbolizer ts( textSym ); osg::Geode* textGeode = new osg::Geode(); textGeode->getOrCreateStateSet()->setRenderBinDetails( 9998, "DepthSortedBin" ); textGeode->getOrCreateStateSet()->setAttributeAndModes( _depthAttribute, 1 ); osg::Drawable* d = ts.create(name); d->getOrCreateStateSet()->setRenderBinToInherit(); textGeode->addDrawable(d); osg::MatrixTransform* mt = new osg::MatrixTransform(ECEF::createLocalToWorld(centerECEF)); mt->addChild(textGeode); group->addChild(mt); } group = buildGZDChildren( group, name ); group = ClusterCullingFactory::createAndInstall( group, centerECEF )->asGroup(); return group; }
// // NOTE: run this sample from the repo/tests directory. // int main(int argc, char** argv) { osg::ArgumentParser arguments(&argc,argv); osgViewer::Viewer viewer(arguments); s_viewer = &viewer; // Start by creating the map: s_mapNode = MapNode::load(arguments); if ( !s_mapNode ) { Map* map = new Map(); // Start with a basemap imagery layer; we'll be using the GDAL driver // to load a local GeoTIFF file: GDALOptions basemapOpt; basemapOpt.url() = "../data/world.tif"; map->addImageLayer( new ImageLayer( ImageLayerOptions("basemap", basemapOpt) ) ); // That's it, the map is ready; now create a MapNode to render the Map: MapNodeOptions mapNodeOptions; mapNodeOptions.enableLighting() = false; s_mapNode = new MapNode( map, mapNodeOptions ); } s_mapNode->setNodeMask( 0x01 ); // Define a style for the feature data. Since we are going to render the // vectors as lines, configure the line symbolizer: StyleSheet* styleSheet = buildStyleSheet( Color::Yellow, 2.0f ); s_source = new FeatureListSource(); LineString* line = new LineString(); line->push_back( osg::Vec3d(-60, 20, 0) ); line->push_back( osg::Vec3d(-120, 20, 0) ); line->push_back( osg::Vec3d(-120, 60, 0) ); line->push_back( osg::Vec3d(-60, 60, 0) ); Feature *feature = new Feature(line, s_mapNode->getMapSRS(), Style(), s_fid++); s_source->insertFeature( feature ); s_activeFeature = feature; s_root = new osg::Group; s_root->addChild( s_mapNode.get() ); Session* session = new Session(s_mapNode->getMap(), styleSheet, s_source.get()); FeatureModelGraph* graph = new FeatureModelGraph( session, FeatureModelSourceOptions(), new GeomFeatureNodeFactory() ); graph->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); graph->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); s_root->addChild( graph ); //Setup the controls ControlCanvas* canvas = ControlCanvas::get( &viewer ); s_root->addChild( canvas ); Grid *toolbar = createToolBar( ); canvas->addControl( toolbar ); canvas->setNodeMask( 0x1 << 1 ); int col = 0; LabelControl* addVerts = new LabelControl("Add Verts"); toolbar->setControl(col++, 0, addVerts ); addVerts->addEventHandler( new AddVertsModeHandler( graph )); LabelControl* edit = new LabelControl("Edit"); toolbar->setControl(col++, 0, edit ); edit->addEventHandler(new EditModeHandler( graph )); unsigned int row = 0; Grid *styleBar = createToolBar( ); styleBar->setPosition(0, 50); canvas->addControl( styleBar ); //Make a list of styles styleBar->setControl(0, row++, new LabelControl("Styles") ); unsigned int numStyles = 8; for (unsigned int i = 0; i < numStyles; ++i) { float w = 50; osg::Vec4 color = randomColor(); float widths[3] = {2, 4, 8}; unsigned int r = row++; for (unsigned int j = 0; j < 3; j++) { Control* l = new Control(); l->setBackColor( color ); l->addEventHandler(new ChangeStyleHandler(graph, buildStyleSheet( color, widths[j] ) )); l->setSize(w,5 * widths[j]); styleBar->setControl(j, r, l); } } viewer.setSceneData( s_root.get() ); viewer.setCameraManipulator( new EarthManipulator() ); if ( s_mapNode ) viewer.getCamera()->addCullCallback( new osgEarth::Util::AutoClipPlaneCullCallback(s_mapNode) ); // add some stock OSG handlers: viewer.addEventHandler(new osgViewer::StatsHandler()); viewer.addEventHandler(new osgViewer::WindowSizeHandler()); viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); return viewer.run(); }
osg::Node* GeodeticGraticule::buildTile( const TileKey& key, Map* map ) const { if ( _options->levels().size() <= key.getLevelOfDetail() ) { OE_WARN << LC << "Tried to create cell at non-existant level " << key.getLevelOfDetail() << std::endl; return 0L; } const GeodeticGraticuleOptions::Level& level = _options->levels()[key.getLevelOfDetail()]; //_levels[key.getLevelOfDetail()]; // the "-2" here is because normal tile paging gives you one subdivision already, // so we only need to account for > 1 subdivision factor. unsigned cellsPerTile = level._subdivisionFactor <= 2u ? 1u : 1u << (level._subdivisionFactor-2u); unsigned cellsPerTileX = std::max(1u, cellsPerTile); unsigned cellsPerTileY = std::max(1u, cellsPerTile); GeoExtent tileExtent = key.getExtent(); FeatureList latLines; FeatureList lonLines; static LatLongFormatter s_llf(LatLongFormatter::FORMAT_DECIMAL_DEGREES); double cellWidth = tileExtent.width() / cellsPerTileX; double cellHeight = tileExtent.height() / cellsPerTileY; const Style& lineStyle = level._lineStyle.isSet() ? *level._lineStyle : *_options->lineStyle(); const Style& textStyle = level._textStyle.isSet() ? *level._textStyle : *_options->textStyle(); bool hasText = textStyle.get<TextSymbol>() != 0L; osg::ref_ptr<osg::Group> labels; if ( hasText ) { labels = new osg::Group(); //TODO: This is a bug, if you don't turn on decluttering the text labels are giant. Need to determine what is wrong with LabelNodes without decluttering. Decluttering::setEnabled( labels->getOrCreateStateSet(), true ); } // spatial ref for features: const SpatialReference* geoSRS = tileExtent.getSRS()->getGeographicSRS(); // longitude lines for( unsigned cx = 0; cx < cellsPerTileX; ++cx ) { double clon = tileExtent.xMin() + cellWidth * (double)cx; LineString* lon = new LineString(2); lon->push_back( osg::Vec3d(clon, tileExtent.yMin(), 0) ); lon->push_back( osg::Vec3d(clon, tileExtent.yMax(), 0) ); lonLines.push_back( new Feature(lon, geoSRS) ); if ( hasText ) { for( unsigned cy = 0; cy < cellsPerTileY; ++cy ) { double clat = tileExtent.yMin() + (0.5*cellHeight) + cellHeight*(double)cy; LabelNode* label = new LabelNode( _mapNode.get(), GeoPoint(geoSRS, clon, clat), s_llf.format(clon), textStyle ); labels->addChild( label ); } } } // latitude lines for( unsigned cy = 0; cy < cellsPerTileY; ++cy ) { double clat = tileExtent.yMin() + cellHeight * (double)cy; if ( clat == key.getProfile()->getExtent().yMin() ) continue; LineString* lat = new LineString(2); lat->push_back( osg::Vec3d(tileExtent.xMin(), clat, 0) ); lat->push_back( osg::Vec3d(tileExtent.xMax(), clat, 0) ); latLines.push_back( new Feature(lat, geoSRS) ); if ( hasText ) { for( unsigned cx = 0; cx < cellsPerTileX; ++cx ) { double clon = tileExtent.xMin() + (0.5*cellWidth) + cellWidth*(double)cy; LabelNode* label = new LabelNode( _mapNode.get(), GeoPoint(geoSRS, clon, clat), s_llf.format(clat), textStyle ); labels->addChild( label ); } } } osg::Group* group = new osg::Group(); GeometryCompiler compiler; osg::ref_ptr<Session> session = new Session( map ); FilterContext context( session.get(), _featureProfile.get(), tileExtent ); // make sure we get sufficient tessellation: compiler.options().maxGranularity() = std::min(cellWidth, cellHeight) / 16.0; compiler.options().geoInterp() = GEOINTERP_GREAT_CIRCLE; osg::Node* lonNode = compiler.compile(lonLines, lineStyle, context); if ( lonNode ) group->addChild( lonNode ); compiler.options().geoInterp() = GEOINTERP_RHUMB_LINE; osg::Node* latNode = compiler.compile(latLines, lineStyle, context); if ( latNode ) group->addChild( latNode ); // add the labels. if ( labels.valid() ) group->addChild( labels.get() ); // get the geocentric tile center: osg::Vec3d tileCenter; tileExtent.getCentroid( tileCenter.x(), tileCenter.y() ); const SpatialReference* ecefSRS = tileExtent.getSRS()->getECEF(); osg::Vec3d centerECEF; tileExtent.getSRS()->transform( tileCenter, ecefSRS, centerECEF ); //tileExtent.getSRS()->transformToECEF( tileCenter, centerECEF ); osg::NodeCallback* ccc = 0L; // set up cluster culling. if ( tileExtent.getSRS()->isGeographic() && tileExtent.width() < 90.0 && tileExtent.height() < 90.0 ) { ccc = ClusterCullingFactory::create( group, centerECEF ); } // add a paging node for higher LODs: if ( key.getLevelOfDetail() + 1 < _options->levels().size() ) { const GeodeticGraticuleOptions::Level& nextLevel = _options->levels()[key.getLevelOfDetail()+1]; osg::BoundingSphere bs = group->getBound(); std::string uri = Stringify() << key.str() << "_" << getID() << "." << GRID_MARKER << "." << GRATICULE_EXTENSION; osg::PagedLOD* plod = new osg::PagedLOD(); plod->setCenter( bs.center() ); plod->addChild( group, std::max(level._minRange,nextLevel._maxRange), FLT_MAX ); plod->setFileName( 1, uri ); plod->setRange( 1, 0, nextLevel._maxRange ); group = plod; } // or, if this is the deepest level and there's a minRange set, we need an LOD: else if ( level._minRange > 0.0f ) { osg::LOD* lod = new osg::LOD(); lod->addChild( group, level._minRange, FLT_MAX ); group = lod; } if ( ccc ) { osg::Group* cccGroup = new osg::Group(); cccGroup->addCullCallback( ccc ); cccGroup->addChild( group ); group = cccGroup; } return group; }
static Feature::geometry_type convertGeometry(const GeometryTileFeature& geometryTileFeature, const CanonicalTileID& tileID) { const double size = util::EXTENT * std::pow(2, tileID.z); const double x0 = util::EXTENT * tileID.x; const double y0 = util::EXTENT * tileID.y; auto tileCoordinatesToLatLng = [&] (const Point<int16_t>& p) { double y2 = 180 - (p.y + y0) * 360 / size; return Point<double>( (p.x + x0) * 360 / size - 180, 360.0 / M_PI * std::atan(std::exp(y2 * M_PI / 180)) - 90.0 ); }; GeometryCollection geometries = geometryTileFeature.getGeometries(); switch (geometryTileFeature.getType()) { case FeatureType::Unknown: { assert(false); return Point<double>(NAN, NAN); } case FeatureType::Point: { MultiPoint<double> multiPoint; for (const auto& p : geometries.at(0)) { multiPoint.push_back(tileCoordinatesToLatLng(p)); } if (multiPoint.size() == 1) { return multiPoint[0]; } else { return multiPoint; } } case FeatureType::LineString: { MultiLineString<double> multiLineString; for (const auto& g : geometries) { LineString<double> lineString; for (const auto& p : g) { lineString.push_back(tileCoordinatesToLatLng(p)); } multiLineString.push_back(std::move(lineString)); } if (multiLineString.size() == 1) { return multiLineString[0]; } else { return multiLineString; } } case FeatureType::Polygon: { MultiPolygon<double> multiPolygon; for (const auto& pg : classifyRings(geometries)) { Polygon<double> polygon; for (const auto& r : pg) { LinearRing<double> linearRing; for (const auto& p : r) { linearRing.push_back(tileCoordinatesToLatLng(p)); } polygon.push_back(std::move(linearRing)); } multiPolygon.push_back(std::move(polygon)); } if (multiPolygon.size() == 1) { return multiPolygon[0]; } else { return multiPolygon; } } } // Unreachable, but placate GCC. return Point<double>(); }