I'm going to simply say shapes throughout most of this response as the same techniques could be used with any shape/rendering layer.
As you are likely discovering, styling by default is done at the layer level rather than at the individual shape level. This is done for performance reasons (big benefit), however, this doesn't mean you can't customize the style of individual shapes, you just have to use data driven styling in the layer.
There are three main ways to customize an individual shapes style using data driven styling.
Option 1: Style by ID
If you don't want to change anything about the shape other than give it a unique style, you can use it's ID to assign a style. This approach works fine if you only need to style a few shapes. Each shape is assigned a unique ID. If you are using the drawing tools, then you will get a atlas.Shape
object from it. This object has a getID
function you can use to get its unique ID. For completeness, if you were to load a GeoJSON objects into the map and already had a value in its ID property, of each GeoJSON object, those usually would be honored (the map will overwrite if it finds duplicate IDs). Azure Maps stores the unique ID in each shape in a property called _azureMapsShapeId
. Once you have an ID you can create a data driven style expression. There are a lot of ways to do this, but with IDs a simple lookup table approach is usually easiest, and this can be done with a Match expression. Here is an example of how this expression might look:
polygonLayer.setOptions({
color: [
'match',
['get', '_azureMapsShapeId'],
'shapeId1', 'red',
'shapeID2', 'blue',
//default fallback color
'black'
]
});
Now if you have multiple shapes, you likely will want to remember and append to this expression which can get complicated quickly. Here are some helper methods for this:
//Start off with a default expression.
polygonLayer.setOptions({
color: [
'match',
['get', '_azureMapsShapeId'],
//Add a non-existant shape ID so that we can start off with a match expression.
'notAShapeID', 'red',
//default fallback color
'black'
]
});
function addShapeColor(shape, color) {
var id = shape.getID();
var colorExpression = polygonLayer.getOptions().color;
//Check to see if the ID is already in the expression.
var idIdx = colorExpression.indexOf(id);
if(idIdx !== -1) {
//It is, so just need to update the color which is the next value after the ID.
colorExpression[idIdx + 1] = color;
} else {
//ID not found, need to insert into the expression, but before the default value.
colorExpression.splice(colorExpression - 2, 0, id, color);
}
//Update the style on the layer.
polygonLayer.setOptions({ color: colorExpression });
}
function removeShapeColor(shape) {
var id = shape.getID();
var colorExpression = polygonLayer.getOptions().color;
//Check to see if the ID is already in the expression.
var idIdx = colorExpression.indexOf(id);
if(idIdx !== -1) {
//Remove the color, then the ID.
colorExpressions.splice(idIdx + 1, 1);
colorExpressions.splice(idIdx, 1);
}
}
If you want to style a lot, then this becomes harder to manage and creates a massive style expression which can impact performance. I likely wouldn't go this route if I had more than a dozen shapes. Option 2 & 3 is better when you want to style a lot of shapes.
Option 2: Style by ID with different layers
If you have only a couple of unique styles you want to us, but lots of shapes you want to style in one of these few ways, another option is to create multiple layers and apply a filter based on the ID of the shape so that the shape is only rendered by one layer. Note that this wouldn't be an option when using the drawing tools, so you can skip this, only including for completeness. If you only have 3 or 4 layers (unique styles), this would work well with tens of thousands of shapes. For this expression you would maintain a simple array of IDs per layer and in the filter have it check to see if a shapes ID is in it by using the in
expression.
polygonLayer.setOptions({
filter: ['in', ['get', '_azureMapsShapeId'], ['shapeID1, 'shapeID2']]
});
//Layer that renders shapes not in the array.
polygonLayer.setOptions({
filter: ['!', ['in', ['get', '_azureMapsShapeId'], ['shapeID1, 'shapeID2']]]
});
Option 3: Style by property
In this scenario you would add the style information as properties of the shapes, then set the style in the layer. You only need to setup the layer style once like this, this assumes we are going to add a color
property to the shape (we can call this just about anything):
//Only need to set the style expression on the layer once.
polygonLayer.setOptions({
color: [
'case',
//Check to see if the shape has a color property.
['has', 'color'],
['get', 'color'],
//default fallback color
'black'
]
});
Then when you want to set the style of a shape, you simply add a property to it. For example:
shape.addProperty('color', 'red');