tangram-es
tangram-es copied to clipboard
Feature Request: Support meters as size unit for points
I am trying to show the accuracy radius of a location around the user. Here, I ran into some problems. I think part of it is a bug, part of it is a missing feature probably.
The relevant portion of the style file:
styles:
location-radius:
base: points
blend: overlay
blend_order: 2
layers:
streetcomplete_location:
data: { source: streetcomplete_location }
draw:
location-radius:
collide: false
color: '#44fd6c3e'
size: function() { return feature.radius }
and the code
locationLayer.clear();
LngLat pos = new LngLat(location.getLongitude(), location.getLatitude());
int accuracy = (int)(Math.ceil(location.getAccuracy()));
Map<String, String> props = new HashMap<>();
props.put("type", "point");
props.put("radius", ""+accuracy+"m");
locationLayer.addPoint(pos, props);
However, this gives i.e. the error:
styleParam.cpp:184: Invalid size parameter '22m'
If I set a static size in the style definition, I get no error, but it doesn't seem to show at all.
So my guess is that meters as a size unit for points is simply not implemented as there hasn't been a use case for this (until now :-P). At least it is not documented. That Tangram-ES does not report an error if you write size: 50m
seems to be a bug then.
In any case, this ticket shall be a feature request to support meters as a unit for points.
Plan B for the use case I have in mind would be to use a circle-polygon for the radius, but I find that rather ugly.
Perhaps another use case would be tree-nodes. (natural=tree)
@westnordost : one other posssible workaround would be to use a circular marker together with a js function for the size. That function would back out the size in pixel from the zoom level so it remains constant in meters. I haven't tested that yet.
Jup, I did that now, looks pretty nice and feels smooth. I call updateAccuracy whenever the zoom changes. However, it's still just a workaround, especially because there is no callback for when the map view changes. (See linked feature request).
private void updateAccuracy()
{
if(accuracyMarker != null && lastLocation != null)
{
if(lastZoom == controller.getZoom()) return;
LngLat pos = new LngLat(lastLocation.getLongitude(), lastLocation.getLatitude());
float size = meters2Pixels(pos, lastLocation.getAccuracy());
accuracyMarker.setStyling("{ style: 'points', color: 'white', size: ["+size+"px, "+size+"px], order: 2000, collide: false }");
lastZoom = controller.getZoom();
}
}
private float meters2Pixels(LngLat at, float meters) {
LatLon pos0 = TangramConst.toLatLon(at);
LatLon pos1 = SphericalEarthMath.translate(pos0, meters, 0);
PointF screenPos0 = controller.lngLatToScreenPosition(at);
PointF screenPos1 = controller.lngLatToScreenPosition(TangramConst.toLngLat(pos1));
return Math.abs(screenPos1.y - screenPos0.y);
}
Another use case where supporting meters for points would be helpful: Rendering mini-roundabouts and turning circles with some realistic diameter in meters on high zoom levels.
Actually, nevermind my earlier algorithm, much easier is
pixels_per_meter = 2^zoom_level * 256 / (cos(latitude * pi/180) * earth_circumference)
There is also a $meters_per_pixel in the docs, which would make it trivial to workaround this via a javascript function, however in tangram-es it doesn't seem to be supported. See #2138
(Ah, realized that $meters_per_pixel does actually not solve this, because it only changes on full zoom levels changes.)