Unable to select a particular geometry layer and modify its color

Blessy Gnanamanickam 40 Reputation points
2024-05-27T22:01:24.2833333+00:00

I attempted to create polygons on the map using the drawingManager class and setOptions method for coloring. However, when I select a polygon and modify its color, the change affects all other polygons as well. Is there a way to alter the color only for the specific polygons we choose while preserving that data?

Azure Maps
Azure Maps
An Azure service that provides geospatial APIs to add maps, spatial analytics, and mobility solutions to apps.
649 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. rbrundritt 16,331 Reputation points Microsoft Employee
    2024-05-28T16:40:25.26+00:00

    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');