mapbox-gl-js
mapbox-gl-js copied to clipboard
Enable property functions for *-translate properties
Supporting *-translate properties isn't as straightforward as supporting other paint properties because translation is incorporated into the transform matrix.
Potential Designs
- incorporate translation property function into position in vertex buffer, requires translate be treated like a layout property
- add new "translate" shader uniform
- don't support this feature
Properties
circle-translatecircle-translate-anchorline-translateline-translate-anchorfill-translatefill-translate-anchor
We should be able to implement this by adding a vec2 translate attribute to the paint buffer. Ideally we could implement this such that the attribute not needed for non-data-driven translate values.
This applies to the symbol properties as well:
text-translatetext-translate-anchoricon-translateicon-translate-anchor
incorporate translation property function into position in vertex buffer, requires translate be treated like a layout property
The only downsides that I can think of for doing it this way is the hackiness of including special-case checking for paint['...-translate'] properties in groupByLayout(). @lucaswoj do you think there's more to it than that?
add new "translate" shader uniform
Would this need to be an attribute, rather than a uniform, to allow for property functions?
I'm looking into implementing this (specifically circle-translate) but I was wondering how to support per feature translation?
From the codebase my understanding is that the layoutVertexArray in CircleBucket is created only once and when draw_circle.js is called to actually draw all circles I'm not able to change (or inject a vec2 translate) into each circle per it's feature. Ideally my geojson should be something like:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"zoom5": [300,300],
"zoom7": [200,200],
},
"geometry": {
"type": "Point",
"coordinates": [-97,39]
}
}
]
}
Any pointers maybe of where to look if I'd like to implement this per feature?
Thanks!
This isn't working? UPDATE: Looks like it is not yet merged.
'circle-translate': ['get', 'markerOffset'] produces Error: layers.stopCircleLayer.paint.circle-translate: property expressions not supported
Geojson being:
var newOffset = someCondition ? [0, -20] : [0, -100];
var feature = {
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [stop.longitude, stop.latitude],
},
'properties': {
'markerOffset': newOffset,
},
};
I was trying to use this as a means of doing cheap spiderfying/exploding/clustering by pre-identifying circles to be offset. In my case I am using a circle to create a dynamic colored icon since markers do not support dynamic coloring. But I can see where someone working with circle graph data would have overlaps.
@ericjames I am pursuing a similar solution and I am kind of confused why property expressions work for *-offset properties e.g. on symbol type layers, but not for circle-translate. Is there known workaround?
While this doesn't work
map.addLayer({ 'id': 'points', 'source': 'points', 'type': 'circle', 'paint': { 'circle-color': ['get', 'color'], 'circle-radius': 16, 'circle-translate': ['get', 'offset'], 'circle-stroke-width': 2, 'circle-stroke-color': ['get', 'outlineColor'] } },'place_town');
this works perfectly fine
map.addLayer({ 'id': 'points_icons', 'source': 'points', 'type': 'symbol', 'layout': { 'icon-image': ['get', 'iconName'], 'icon-size': 0.5, 'icon-offset': ['get', 'offset'], 'icon-allow-overlap': true, 'icon-ignore-placement': true } },'place_town');
Only thing I can think of right now is adding a separate layer for each feature in a loop like this:
points.features.forEach((feature) => { map.addLayer({ 'id': feature.properties.object_type, 'source': 'points', 'type': 'circle', 'paint': { 'circle-color': feature.properties.color, 'circle-radius': 16, 'circle-translate': feature.properties.offset, 'circle-stroke-width': 2, 'circle-stroke-color': feature.properties.outlineColor } },'place_town'); });
but that's messy to handle. Or I could switch to symbol type layers, but since I am working with many different colors that would make my sprites messy as well.
If this is just a matter of merging, maybe this could be implemented fast?
@jfirebaugh @lucaswoj @ericrwolfe any news on this?
We would love to be able to use these properties with the data-driven styling API. Makes the API a lot more complete. A less than ideal workaround we often need to use is to create separate layers with the corresponding settings, which impacts performance and makes the code less readable.
I'm simply modifying the actual coordinates of the values of markers I want spiderfied.
@asheemmamoowala does this mean that there is an alternative solution that doesn't rely upon duplication and main-thread js processing of all the features we need to offset? For that matter, this is from 2016. Quite frankly, I find it kind of impossible that this is still not tackled? Especially since sources accept urls (read sources containing data that's not available locally) and that's kind of severely affected by this.
I'm wondering if there have been other solutions since. I ran into this thread looking for ways to evenly distribute circles that share the same coordinate without manually adjusting the coordinates. This is what I've got so far but it's not ideal because it doesn't take into consideration zoom or the pixel size of circles:
function distributeFeatures(center, features) {
const numFeatures = features.length;
const radius = 0.005
const angleIncrement = (2 * Math.PI) / numFeatures
features.forEach((feature, index) => {
const angle = index * angleIncrement;
const newX = center[0] + radius * Math.cos(angle)
const newY = center[1] + radius * Math.sin(angle)
feature.geometry.coordinates = [newX, newY]
})
return features
}