FilterContext ScaleFilter::push( FeatureList& input, FilterContext& cx ) { for( FeatureList::iterator i = input.begin(); i != input.end(); ++i ) { Feature* input = i->get(); if ( input && input->getGeometry() ) { Bounds envelope = input->getGeometry()->getBounds(); // now scale and shift everything GeometryIterator scale_iter( input->getGeometry() ); while( scale_iter.hasMore() ) { Geometry* geom = scale_iter.next(); for( osg::Vec3dArray::iterator v = geom->begin(); v != geom->end(); v++ ) { double xr = (v->x() - envelope.xMin()) / envelope.width(); v->x() += (xr - 0.5) * _scale; double yr = (v->y() - envelope.yMin()) / envelope.height(); v->y() += (yr - 0.5) * _scale; } } } } return cx; }
// Returns true if these Bounds share any area in common with another. bool overlaps(const Bounds& other, bool force2d = false) const { Point otherMid(other.mid()); return std::abs(m_mid.x - otherMid.x) <= width() / 2.0 + other.width() / 2.0 && std::abs(m_mid.y - otherMid.y) <= depth() / 2.0 + other.depth() / 2.0 && (force2d || std::abs(m_mid.z - otherMid.z) <= height() / 2.0 + other.height() / 2.0); }
/** * Tiles the Geometry into the given number of columns and rows */ void tileGeometry(Geometry* geometry, const SpatialReference* featureSRS, unsigned int numCols, unsigned int numRows, GeometryCollection& out) { // Clear the output list. out.clear(); Bounds b = geometry->getBounds(); double tw = b.width() / (double)numCols; double th = b.height() / (double)numRows; // Get the average Z, since GEOS will set teh Z of new verts to that of the cropping polygon, // which is stupid but that's how it is. double z = 0.0; for(unsigned i=0; i<geometry->size(); ++i) z += geometry->at(i).z(); z /= geometry->size(); osg::ref_ptr<Polygon> poly = new Polygon; poly->resize( 4 ); for(int x=0; x<(int)numCols; ++x) { for(int y=0; y<(int)numRows; ++y) { (*poly)[0].set( b.xMin() + tw*(double)x, b.yMin() + th*(double)y, z ); (*poly)[1].set( b.xMin() + tw*(double)(x+1), b.yMin() + th*(double)y, z ); (*poly)[2].set( b.xMin() + tw*(double)(x+1), b.yMin() + th*(double)(y+1), z ); (*poly)[3].set( b.xMin() + tw*(double)x, b.yMin() + th*(double)(y+1), z ); osg::ref_ptr<Geometry> ringTile; if ( geometry->crop(poly.get(), ringTile) ) { // Use an iterator since crop could return a multi-polygon GeometryIterator gi( ringTile.get(), false ); while( gi.hasMore() ) { Geometry* geom = gi.next(); out.push_back( geom ); } } } } }
bool ExtrudeGeometryFilter::buildStructure(const Geometry* input, double height, double heightOffset, bool flatten, const SkinResource* wallSkin, const SkinResource* roofSkin, Structure& structure, FilterContext& cx ) { bool makeECEF = false; const SpatialReference* srs = 0L; const SpatialReference* mapSRS = 0L; if ( cx.isGeoreferenced() ) { srs = cx.extent()->getSRS(); makeECEF = cx.getSession()->getMapInfo().isGeocentric(); mapSRS = cx.getSession()->getMapInfo().getProfile()->getSRS(); } // whether this is a closed polygon structure. structure.isPolygon = (input->getComponentType() == Geometry::TYPE_POLYGON); // extrusion working variables double targetLen = -DBL_MAX; osg::Vec3d minLoc(DBL_MAX, DBL_MAX, DBL_MAX); double minLoc_len = DBL_MAX; osg::Vec3d maxLoc(0,0,0); double maxLoc_len = 0; // Initial pass over the geometry does two things: // 1: Calculate the minimum Z across all parts. // 2: Establish a "target length" for extrusion double absHeight = fabs(height); ConstGeometryIterator zfinder( input ); while( zfinder.hasMore() ) { const Geometry* geom = zfinder.next(); for( Geometry::const_iterator m = geom->begin(); m != geom->end(); ++m ) { osg::Vec3d m_point = *m; if ( m_point.z() + absHeight > targetLen ) targetLen = m_point.z() + absHeight; if (m_point.z() < minLoc.z()) minLoc = m_point; if (m_point.z() > maxLoc.z()) maxLoc = m_point; } } // apply the height offsets height -= heightOffset; targetLen -= heightOffset; float roofRotation = 0.0f; Bounds roofBounds; float sinR = 0.0f, cosR = 0.0f; double roofTexSpanX = 0.0, roofTexSpanY = 0.0; osg::ref_ptr<const SpatialReference> roofProjSRS; if ( roofSkin ) { roofBounds = input->getBounds(); // if our data is lat/long, we need to reproject the geometry and the bounds into a projected // coordinate system in order to properly generate tex coords. if ( srs && srs->isGeographic() ) { osg::Vec2d geogCenter = roofBounds.center2d(); roofProjSRS = srs->createUTMFromLonLat( Angle(geogCenter.x()), Angle(geogCenter.y()) ); if ( roofProjSRS.valid() ) { roofBounds.transform( srs, roofProjSRS.get() ); osg::ref_ptr<Geometry> projectedInput = input->clone(); srs->transform( projectedInput->asVector(), roofProjSRS.get() ); roofRotation = getApparentRotation( projectedInput.get() ); } } else { roofRotation = getApparentRotation( input ); } sinR = sin(roofRotation); cosR = cos(roofRotation); if ( !roofSkin->isTiled().value() ) { //note: non-tiled roofs don't really work atm. roofTexSpanX = cosR*roofBounds.width() - sinR*roofBounds.height(); roofTexSpanY = sinR*roofBounds.width() + cosR*roofBounds.height(); } else { roofTexSpanX = roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : 10.0; if ( roofTexSpanX <= 0.0 ) roofTexSpanX = 10.0; roofTexSpanY = roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : 10.0; if ( roofTexSpanY <= 0.0 ) roofTexSpanY = 10.0; } } // prep for wall texture coordinate generation. double texWidthM = wallSkin ? *wallSkin->imageWidth() : 0.0; double texHeightM = wallSkin ? *wallSkin->imageHeight() : 1.0; ConstGeometryIterator iter( input ); while( iter.hasMore() ) { const Geometry* part = iter.next(); // skip a part that's too small if (part->size() < 2) continue; // add a new wall. structure.elevations.push_back(Elevation()); Elevation& elevation = structure.elevations.back(); double maxHeight = targetLen - minLoc.z(); // Adjust the texture height so it is a multiple of the maximum height double div = osg::round(maxHeight / texHeightM); elevation.texHeightAdjustedM = div > 0.0 ? maxHeight / div : maxHeight; // Step 1 - Create the real corners and transform them into our target SRS. Corners corners; for(Geometry::const_iterator m = part->begin(); m != part->end(); ++m) { Corners::iterator corner = corners.insert(corners.end(), Corner()); // mark as "from source", as opposed to being inserted by the algorithm. corner->isFromSource = true; corner->base = *m; // extrude: if ( height >= 0 ) // extrude up { if ( flatten ) corner->roof.set( corner->base.x(), corner->base.y(), targetLen ); else corner->roof.set( corner->base.x(), corner->base.y(), corner->base.z() + height ); } else // height < 0 .. extrude down { corner->roof = *m; corner->base.z() += height; } // figure out the rooftop texture coords before doing any transformation: if ( roofSkin && srs ) { double xr, yr; if ( srs && srs->isGeographic() && roofProjSRS ) { osg::Vec3d projRoofPt; srs->transform( corner->roof, roofProjSRS.get(), projRoofPt ); xr = (projRoofPt.x() - roofBounds.xMin()); yr = (projRoofPt.y() - roofBounds.yMin()); } else { xr = (corner->roof.x() - roofBounds.xMin()); yr = (corner->roof.y() - roofBounds.yMin()); } corner->roofTexU = (cosR*xr - sinR*yr) / roofTexSpanX; corner->roofTexV = (sinR*xr + cosR*yr) / roofTexSpanY; } // transform into target SRS. transformAndLocalize( corner->base, srs, corner->base, mapSRS, _world2local, makeECEF ); transformAndLocalize( corner->roof, srs, corner->roof, mapSRS, _world2local, makeECEF ); } // Step 2 - Insert intermediate Corners as needed to satify texturing // requirements (if necessary) and record each corner offset (horizontal distance // from the beginning of the part geometry to the corner.) double cornerOffset = 0.0; double nextTexBoundary = texWidthM; for(Corners::iterator c = corners.begin(); c != corners.end(); ++c) { Corners::iterator this_corner = c; Corners::iterator next_corner = c; if ( ++next_corner == corners.end() ) next_corner = corners.begin(); osg::Vec3d base_vec = next_corner->base - this_corner->base; double span = base_vec.length(); this_corner->offsetX = cornerOffset; if (wallSkin) { base_vec /= span; // normalize osg::Vec3d roof_vec = next_corner->roof - this_corner->roof; roof_vec.normalize(); while(nextTexBoundary < cornerOffset+span) { // insert a new fake corner. Corners::iterator new_corner = corners.insert(next_corner, Corner()); new_corner->isFromSource = false; double advance = nextTexBoundary-cornerOffset; new_corner->base = this_corner->base + base_vec*advance; new_corner->roof = this_corner->roof + roof_vec*advance; new_corner->offsetX = cornerOffset + advance; nextTexBoundary += texWidthM; // advance the main iterator c = new_corner; } } cornerOffset += span; } // Step 3 - Calculate the angle of each corner. osg::Vec3d prev_vec; for(Corners::iterator c = corners.begin(); c != corners.end(); ++c) { Corners::const_iterator this_corner = c; Corners::const_iterator next_corner = c; if ( ++next_corner == corners.end() ) next_corner = corners.begin(); if ( this_corner == corners.begin() ) { Corners::const_iterator prev_corner = corners.end(); --prev_corner; prev_vec = this_corner->roof - prev_corner->roof; prev_vec.normalize(); } osg::Vec3d this_vec = next_corner->roof - this_corner->roof; this_vec.normalize(); if ( c != corners.begin() ) { c->cosAngle = prev_vec * this_vec; } } // Step 4 - Create faces connecting each pair of Posts. Faces& faces = elevation.faces; for(Corners::const_iterator c = corners.begin(); c != corners.end(); ++c) { Corners::const_iterator this_corner = c; Corners::const_iterator next_corner = c; if ( ++next_corner == corners.end() ) next_corner = corners.begin(); // only close the shape for polygons. if (next_corner != corners.begin() || structure.isPolygon) { faces.push_back(Face()); Face& face = faces.back(); face.left = *this_corner; face.right = *next_corner; // recalculate the final offset on the last face if ( next_corner == corners.begin() ) { osg::Vec3d vec = next_corner->roof - this_corner->roof; face.right.offsetX = face.left.offsetX + vec.length(); } face.widthM = next_corner->offsetX - this_corner->offsetX; } } } return true; }
bool ExtrudeGeometryFilter::extrudeGeometry(const Geometry* input, double height, double heightOffset, bool flatten, osg::Geometry* walls, osg::Geometry* roof, osg::Geometry* base, osg::Geometry* outline, const osg::Vec4& wallColor, const osg::Vec4& wallBaseColor, const osg::Vec4& roofColor, const osg::Vec4& outlineColor, const SkinResource* wallSkin, const SkinResource* roofSkin, FilterContext& cx ) { bool makeECEF = false; const SpatialReference* srs = 0L; const SpatialReference* mapSRS = 0L; if ( cx.isGeoreferenced() ) { srs = cx.extent()->getSRS(); makeECEF = cx.getSession()->getMapInfo().isGeocentric(); mapSRS = cx.getSession()->getMapInfo().getProfile()->getSRS(); } bool made_geom = false; double tex_width_m = wallSkin ? *wallSkin->imageWidth() : 1.0; double tex_height_m = wallSkin ? *wallSkin->imageHeight() : 1.0; bool tex_repeats_y = wallSkin ? *wallSkin->isTiled() : false; bool useColor = (!wallSkin || wallSkin->texEnvMode() != osg::TexEnv::DECAL) && !_makeStencilVolume; bool isPolygon = input->getComponentType() == Geometry::TYPE_POLYGON; unsigned pointCount = input->getTotalPointCount(); // If we are extruding a polygon, and applying a wall texture, we need an extra // point in the geometry in order to close the polygon and generate a unique // texture coordinate for that final point. bool isSkinnedPolygon = isPolygon && wallSkin != 0L; // Total number of verts. Add 2 to close a polygon (necessary so the first and last // points can have unique texture coordinates) unsigned numWallVerts = 2 * pointCount + (isSkinnedPolygon? (2 * input->getNumGeometries()) : 0); // create all the OSG geometry components osg::Vec3Array* verts = new osg::Vec3Array( numWallVerts ); walls->setVertexArray( verts ); osg::Vec2Array* wallTexcoords = 0L; if ( wallSkin ) { wallTexcoords = new osg::Vec2Array( numWallVerts ); walls->setTexCoordArray( 0, wallTexcoords ); } osg::Vec4Array* colors = 0L; if ( useColor ) { // per-vertex colors are necessary if we are going to use the MeshConsolidator -gw colors = new osg::Vec4Array(); colors->reserve( numWallVerts ); colors->assign( numWallVerts, wallColor ); walls->setColorArray( colors ); walls->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); } // set up rooftop tessellation and texturing, if necessary: osg::Vec3Array* roofVerts = 0L; osg::Vec2Array* roofTexcoords = 0L; float roofRotation = 0.0f; Bounds roofBounds; float sinR = 0.0f, cosR = 0.0f; double roofTexSpanX = 0.0, roofTexSpanY = 0.0; osg::ref_ptr<const SpatialReference> roofProjSRS; if ( roof ) { roofVerts = new osg::Vec3Array( pointCount ); roof->setVertexArray( roofVerts ); // per-vertex colors are necessary if we are going to use the MeshConsolidator -gw if ( useColor ) { osg::Vec4Array* roofColors = new osg::Vec4Array(); roofColors->reserve( pointCount ); roofColors->assign( pointCount, roofColor ); roof->setColorArray( roofColors ); roof->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); } if ( roofSkin ) { roofTexcoords = new osg::Vec2Array( pointCount ); roof->setTexCoordArray( 0, roofTexcoords ); // Get the orientation of the geometry. This is a hueristic that will help // us align the roof skin texture properly. TODO: make this optional? It makes // sense for buildings and such, but perhaps not for all extruded shapes. roofRotation = getApparentRotation( input ); roofBounds = input->getBounds(); // if our data is lat/long, we need to reproject the geometry and the bounds into a projected // coordinate system in order to properly generate tex coords. if ( srs && srs->isGeographic() ) { osg::Vec2d geogCenter = roofBounds.center2d(); roofProjSRS = srs->createUTMFromLonLat( Angular(geogCenter.x()), Angular(geogCenter.y()) ); roofBounds.transform( srs, roofProjSRS.get() ); osg::ref_ptr<Geometry> projectedInput = input->clone(); srs->transform( projectedInput->asVector(), roofProjSRS.get() ); roofRotation = getApparentRotation( projectedInput.get() ); } else { roofRotation = getApparentRotation( input ); } sinR = sin(roofRotation); cosR = cos(roofRotation); if ( !roofSkin->isTiled().value() ) { //note: doesn't really work roofTexSpanX = cosR*roofBounds.width() - sinR*roofBounds.height(); roofTexSpanY = sinR*roofBounds.width() + cosR*roofBounds.height(); } else { roofTexSpanX = roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : 10.0; if ( roofTexSpanX <= 0.0 ) roofTexSpanX = 10.0; roofTexSpanY = roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : 10.0; if ( roofTexSpanY <= 0.0 ) roofTexSpanY = 10.0; } } } osg::Vec3Array* baseVerts = NULL; if ( base ) { baseVerts = new osg::Vec3Array( pointCount ); base->setVertexArray( baseVerts ); } osg::Vec3Array* outlineVerts = 0L; osg::Vec3Array* outlineNormals = 0L; if ( outline ) { outlineVerts = new osg::Vec3Array( numWallVerts ); outline->setVertexArray( outlineVerts ); osg::Vec4Array* outlineColors = new osg::Vec4Array(); outlineColors->reserve( numWallVerts ); outlineColors->assign( numWallVerts, outlineColor ); outline->setColorArray( outlineColors ); outline->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); // cop out, just point all the outline normals up. fix this later. outlineNormals = new osg::Vec3Array(); outlineNormals->reserve( numWallVerts ); outlineNormals->assign( numWallVerts, osg::Vec3(0,0,1) ); outline->setNormalArray( outlineNormals ); } unsigned wallVertPtr = 0; unsigned roofVertPtr = 0; unsigned baseVertPtr = 0; double targetLen = -DBL_MAX; osg::Vec3d minLoc(DBL_MAX, DBL_MAX, DBL_MAX); double minLoc_len = DBL_MAX; osg::Vec3d maxLoc(0,0,0); double maxLoc_len = 0; // Initial pass over the geometry does two things: // 1: Calculate the minimum Z across all parts. // 2: Establish a "target length" for extrusion double absHeight = fabs(height); ConstGeometryIterator zfinder( input ); while( zfinder.hasMore() ) { const Geometry* geom = zfinder.next(); for( Geometry::const_iterator m = geom->begin(); m != geom->end(); ++m ) { osg::Vec3d m_point = *m; if ( m_point.z() + absHeight > targetLen ) targetLen = m_point.z() + absHeight; if (m_point.z() < minLoc.z()) minLoc = m_point; if (m_point.z() > maxLoc.z()) maxLoc = m_point; } } // apply the height offsets height -= heightOffset; targetLen -= heightOffset; // now generate the extruded geometry. ConstGeometryIterator iter( input ); while( iter.hasMore() ) { const Geometry* part = iter.next(); double tex_height_m_adj = tex_height_m; unsigned wallPartPtr = wallVertPtr; unsigned roofPartPtr = roofVertPtr; unsigned basePartPtr = baseVertPtr; double partLen = 0.0; double maxHeight = 0.0; maxHeight = targetLen - minLoc.z(); // Adjust the texture height so it is a multiple of the maximum height double div = osg::round(maxHeight / tex_height_m); if (div == 0) div = 1; //Prevent divide by zero tex_height_m_adj = maxHeight / div; //osg::DrawElementsUShort* idx = new osg::DrawElementsUShort( GL_TRIANGLES ); osg::DrawElementsUInt* idx = new osg::DrawElementsUInt( GL_TRIANGLES ); for( Geometry::const_iterator m = part->begin(); m != part->end(); ++m ) { osg::Vec3d basePt = *m; osg::Vec3d roofPt; if ( height >= 0 ) { if ( flatten ) roofPt.set( basePt.x(), basePt.y(), targetLen ); else roofPt.set( basePt.x(), basePt.y(), basePt.z() + height ); } else // height < 0 { roofPt = *m; basePt.z() += height; } // add to the approprate vertex lists: int p = wallVertPtr; // figure out the rooftop texture coordinates before doing any // transformations: if ( roofSkin && roofProjSRS && srs ) { double xr, yr; if ( srs && srs->isGeographic() ) { osg::Vec3d projRoofPt; srs->transform( roofPt, roofProjSRS.get(), projRoofPt ); xr = (projRoofPt.x() - roofBounds.xMin()); yr = (projRoofPt.y() - roofBounds.yMin()); } else { xr = (roofPt.x() - roofBounds.xMin()); yr = (roofPt.y() - roofBounds.yMin()); } float u = (cosR*xr - sinR*yr) / roofTexSpanX; float v = (sinR*xr + cosR*yr) / roofTexSpanY; (*roofTexcoords)[roofVertPtr].set( u, v ); } transformAndLocalize( basePt, srs, basePt, mapSRS, _world2local, makeECEF ); transformAndLocalize( roofPt, srs, roofPt, mapSRS, _world2local, makeECEF ); if ( base ) { (*baseVerts)[baseVertPtr] = basePt; } if ( roof ) { (*roofVerts)[roofVertPtr] = roofPt; } baseVertPtr++; roofVertPtr++; (*verts)[p] = roofPt; (*verts)[p+1] = basePt; if ( useColor ) { (*colors)[p+1] = wallBaseColor; } if ( outline ) { (*outlineVerts)[p] = roofPt; (*outlineVerts)[p+1] = basePt; } partLen += wallVertPtr > wallPartPtr ? ((*verts)[p] - (*verts)[p-2]).length() : 0.0; double h = tex_repeats_y ? -((*verts)[p] - (*verts)[p+1]).length() : -tex_height_m_adj; if ( wallSkin ) { (*wallTexcoords)[p].set( partLen/tex_width_m, 0.0f ); (*wallTexcoords)[p+1].set( partLen/tex_width_m, h/tex_height_m_adj ); } // form the 2 triangles if ( (m+1) == part->end() ) { if ( isPolygon ) { // end of the wall; loop around to close it off. if ( isSkinnedPolygon ) { // if we requested an extra geometry point, that means we are generating // a polygon-closing line so we can have a unique texcoord for it. idx->push_back(wallVertPtr); idx->push_back(wallVertPtr+1); idx->push_back(wallVertPtr+2); idx->push_back(wallVertPtr+1); idx->push_back(wallVertPtr+3); idx->push_back(wallVertPtr+2); (*verts)[p+2] = (*verts)[wallPartPtr]; (*verts)[p+3] = (*verts)[wallPartPtr+1]; if ( wallSkin ) { partLen += ((*verts)[p+2] - (*verts)[p]).length(); double h = tex_repeats_y ? -((*verts)[p+2] - (*verts)[p+3]).length() : -tex_height_m_adj; (*wallTexcoords)[p+2].set( partLen/tex_width_m, 0.0f ); (*wallTexcoords)[p+3].set( partLen/tex_width_m, h/tex_height_m_adj ); } wallVertPtr += 2; } else { // either not a poly, or no wall skin, so we can share the polygon-closing // loop point. idx->push_back(wallVertPtr); idx->push_back(wallVertPtr+1); idx->push_back(wallPartPtr); idx->push_back(wallVertPtr+1); idx->push_back(wallPartPtr+1); idx->push_back(wallPartPtr); } } else { //nop - no elements required at the end of a line } } else { idx->push_back(wallVertPtr); idx->push_back(wallVertPtr+1); idx->push_back(wallVertPtr+2); idx->push_back(wallVertPtr+1); idx->push_back(wallVertPtr+3); idx->push_back(wallVertPtr+2); } wallVertPtr += 2; made_geom = true; } walls->addPrimitiveSet( idx ); if ( roof ) { roof->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINE_LOOP, roofPartPtr, roofVertPtr - roofPartPtr ) ); } if ( base ) { // reverse the base verts: int len = baseVertPtr - basePartPtr; for( int i=basePartPtr; i<len/2; i++ ) std::swap( (*baseVerts)[i], (*baseVerts)[basePartPtr+(len-1)-i] ); base->addPrimitiveSet( new osg::DrawArrays( osg::PrimitiveSet::LINE_LOOP, basePartPtr, baseVertPtr - basePartPtr ) ); } if ( outline ) { unsigned len = baseVertPtr - basePartPtr; GLenum roofLineMode = isPolygon ? GL_LINE_LOOP : GL_LINE_STRIP; osg::DrawElementsUInt* roofLine = new osg::DrawElementsUInt( roofLineMode ); roofLine->reserveElements( len ); for( unsigned i=0; i<len; ++i ) roofLine->addElement( basePartPtr + i*2 ); outline->addPrimitiveSet( roofLine ); // if the outline is tessellated, we only want outlines on the original // points (not the inserted points) unsigned step = std::max( 1u, _outlineSymbol->tessellation().isSet() ? *_outlineSymbol->tessellation() : 1u ); osg::DrawElementsUInt* wallLines = new osg::DrawElementsUInt( GL_LINES ); wallLines->reserve( len*2 ); for( unsigned i=0; i<len; i+=step ) { wallLines->push_back( basePartPtr + i*2 ); wallLines->push_back( basePartPtr + i*2 + 1 ); } outline->addPrimitiveSet( wallLines ); applyLineSymbology( outline->getOrCreateStateSet(), _outlineSymbol.get() ); } } return made_geom; }
void ScatterFilter::polyScatter(const Geometry* input, const SpatialReference* inputSRS, const FilterContext& context, PointSet* output ) { Bounds bounds; double areaSqKm = 0.0; ConstGeometryIterator iter( input, false ); while( iter.hasMore() ) { const Polygon* polygon = dynamic_cast<const Polygon*>( iter.next() ); if ( !polygon ) continue; if ( /*context.isGeocentric() ||*/ context.profile()->getSRS()->isGeographic() ) { bounds = polygon->getBounds(); double avglat = bounds.yMin() + 0.5*bounds.height(); double h = bounds.height() * 111.32; double w = bounds.width() * 111.32 * sin( 1.57079633 + osg::DegreesToRadians(avglat) ); areaSqKm = w * h; } else if ( context.profile()->getSRS()->isProjected() ) { bounds = polygon->getBounds(); areaSqKm = (0.001*bounds.width()) * (0.001*bounds.height()); } double zMin = 0.0; unsigned numInstancesInBoundingRect = (unsigned)(areaSqKm * (double)osg::clampAbove( 0.1f, _density )); if ( numInstancesInBoundingRect == 0 ) continue; if ( _random ) { // Random scattering. Note, we try to place as many instances as would // fit in the bounding rectangle; The real placed number will be less since // we only place models inside the actual polygon. But the density will // be correct. for( unsigned j=0; j<numInstancesInBoundingRect; ++j ) { double x = bounds.xMin() + _prng.next() * bounds.width(); double y = bounds.yMin() + _prng.next() * bounds.height(); bool include = true; if ( include && polygon->contains2D( x, y ) ) output->push_back( osg::Vec3d(x, y, zMin) ); } } else { // regular interval scattering: double numInst1D = sqrt((double)numInstancesInBoundingRect); double ar = bounds.width() / bounds.height(); unsigned cols = (unsigned)( numInst1D * ar ); unsigned rows = (unsigned)( numInst1D / ar ); double colInterval = bounds.width() / (double)(cols-1); double rowInterval = bounds.height() / (double)(rows-1); double interval = 0.5*(colInterval+rowInterval); for( double cy=bounds.yMin(); cy<=bounds.yMax(); cy += interval ) { for( double cx = bounds.xMin(); cx <= bounds.xMax(); cx += interval ) { bool include = true; if ( include && polygon->contains2D( cx, cy ) ) output->push_back( osg::Vec3d(cx, cy, zMin) ); } } } } }