osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthFeatures/BuildTextOperator.cpp

Go to the documentation of this file.
00001 /* -*-c++-*- */
00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
00003  * Copyright 2008-2010 Pelican Mapping
00004  * http://osgearth.org
00005  *
00006  * osgEarth is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU Lesser General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>
00018  */
00019 #include <osgEarthFeatures/BuildTextOperator>
00020 #include <osgEarth/Utils>
00021 #include <osgDB/ReadFile>
00022 #include <osgDB/ReaderWriter>
00023 #include <osg/Geometry>
00024 #include <osg/MatrixTransform>
00025 #include <osg/Material>
00026 #include <osg/Geode>
00027 #include <osg/Version>
00028 #include <osg/Geometry>
00029 #include <osg/LineWidth>
00030 #include <osg/Point>
00031 #include <osg/Depth>
00032 #include <osg/PolygonOffset>
00033 #include <osg/ClusterCullingCallback>
00034 #include <osgText/Text>
00035 #include <osgUtil/IntersectionVisitor>
00036 #include <osgUtil/PolytopeIntersector>
00037 
00038 #define LC "[BuildTextOperator] "
00039 
00040 using namespace osgEarth::Symbology;
00041 using namespace osgEarth::Features;
00042 
00043 // Parse a content string and replace attributes (marked with delim) with their value
00044 // eg. "City [name], [state]" -> "City San Fransisco, California" if delim is "[]"
00045 static std::string parseAttributes(Feature*           feature, 
00046                                    const std::string& content,
00047                                    const std::string& delim)
00048 {
00049     if (delim.size()<2)
00050         return content;
00051 
00052     std::string out;
00053     size_t pos = 0;
00054     while(true) {
00055         // Search for an attribute delimiter
00056         size_t d = content.find_first_of(delim[0], pos);
00057         if (d==std::string::npos) {
00058             // No more delimiters? Add remaining text and we're done
00059             out += content.substr(pos);
00060             break;
00061         }
00062         // An attribute was found!
00063         // Add non-attribute text
00064         out += content.substr(pos, d-pos);
00065         pos = d+1;
00066         // Extract attribute name
00067         d = content.find_first_of(delim[1], pos);
00068         if (d==std::string::npos) {
00069             // No more delimiters? Add remaining text and we're done
00070             out += content.substr(pos);
00071             break;
00072         }
00073         // Add attribute and continue
00074         out += feature->getString( content.substr(pos, d-pos) );
00075         pos = d+1;
00076     }
00077 
00078     return out;
00079 }
00080 
00081 osg::Node* BuildTextOperator::operator()(const FeatureList&   features, 
00082                                          const TextSymbol*    symbol,
00083                                          const FilterContext& context)
00084 {
00085     if (!symbol) return 0;
00086 
00087     std::set< std::string > labelNames;
00088 
00089     bool removeDuplicateLabels = symbol->removeDuplicateLabels().isSet() ? symbol->removeDuplicateLabels().get() : false;
00090 
00091     StringExpression contentExpr = *symbol->content();
00092 
00093     osg::Geode* result = new osg::Geode;
00094     for (FeatureList::const_iterator itr = features.begin(); itr != features.end(); ++itr)
00095     {
00096         Feature* feature = itr->get();
00097         if (!feature->getGeometry()) continue;
00098 
00099         std::string text;
00100         if (symbol->content().isSet())
00101         {
00102             //Get the text from the specified content and referenced attributes
00103             text = feature->eval( contentExpr );
00104         }
00105 
00106         if (text.empty()) continue;
00107 
00108         //See if there is a duplicate name
00109         if (removeDuplicateLabels && labelNames.find(text) != labelNames.end()) continue;
00110 
00111         bool rotateToScreen = symbol->rotateToScreen().isSet() ? symbol->rotateToScreen().value() : false;
00112 
00113         // find the centroid
00114         osg::Vec3d position;
00115         osg::Quat orientation;
00116 
00117         GeometryIterator gi( feature->getGeometry() );
00118         while( gi.hasMore() )
00119         {
00120             Geometry* geom = gi.next();
00121 
00122             TextSymbol::LinePlacement linePlacement = symbol->linePlacement().isSet() ? symbol->linePlacement().get() : TextSymbol::LINEPLACEMENT_ALONG_LINE;
00123             if (geom->getType() == Symbology::Geometry::TYPE_LINESTRING && linePlacement == TextSymbol::LINEPLACEMENT_ALONG_LINE)
00124             {
00125                 //Compute the "middle" of the line string
00126                 LineString* lineString = static_cast<LineString*>(geom);
00127                 double length = lineString->getLength();
00128                 double center = length / 2.0;
00129                 osg::Vec3d start, end;
00130                 if (lineString->getSegment(center, start, end))
00131                 {
00132                     TextSymbol::LineOrientation lineOrientation = symbol->lineOrientation().isSet() ? symbol->lineOrientation().get() : TextSymbol::LINEORIENTATION_HORIZONTAL;
00133 
00134                     position = (end + start) / 2.0;
00135                     //We don't want to orient the text at all if we are rotating to the screen
00136                     if (!rotateToScreen && lineOrientation != TextSymbol::LINEORIENTATION_HORIZONTAL)
00137                     {
00138                         osg::Vec3d dir = (end-start);
00139                         dir.normalize();
00140 
00141                         if (lineOrientation == TextSymbol::LINEORIENTATION_PERPENDICULAR)
00142                         {
00143                             osg::Vec3d up(0,0,1);
00144                             const SpatialReference* srs = context.profile()->getSRS();
00145                             if (srs && context.isGeocentric() && srs->getEllipsoid())
00146                             {
00147                                 osg::Vec3d w = context.toWorld( position );
00148                                 up = srs->getEllipsoid()->computeLocalUpVector(w.x(), w.y(), w.z());
00149                             }
00150                             dir = up ^ dir;
00151                         }
00152                         orientation.makeRotate(osg::Vec3d(1,0,0), dir);
00153                     }                
00154                 }
00155                 else
00156                 {
00157                     //Fall back on using the center
00158                     position = lineString->getBounds().center();
00159                 }
00160             }
00161             else
00162             {
00163                 position = geom->getBounds().center();
00164             }
00165         }
00166 
00167         osgText::Text* t = new osgText::Text();
00168         t->setText( text );
00169 
00170         std::string font = "fonts/arial.ttf";
00171         if (symbol->font().isSet() && !symbol->font().get().empty())
00172         {
00173             font = symbol->font().value();
00174         }
00175 
00176         t->setFont( font );
00177         t->setAutoRotateToScreen( rotateToScreen );
00178 
00179         TextSymbol::SizeMode sizeMode = symbol->sizeMode().isSet() ? symbol->sizeMode().get() : TextSymbol::SIZEMODE_SCREEN;
00180         if (sizeMode == TextSymbol::SIZEMODE_SCREEN) {
00181             t->setCharacterSizeMode( osgText::TextBase::SCREEN_COORDS );
00182         }
00183         else if (sizeMode == TextSymbol::SIZEMODE_OBJECT) {
00184             t->setCharacterSizeMode( osgText::TextBase::OBJECT_COORDS );
00185         }
00186         float size = symbol->size().isSet() ? symbol->size().get() : 32.0f;
00187         t->setCharacterSize( size );
00188         /*
00189         //TODO:  We need to do something to account for autotransformed text that is under a LOD.  Setting the initial bound works sometimes but not all the time.
00190         if (rotateToScreen)
00191         {
00192         //Set the initial bound so that OSG will traverse the text even if it's under an LOD.
00193         osg::BoundingBox bb;
00194         bb.expandBy( osg::BoundingSphere(position, size));
00195         t->setInitialBound( bb);
00196         }*/
00197         //t->setCharacterSizeMode( osgText::TextBase::OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT );
00198         //t->setCharacterSize( 300000.0f );
00199         t->setPosition( position );
00200         t->setRotation( orientation);
00201         t->setAlignment( osgText::TextBase::CENTER_CENTER );
00202         t->getOrCreateStateSet()->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS), osg::StateAttribute::ON );
00203         t->getOrCreateStateSet()->setRenderBinDetails( 99999, "RenderBin", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS );
00204 
00205         // apply styling as appropriate:
00206         osg::Vec4f textColor = symbol->fill()->color();
00207         osg::Vec4f haloColor = symbol->halo()->color();
00208 
00209         t->setColor( textColor );
00210         t->setBackdropColor( haloColor );
00211         t->setBackdropType( osgText::Text::OUTLINE );
00212 
00213         if ( context.isGeocentric() )
00214         {
00215             // install a cluster culler
00216             t->setCullCallback( new CullDrawableByNormal(position * context.inverseReferenceFrame()) );
00217         }
00218 
00219         if (_hideClutter)
00220         {
00221             osg::BoundingBox tBound = t->getBound();
00222             osg::ref_ptr<osgUtil::PolytopeIntersector> intersector = new osgUtil::PolytopeIntersector(osgUtil::Intersector::MODEL, tBound.xMin(), tBound.yMin(), tBound.xMax(), tBound.yMax());
00223             osgUtil::IntersectionVisitor intersectVisitor(intersector.get());
00224             result->accept(intersectVisitor);
00225 
00226             if (!intersector->containsIntersections())
00227             {
00228                 result->addDrawable( t );
00229                 if (removeDuplicateLabels) labelNames.insert(text);
00230             }
00231         }
00232         else
00233         {
00234             result->addDrawable( t );
00235             if (removeDuplicateLabels) labelNames.insert(text);
00236         }
00237     }
00238     return result;
00239 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines