The accepted answer does provide the correct approach. However, the specific implementation does not really add an inset border. It scales toward the center of the polygon. This works for squares and circles, but not for any non uniformly shaped polygon.
This solution (which is for OpenLayers!) solves this mathematically correct: OpenLayers 3: Offset stroke style
This can be adapted into the previous solution, as follows.
I am using this successfully with very odd shapes.
Hint: For very acute angles, set the lineJoin to 'miter' in the extraStyle definition.
import * as L from "leaflet";
import * as math from "mathjs";
const styleProperties = [
"stroke",
"color",
"weight",
"opacity",
"fill",
"fillColor",
"fillOpacity"
];
/*
* @class Polygon.MultiStyle
* @aka L.Polygon.MultiStyle
* @inherits L.Polygon
*/
L.Canvas.WithExtraStyles = L.Canvas.extend({
_updatePoly: function (layer, closed) {
const originalParts = layer._parts.slice()
// Draw extra styles
if (Array.isArray(layer.options.extraStyles)) {
const originalStyleProperties = styleProperties.reduce(
(acc, cur) => ({...acc, [cur]: layer.options[cur]}),
{}
);
for (let extraStyle of layer.options.extraStyles) {
const offsetDistance = extraStyle.inset || 1
layer._parts = layer._parts.map(points => {
const coordinates = []
let counter = 0
for (let i = 0; i < points.length - 1; ++i) {
// get each pair of points (each line)
const from = points[i]
const to = points[i+1]
// calculate angle of the line
const angle = Math.atan2(to.y - from.y, to.x - from.x)
// offset the line ( the line's points)
const newFrom = [Math.sin(angle) * offsetDistance + from.x, -Math.cos(angle) * offsetDistance + from.y]
const newTo = [Math.sin(angle) * offsetDistance + to.x, -Math.cos(angle) * offsetDistance + to.y]
coordinates.push(newFrom)
coordinates.push(newTo)
// When two lines cross each other, the inset will be drawn overlaying itself.
// So if lines intersect, find the coordinate of WHERE they intersect and use this as new common endpoint for the two lines.
if (coordinates.length > 2) {
const intersection = math.intersect(coordinates[counter], coordinates[counter + 1], coordinates[counter + 2], coordinates[counter + 3])
coordinates[counter + 1] = (intersection) ? intersection : coordinates[counter + 1]
coordinates[counter + 2] = (intersection) ? intersection : coordinates[counter + 2]
counter += 2
}
}
// convert Array of [[x,y], [x,y],...] to Points
return coordinates.map(semiPoint => {
return {
x: semiPoint[0],
y: semiPoint[1],
}
})
})
Object.keys(extraStyle).map(k => (layer.options[k] = extraStyle[k]))
L.Canvas.prototype._updatePoly.call(this, layer, closed)
}
// Resetting original conf
layer._parts = originalParts;
Object.assign(layer.options, originalStyleProperties)
}
L.Canvas.prototype._updatePoly.call(this, layer, closed)
}
})
L.Canvas.withExtraStyles = function (options) {
return new L.Canvas.WithExtraStyles(options)
}