-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow developers to specify a default/fallback value for a token when a feature property is undefined #4079
Comments
From @ansis on July 16, 2014 0:47 Maybe the default values should be in a separate object so that the string parsing doesn't become more complicated. |
From @ajashton on July 16, 2014 17:40 The cases where I've most wanted fallbacks are not usually to a static string but to an alternate column or multiple alternate columns.
|
From @kkaefer on July 16, 2014 17:52 Maybe we can do recursive replacements, like |
From @ajashton on July 17, 2014 19:6 Seems good to me |
From @ajashton on November 25, 2015 21:52 Re-reading this discussion it sounds like we're mostly thinking about fallbacks for situations where the token field is null/unset in the data. But another important situation when working with sprites is being able to fall back when the token field contains an unexpected value. Using the |
From @jfirebaugh on December 4, 2015 2:55 See mapbox/mapbox-gl-style-spec#362 (comment) for my preferred syntax. |
From @1ec5 on July 11, 2016 4:32 I’ve pushed two proofs of concepts implemented in GL JS, each with a different design: 1ec5-token-default-104 implements exactly the syntax described in mapbox/mapbox-gl-style-spec#362 (comment). This approach has the more straightforward implementation of the two, and there’s less repetition if you intend to surround a particular token with the same text no matter what the token expands to. 1ec5-token-selection-104 implements a different syntax that alternates on the entire string rather than individual tokens but still allows for an arbitrary number of alternative values. This approach sacrifices compactness for expressiveness. Now you can append a parenthetical gloss to a label but omit the parentheses if the gloss is empty (that is, if any of the constituent tokens is missing): {
"type": "selection",
"cases": [
[{"ref": "name"}, " (", {"ref": "name_en"}, ")"],
[{"ref": "name"}, " (", {"ref": "name_fr"}, ")"],
[{"ref": "name"}]
]
} On this second branch, I’ve also implemented the image fallback described in mapbox/mapbox-gl-style-spec#104 (comment), so you can specify a series of fallbacks in the event that the {
"type": "selection",
"cases": [
[{"ref": "bespoke-icon"}],
[{"ref": "maki"}, "-12"],
["generic-12"]
]
} Both proposals reserve the ability to add additional information beside {
"type": "selection",
"cases": [
[{"ref": "name", "transform": "uppercase"}, " (", {"ref": "name_en"}, ")"],
[{"ref": "name", "transform": "uppercase"}]
]
} Finally, I’m totally open to naming suggestions. With |
From @kkaefer on July 11, 2016 8:46 @1ec5 that looks like a good first start, but I'm wondering if we need the |
From @1ec5 on July 11, 2016 8:56 The |
From @jfirebaugh on July 12, 2016 16:56 Let's keep the object syntax with |
From @jfirebaugh on July 12, 2016 19:37 I also prefer the syntax from |
From @1ec5 on July 12, 2016 20:4 Unfortunately, the syntax in I think it would also be inadequate as the basis for image fallback beyond what’s described in mapbox/mapbox-gl-style-spec#104 (comment). The string as a whole is what’s being tested for validity, not the individual token that’s being substituted.
I consider the |
From @1ec5 on July 12, 2016 20:41 Now that we’ve abandoned the mini-language in favor of a structured syntax, software like Mapbox Studio will have to implement a WYSIWYG UI around token defaults if the feature is to be viable. It’s pretty straightforward to implement a text field containing placeholder tokens – one example is the search bar at the top of this page, with its “This repository” token. With the |
Token defaults could be implemented as a use of the proposed conditional primitive and the existing ["if", ["has", "maki"], "{maki}-15", "generic-15"] ["if", condition, trueValue, falseValue] The conditional primitive would be useful in other cases. It has has been proposed previously at mapbox/mapbox-gl-style-spec#402 (comment). |
From @1ec5 on September 20, 2016 21:4 That would be an elegant solution. In mapbox/mapbox-gl-style-spec#402 (comment), I’ve extended your proposal with an |
From @kkaefer on November 28, 2016 12:50 I've spent some time thinking through potential syntax in the stylesheet and dug out an old prototype from this spring. Ultimately, a syntax like @lucaswoj proposed in mapbox/mapbox-gl-style-spec#104 (comment) makes most sense and would fit into the existing filter/expression model that we already use for feature filtering. The main caveat I'm seeing is that expressions like |
From @davidtheclark on November 28, 2016 16:20 I'm in favor of the My immediate thought about adding additional properties to token objects (like Also, if we don't have token objects, the array fallback syntax could be very straightforward indeed:
We'd parse the string for tokens, and if any of the fields they point to are empty, we move on to the next item. |
From @kkaefer on November 28, 2016 16:51 While we're expanding the syntax to support fallbacks, it'd be great to add a slightly more complex syntax to allow for conversions like meters to feet, and basic mathematical operations like rounding, and number formatting (think thousands formatting, or limiting float precision). The syntax in As for integrating this into Studio, we could still build a "fallback" UI, and translate them to the respective JSON syntax. In addition, we could also build a simple expression parser that translates a simple expression grammar has("name_en") ? "{name} ({name_en})" : (has("name_fr") ? "{name} ({name_fr})" : "{name}") into the JSON representation of its AST: ["if", ["has", "name_en"],
"{name} ({name_en})",
["if", ["has", "name_fr"],
"{name} ({name_fr})",
"{name}"
]] |
From @kkaefer on November 28, 2016 17:19 I looked at various possible use cases of expressions and identified these:
Please post here if you think your use case isn't covered by any of these. |
From @tmcw on November 28, 2016 17:40 To frame how we think about this kind of feature for Studio: Having a simple representation of a value and a complex, powerful representation of the same value usually means, for Studio, that we need to build a nice, robust UI for both. The thing is, base styles are editable in Studio: you can open a bright, dark, emerald, etc style - a complex style that should exploit every advantage we have - and you should be able to edit it. So the idea that 'novice users will write and edit novice-level styles' doesn't work out: novice users are often editing styles written by pro users. Thus far, we've avoided any kind of JSON editing in the Style UI. There are a few raw text editing areas, but JSON is a level above the complexity of, say, a color string or a text-field value. JSON is very picky about " and , and [, things that us programmers know unconsciously but are very hard for anyone else to pick up. We try to promise that we don't let you screw up, whereas the majority of guessed-inputs to a JSON textfield will be invalid. This issue has had two years to grow, and it looks like it started as token defaults and is currently building a new grammar and small programming language. Not that that's a bad thing: the problems it now aims to solve are valid. But I do question whether we want to throw this much complexity into a feature in order to move processing from the tile generation to the tile rendering step, and how we plan to deal with the eventuality that people will use the expressiveness of the language to write multi-hundred-line functions in text-fields. |
From @ajashton on November 28, 2016 18:18
Would this include checking presence of icons in the sprite? |
From @1ec5 on November 28, 2016 19:5
Note that mapbox/mapbox-gl-style-spec#362 (comment) is more or less implemented in 1ec5-token-default-104. I implemented the alternative syntax in 1ec5-token-selection-104 for some use cases that weren't addressed by the token-default syntax. I'd encourage everyone to check out the unit tests on both branches to get a sense of what's possible with each approach. The "selection" syntax implemented in 1ec5-token-selection-104 may suffer from combinatorial explosion, as described in mapbox/mapbox-gl-style-spec#104 (comment), but I think it's a good tradeoff compared to 1ec5-token-default-104. In exchange for making it harder to permute a large number of fields and to a small extent hiding what "fails" a case, we make it easier to build an intuitive UI, discourage users from stuffing deeply nested logic or "multi-hundred-line functions" into a single field, and make it possible to vary fallbacks beyond simple replacements. The key insight is that the user rarely wants to only substitute one field for another inside text-field; usually adjustments to surrounding punctuation are needed as well. The token-selection syntax also provides an easy (already implemented) fallback for missing style icons. A generalized conditional syntax does address the superset of use cases and jives with the trend towards more prosaic syntax, but at the expense of having to build a generalized string template editor in Studio. |
From @1ec5 on November 30, 2016 2:13 Up to this point, we’ve discussed two primary use cases for token fallbacks (formatting and conversion options aside):
Reading mapbox/mapbox-gl-style-spec#104 (comment) made me reconsider whether the two use cases really need to be served by the same feature. So I’ve filed #597 to support the image fallback use case specifically with a separate syntax reminiscent of font stacks. Meanwhile, I still think the bilingual label use case is best served by the token-selection syntax described in mapbox/mapbox-gl-style-spec#104 (comment), not least because the Studio UI for it would be just as straightforward to implement and intuitive to use as the UI envisioned for image fallbacks in #597. A generalized conditional syntax would also serve this use case well: in the example above, the designer might want to use One idea that has come up is to present the designer with a syntax-highlighted JSON editor if the value exceeds a certain level of complexity. However, I fear that the designer will never see the “simple mode” in practice, given that the bilingual label use case above would already require some alternation beyond simple token replacement. |
From @ajashton on November 30, 2016 5:44
I've thought about this and have no good suggestions, only more complications. Mainly that it might be undesirable to show name_en even if is just similar to the untranslated name. Eg "Québec (Quebec)" does not seem like a label I would want to add to a map. |
From @1ec5 on November 30, 2016 5:49
The case and diacritic folding functionality requested in #548 would fit in with the generalized conditional syntaxes that have been proposed so far. For anything beyond case and diacritic folding, we’d probably have to implement a Levenshtein distance function for use inside conditionals, or the source would have to provide more fields to indicate the desired behavior. |
From @kkaefer on December 2, 2016 9:50 While I think I'm warming up to the selection syntax, #548 won't solve the issue of "Québec (Quebec)", since it'll take the first one that is available? It seems like most of the discussion here revolves around creating a UI for a formatting/control flow. A flow-chart like UI could capture this, but is hard to develop and adds a lot of additional complexity to studio. Creating a mini-language that we parse to the JSON AST could also work, and we could show errors. Something could also work is a combination of the selection syntax with conditionals:
So the example we've been using could look like this: [
[["all", ["has", "name_en"], ["!=", "name", ["key", "name_en"]] ], "{name} ({name_en})"],
[["all", ["has", "name_fr"], ["!=", "name", ["key", "name_fr"]] ], "{name} ({name_fr})"],
["all", ["has", "name_fr"]], "{name}"],
[true, "unnamed"]
] But at this point, we're seeing the same issues I pointed out above: The current UI and syntax only allows you to specify a property name (key), and only a verbatim value for comparison operations. |
From @1ec5 on December 2, 2016 10:56
That’s correct. #548 is only relevant to this discussion if we implement a generalized conditional syntax that looks uncannily similar to the filter syntax.
This is going to get into the weeds real fast, but one solution would be to introduce a complementary set of operators, such as Except that there would be a button to the left of each input box, and clicking it would open a flyout containing the usual filter UI: And that UI would have twice as many operators to choose from, with checkboxes for case and diacritic folding. I think this illustrates that the more use cases we try to address within the scope of this issue, the more bloated any UI would be and the more places a surprising value can lurk unnoticed. (Or the more the UI resembles a JSON editor.) |
From @ajashton on January 3, 2017 18:37
A lot of different use cases and edge cases have come up in this issue, and they would all be nice to solve. But the initial basic idea of falling back from one field to another if the first one is undefined would still unlock a number of important data/cartography needs. Eg being able to greatly expand the number of label languages in vector tile sources without bloating them with mostly-duplicate values in every tile. (Not thinking about multilingual labels - just single names at a time pulling from one of multiple possible fields.) If coming up with a full expressions syntax won't be doable soon due to complexity or UX concerns, solving just the basic fallback case first would still be extremely helpful. |
Closed in #4777 |
Sorry to comment on this old thread but Google keeps pointing me back here. None of the arbitrary expressions I am looking at seem to support icon-image fallback. How do I set a fallback for layout property For example "icon-image": "{myIconPropertyName}" might not exist, where myIconPropertyName would say be svg names "house" "car" "tree". But let's say tree didn't exist, so it would fallback to a marker name I do know exists. One of the comments above proposed |
An image availability expression is being tracked in #5261. |
@1ec5 @ericjames sorry for interrupting, but if I understand the question right, a case expression should do the trick:
|
The |
From @kkaefer on July 16, 2014 0:16
For certain tokens, a default string might be useful, e.g. when pulling the icon name from a feature, there may be features with that property not set. In those cases, it'd be cool to specify a default value for a token, for example:
"{maki:generic}-12"
would generate "cafe-12" when the feature has the propertymaki
set to"cafe"
, and "generic-12" when that property isn't set.Copied from original issue: mapbox/mapbox-gl-style-spec#104
The text was updated successfully, but these errors were encountered: