osg::BoundingSphered FeatureModelGraph::getBoundInWorldCoords( const GeoExtent& extent ) const { osg::Vec3d center, corner; GeoExtent workingExtent; if ( extent.getSRS()->isEquivalentTo( _usableMapExtent.getSRS() ) ) { workingExtent = extent; } else { workingExtent = extent.transform( _usableMapExtent.getSRS() ); // safe. } workingExtent.getCentroid( center.x(), center.y() ); corner.x() = workingExtent.xMin(); corner.y() = workingExtent.yMin(); if ( _session->getMapInfo().isGeocentric() ) { workingExtent.getSRS()->transformToECEF( center, center ); workingExtent.getSRS()->transformToECEF( corner, corner ); } return osg::BoundingSphered( center, (center-corner).length() ); }
osg::BoundingSphered FeatureModelGraph::getBoundInWorldCoords(const GeoExtent& extent, const MapFrame* mapf ) const { osg::Vec3d center, corner; GeoExtent workingExtent; if ( !extent.isValid() ) { return osg::BoundingSphered(); } if ( extent.getSRS()->isEquivalentTo( _usableMapExtent.getSRS() ) ) { workingExtent = extent; } else { workingExtent = extent.transform( _usableMapExtent.getSRS() ); // safe. } workingExtent.getCentroid( center.x(), center.y() ); double centerZ = 0.0; if ( mapf ) { // Use an appropriate resolution for this extents width double resolution = workingExtent.width(); ElevationQuery query( *mapf ); GeoPoint p( mapf->getProfile()->getSRS(), center, ALTMODE_ABSOLUTE ); query.getElevation( p, center.z(), resolution ); centerZ = center.z(); } corner.x() = workingExtent.xMin(); corner.y() = workingExtent.yMin(); corner.z() = 0; if ( _session->getMapInfo().isGeocentric() ) { const SpatialReference* ecefSRS = workingExtent.getSRS()->getECEF(); workingExtent.getSRS()->transform( center, ecefSRS, center ); workingExtent.getSRS()->transform( corner, ecefSRS, corner ); //workingExtent.getSRS()->transformToECEF( center, center ); //workingExtent.getSRS()->transformToECEF( corner, corner ); } if (workingExtent.getSRS()->isGeographic() && ( workingExtent.width() >= 90 || workingExtent.height() >= 90 ) ) { return osg::BoundingSphered( osg::Vec3d(0,0,0), 2*center.length() ); } return osg::BoundingSphered( center, (center-corner).length() ); }
osg::BoundingSphered FeatureModelGraph::getBoundInWorldCoords(const GeoExtent& extent, const MapFrame* mapf ) const { osg::Vec3d center, corner; //double z = 0.0; GeoExtent workingExtent; if ( extent.getSRS()->isEquivalentTo( _usableMapExtent.getSRS() ) ) { workingExtent = extent; } else { workingExtent = extent.transform( _usableMapExtent.getSRS() ); // safe. } workingExtent.getCentroid( center.x(), center.y() ); double centerZ = 0.0; if ( mapf ) { // Use an appropriate resolution for this extents width double resolution = workingExtent.width(); ElevationQuery query( *mapf ); query.getElevation( GeoPoint(mapf->getProfile()->getSRS(),center), center.z(), resolution ); centerZ = center.z(); } corner.x() = workingExtent.xMin(); corner.y() = workingExtent.yMin(); corner.z() = 0; if ( _session->getMapInfo().isGeocentric() ) { workingExtent.getSRS()->transformToECEF( center, center ); workingExtent.getSRS()->transformToECEF( corner, corner ); } return osg::BoundingSphered( center, (center-corner).length() ); }
void FeaturesToNodeFilter::computeLocalizers( const FilterContext& context ) { if ( context.isGeoreferenced() ) { if ( context.getSession()->getMapInfo().isGeocentric() ) { const SpatialReference* geogSRS = context.profile()->getSRS()->getGeographicSRS(); GeoExtent geodExtent = context.extent()->transform( geogSRS ); if ( geodExtent.width() < 180.0 ) { osg::Vec3d centroid, centroidECEF; geodExtent.getCentroid( centroid.x(), centroid.y() ); geogSRS->transform( centroid, geogSRS->getECEF(), centroidECEF ); geogSRS->getECEF()->createLocalToWorld( centroidECEF, _local2world ); _world2local.invert( _local2world ); } } else // projected { if ( context.extent().isSet() ) { osg::Vec3d centroid; context.extent()->getCentroid(centroid.x(), centroid.y()); context.extent()->getSRS()->transform( centroid, context.getSession()->getMapInfo().getProfile()->getSRS(), centroid ); _world2local.makeTranslate( -centroid ); _local2world.invert( _world2local ); } } } }
void FeaturesToNodeFilter::computeLocalizers( const FilterContext& context, const osgEarth::GeoExtent &extent, osg::Matrixd &out_w2l, osg::Matrixd &out_l2w ) { if ( context.isGeoreferenced() ) { if ( context.getSession()->getMapInfo().isGeocentric() ) { const SpatialReference* geogSRS = context.profile()->getSRS()->getGeographicSRS(); GeoExtent geodExtent = extent.transform( geogSRS ); if ( geodExtent.width() < 180.0 ) { osg::Vec3d centroid, centroidECEF; geodExtent.getCentroid( centroid.x(), centroid.y() ); geogSRS->transform( centroid, geogSRS->getECEF(), centroidECEF ); geogSRS->getECEF()->createLocalToWorld( centroidECEF, out_l2w ); out_w2l.invert( out_l2w ); } } else // projected { if ( extent.isValid() ) { osg::Vec3d centroid; extent.getCentroid(centroid.x(), centroid.y()); extent.getSRS()->transform( centroid, context.getSession()->getMapInfo().getProfile()->getSRS(), centroid ); out_w2l.makeTranslate( -centroid ); out_l2w.invert( out_w2l ); } } } }
/* method: getCentroid of class GeoExtent */ static int tolua_Lua_ScriptEngine_tolua_GeoExtent_getCentroid00(lua_State* tolua_S) { #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( !tolua_isusertype(tolua_S,1,"GeoExtent",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { GeoExtent* self = (GeoExtent*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'getCentroid'",NULL); #endif { GeoPoint tolua_ret = (GeoPoint) self->getCentroid(); { #ifdef __cplusplus void* tolua_obj = new GeoPoint(tolua_ret); tolua_pushusertype(tolua_S,tolua_clone(tolua_S,tolua_obj,tolua_collect_GeoPoint),"GeoPoint"); #else void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(GeoPoint)); tolua_pushusertype(tolua_S,tolua_clone(tolua_S,tolua_obj,tolua_collect),"GeoPoint"); #endif } } } return 1; #ifndef TOLUA_RELEASE tolua_lerror: tolua_error(tolua_S,"#ferror in function 'getCentroid'.",&tolua_err); return 0; #endif }
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; }
//override bool renderFeaturesForStyle( Session* session, const Style& style, const FeatureList& features, osg::Referenced* buildData, const GeoExtent& imageExtent, osg::Image* image ) { OE_DEBUG << LC << "Rendering " << features.size() << " features for " << imageExtent.toString() << "\n"; // A processing context to use with the filters: FilterContext context( session ); context.setProfile( getFeatureSource()->getFeatureProfile() ); const LineSymbol* masterLine = style.getSymbol<LineSymbol>(); const PolygonSymbol* masterPoly = style.getSymbol<PolygonSymbol>(); const CoverageSymbol* masterCov = style.getSymbol<CoverageSymbol>(); // sort into bins, making a copy for lines that require buffering. FeatureList polygons; FeatureList lines; for(FeatureList::const_iterator f = features.begin(); f != features.end(); ++f) { if ( f->get()->getGeometry() ) { bool hasPoly = false; bool hasLine = false; if ( masterPoly || f->get()->style()->has<PolygonSymbol>() ) { polygons.push_back( f->get() ); hasPoly = true; } if ( masterLine || f->get()->style()->has<LineSymbol>() ) { Feature* newFeature = new Feature( *f->get() ); if ( !newFeature->getGeometry()->isLinear() ) { newFeature->setGeometry( newFeature->getGeometry()->cloneAs(Geometry::TYPE_RING) ); } lines.push_back( newFeature ); hasLine = true; } // if there are no geometry symbols but there is a coverage symbol, default to polygons. if ( !hasLine && !hasPoly ) { if ( masterCov || f->get()->style()->has<CoverageSymbol>() ) { polygons.push_back( f->get() ); } } } } // initialize: RenderFrame frame; frame.xmin = imageExtent.xMin(); frame.ymin = imageExtent.yMin(); frame.xf = (double)image->s() / imageExtent.width(); frame.yf = (double)image->t() / imageExtent.height(); if ( lines.size() > 0 ) { // We are buffering in the features native extent, so we need to use the // transformed extent to get the proper "resolution" for the image const SpatialReference* featureSRS = context.profile()->getSRS(); GeoExtent transformedExtent = imageExtent.transform(featureSRS); double trans_xf = (double)image->s() / transformedExtent.width(); double trans_yf = (double)image->t() / transformedExtent.height(); // resolution of the image (pixel extents): double xres = 1.0/trans_xf; double yres = 1.0/trans_yf; // downsample the line data so that it is no higher resolution than to image to which // we intend to rasterize it. If you don't do this, you run the risk of the buffer // operation taking forever on very high-res input data. if ( _options.optimizeLineSampling() == true ) { ResampleFilter resample; resample.minLength() = osg::minimum( xres, yres ); context = resample.push( lines, context ); } // now run the buffer operation on all lines: BufferFilter buffer; double lineWidth = 1.0; if ( masterLine ) { buffer.capStyle() = masterLine->stroke()->lineCap().value(); if ( masterLine->stroke()->width().isSet() ) { lineWidth = masterLine->stroke()->width().value(); GeoExtent imageExtentInFeatureSRS = imageExtent.transform(featureSRS); double pixelWidth = imageExtentInFeatureSRS.width() / (double)image->s(); // if the width units are specified, process them: if (masterLine->stroke()->widthUnits().isSet() && masterLine->stroke()->widthUnits().get() != Units::PIXELS) { const Units& featureUnits = featureSRS->getUnits(); const Units& strokeUnits = masterLine->stroke()->widthUnits().value(); // if the units are different than those of the feature data, we need to // do a units conversion. if ( featureUnits != strokeUnits ) { if ( Units::canConvert(strokeUnits, featureUnits) ) { // linear to linear, no problem lineWidth = strokeUnits.convertTo( featureUnits, lineWidth ); } else if ( strokeUnits.isLinear() && featureUnits.isAngular() ) { // linear to angular? approximate degrees per meter at the // latitude of the tile's centroid. double lineWidthM = masterLine->stroke()->widthUnits()->convertTo(Units::METERS, lineWidth); double mPerDegAtEquatorInv = 360.0/(featureSRS->getEllipsoid()->getRadiusEquator() * 2.0 * osg::PI); double lon, lat; imageExtent.getCentroid(lon, lat); lineWidth = lineWidthM * mPerDegAtEquatorInv * cos(osg::DegreesToRadians(lat)); } } // enfore a minimum width of one pixel. float minPixels = masterLine->stroke()->minPixels().getOrUse( 1.0f ); lineWidth = osg::clampAbove(lineWidth, pixelWidth*minPixels); } else // pixels { lineWidth *= pixelWidth; } } } buffer.distance() = lineWidth * 0.5; // since the distance is for one side buffer.push( lines, context ); } // Transform the features into the map's SRS: TransformFilter xform( imageExtent.getSRS() ); xform.setLocalizeCoordinates( false ); FilterContext polysContext = xform.push( polygons, context ); FilterContext linesContext = xform.push( lines, context ); // set up the AGG renderer: agg::rendering_buffer rbuf( image->data(), image->s(), image->t(), image->s()*4 ); // Create the renderer and the rasterizer agg::rasterizer ras; // Setup the rasterizer if ( _options.coverage() == true ) ras.gamma(1.0); else ras.gamma(_options.gamma().get()); ras.filling_rule(agg::fill_even_odd); // construct an extent for cropping the geometry to our tile. // extend just outside the actual extents so we don't get edge artifacts: GeoExtent cropExtent = GeoExtent(imageExtent); cropExtent.scale(1.1, 1.1); osg::ref_ptr<Symbology::Polygon> cropPoly = new Symbology::Polygon( 4 ); cropPoly->push_back( osg::Vec3d( cropExtent.xMin(), cropExtent.yMin(), 0 )); cropPoly->push_back( osg::Vec3d( cropExtent.xMax(), cropExtent.yMin(), 0 )); cropPoly->push_back( osg::Vec3d( cropExtent.xMax(), cropExtent.yMax(), 0 )); cropPoly->push_back( osg::Vec3d( cropExtent.xMin(), cropExtent.yMax(), 0 )); // If there's a coverage symbol, make a copy of the expressions so we can evaluate them optional<NumericExpression> covValue; const CoverageSymbol* covsym = style.get<CoverageSymbol>(); if (covsym && covsym->valueExpression().isSet()) covValue = covsym->valueExpression().get(); // render the polygons for(FeatureList::iterator i = polygons.begin(); i != polygons.end(); i++) { Feature* feature = i->get(); Geometry* geometry = feature->getGeometry(); osg::ref_ptr<Geometry> croppedGeometry; if ( geometry->crop( cropPoly.get(), croppedGeometry ) ) { const PolygonSymbol* poly = feature->style().isSet() && feature->style()->has<PolygonSymbol>() ? feature->style()->get<PolygonSymbol>() : masterPoly; if ( _options.coverage() == true && covValue.isSet() ) { float value = (float)feature->eval(covValue.mutable_value(), &context); rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf); } else { osg::Vec4f color = poly->fill()->color(); rasterize(croppedGeometry.get(), color, frame, ras, rbuf); } } } // render the lines for(FeatureList::iterator i = lines.begin(); i != lines.end(); i++) { Feature* feature = i->get(); Geometry* geometry = feature->getGeometry(); osg::ref_ptr<Geometry> croppedGeometry; if ( geometry->crop( cropPoly.get(), croppedGeometry ) ) { const LineSymbol* line = feature->style().isSet() && feature->style()->has<LineSymbol>() ? feature->style()->get<LineSymbol>() : masterLine; if ( _options.coverage() == true && covValue.isSet() ) { float value = (float)feature->eval(covValue.mutable_value(), &context); rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf); } else { osg::Vec4f color = line ? static_cast<osg::Vec4>(line->stroke()->color()) : osg::Vec4(1,1,1,1); rasterize(croppedGeometry.get(), color, frame, ras, rbuf); } } } return true; }
// Calculates a sub-extent of a larger extent, given the number of children and // the child number. This currently assumes the subdivision ordering used by // VirtualPlanetBuilder. GeoExtent TerrainUtils::getSubExtent(const GeoExtent& extent, int num_children, int child_no) { GeoPoint centroid = extent.getCentroid(); GeoExtent sub_extent; switch( num_children ) { case 0: case 1: sub_extent = extent; break; case 2: if ( child_no == 0 ) { sub_extent = GeoExtent( extent.getXMin(), extent.getYMin(), centroid.x(), extent.getYMax(), extent.getSRS() ); } else { sub_extent = GeoExtent( centroid.x(), extent.getYMin(), extent.getXMax(), extent.getYMax(), extent.getSRS() ); } break; case 4: if ( child_no == 2 ) { sub_extent = GeoExtent( extent.getXMin(), centroid.y(), centroid.x(), extent.getYMax(), extent.getSRS() ); } else if ( child_no == 3 ) { sub_extent = GeoExtent( centroid.x(), centroid.y(), extent.getXMax(), extent.getYMax(), extent.getSRS() ); } else if ( child_no == 0 ) { sub_extent = GeoExtent( extent.getXMin(), extent.getYMin(), centroid.x(), centroid.y(), extent.getSRS() ); } else if ( child_no == 1 ) { sub_extent = GeoExtent( centroid.x(), extent.getYMin(), extent.getXMax(), centroid.y(), extent.getSRS() ); } } return sub_extent; }