Reputation: 3
I need to write a function that takes a GeoJSON based on lat
and lon
values and draws polygon to 2d canvas.
In addition to that I need to add some distance and draw inner polygon where 4 sides (north,east,west,south) of the polygon will have different distances from outer polygon. I have a problem with edge coordinates being output in one dimension only, where somehow when drawing a polygon some of the point/lines need to be offset by a single side either north or south etc... or in some cases 2 sides need to be taken into account.
Can anyone point me in the direction of the solution? So far most of the lines are drawn correctly, except for the edge cases in the green circles.
The correct image should be like this
Correct
The codepen of current implementation
https://codepen.io/Zlatko-Memisevic/pen/qBGNVgj
const exampleGeoJson = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[9.409277330768161, 47.330294996637527],
[9.40929555711242, 47.330394995087424],
[9.409449188016863, 47.330385694636568],
[9.409441872135783, 47.330340644668418],
[9.409432145964761, 47.330341377653177],
[9.409430621953572, 47.330332010609965],
[9.409383287787568, 47.330335311475089],
[9.409376169945089, 47.330288098791449],
[9.409349057871147, 47.330289992509627],
[9.409347814140572, 47.330283553765319],
[9.409279252163072, 47.330288260899749],
[9.409281541354549, 47.330294699779891],
[9.409277330768161, 47.330294996637527]
]
]
}
}]
};
function drawGeoJsonOnCanvas(geoJson, canvasId, offsets) {
const canvas = document.getElementById(canvasId);
const ctx = canvas.getContext('2d');
if (!canvas) {
console.error(`Canvas element with id "${canvasId}" not found.`);
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
const bbox = turf.bbox(geoJson);
const scaleX = canvas.width / (bbox[2] - bbox[0]);
const scaleY = canvas.height / (bbox[3] - bbox[1]);
function drawPolygon(coordinates, dotted, labelSides = false) {
ctx.beginPath();
coordinates.forEach((coord, index) => {
const x = (coord[0] - bbox[0]) * scaleX;
const y = (bbox[3] - coord[1]) * scaleY;
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.closePath();
if (dotted) {
ctx.setLineDash([5, 5]);
} else {
ctx.setLineDash([]);
}
ctx.stroke();
if (labelSides) {
for (let i = 0; i < coordinates.length - 1; i++) {
const start = coordinates[i];
const end = coordinates[i + 1];
const mid = turf.midpoint(start, end);
const midX = (mid.geometry.coordinates[0] - bbox[0]) * scaleX;
const midY = (bbox[3] - mid.geometry.coordinates[1]) * scaleY;
const length = turf.distance(start, end, {
units: 'meters'
}).toFixed(2);
ctx.fillText(`${length}m`, midX, midY);
}
}
}
function getOffsetDistance(direction, offsets) {
if (direction >= -45 && direction < 45) {
return offsets.east;
} else if (direction >= 45 && direction < 135) {
return offsets.north;
} else if (direction >= -135 && direction < -45) {
return offsets.south;
} else {
return offsets.west;
}
}
const features = geoJson.features;
features.forEach(feature => {
const coordinates = feature.geometry.coordinates[0];
drawPolygon(coordinates, false, true);
const offsettedCoordinates = [];
for (let i = 0; i < coordinates.length - 1; i++) {
const start = coordinates[i];
const end = coordinates[(i + 1) % coordinates.length];
const line = turf.lineString([start, end]);
const direction = turf.bearing(start, end);
const offsetDistance = getOffsetDistance(direction, offsets);
const offsettedLine = turf.transformTranslate(line, offsetDistance, direction + 90, {
units: 'centimeters'
});
offsettedCoordinates.push(offsettedLine.geometry.coordinates[0]);
offsettedCoordinates.push(offsettedLine.geometry.coordinates[1]);
}
const uniqueOffsettedCoordinates = Array.from(new Set(offsettedCoordinates.map(JSON.stringify))).map(JSON.parse);
uniqueOffsettedCoordinates.push(uniqueOffsettedCoordinates[0]); // Close the polygon
const innerPolygon = turf.polygon([uniqueOffsettedCoordinates]);
if (innerPolygon.geometry.type === 'Polygon') {
const innerCoordinates = innerPolygon.geometry.coordinates[0];
drawPolygon(innerCoordinates, true);
}
});
}
// Example usage
document.addEventListener('DOMContentLoaded', () => {
const offsets = {
north: 50, // in centimeters
south: 50, // in centimeters
east: 50, // in centimeters
west: 50 // in centimeters
};
drawGeoJsonOnCanvas(exampleGeoJson, 'geoCanvas', offsets);
});
#geoCanvas {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Turf.js/6.5.0/turf.min.js"></script>
<canvas id="geoCanvas" width="800" height="600"></canvas>
Upvotes: 0
Views: 103
Reputation: 340
Have never seen an out of the box solution for what you're trying to do.
One novel solution would be:
It would be fiddly and involve some math, though should be pretty robust, at least for shapes as complex as the one you've shown.
Upvotes: 0