How to programmatically clone a layer of MapBox? - mapbox

I'm loading a style with an existing CircleLayer and I'd like to programmatically add another such layer with similar styling (say, same except different color). Its geo data will be created programatically as well.
I could not find an easy API for cloning a layer.
But even if I do something like:
CircleLayer oldLayer, newLayer = ...;
newLayer.withProperties(
PropertyFactory.circleColor(Color.parseColor("#e55e5e")),
oldLayer.getCircleStrokeColor()
);
Setting circle color (as a literal) works but for setting stroke color (taken from the other layer) I'm getting:
09-25 20:59:20.783 18014-18014/com.example.client E/mbgl: {example.client}[JNI]: Error setting property: circle-stroke-color property not found
I checked, oldLayer is properly initialized and oldLayer.getCircleStrokeColor() does return a proper PropertyValue. What am I missing?

Related

Mapbox GL JS Set Paint property on specific Feature in Layer

I am using Mapbox Studio as basis for mapping and styling and then using HTML for additional map features.
One of the features is to change Icon opacity when hovering or mouse enter. I've checked other examples and all other refer to feature when you create it directly in HTML. I managed to change opacity but only for whole layer.
Can I use somehow e.features[0] command line to apply changes only to one feature rather than to whole layer?
I used this code which changer opacity for whole Layer 'Icon' (Layer contains 5 icons with text):
// Change the cursor to a default and change opacity when the it enters a feature in the 'Icons' layer.
map.on('mouseenter', 'Icons', function() {
map.getCanvas().style.cursor = 'default';
var feature = e.features[0];
map.setPaintProperty('Icons', 'icon-opacity', 0.5);
});
// Change it back to a pointer and reset opacity when it leaves.
map.on('mouseleave', 'Icons', function() {
map.getCanvas().style.cursor = '',
map.setPaintProperty('Icons', 'icon-opacity', 1);
});
Thank you!!!
There are a few ways which you could achieve this. One approach is to add each feature as separate layer, so that when you want to change the opacity of an icon added in a layer 'specific-icon-layer', you can pass 'specific-icon-layer' to the Map#on method. This is likely the most straightforward option if you have a relatively minimal number of markers.
Another approach is to add unique IDs to each icon feature, so that you can use a filter expression in conjunction with Map#setPaintProperty and Map#queryRenderedFeatures (or Map#querySourceFeatures). For example, suppose you add an 'id' property to each GeoJSON feature representing an icon in the source for the 'Icons' layer. Then, you could set up an event listener similar to this example, retrieve the 'id' of the returned feature, and use the 'id' (suppose here it is 'example-id') to update the paint property for the 'Icons' layer:
map.setPaintProperty(
'Icons',
'icon-opacity',
['match', ['get', 'id'], 'example-id', 0.5 , 1]
);
Here, we use match and get expressions to say "if the 'id' of a feature is 'example-id', paint its icon with opacity 0.5, otherwise use opacity 1."
Check the example at https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/
This approach makes use of setFeatureState and feature-state expressions
The problem with using map.setPaintProperty(layer, property, filter, matchValue, styleValue, fallbackStyleValue) every time is that it restyles every feature on the layer instead of only the feature being interacted with. This can cause poor performance when the layer has a high number of features.

How to set position of mapbox-gl-draw control?

I'm using https://github.com/mapbox/mapbox-gl-draw for draw tools, and it by default sets the control position to the top right. Like so:
It seems like it's possible to change the position of the control by setting an option, but I cannot figure out how to do so. I've been looking through the source and the documentation.
you can set the position in the addControl method.
var Draw = new MapboxDraw();
map.addControl(Draw, 'bottom-left');
addControl( control, position )
Parameters
position(string?), position on the map to which the control will be added.
Valid values are
'top-left'
'top-right'
'bottom-left'
'bottom-right'
Defaults to 'top-right'
Example App

Material doesn't have a color property '_TintColor'

I want to modify the TintColor of a material using script but while doing it, error below is coming:
Material doesn't have a color property '_TintColor'
I Googled about it but all efforts in vain.
I read this post and it says that it can be modified using script.
The shader that I am using is Sprites/Default
public Color[] colors = { new Color(42/255.0f, 246/255.0f, 229/255.0f,1),new Color(20/255.0f, 127/255.0f, 255/255.0f,1),new Color(73/255.0f, 3/255.0f, 198/255.0f,1);
Debug.Log(renderer.material.GetColor("_TintColor"));
//renderer.material.SetColor("_TintColor", colors[2]); /*This eventually I want to use */
I tried playing with alpha value, but it didn't work
I don't know what I am doing wrong. It would be very helpful if someone can point me out my mistake or let me how to change TintColor of a material.
The shader Sprites/Default does not have a "_TintColor" property, but instead a "_Color" property. So all you have to do is change
Debug.Log(renderer.material.GetColor("_TintColor"));
to
Debug.Log(renderer.material.GetColor("_Color"));

No setBounds function for Leaflet imageOverlay

I'm reading an imageOverlay URL from an ArcGIS webserver that uses the leaflet getBound() coordinates as part of the URL (we have large maps that are filtered for the current window 'extent'). Apologies for not including the actual path (I'm working with sensitive client data). Eg:
http://myarcgiswebserver.com/MapServer/export/dpi=96&format=png32&bbox=27.119750976562504%2C-31.194007509998823%2C32.39044189453126%2C-29.692824739380754&size=1719%2C434
[bbox] = current imageBounds
When dragging my map the imageOverlay url is updated correctly but my leaflet window is no longer aligned to the imageBound values that were set when first adding the imageOverlay which results in a skewed output (this is my assumption):
The only workaround is to remove the existing imageOverlay and add a new one (which ruins the user experience as the map disappears then reappears each time the window is dragged or zoomed).
Am i approaching this problem incorrectly or would the introduction of a function to update the current imageBounds resolve this? Perhaps not a new function but the expansion of setUrl with additional parameters...?
Many thanks for any feedback...
As #ghybs pointed out, your use case might be better served by using the WMS
interface of your ArcGIS server.
Anyway, you say
The only workaround is to remove the existing imageOverlay and add a new one (which ruins the user experience as the map disappears then reappears each time the window is dragged or zoomed).
Well, that glitch is due to you probably doing something like:
Remove old overlay
Add new overlay
Wait until the image is received from the network
Wait one frame so the new overlay is shown
and instead you should be doing something like:
Add new overlay
Wait until the image is received from the network
Remove old overlay
Wait one frame so the new overlay is shown
The problem is just the async wait and the possible race conditions there, but should be easy to hack together, e.g.:
var activeOverlay = null;
var overlayInRequest = null;
map.on('moveend zoomend', {
// If we are already requesting a new overlay, ignore it.
// This might need some additional debouncing logic to prevent
// lots of concurrent requests
if (overlayInRequest) {
overlayInRequest.off('load', showOverlay);
}
overlayInRequest = L.imageOverlay( computeUrl( map.getBounds() ), myOverlayOptions );
overlayInRequest.on('load', showOverlay);
});
function showOverlay(ev) {
activeOverlay.remove();
activeOverlay = overlayInRequest;
activeOverlay.addTo(map);
overlayInRequest = undefined;
}
If you use an ImageOverlay but change its url dynamically, with a new image that reflects a new bounding box, then indeed that is the reason for the behaviour you describe: you display an image that has been generated using a new bbox, but positioned in the initial bbox, since the image overlay remains at the same geographical position on the map.
Instead, it sounds to me that you should use a TileLayer.WMS.
It would automatically manage the bounding box update for you. You may need to find the correct options to fit your service provider required URL syntax, though.
Example: http://playground-leaflet.rhcloud.com/yel/1/edit?html,output

Why cant I display the layer property value of Uibutton

I was just experimenting with layer property of my UIButton by setting a outlet n the code for a button created in xib.
NSLog(#"d button layer value%#",dButton.layer.backgroundColor);
But the output is coming as:
d button layer value(null)
My question is: can't we display the layer property value of UIButton. We all know that some default value would have been set for the button.
According to the documentation for CALayer:
The default is nil.
unless you explicitly change it.
In the documentation for UIView, it says this:
The default value is nil, which results in a transparent background color.
This excerpt describes the view.backgroundColor property, so I assume that the view.layer.backgroundColor property is the same.
Hope this helps!
Phew !! I finally got the answer.
Actually I was trying to display the structure value using %# in NSlog statement.
dButton.layer.backgroundColor is a fundamental data type used internally by Quartz to represent colors.
So, way to display them is to make your own class which can parse the value.
Like, for example there are class functions for CGRect, CGPoint etc
like for displaying CGRect we use this code
NSLog(#"d button layer value%#",NSStringFromCGRect(dButton.layer.bounds));
but there arent any function defined for CGColorRef.
Ok, as per jrturton said,for displaying the above value we can use
NSLog(#"d button layer value%#",[UIColor colorWithCGColor:button.layer.backgroundColor)]
I hope everyone got it now !!