Skip to content
This repository has been archived by the owner on Jan 12, 2024. It is now read-only.

allow auto and none keywords for symbolizers #477

Merged
merged 1 commit into from
Jul 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ For Mapnik XML all character data as tag content is now prefixed with CDATA.
([#377](https://github.com/mapbox/carto/issues/377))
* carto now accepts custom references for validating rules (part of [#413](https://github.com/mapbox/carto/issues/413))
* The JavaScript API has been documented ([#479](https://github.com/mapbox/carto/issues/479))
* New symbolizer rules (work on the whole symbolizer) enable control of symbolizer serialization. Write e.g. `line: none` to suppress output
of the line symbolizer for this definition. Write e.g. `marker: auto` to output a markers symbolizer with default values. Symbolizer rules
are considered an advanced features and are never inherited to other definitions ([#477](https://github.com/mapbox/carto/pull/477)).

### Breaking changes

Expand Down
39 changes: 35 additions & 4 deletions docs/language_elements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,40 @@ Mapnik >= 3.0.0 supports variables of the form ``@var``. These can be used from

For this to have any effect you have to pass the variables to Mapnik at render time in a hashmap of the form ``variable_name:variable_value``.

Resetting properties
====================
Controlling output of symbolizers and symbolizer attributes
===========================================================

You can reset properties by specifying them with the keyword ``none``. They will then be removed from the symbolizer thus using their default value
or not using them at all. This does not work or makes sense for all properties like e.g. not for ``text-face-name``. For an overview over properties
You can control symbolizer output by using rules that work on the whole symbolizer. E.g. ``line`` works on the
line symbolizer. By using the keywords ``none`` or ``auto`` you can either suppress the symbolizer or output it with
default values. The keyword ``auto`` does not work on shield and text symbolizers because they have attributes without
default values. Here is an example how this works::

#layer {
line: none;
line-width: 2;
[feature = 'redfeature'] {
line-color: red;
}
[feature = 'bluefeature'] {
line-color: blue;
}
}

Without ``line: none`` carto would output a line symbolizer with default values for all features
other than redfeature and bluefeature, that is a black line with width 1. In contrast, you can
quickly output a symbolizer with default value by using ``auto``::

#layer {
[feature = 'quickfeature'] {
marker: auto;
}
}

This outputs a default markers symbolizer for all quickfeature features.

You can also control the output of individual symbolizer properties by specifying them with the keyword ``none`` e.g. ``line-color: none``.
They will then be removed from the symbolizer thus using their default value
or not using them at all. This does not work or makes sense for all properties like e.g. not for ``text-face-name`` as it does not have a default value. For an overview over properties
where this works or makes sense see `this list <https://github.com/mapbox/carto/blob/master/test/rendering-mss/issue_214.mss>`_.
In this case the use of ``none`` and ``auto`` is equivalent. In both cases the default
value will be used as Mapnik uses the default value automatically when the property is not present.
33 changes: 29 additions & 4 deletions lib/carto/tree/definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var assert = require('assert'),
// }
//
// The selector can have filters
tree.Definition = function Definition(selector, rules) {
tree.Definition = function Definition(env, selector, rules) {
this.elements = selector.elements;
assert.ok(selector.filters instanceof tree.Filterset);
this.rules = rules;
Expand All @@ -24,6 +24,7 @@ tree.Definition = function Definition(selector, rules) {
this.attachment = selector.attachment || '__default__';
this.specificity = selector.specificity();
this.matchCount = 0;
this.ref = env.ref;

// special handling for Map selector
if (_.isArray(this.elements) && this.elements.length > 0 &&
Expand All @@ -47,15 +48,18 @@ tree.Definition.prototype.clone = function(filters) {
clone.ruleIndex = _.clone(this.ruleIndex);
clone.filters = filters ? filters : this.filters.clone();
clone.attachment = this.attachment;
clone.ref = this.ref;
return clone;
};

tree.Definition.prototype.addRules = function(rules) {
var added = 0;

// Add only unique rules.
for (var i = 0; i < rules.length; i++) {
if (!this.ruleIndex[rules[i].id]) {
// only add rule if not for whole symbolizer (default)
// and if unique
if (this.ref.selectorName(rules[i].name) !== 'default' &&
!this.ruleIndex[rules[i].id]) {
this.rules.push(rules[i]);
this.ruleIndex[rules[i].id] = true;
added++;
Expand Down Expand Up @@ -118,6 +122,7 @@ tree.Definition.prototype.symbolizersToObject = function(env, symbolizers, zoom)
var sym_count = 0;

for (var i = 0; i < sym_order.length; i++) {
var doNotSerialize = false;
var attributes = symbolizers[sym_order[i]];
var symbolizer = sym_order[i].split('/').pop();

Expand Down Expand Up @@ -159,8 +164,24 @@ tree.Definition.prototype.symbolizersToObject = function(env, symbolizers, zoom)
tagcontent = attributes[j].ev(env).toObject(env, true);
} else {
var attr = attributes[j].ev(env);
// if we have a rule for the whole symbolizer (default) with keyword none
// don't output the rule
if (env.ref.selectorName(attr.name) === 'default' &&
attr.value.value[0].is === 'keyword') {
if (attr.value.value[0].value === 'none') {
doNotSerialize = true;
// still call toObject for validation
attr.toObject(env);
}
else if (attr.value.value[0].value === 'auto') {
// still call toObject for validation
attr.toObject(env);
}
}

if (!(attr.value.value[0].is === 'keyword' && attr.value.value[0].value === 'none')) {
// only output attributes that don't use the keywords none and auto
if (!(attr.value.value[0].is === 'keyword' &&
(attr.value.value[0].value === 'none' || attr.value.value[0].value === 'auto'))) {
symbolizerAttr.push(attr.toObject(env));
}
else {
Expand All @@ -174,6 +195,10 @@ tree.Definition.prototype.symbolizersToObject = function(env, symbolizers, zoom)
}
});

if (doNotSerialize) {
continue;
}

if (symbolizerAttr.length) {
var attrObj = {};

Expand Down
12 changes: 12 additions & 0 deletions lib/carto/tree/reference.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,18 @@ function generateRequiredProperties(data) {
}

tree.Reference.prototype.requiredProperties = function (symbolizer_name, rules) {
var that = this,
doNotSerialize = false;

_.forEach(rules, function (rule) {
if (that.selectorName(rule.name) === 'default') {
doNotSerialize = true;
}
});
if (doNotSerialize) {
return null;
}

var req = this.required_cache[symbolizer_name];
for (var i in req) {
if (!(req[i] in rules)) {
Expand Down
2 changes: 1 addition & 1 deletion lib/carto/tree/ruleset.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ tree.Ruleset.prototype = {
if (index !== false) {
selectors[i].index = index;
}
result.push(new tree.Definition(selectors[i], rules.slice()));
result.push(new tree.Definition(env, selectors[i], rules.slice()));
}

return result;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"hsluv": "~0.0.1",
"js-yaml": "~3.8.4",
"lodash": "~4.17.2",
"mapnik-reference": "~8.7.0",
"mapnik-reference": "~8.8.1",
"semver": "~5.3.0",
"yargs": "~8.0.1"
},
Expand Down
6 changes: 6 additions & 0 deletions test/errorhandling/issue_462.mss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#test {
shield: auto;
text: auto;
line-pattern: auto;
polygon-pattern: auto;
}
8 changes: 8 additions & 0 deletions test/errorhandling/issue_462.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Warning: issue_462.mss:2:4 shield is unstable. It may change in the future.
Error: issue_462.mss:2:4 Invalid value for shield, the type keyword (options: none) is expected. auto (of type keyword) was given.
Warning: issue_462.mss:3:4 text is unstable. It may change in the future.
Error: issue_462.mss:3:4 Invalid value for text, the type keyword (options: none) is expected. auto (of type keyword) was given.
Warning: issue_462.mss:4:4 line-pattern is unstable. It may change in the future.
Error: issue_462.mss:4:4 Invalid value for line-pattern, the type keyword (options: none) is expected. auto (of type keyword) was given.
Warning: issue_462.mss:5:4 polygon-pattern is unstable. It may change in the future.
Error: issue_462.mss:5:4 Invalid value for polygon-pattern, the type keyword (options: none) is expected. auto (of type keyword) was given.
10 changes: 10 additions & 0 deletions test/rendering-mss/issue_462.mss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#foo {
line: none;
line-width: 2;
[feature = 'bar'] {
line-color: red;
}
[feature = 'baz'] {
line-color: blue;
}
}
10 changes: 10 additions & 0 deletions test/rendering-mss/issue_462.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Style filter-mode="first" name="style">
<Rule>
<Filter><![CDATA[([feature] = 'baz')]]></Filter>
<LineSymbolizer stroke="#0000ff" stroke-width="2" />
</Rule>
<Rule>
<Filter><![CDATA[([feature] = 'bar')]]></Filter>
<LineSymbolizer stroke="#ff0000" stroke-width="2" />
</Rule>
</Style>
9 changes: 9 additions & 0 deletions test/rendering-mss/issue_462a.mss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#foo {
line: auto;
[feature = 'bar'] {
line-color: red;
}
[feature = 'baz'] {
line-color: blue;
}
}
13 changes: 13 additions & 0 deletions test/rendering-mss/issue_462a.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Style filter-mode="first" name="style">
<Rule>
<Filter><![CDATA[([feature] = 'baz')]]></Filter>
<LineSymbolizer stroke="#0000ff" />
</Rule>
<Rule>
<Filter><![CDATA[([feature] = 'bar')]]></Filter>
<LineSymbolizer stroke="#ff0000" />
</Rule>
<Rule>
<LineSymbolizer />
</Rule>
</Style>
15 changes: 15 additions & 0 deletions test/rendering-mss/issue_462b.mss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#foo {
shield: none;
[feature = 'bar'] {
shield-name: "[refs]";
shield-face-name: "Arial";
shield-file: url('test.svg');
shield-fill: red;
}
[feature = 'baz'] {
shield-name: "[refs]";
shield-face-name: "Arial";
shield-file: url('test.svg');
shield-fill: blue;
}
}
10 changes: 10 additions & 0 deletions test/rendering-mss/issue_462b.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Style filter-mode="first" name="style">
<Rule>
<Filter><![CDATA[([feature] = 'baz')]]></Filter>
<ShieldSymbolizer face-name="Arial" file="test.svg" fill="#0000ff"><![CDATA[[refs]]]></ShieldSymbolizer>
</Rule>
<Rule>
<Filter><![CDATA[([feature] = 'bar')]]></Filter>
<ShieldSymbolizer face-name="Arial" file="test.svg" fill="#ff0000"><![CDATA[[refs]]]></ShieldSymbolizer>
</Rule>
</Style>