GeoExtent Profile::clampAndTransformExtent( const GeoExtent& input, bool* out_clamped ) const { if ( out_clamped ) *out_clamped = false; // do the clamping in LAT/LONG. const SpatialReference* geo_srs = getSRS()->getGeographicSRS(); // get the input in lat/long: GeoExtent gcs_input = input.getSRS()->isGeographic()? input : input.transform( geo_srs ); // bail out on a bad transform: if ( !gcs_input.isValid() ) return GeoExtent::INVALID; // bail out if the extent's do not intersect at all: if ( !gcs_input.intersects(_latlong_extent, false) ) return GeoExtent::INVALID; // clamp it to the profile's extents: GeoExtent clamped_gcs_input = GeoExtent( gcs_input.getSRS(), osg::clampBetween( gcs_input.xMin(), _latlong_extent.xMin(), _latlong_extent.xMax() ), osg::clampBetween( gcs_input.yMin(), _latlong_extent.yMin(), _latlong_extent.yMax() ), osg::clampBetween( gcs_input.xMax(), _latlong_extent.xMin(), _latlong_extent.xMax() ), osg::clampBetween( gcs_input.yMax(), _latlong_extent.yMin(), _latlong_extent.yMax() ) ); if ( out_clamped ) *out_clamped = (clamped_gcs_input != gcs_input); // finally, transform the clamped extent into this profile's SRS and return it. GeoExtent result = clamped_gcs_input.getSRS()->isEquivalentTo( this->getSRS() )? clamped_gcs_input : clamped_gcs_input.transform( this->getSRS() ); if (result.isValid()) { OE_DEBUG << LC << "clamp&xform: input=" << input.toString() << ", output=" << result.toString() << std::endl; } return result; }
//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; }