Skip to content

Commit

Permalink
Add feature mutli-styling using the select symbol; feature_multi_styl…
Browse files Browse the repository at this point in the history
…ing.earth demo file
  • Loading branch information
gwaldron committed Jan 28, 2025
1 parent f1e9d1d commit 34b7415
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 37 deletions.
78 changes: 42 additions & 36 deletions src/osgEarth/FeatureStyleSorter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,51 +93,57 @@ FeatureStyleSorter::sort_usingSelectors(
{
Feature* feature = itr->get();

const std::string& styleString = feature->eval(styleExprCopy, &context);
if (!styleString.empty() && styleString != "null")
const std::string& delimitedStyleStrings = feature->eval(styleExprCopy, &context);
if (!delimitedStyleStrings.empty() && delimitedStyleStrings != "null")
{
// resolve the style:
const Style* resolved_style = nullptr;
int resolved_index = 0;
std::vector<std::string> styleStrings;
StringTokenizer(delimitedStyleStrings, styleStrings, ",", "", false, true);

// if the style string begins with an open bracket, it's an inline style definition.
if (styleString.length() > 0 && styleString[0] == '{')
for (auto& styleString : styleStrings)
{
Config conf("style", styleString);
conf.setReferrer(sel.styleExpression().get().uriContext().referrer());
conf.set("type", "text/css");
auto& literal_style_and_index = literal_styles[conf.toJSON()];
if (literal_style_and_index.first.empty())
// resolve the style:
const Style* resolved_style = nullptr;
int resolved_index = 0;

// if the style string begins with an open bracket, it's an inline style definition.
if (styleString.length() > 0 && styleString[0] == '{')
{
literal_style_and_index.first = Style(conf);
// literal styles always come AFTER sheet styles
literal_style_and_index.second = literal_styles.size() + session->styles()->getStyles().size();
Config conf("style", styleString);
conf.setReferrer(sel.styleExpression().get().uriContext().referrer());
conf.set("type", "text/css");
auto& literal_style_and_index = literal_styles[conf.toJSON()];
if (literal_style_and_index.first.empty())
{
literal_style_and_index.first = Style(conf);
// literal styles always come AFTER sheet styles
literal_style_and_index.second = literal_styles.size() + session->styles()->getStyles().size();
}
resolved_style = &literal_style_and_index.first;
resolved_index = literal_style_and_index.second;
}
resolved_style = &literal_style_and_index.first;
resolved_index = literal_style_and_index.second;
}

// otherwise, look up the style in the stylesheet. Do NOT fall back on a default
// style in this case: for style expressions, the user must be explicit about
// default styling; this is because there is no other way to exclude unwanted
// features.
else
{
auto style_and_index = session->styles()->getStyleAndIndex(styleString);

//const Style* selected_style = session->styles()->getStyle(styleString, false);
if (style_and_index.first)
// otherwise, look up the style in the stylesheet. Do NOT fall back on a default
// style in this case: for style expressions, the user must be explicit about
// default styling; this is because there is no other way to exclude unwanted
// features.
else
{
resolved_style = style_and_index.first;
resolved_index = style_and_index.second;
auto style_and_index = session->styles()->getStyleAndIndex(styleString);

//const Style* selected_style = session->styles()->getStyle(styleString, false);
if (style_and_index.first)
{
resolved_style = style_and_index.first;
resolved_index = style_and_index.second;
}
}
}

if (resolved_style)
{
auto& bucket = style_buckets[resolved_index];
bucket.first = resolved_style;
bucket.second.emplace_back(feature);
if (resolved_style)
{
auto& bucket = style_buckets[resolved_index];
bucket.first = resolved_style;
bucket.second.emplace_back(feature);
}
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/osgEarth/StyleSheet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,14 +215,16 @@ StyleSheet::Options::fromConfig(const Config& conf)

auto_script << "// __oe_auto__\n";
auto_script << "function __oe_select_style() {\n";
auto_script << " var combo = '';\n";
}

auto_script << " if (" << selector_symbol->predicate().get() << ") return \"" << style.getName() << "\";\n";
auto_script << " if (" << selector_symbol->predicate().get() << ") combo = combo + '" << style.getName() << ",';\n";
}
}

if (auto_selector)
{
auto_script << " if (combo.length > 0) return combo.substring(0, combo.length-1);\n";
auto_script << " return 'default';\n}\n";
auto new_code = auto_script.str();

Expand Down
74 changes: 74 additions & 0 deletions tests/feature_multi_styling.earth
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<!--
By using include "select" symbols in the Roads layer, you can select multiple
styles for each feature. Styles render in the order of appearance. The "select"
symbol can take any javascript expression.

This demo will render all road backgrounds first, and then the roads
themselves afterwards for a nice outlining effect.
-->

<map name="Demo: feature mulit-styling">

<XYZFeatures name="osm-roads">
<url>https://readymap.org/readymap/mbtiles/daylight-v1.2/{z}/{x}/{-y}.pbf</url>
<min_level>14</min_level>
<max_level>14</max_level>
<profile>spherical-mercator</profile>
<format>pbf</format>
<fid_attribute>@id</fid_attribute>
<filters>
<attributes>highway</attributes>
</filters>
</XYZFeatures>

<TMSImage name="Imagery">
<url>https://readymap.org/readymap/tiles/1.0.0/7</url>
</TMSImage>

<FeatureImage name="Roads" min_level="14" max_data_level="19">
<tile_size>512</tile_size>
<features>osm-roads</features>
<buffer_width>25m</buffer_width>
<styles>
<style type="text/css">
minor_road_outline {
select: !is_major();
stroke: #ffffff;
stroke-width: 15m;
}
major_road_outline {
select: is_major();
stroke: #ffffff;
stroke-width: 20m;
}
minor_road {
select: !is_major();
stroke: #3f3f3f;
stroke-width: 10m;
}
major_road {
select: is_major();
stroke: #1f1f1f;
stroke-width: 15m;
}
</style>
<script><![CDATA[
function is_major() {
var v = feature.properties.highway;
return v === 'trunk' || v === 'primary' || v === 'motorway' || v === 'secondary' || v === 'tertiary';
}
]]>
</script>
</styles>
</FeatureImage>

<Viewpoints home="0" time="1">
<viewpoint name="Annandale">
<heading>-3.2425e-07</heading>
<pitch>-89</pitch>
<range>2928.14m</range>
<long>-77.19548706709453</long>
<lat>38.83147622799055</lat>
</viewpoint>
</Viewpoints>
</map>

0 comments on commit 34b7415

Please sign in to comment.