DevC
DevC

Reputation: 23

How to plot multiple points of different color in the same layer using mapbox?

I want to plot multiple points in the same layer using mapbox and all these points will have their own property like color and label. And also upon clicking on an individual point, its should show a popup with the point's id.

Is this possible to achieve, as of now I have been looping over the points array and for each point I am creating a separate source and layer.

for(let i = 0; i < buildings.length; i++){
      let mapSource = map.getSource(buildings[i].building_id);
      if(!mapSource){
        map.addSource(buildings[i].building_id, {
          'type': 'geojson',
          'data': {
            'type': 'Feature',
            'geometry': {
              'type': 'Point',
              'coordinates': buildings[i].centroid.coordinates
            }
          }
        });
      }
      let mapLayer = map.getLayer(buildings[i].building_id);
      if(!mapLayer){
          map.addLayer({
            'id': buildings[i].building_id,
            'type': 'circle',
            "paint": {
              "circle-radius": 6,
              "circle-color": SectionColors[buildings[i].section[0]]
            },
            "source": buildings[i].building_id
          });
      }
      let mapLabel = map.getLayer(`${buildings[i].building_id}-label`);
      if(!mapLabel){
        map.addLayer({
          'id': `${buildings[i].building_id}-label`,
          'type': 'symbol',
          'source': buildings[i].building_id,
          'layout': {
            'text-field': `${buildings[i].building_id}`,
            'text-size': 14,
            'text-variable-anchor': ["left"],
            'text-radial-offset': 0.3,
            'text-justify': 'auto',
          },
          'paint': {
            'text-color': '#fff308'
          }
        });
      }
      map.on('click', buildings[i].building_id, function(e){
        setPopUpTop(e.point.y+10);
        setPopUpLeft(e.point.x+10);
        setBuildingPopup(true);
        setBuilding(buildings[i].building_id);
        setInfoPanel(false);
      });
      map.on('mousemove', buildings[i].building_id, function(e){
        map.getCanvas().style.cursor = 'pointer';
        if(buildings[i].section){
          let sectionBuildings = buildings.filter(item => item.section && item.section.includes(buildings[i].section[0]))
          for(let j = 0; j < sectionBuildings.length; j++){
            let mapLayer = map.getLayer(`${sectionBuildings[j].building_id}-borders-onHover`);
            if(typeof mapLayer === 'undefined'){
              map.addLayer({
                'id': `${sectionBuildings[j].building_id}-borders-onHover`,
                'type': 'circle',
                'source': sectionBuildings[j].building_id,
                "paint": {
                  "circle-radius": 8,
                  "circle-color": '#fff308'
                },
              });
            }
          }
        }
      });
      map.on('mouseleave', buildings[i].building_id, function(e){
        map.getCanvas().style.cursor = '';
        if(buildings[i].section){
          let sectionBuildings = buildings.filter(item => item.section && item.section.includes(buildings[i].section[0]))
          for(let j = 0; j < sectionBuildings.length; j++){
            let mapLayer = map.getLayer(`${sectionBuildings[j].building_id}-borders-onHover`);
            if(typeof mapLayer !== 'undefined'){
              map.removeLayer(`${sectionBuildings[j].building_id}-borders-onHover`);
            }
          }
        }
      });
    }

When the number of points are few there is not much difference in performance, but when I am plotting a high number of points like 300-400, the performance is extremely slow.

Upvotes: 2

Views: 1991

Answers (1)

Nick VN
Nick VN

Reputation: 336

The first thing you need to do is use a feature collection to have multiple points in one source:

map.addSource('multiple-points-source', {
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "color": "#FFFFFF"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [-46.757, 71.413]
      }
    },
    {
      "type": "Feature",
      "properties": {
        "color": "#000000"
      },
      "geometry": {
        "type": "Point",
        "coordinates": [-32.345, 72.816]
      }
    }
  ]
}

After that you can use data-driven styling to fill the circles with a color from the point feature's properties:

map.addLayer({
  'id': 'multiple-points-layer',
  'type': 'circle',
  'source': 'multiple-points-source',
  'layout': {
  },
  'paint': {
    // use data-driven styling
    'circle-color': ['get', 'color'],
  },
});

Here is a working example using this technique: https://jsfiddle.net/132ygokd/2/

Upvotes: 6

Related Questions