rediz
rediz

Reputation: 93

Mapbox: Create a Rectangle with border-radius (Layer)

I've been trying to get the same layer as you can see here. But I can't find a right type and options for it. For now I was only using a circle, but I can't give a different padding to X and Y axis.

This is the expected layer (For unclustered-point)

enter image description here

And this is my code:

    map.addLayer({
    id: 'unclustered-point',
    type: 'circle',
    source: 'places',
    filter: ['!', ['has', 'point_count']],
    paint: {
        'circle-color': '#486fd1',
        'circle-radius': 20,
    }
});

map.addLayer({
    id: 'unclustered-point-count',
    type: 'symbol',
    source: 'places',
    filter: ['!', ['has', 'point_count']],
    layout: {
        'text-field': ['get', 'price'],
        'text-font': ['DIN Offc Pro Bold'],
        'text-size': 11,
    },
    paint: {
        'text-color': "#fff",
    }
});

And this is what I have at the moment:

enter image description here

Upvotes: -1

Views: 722

Answers (3)

Placko
Placko

Reputation: 1

You need to use a stretchable image as a background for text: https://docs.mapbox.com/mapbox-gl-js/example/add-image-stretchable/

Upvotes: 0

kush
kush

Reputation: 11

Details

I had a similar case, I didn't find a way to do it with a mapbox SDK, so I decided to dig other ways and that's how I solved it:

  • Use a canvas to create a rounded rectangle
  • Make it a bitmap
  • Add it to a mapbox map as a symbol layer
  • Add a text layer over it

Also, it'll look pixelized on MacBooks and iPhones because of the device pixel ratio, so we need to consider this

Here's my solution


const dpr = window.devicePixelRatio;

const getRoundedRectangleImage = async ({color, width, height, radius}) => {
  // Make the image not pixelized on devices with high DPR
  const canvas = document.createElement('canvas');
  canvas.width = width * dpr;
  canvas.height = height * dpr;
  const ctx = canvas.getContext('2d')!;

  ctx.scale(dpr, dpr);

  // Draw a rounded rectangle
  ctx.fillStyle = color;
  ctx.beginPath();
  ctx.roundRect(0, 0, width, height, [radius]);
  ctx.fill();

  const image = await createImageBitmap(canvas);
  return image;
}

// ...

// Add the image to the map
getRoundedRectangleImage({
  width: 68, 
  height: 36,
  color: '#486fd1',
  radius: 100,
}).then((image) => {
  map.addImage('rounded-rectangle', image);
});

// Add layer to display rounded rectangles
map.addLayer({
  id: 'rounded-rectangle-layer',
  type: 'symbol',
  source: 'YOUR_SOURCE',
  filter: ['!', ['has', 'point_count']],
  layout: {
    'icon-image': 'rounded-rectangle',
    'icon-size': 1 / dpr, // compensate device pixel ratio
    'icon-allow-overlap': true,
    'text-allow-overlap': true,
  },
});

// Display text over rounded rectangles
map.addLayer({
  id: 'count-text',
  type: 'symbol',
  source: 'YOUR_SOURCE',
  filter: ['!', ['has', 'point_count']],
  layout: {
    'text-field': ['get', 'price'],
    'text-font': ['DIN Offc Pro Medium'],
    'text-size': 11,
    'text-justify': 'center',
   },
   paint: {
     'text-color': '#fff',
   },
});

Upvotes: 1

nisnym
nisnym

Reputation: 41

Not sure but I think this can work.

'circle-radius': {
        'base': 1,
        'stops': [[0, 10], [20, 30]] 
    },

Upvotes: -1

Related Questions