# Generate concentric polygons using Turf

I'm interested in trying to reproduce this kind of cartography in a Mapbox/Turf/vector tile stack:

I would start with the polygon for the lake edge, and generate a series of concentric polygons inside. Note that when the edges of the internal polygons meet, they split, so we will end up with multi-polygons.

Is there a way I can achieve this in Turf? Some possible candidates:

• `transformScale`: just produces identical polygons, but smaller
• `lineOffset`: would maybe solve part of the problem, but I'd need to manually detect when the lines are intersecting, then remove the intersecting bits.
• `buffer`: sounds like the right tool, but the description is vague about how it would operate on lines or polygons.

To make matters more complicated, because the lake features might cross vector tile boundaries, I really want to first convert the polygon to a line, and remove the segment along the tile edge. This might limit options further.

If it can't be done in Turf, pointers to algorithms that can be implemented in JavaScript would still be valuable.

I have achieved the basic functionality using a combination of many Turf functions, particularly:

• `polygonToLine` to transform the polygon into something we can call `lineOffset` on
• `lineOffset` produces the offset itself
• `lineToPolygon` converts the offset back into a polygon so we can start removing inside out pieces
• `unkinkPolygon` breaks a polygon into pieces of different clockwiseness
• `booleanClockwise` lets us filter out the inside-out pieces
• `booleanWithin` lets us filter out "outliers"

Here's a basic implementation:

``````const turf = require('@turf/turf');

const waterPolygon = {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[117.90527343750001,-41.90227704096369],[116.630859375,-44.33956524809713],[120.9375,-46.92025531537451],[124.23339843749999,-43.96119063892024],[129.9462890625,-43.771093817756494],[131.748046875,-46.58906908309183],[136.23046875,-44.902577996288855],[132.1435546875,-38.856820134743614],[129.8583984375,-42.293564192170074],[124.49707031249999,-42.45588764197166],[121.025390625,-39.30029918615028],[117.90527343750001,-41.90227704096369]]]}}]}.features[0];
const line = turf.polygonToLine(waterPolygon);

// when an offset intersects itself, it makes inside out pieces. We can strip them out
// like this
function removeInsideOuts(polygon) {
const out = turf.unkinkPolygon(polygon)
out.features = out.features.filter(p => !turf.booleanClockwise(turf.polygonToLine(p)));
return out;
}

// offsets can get so extreme they make correctly oriented polygons that are no longer
// inside the original feature. So we filter them out
function removeOutliers(polygons) {
return polygons.filter(p => turf.booleanWithin(p, waterPolygon));
}

function makeOffset(line, distance) {
return turf.lineOffset(line, distance);
}
const offsets = [50];
for (let i = 0; i < 10; i++) {
offsets.push(offsets[i] * 1.3);
}

const features = [];
for (const offset of offsets) {
const offsetPolygon = turf.lineToPolygon(makeOffset(line, -offset));
const cleanPieces = removeInsideOuts(offsetPolygon).features;
features.push(...removeOutliers(cleanPieces))
}
const out = turf.featureCollection(features);
``````