Skip to content

Commit

Permalink
Add inline style selection to stylesheets
Browse files Browse the repository at this point in the history
  • Loading branch information
gwaldron committed Dec 12, 2024
1 parent 8bccef7 commit b075051
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 8 deletions.
25 changes: 23 additions & 2 deletions src/osgEarth/StyleSelector
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace osgEarth
{
public:
/** Constructs a style selector */
StyleSelector( const Config& conf =Config() );
StyleSelector(const Config& conf = Config());

/** Constructs a style selector with a name and expression */
StyleSelector(const std::string& name, const StringExpression& expr);
Expand Down Expand Up @@ -64,7 +64,7 @@ namespace osgEarth
std::string getSelectedStyleName() const;

//Configurable
virtual void mergeConfig( const Config& conf );
virtual void mergeConfig(const Config& conf);
virtual Config getConfig() const;

protected:
Expand All @@ -74,6 +74,27 @@ namespace osgEarth
optional<Query> _query;
};

class OSGEARTH_EXPORT SelectorSymbol : public Symbol
{
public:
META_Object(osgEarth, SelectorSymbol);

SelectorSymbol(const Config& conf = {});
SelectorSymbol(const SelectorSymbol& rhs, const osg::CopyOp& op);

//! A predicate function (in a scripting language) that returns true if the style should be selected.
OE_OPTION(std::string, predicate);

public:
Config getConfig() const override;
void mergeConfig(const Config& conf) override;
static void parseSLD(const Config& c, class Style& style);

protected:

virtual ~SelectorSymbol() { }
};

} // namespace osgEarth


Expand Down
41 changes: 41 additions & 0 deletions src/osgEarth/StyleSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,44 @@ StyleSelector::getConfig() const
conf.set( "query", _query );
return conf;
}



OSGEARTH_REGISTER_SIMPLE_SYMBOL(select, SelectorSymbol);

SelectorSymbol::SelectorSymbol(const Config& conf) :
Symbol(conf)
{
mergeConfig(conf);
}

SelectorSymbol::SelectorSymbol(const SelectorSymbol& rhs, const osg::CopyOp& copy) :
Symbol(rhs, copy),
_predicate(rhs._predicate)
{
//nop
}

Config
SelectorSymbol::getConfig() const
{
auto conf = Symbol::getConfig();
conf.key() = "selector";
conf.set("predicate", predicate());
return conf;
}

void
SelectorSymbol::mergeConfig(const Config& conf)
{
conf.get("predicate", predicate());
}

void
SelectorSymbol::parseSLD(const Config& c, Style& style)
{
if (match(c.key(), "select") || match(c.key(), "select-if"))
{
style.getOrCreate<SelectorSymbol>()->predicate() = c.value();
}
}
67 changes: 61 additions & 6 deletions src/osgEarth/StyleSheet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ StyleSheet::Options::getConfig() const
Config conf = Layer::Options::getConfig();

conf.remove("selector");
for (StyleSelectors::const_iterator i = selectors().begin();
i != selectors().end();
++i)
for(auto& selector_pair : selectors())
{
conf.add("selector", i->second.getConfig());
if (selector_pair.first != "__oe_auto") // do not save the auto-gen one
{
conf.add("selector", selector_pair.second.getConfig());
}
}

conf.remove("style");
Expand Down Expand Up @@ -72,10 +73,19 @@ StyleSheet::Options::getConfig() const
scriptConf.set("language", _script->language);
if (_script->uri.isSet())
scriptConf.set("url", _script->uri->base());
//if (!_script->profile.empty())
// scriptConf.set("profile", _script->profile);
else if (!_script->code.empty())
{
auto code = _script->code;

// remove the auto-gen code
auto pos = code.find("// __oe_auto__");
if (pos != std::string::npos)
{
code = code.substr(0, pos);
}

scriptConf.setValue(_script->code);
}

conf.add(scriptConf);
}
Expand Down Expand Up @@ -184,6 +194,51 @@ StyleSheet::Options::fromConfig(const Config& conf)
_styles[style.getName()] = style;
}
}

// finally, parse each style and see if it contains a "select" symbol.
// if so, automatically add a selector and a script to support each one.
StyleSelector* auto_selector = nullptr;
std::stringstream auto_script;

for (auto& style_entry : _styles)
{
auto& style = style_entry.second;
auto* selector_symbol = style.get<SelectorSymbol>();
if (selector_symbol)
{
if (!auto_selector)
{
auto_selector = new StyleSelector();
auto_selector->name() = "__oe_auto";
auto_selector->styleExpression() = StringExpression("__oe_select_style()");
_selectors[auto_selector->name().get()] = *auto_selector;

auto_script << "// __oe_auto__\n";
auto_script << "function __oe_select_style() {\n";
}

auto_script << " if (" << selector_symbol->predicate().get() << ") return \"" << style.getName() << "\";\n";
}
}

if (auto_selector)
{
auto_script << " return 'default';\n}\n";
auto new_code = auto_script.str();

if (!_script)
{
_script = new ScriptDef();
_script->language = "javascript";
_script->code = new_code;
}
else
{
_script->code = _script->code + "\n\n" + new_code;
}

//OE_INFO << LC << "Generated script:\n" << new_code << std::endl;
}
}

//...................................................................
Expand Down
63 changes: 63 additions & 0 deletions tests/feature_select_symbol.earth
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!--
osgEarth Sample
Demonstrates how to select a style name using javascript.
-->

<map name="Demo: style selectors">

<options>
<terrain color="#004d8f"/>
</options>

<OGRFeatures name="country-data">
<url>../data/world.shp</url>
<filters>
<buffer distance="-0.05"/>
</filters>
</OGRFeatures>

<FeatureModel name="countries" features="country-data" pickable="true">
<styles>
<style type="text/css">
base {
altitude-clamping: terrain-drape;
render-backface-culling: false;
}

p1 : base {
select: between(feature.properties.pop, 0, 14045470);
fill: #ff3f3f;
}

p2 : base {
select: between(feature.properties.pop, 14045470, 43410900);
fill: #3fff3f;
}

p3 : base {
select: between(feature.properties.pop, 43410900, 97228750);
fill: #ff8f00;
}

p4 : base {
select: between(feature.properties.pop, 97228750, 258833000);
fill: #ff3fff;
}

default : base {
fill: #ffff3f;
}
</style>

<script language="javascript">
<![CDATA[
function between(val_as_string, low, high) {
var v = parseFloat(val_as_string);
return low <= v && v < high;
}
]]>
</script>
</styles>
</FeatureModel>

</map>

0 comments on commit b075051

Please sign in to comment.