-
Notifications
You must be signed in to change notification settings - Fork 130
Support Format and Layout nodes in text symbolizer #347
Comments
Not that I am aware, so I'm you are planning on working on this. Thanks for kicking off discussion of syntax ideas - certainly challenging to figure out how to model this in CSS. But finding a good syntax is critical. We should feel free to change the way Mapnik XML/load_map parsing works if needed. As far as your idea - seems like a good start, but I think it would be ideal to have something different than the existing instance syntax to avoid confusion. What about turning https://github.com/mapnik/mapnik/wiki/TextSymbolizer#layouts into: #label {
text-face-name:@font_reg;
text-name: layout (
dy: -5;
text-name:"[name1]";
), layout (
dy: 5;
text-name:"[name2]";
);
} This would be more like the list of stops syntax of the |
/cc @rcoup for help and critque |
Random dump of ideas... CSS3 pseudo-elements is the first thing that kinda springs to mind... <q>Some quotes</q>, he said, <q>are better than none</q>. q::before {
content: "«";
color: blue;
}
q::after {
content: "»";
color: red;
}
There's been some (abandoned) discussion about Nesting pseudo-elements in CSS3 which could provide some helpful ideas? Along those lines...
Which is along the lines of your Some of the text symbolizer properties can probably only apply to the "main" symbolizer? eg. placement. How that maps easily back to mapnik's XML or internals I haven't thought through... though at an XML level it could map to something like (simplistically): <TextSymbolizer face-name="DejaVu Sans" size="14">
<TextSymbolizer size="9" dy="-5">First: </TextSymbolizer>
[name1]
<TextSymbolizer size="9" dy="-5"> Last: </TextSymbolizer>
<TextSymbolizer face-name="DejaVu Sans Bold">[name2]</TextSymbolizer>
</TextSymbolizer> Maybe Semi-related stuff:
|
@rcoup wins: wow - thanks for these excellent ideas. @jhollinger2 - want to digest and provide a new proposal? |
Thanks a lot for providing these suggestions! First of all, I would second the notion of deprecating An big advantage of using pseudo-elements is that specific style attributes of those elements can be overriden within subsequent definitions without having to rewrite the whole list. I think there could be several approaches though. Using Consider the following XML definition, which places one bold, red word in the middle of a label: <TextSymbolizer face-name="DejaVu Sans">
"Here is a "
<Format fill="#ff0000">red</Format>
" word."
</TextSymbolizer> In Carto CSS, I think the most consise way to represent this using #label {
text-content: "Here is a ";
text-face-name: "DejaVu Sans";
text::after(1) {
content: "red";
face-name: "DejaVu Sans Bold";
fill: #ff0000;
}
text::after(2) {
content: " word.";
}
} I also note here, that this would generate an unneeded format node around the last part: <TextSymbolizer face-name="DejaVu Sans">
"Here is a "
<Format fill="#ff0000">"red"</Format>
<Format>" word."</Format>
</TextSymbolizer> Adding logic here to avoid creating XML nodes when no attributes are set other than content would create the desired XML. Another potential problem is that this syntax does not clearly distinguish between format and a layout. On the XML side, I don't see a good way to combine this into a single I think that a solution to that could be to simply create either one or both types of XML element based on what attributes are defined within a psuedo-element. The following combinations would result in XML rendering: text::after(1) {
content: 'text';
} text text::after(1) {
content: 'text';
size: 12;
} <Format size="12">text</Format> text::after(1) {
content: 'text';
dy: 8;
} <Layout dy="8">text</Layout> text::after(1) {
content: 'text';
size: 12;
dy: 8;
} <Layout dy="8">
<Format size="12">text</Format>
</Layout> Furthermore, nested elements could be supported as follows: #style {
text-face-name: 'DejaVu Sans';
text-name: [name];
text::before(1) {
content: [alt];
dy: 8;
}
text::before(1)::after(1) {
content: 'first';
fill: #0000ff;
}
text::before(1)::after(2) {
content: 'second';
size: 10;
}
} <TextSymbolizer face-name="DejaVu Sans">
<Layout dy="8">
[alt]
<Format fill="#0000ff">"first"</Format>
<Format size="10">"second"</Format>
</Layout>
[name]
</TextSymbolizer> I think this syntax could still be a bit cumbersome, depending on what the user is trying to accomplish, but it would work. The only other thought that I had is, rather than using ::before and ::after, combine the psuedo-element syntax with the ability to reference a format or layout style within the parent text. This could be done potentially with function call syntax. For example: #style {
text-name: 'Here is a' format(1, 'red') 'word.'
text::format(1) {
fill: #ff0000;
}
} and #style {
text-face-name: 'DejaVu Sans';
text-name:
layout(1,
[alt]
format(1, 'first')
format(2, 'second')
'third'
) [name];
text::layout(1) {
dy: 8;
}
text::format(1) {
fill: #0000ff;
}
text::format(2) {
size: 10;
}
} <TextSymbolizer face-name="DejaVu Sans">
<Layout dy="8">
[alt]
<Format fill="#0000ff">"first"</Format>
<Format size="10">"second"</Format>
"third"
</Layout>
[name]
</TextSymbolizer> This would make the distinction between format and layout nodes more clearly defined and keep the text content itself all in one place. However, it does deviate more from standard CSS conventions, and the text-name string can get messy if a lot of nesting is happening. Ultimately, sticking with |
Any further thoughts about the psuedo-element syntax? I am planning to start some of the initial work on this soon. Also, some additions will need to be made in mapnik-reference to define which attributes can be set in Layout and Format nodes. |
/cc @ajashton for thoughts. My gut is that its a step in the right direction but feels to complicated still. |
Another suggestion about the distinction between format vs layout: Since layout nodes are placed completely independently of the parent, it makes no difference where the e.g. The following two text xml definitions will result in the exact same rendering: <TextSymbolizer><Layout dy="8">[first]</Layout>[second]</TextSymbolizer> <TextSymbolizer>[second]<Layout dy="8">[first]</Layout></TextSymbolizer> By contast, these two definitions will render differently: <TextSymbolizer><Format size="12">[first]</Format>[second]</TextSymbolizer> <TextSymbolizer>[second]<Format size="12">[first]</Format></TextSymbolizer> I think that it would help to eliminate some confusion if layouts and formats were handled differently in terms of Carto CSS. Layouts actually present a simpler case than formats, because any Layouts that are defined can just be appended to the end of the xml content inside the symbolizer. Therefore, for layouts specifically, I would suggest representing a layout as a pseudo-element of it's own: e.g. #style {
text-face-name: 'DejaVu Sans';
text-name: [name];
text::layout(1) {
content: 'up';
dy: 15;
}
text::layout(1)::layout(1) {
content: 'up, right';
dx: 15;
}
} <TextSymbolizer face-name="DejaVu Sans">
[name]
<Layout dy="15">
"up"
<Layout dx="15">"up, left"</Layout>
</Layout>
</TextSymbolizer> Note that the above example shows a nested Layout node, which I think might still be worth supporting on the grounds that it inherits attributes from the parent. The nested layout nodes would still always be appended to the end of the xml content inside the parent. Format nodes will need to be handled differently. I think it could still work to represent formats with |
I'm not that happy with pseudo element syntax as it is quite similar to attachments and the extra : is easy to overlook or not obvious for newcomers. Hard to figure out when to use what. I think the the formats and layouts have to be defined separately and referenced in the text-name string (content). Defining them separately enables to override them later and referencing them in a content string gives you overview. The syntax with ::before(1)::after(1) is too complex and you easily get lost in it. Then there is the distinction between format and layout. Location of the layout reference is not important, but you should be able to nest layout references. So referencing them in the content string may lead to the false assumption that the order or location is important. I'm not sure if it is better to have them separate or similar as format references. |
This is related to #238, but is a somewhat separate issue from list placements. I would like to see carto support for child formats and layouts within a text symbolizer, other than writing XML directly into the text-name field. I am interested in supporting Layout in particular, which is a new feature that I added in mapnik (See mapnik/mapnik#2138). I am thinking about implementing this on my fork of this project for now.
Has any thought been given to what the syntax for creating this structure would look like in carto?
It occurred to me that perhaps these could be created using instance names, similar to additional symbolizers, and then referenced in the name field in some way.
Simple example:
An xml format node would be created from the provided css attributes within the instance name and then substituted into the symbolizer content where referenced. Layout could work the same way, and the instance names could be used to override attributes of the individual formats and layouts. This is an initial idea, and could probably use some improvement.
I was wondering if anyone has been working on this at all or has a better idea about what the syntax should look like.
The text was updated successfully, but these errors were encountered: