osgearth StringUtils

/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
 * Copyright 2019 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
#ifndef OSGEARTH_STRING_UTILS_H
#define OSGEARTH_STRING_UTILS_H 1

#include <osgEarth/Common>
#include <osg/Vec3>
#include <osg/Vec3d>
#include <osg/Vec4>
#include <osg/Vec4ub>
#include <string>
#include <algorithm>
#include <vector>
#include <sstream>
#include <locale>
#include <iomanip>
#include <map>
#include <set>
#include <ctype.h>

namespace osgEarth
{
    extern OSGEARTH_EXPORT const std::string EMPTY_STRING;

    typedef std::vector<std::string> StringVector;
    typedef std::set<std::string> StringSet;

    /** Replaces all the instances of "pattern" with "replacement" in "in_out" */
    extern OSGEARTH_EXPORT std::string& replaceIn(
        std::string&       in_out,
        const std::string& pattern,
        const std::string& replacement );

    /** Replaces all the instances of "pattern" with "replacement" in "in_out" (case-insensitive) */
    extern OSGEARTH_EXPORT std::string& ciReplaceIn(
        std::string&       in_out,
        const std::string& pattern,
        const std::string& replacement );

    /**
     * Trims whitespace from the ends of a string.
     */
    extern OSGEARTH_EXPORT std::string trim( const std::string& in );

    /**
     * Trims whitespace from the ends of a string; in-place modification on the string to reduce string copies.
     */
    extern OSGEARTH_EXPORT void trim2( std::string& str );

    //! Removes leading and trailing whitespace, and replaces all other
    //! whitespace with single spaces
    extern OSGEARTH_EXPORT std::string trimAndCompress(const std::string& in);

    /**
     * True is "ref" starts with "pattern"
     */
    extern OSGEARTH_EXPORT bool startsWith(
        const std::string& ref,
        const std::string& pattern,
        bool               caseSensitive =true,
        const std::locale& locale        =std::locale() );

    /**
     * True is "ref" ends with "pattern"
     */
    extern OSGEARTH_EXPORT bool endsWith(
        const std::string& ref,
        const std::string& pattern,
        bool               caseSensitive =true,
        const std::locale& locale        =std::locale() );

    /**
     * Case-insensitive compare
     */
    extern OSGEARTH_EXPORT bool ciEquals(
        const std::string& lhs,
        const std::string& rhs,
        const std::locale& local = std::locale() );

    /**
     * Case-insensitive STL comparator
     */
    struct OSGEARTH_EXPORT CIStringComp {
        bool operator()(const std::string& lhs, const std::string& rhs) const;
    };


    extern OSGEARTH_EXPORT std::string joinStrings( const StringVector& input, char delim );

    /** Returns a lower-case version of the input string. */
    extern OSGEARTH_EXPORT std::string toLower( const std::string& input );

    /** Parses a color string in the form "255 255 255 255" (r g b a [0..255]) into an OSG color. */
    extern OSGEARTH_EXPORT osg::Vec4ub stringToColor(const std::string& str, osg::Vec4ub default_value);

    /** Creates a string in the form "255 255 255 255" (r g b a [0..255]) from a color */
    extern OSGEARTH_EXPORT std::string colorToString( const osg::Vec4ub& c );

    /** Converts a string to a vec3f */
    extern OSGEARTH_EXPORT osg::Vec3f stringToVec3f( const std::string& str, const osg::Vec3f& default_value );

    /** Converts a vec3f to a string */
    extern OSGEARTH_EXPORT std::string vec3fToString( const osg::Vec3f& v );

    /** Parses an HTML color ("#rrggbb" or "#rrggbbaa") into an OSG color. */
    extern OSGEARTH_EXPORT osg::Vec4f htmlColorToVec4f( const std::string& html );

    /** Makes an HTML color ("#rrggbb" or "#rrggbbaa") from an OSG color. */
    extern OSGEARTH_EXPORT std::string vec4fToHtmlColor( const osg::Vec4f& c );

    /** Makes a valid filename out of a string */
    extern OSGEARTH_EXPORT std::string toLegalFileName( const std::string& input, bool allowSubdir=false );

    /** Generates a hashed integer for a string (poor man's MD5) */
    extern OSGEARTH_EXPORT unsigned hashString( const std::string& input );

    /** Same as hashString but returns a string value. */
    extern OSGEARTH_EXPORT std::string hashToString(const std::string& input);

    /**
    * Gets the total number of seconds formatted as H:M:S
    */
    extern OSGEARTH_EXPORT std::string prettyPrintTime( double seconds );

    /**
    * Gets a pretty printed version of the given size in MB.
    */
    extern OSGEARTH_EXPORT std::string prettyPrintSize( double mb );

    //------------------------------------------------------------------------
    // conversion templates

    // converts a string to primitive using serialization
    template<typename T> inline T
    as( const std::string& str, const T& default_value )
    {
        T temp = default_value;
        std::istringstream strin( str );
        if ( !strin.eof() ) strin >> temp;
        return temp;
    }

    // template specialization for integers (to handle hex)
#define AS_INT_DEC_OR_HEX(TYPE) \
    template<> inline TYPE \
    as< TYPE >(const std::string& str, const TYPE & dv) { \
        TYPE temp = dv; \
        std::istringstream strin( trim(str) ); \
        if ( !strin.eof() ) { \
            if ( str.length() >= 2 && str[0] == '0' && str[1] == 'x' ) { \
                strin.seekg( 2 ); \
                strin >> std::hex >> temp; \
            } \
            else { \
                strin >> temp; \
            } \
        } \
        return temp; \
    }

    AS_INT_DEC_OR_HEX(int)
    AS_INT_DEC_OR_HEX(unsigned)
    AS_INT_DEC_OR_HEX(short)
    AS_INT_DEC_OR_HEX(unsigned short)
    AS_INT_DEC_OR_HEX(long)
    AS_INT_DEC_OR_HEX(unsigned long)

    // template specialization for a bool
    template<> inline bool
    as<bool>( const std::string& str, const bool& default_value )
    {
        std::string temp = toLower(str);
        return
            temp == "true"  || temp == "yes" || temp == "on" ? true :
            temp == "false" || temp == "no" || temp == "off" ? false :
            default_value;
    }

    template<> inline osg::Vec3f
    as<osg::Vec3f>( const std::string& str, const osg::Vec3f& default_value )
    {
        return stringToVec3f(str, default_value);
    }

    // template specialization for string
    template<> inline std::string
    as<std::string>( const std::string& str, const std::string& default_value )
    {
        return str;
    }

    // snips a substring and parses it.
    template<typename T> inline bool
    as(const std::string& in, unsigned start, unsigned len, T default_value)
    {
        std::string buf;
        std::copy( in.begin()+start, in.begin()+start+len, std::back_inserter(buf) );
        return as<T>(buf, default_value);
    }

    // converts a primitive to a string
    // TODO: precision??
    template<typename T> inline std::string
    toString(const T& value)
    {
        std::stringstream out;
		//out << std::setprecision(20) << std::fixed << value;
		out << std::setprecision(20) <<  value;
        std::string outStr;
        outStr = out.str();
        return outStr;
    }

    // template speciallization for a bool to print out "true" or "false"
    template<> inline std::string
    toString<bool>(const bool& value)
    {
        return value ? "true" : "false";
    }

    template<> inline std::string
    toString<osg::Vec3f>(const osg::Vec3f& value)
    {
        return vec3fToString(value);
    }

    /**
     * Assembles and returns an inline string using a stream-like << operator.
     * Example:
     *     std::string str = Stringify() << "Hello, world " << variable;
     */
    struct Stringify
    {
        operator std::string () const
        {
            std::string result;
            result = buf.str();
            return result;
        }

        template<typename T>
        Stringify& operator << (const T& val) { buf << val; return (*this); }

        Stringify& operator << (const Stringify& val) { buf << (std::string)val; return (*this); }

    protected:
        std::stringstream buf;
    };

    template<> inline
    Stringify& Stringify::operator << <bool>(const bool& val) { buf << (val ? "true" : "false"); return (*this); }

    template<> inline
    Stringify& Stringify::operator << <osg::Vec3f>(const osg::Vec3f& val) {
        buf << val.x() << " " << val.y() << " " << val.z(); return (*this); }

    template<> inline
    Stringify& Stringify::operator << <osg::Vec3d>(const osg::Vec3d& val ) {
        buf << val.x() << " " << val.y() << " " << val.z(); return (*this); }

    template<> inline
    Stringify& Stringify::operator << <osg::Vec4f>(const osg::Vec4f& val) {
        buf << val.r() << " " << val.g() << " " << val.b() << " " << val.a(); return (*this); }

    /**
     * Splits a string up into a vector of strings based on a set of
     * delimiters, quotes, and rules.
     */
    class OSGEARTH_EXPORT StringTokenizer
    {
    public:
        StringTokenizer( const std::string& delims =" \t\r\n", const std::string& quotes ="'\"" );

        StringTokenizer(
            const std::string& input, StringVector& output,
            const std::string& delims =" \t\r\n", const std::string& quotes ="'\"",
            bool keepEmpties =true, bool trimTokens =true);

        void tokenize( const std::string& input, StringVector& output ) const;

        bool& keepEmpties() { return _allowEmpties; }

        bool& trimTokens() { return _trimTokens; }

        void addDelim( char delim, bool keepAsToken =false );

        void addDelims( const std::string& delims, bool keepAsTokens =false );

        void addQuote( char delim, bool keepInToken =false );

        void addQuotes( const std::string& delims, bool keepInTokens =false );

    private:
        typedef std::map<char,bool> TokenMap;
        TokenMap _delims;
        TokenMap _quotes;
        bool     _allowEmpties;
        bool     _trimTokens;
    };
}

#endif // OSGEARTH_STRING_UTILS_H



/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
 * Copyright 2019 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

#include <osgEarth/StringUtils>
#include <cctype>

#include <cstring>

using namespace osgEarth;

StringTokenizer::StringTokenizer( const std::string& delims, const std::string& quotes ) :
_allowEmpties( true ),
_trimTokens  ( true )
{
    addDelims( delims );
    addQuotes( quotes );
}

StringTokenizer::StringTokenizer(const std::string& input,
                                 StringVector&      output,
                                 const std::string& delims,
                                 const std::string& quotes,
                                 bool               allowEmpties,
                                 bool               trimTokens ) :
_allowEmpties( allowEmpties ),
_trimTokens  ( trimTokens )
{
    addDelims( delims );
    addQuotes( quotes );
    tokenize( input, output );
}

void
StringTokenizer::addDelim( char delim, bool keep )
{
    _delims[delim] = keep;
}

void
StringTokenizer::addDelims( const std::string& delims, bool keep )
{
    for( unsigned i=0; i<delims.size(); ++i )
        addDelim( delims[i], keep );
}

void
StringTokenizer::addQuote( char quote, bool keep )
{
    _quotes[quote] = keep;
}

void
StringTokenizer::addQuotes( const std::string& quotes, bool keep )
{
    for( unsigned i=0; i<quotes.size(); ++i )
        addQuote( quotes[i], keep );
}

void
StringTokenizer::tokenize( const std::string& input, StringVector& output ) const
{
    output.clear();

    std::stringstream buf;
    bool quoted = false;
    char lastQuoteChar = '\0';

    for( std::string::const_iterator i = input.begin(); i != input.end(); ++i )
    {
        char c = *i;

        TokenMap::const_iterator q = _quotes.find( c );

        if ( quoted )
        {
            if( q != _quotes.end() && lastQuoteChar == c)
            {
                quoted = false;
                lastQuoteChar = '\0';
                if ( q->second )
                    buf << c;
            }
            else
            {
                buf << c;
            }
        }
        else
        {
            if ( q != _quotes.end() )
            {
                quoted = true;
                lastQuoteChar = c;
                if ( q->second )
                    buf << c;
            }
            else
            {
                TokenMap::const_iterator d = _delims.find( c );
                if ( d == _delims.end() )
                {
                    buf << c;
                }
                else
                {
                    std::string token = buf.str();
                    if ( _trimTokens )
                        trim2( token );

                    if ( _allowEmpties || !token.empty() )
                        output.push_back( token );

                    if ( d->second == true )
                    {
                        output.push_back( std::string(1, c) );
                    }

                    buf.str("");
                }
            }
        }
    }

    std::string bufstr = buf.str();
    if ( _trimTokens )
        trim2( bufstr );
    if ( !bufstr.empty() )
        output.push_back( bufstr );
}

//--------------------------------------------------------------------------

const std::string osgEarth::EMPTY_STRING;

std::string
osgEarth::toLegalFileName(const std::string& input, bool allowSubdirs)
{
    // See: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282
    // We omit '-' so we can use it for the HEX identifier.
    static const std::string legalWithoutSubdirs("ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvwxyz0123456789_.");
    static const std::string legalWithDirs      ("ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvwxyz0123456789_./");

    
    std::size_t pos = input.find("://");
    pos = pos == std::string::npos ? 0 : pos+3;

    const std::string& legal = allowSubdirs? legalWithDirs : legalWithoutSubdirs;

    std::stringstream buf;
    for( ; pos < input.size(); ++pos )
    {
        std::string::const_reference c = input[pos];
        if (legal.find(c) != std::string::npos)
            buf << c;
        else
            buf << "-" << std::hex << static_cast<unsigned>(c) << "-";
    }

    std::string result;
    result = buf.str();

    return result;
}

/** MurmurHash 2.0 (http://sites.google.com/site/murmurhash/) */
unsigned
osgEarth::hashString( const std::string& input )
{
    const unsigned int m = 0x5bd1e995;
    const int r = 24;
    unsigned int len = input.length();
    const char* data = input.c_str();
    unsigned int h = m ^ len; // using "m" as the seed.

    while(len >= 4)
    {
        unsigned int k = *(unsigned int *)data;
        k *= m;
        k ^= k >> r;
        k *= m;
        h *= m;
        h ^= k;
        data += 4;
        len -= 4;
    }

    switch(len)
    {
    case 3: h ^= data[2] << 16;
    case 2: h ^= data[1] << 8;
    case 1: h ^= data[0];
        h *= m;
    };

    h ^= h >> 13;
    h *= m;
    h ^= h >> 15;

    return h;
}

std::string
osgEarth::hashToString(const std::string& input)
{
    return Stringify() << std::hex << std::setw(8) << std::setfill('0') << hashString(input);
}


/** Parses an HTML color ("#rrggbb" or "#rrggbbaa") into an OSG color. */
osg::Vec4f
osgEarth::htmlColorToVec4f( const std::string& html )
{
    std::string t = osgEarth::toLower(html);
    osg::Vec4ub c(0,0,0,255);
    if ( t.length() >= 7 ) {
        c.r() |= t[1]<='9' ? (t[1]-'0')<<4 : (10+(t[1]-'a'))<<4;
        c.r() |= t[2]<='9' ? (t[2]-'0')    : (10+(t[2]-'a'));
        c.g() |= t[3]<='9' ? (t[3]-'0')<<4 : (10+(t[3]-'a'))<<4;
        c.g() |= t[4]<='9' ? (t[4]-'0')    : (10+(t[4]-'a'));
        c.b() |= t[5]<='9' ? (t[5]-'0')<<4 : (10+(t[5]-'a'))<<4;
        c.b() |= t[6]<='9' ? (t[6]-'0')    : (10+(t[6]-'a'));
        if ( t.length() == 9 ) {
            c.a() = 0;
            c.a() |= t[7]<='9' ? (t[7]-'0')<<4 : (10+(t[7]-'a'))<<4;
            c.a() |= t[8]<='9' ? (t[8]-'0')    : (10+(t[8]-'a'));
        }
    }
    return osg::Vec4f( ((float)c.r())/255.0f, ((float)c.g())/255.0f, ((float)c.b())/255.0f, ((float)c.a())/255.0f );
}

/** Makes an HTML color ("#rrggbb" or "#rrggbbaa") from an OSG color. */
std::string
osgEarth::vec4fToHtmlColor( const osg::Vec4f& c )
{
    std::stringstream buf;
    buf << "#";
    buf << std::hex << std::setw(2) << std::setfill('0') << (int)(c.r()*255.0f);
    buf << std::hex << std::setw(2) << std::setfill('0') << (int)(c.g()*255.0f);
    buf << std::hex << std::setw(2) << std::setfill('0') << (int)(c.b()*255.0f);
    if ( c.a() < 1.0f )
        buf << std::hex << std::setw(2) << std::setfill('0') << (int)(c.a()*255.0f);
    std::string ssStr;
    ssStr = buf.str();
    return ssStr;
}

/** Parses a color string in the form "255 255 255 255" (r g b a [0..255]) into an OSG color. */
osg::Vec4ub
osgEarth::stringToColor(const std::string& str, osg::Vec4ub default_value)
{
    osg::Vec4ub color = default_value;
    std::istringstream strin(str);
    int r, g, b, a;
    if (strin >> r && strin >> g && strin >> b && strin >> a)
    {
        color.r() = (unsigned char)r;
        color.g() = (unsigned char)g;
        color.b() = (unsigned char)b;
        color.a() = (unsigned char)a;
    }
    return color;
}

/** Creates a string in the form "255 255 255 255" (r g b a [0..255]) from a color */
std::string
osgEarth::colorToString( const osg::Vec4ub& c )
{
    std::stringstream ss;
    ss << (int)c.r() << " " << (int)c.g() << " " << (int)c.b() << " " << (int)c.a();
    std::string ssStr;
    ssStr = ss.str();
    return ssStr;
}

/** Converts a string to a vec3f */
osg::Vec3f
osgEarth::stringToVec3f( const std::string& str, const osg::Vec3f& default_value )
{
    std::stringstream buf(str);
    osg::Vec3f out = default_value;
    buf >> out.x();
    if ( !buf.eof() ) {
        buf >> out.y() >> out.z();
    }
    else {
        out.y() = out.x();
        out.z() = out.x();
    }
    return out;
}

/** Converts a vec3f to a string */
std::string
osgEarth::vec3fToString( const osg::Vec3f& v )
{
    std::stringstream buf;
    buf << std::setprecision(6)
        << v.x() << " " << v.y() << " " << v.z()
        << std::endl;
    std::string result;
    result = buf.str();
    return result;
}


/** Replaces all the instances of "sub" with "other" in "s". */
std::string&
osgEarth::replaceIn( std::string& s, const std::string& sub, const std::string& other)
{
    if ( sub.empty() ) return s;
    size_t b=0;
    for( ; ; )
    {
        b = s.find( sub, b );
        if ( b == s.npos ) break;
        s.replace( b, sub.size(), other );
        b += other.size();
    }
    return s;
}

std::string&
osgEarth::ciReplaceIn( std::string& s, const std::string& pattern, const std::string& replacement )
{
    if ( pattern.empty() ) return s;

    std::string upperSource = s;
    std::transform( upperSource.begin(), upperSource.end(), upperSource.begin(), (int(*)(int))std::toupper );

    std::string upperPattern = pattern;
    std::transform( upperPattern.begin(), upperPattern.end(), upperPattern.begin(), (int(*)(int))std::toupper );

    for( size_t b = 0; ; )
    {
        b = upperSource.find( upperPattern, b );
        if ( b == s.npos ) break;
        s.replace( b, pattern.size(), replacement );
        upperSource.replace( b, upperPattern.size(), replacement );
        b += replacement.size();
    }

    return s;
}

/**
* Trims whitespace from the ends of a string.
* by Rodrigo C F Dias
* http://www.codeproject.com/KB/stl/stdstringtrim.aspx
*/
void
osgEarth::trim2( std::string& str )
{
    static const std::string whitespace (" \t\f\v\n\r");
    std::string::size_type pos = str.find_last_not_of( whitespace );
    if(pos != std::string::npos) {
        str.erase(pos + 1);
        pos = str.find_first_not_of( whitespace );
        if(pos != std::string::npos) str.erase(0, pos);
    }
    else str.erase(str.begin(), str.end());
}

/**
* Trims whitespace from the ends of a string, returning a
* copy of the string with whitespace removed.
*/
std::string
osgEarth::trim( const std::string& in )
{
    std::string str = in;
    trim2( str );
    return str;
}

std::string
osgEarth::trimAndCompress(const std::string& in)
{
    bool inwhite = true;
    std::stringstream buf;
    for (unsigned i = 0; i < in.length(); ++i)
    {
        char c = in[i];
        if (::isspace(c))
        {
            if (!inwhite)
            {
                buf << ' ';
                inwhite = true;
            }
        }
        else
        {
            inwhite = false;
            buf << c;
        }
    }
    std::string r;
    r = buf.str();
    trim2(r);
    return r;
}

std::string
osgEarth::joinStrings( const StringVector& input, char delim )
{
    std::stringstream buf;
    for( StringVector::const_iterator i = input.begin(); i != input.end(); ++i )
    {
        buf << *i;
        if ( (i+1) != input.end() ) buf << delim;
    }
    std::string result;
    result = buf.str();
    return result;
}

/** Returns a lower-case version of the input string. */
std::string
osgEarth::toLower( const std::string& input )
{
    std::string output = input;
    std::transform( output.begin(), output.end(), output.begin(), ::tolower );
    return output;
}

std::string
osgEarth::prettyPrintTime( double seconds )
{
    int hours = (int)floor(seconds / (3600.0) );
    seconds -= hours * 3600.0;

    int minutes = (int)floor(seconds/60.0);
    seconds -= minutes * 60.0;

    std::stringstream buf;
    buf << hours << ":" << minutes << ":" << seconds;
    return buf.str();
}

std::string
osgEarth::prettyPrintSize( double mb )
{
    std::stringstream buf;
    // Convert to terabytes
    if ( mb > 1024 * 1024 )
    {
        buf << (mb / (1024.0*1024.0)) << " TB";
    }
    else if (mb > 1024)
    {
        buf << (mb / 1024.0) << " GB";
    }
    else
    {
        buf << mb << " MB";
    }
    return buf.str();
}


namespace
{
    template<typename charT>
    struct ci_equal {
        ci_equal( const std::locale& loc ) : _loc(loc) { }
        bool operator()(charT c1, charT c2) {
            return std::toupper(c1,_loc) == std::toupper(c2,_loc);
        }
        const std::locale& _loc;
    };
}

bool
osgEarth::ciEquals(const std::string& lhs, const std::string& rhs, const std::locale& loc )
{
    if ( lhs.length() != rhs.length() )
        return false;

    for( unsigned i=0; i<lhs.length(); ++i )
    {
        if ( std::toupper(lhs[i], loc) != std::toupper(rhs[i], loc) )
            return false;
    }

    return true;
}

#if defined(WIN32) && !defined(__CYGWIN__)
#  define STRICMP ::stricmp
#else
#  define STRICMP ::strcasecmp
#endif

bool CIStringComp::operator()(const std::string& lhs, const std::string& rhs) const
{
    return STRICMP( lhs.c_str(), rhs.c_str() ) < 0;
}

bool
osgEarth::startsWith( const std::string& ref, const std::string& pattern, bool caseSensitive, const std::locale& loc )
{
    if ( pattern.length() > ref.length() )
        return false;

    if ( caseSensitive )
    {
        for( unsigned i=0; i<pattern.length(); ++i )
        {
            if ( ref[i] != pattern[i] )
                return false;
        }
    }
    else
    {
        for( unsigned i=0; i<pattern.length(); ++i )
        {
            if ( std::toupper(ref[i], loc) != std::toupper(pattern[i],loc) )
                return false;
        }
    }
    return true;
}

bool
osgEarth::endsWith( const std::string& ref, const std::string& pattern, bool caseSensitive, const std::locale& loc )
{
    if ( pattern.length() > ref.length() )
        return false;

    unsigned offset = ref.size()-pattern.length();
    if ( caseSensitive )
    {
        for( unsigned i=0; i < pattern.length(); ++i )
        {
            if ( ref[i+offset] != pattern[i] )
                return false;
        }
    }
    else
    {
        for( unsigned i=0; i < pattern.length(); ++i )
        {
            if ( std::toupper(ref[i+offset], loc) != std::toupper(pattern[i],loc) )
                return false;
        }
    }
    return true;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
osgEarth 是一个用于地球渲染和地理空间应用程序的开源软件工具包。要在 osgEarth 中实现地图比例尺,可以使用 osgEarth::Util::Controls::Label 控件。以下是一个示例代码: ```cpp #include <osgEarth/MapNode> #include <osgEarthUtil/Controls> // 创建一个 osgEarth 的地图节点 osg::ref_ptr<osgEarth::MapNode> mapNode = osgEarth::MapNode::create(map); // 创建一个 Label 控件 osgEarth::Util::Controls::Label* label = new osgEarth::Util::Controls::Label(); label->setHorizAlign(osgEarth::Util::Controls::Control::ALIGN_LEFT); label->setVertAlign(osgEarth::Util::Controls::Control::ALIGN_BOTTOM); label->setFontSize(14.0f); label->setFontColor(osgEarth::Util::Controls::Color::White); label->setMargin(10.0f); label->setPadding(5.0f); // 将 Label 控件添加到 osgEarth 的地图节点中 osgEarth::Util::Controls::ControlCanvas* canvas = osgEarth::Util::Controls::ControlCanvas::get(mapNode); canvas->addControl(label); // 设置 Label 控件的文本内容为地图比例尺 osgbEarth::Util::EarthManipulator* manip = dynamic_cast<osgEarth::Util::EarthManipulator*>(viewer->getCameraManipulator()); double scale = manip->getDistance() / osgEarth::Units::METERS_PER_KILOMETER; label->setText("比例尺 1:" + osgEarth::StringUtils::toString((int)scale)); ``` 该代码创建了一个 Label 控件并将其添加到 osgEarth 的地图节点中。然后,它使用 EarthManipulator 类计算当前地图比例尺,并将其设置为 Label 控件的文本内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值