I have a collection of GeoJSON features as Polygon & MultiPolygon which are saved in MongoDB. Many among them are in rectangular or square shape, while others are odd shaped. How can I find all the four corner (top left, top right, bottom left and bottom right) points if that feature is in rectangular or square shape?
I first tried to filter those features which has only five co-ordinates, so that those points will be corner co-ordinates essentially. But some of them are having more than five co-ordinates but as shape they are rectangle or square. Check below given feature examples. First is odd shaped feature, second is rectangle with redundant points and third is rectangle with 4+1 points.
I checked TurfJS, but didn't find any helpful method. BBox gives me non-rotated rectangle, which is not useful because it can also give bbox for non-rectangular shape also.
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[
-102.61383274599996,
32.37585257400008
],
[
-102.61764297999997,
32.375000929000066
],
[
-102.62239479299996,
32.388750307000066
],
[
-102.60775890199994,
32.392205895000075
],
[
-102.60566771099997,
32.392699906000075
],
[
-102.60510962499995,
32.39093294600008
],
[
-102.61772064899998,
32.387951290000046
],
[
-102.61383274599996,
32.37585257400008
]
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[
-102.59602204999999,
32.36491866700004
],
[
-102.59952111499996,
32.37500140000003
],
[
-102.60087906199999,
32.378914276000046
],
[
-102.58443896599994,
32.38292495700006
],
[
-102.58184811899997,
32.37500015000006
],
[
-102.57980711899995,
32.36875659300006
],
[
-102.59602204999999,
32.36491866700004
]
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[
-102.62506373399998,
32.34304188700003
],
[
-102.64139887699997,
32.33901489700003
],
[
-102.64604796299994,
32.352882700000066
],
[
-102.62962431499994,
32.35679300500004
],
[
-102.62506373399998,
32.34304188700003
]
]
]
]
}
}
]
}
https://bl.ocks.org/Xyroid/raw/800dafa55111ce139b8158c62f858c98/
Answer
You could sum up angles. If you want to stick to Turf.js, try
function isRectangle(turfInputPolygon, threshold) {
var threshold = threshold || 2;
var turfPolygon = turfInputPolygon;
if (turf.booleanClockwise(turf.polygonToLine(turfPolygon).features[0])) {
turfPolygon = turf.rewind(turfPolygon);
};
var turfPolygonPts = turf.explode(turfPolygon);
turfPolygonPts.features.push(turfPolygonPts.features[1]);
var rightAngles = 0;
var sumAngles = 0;
for (var i = 1, len = turfPolygonPts.features.length; i < len - 1; i++) {
var b1 = turf.bearing(turfPolygonPts.features[i - 1], turfPolygonPts.features[i]);
var b2 = turf.bearing(turfPolygonPts.features[i], turfPolygonPts.features[i + 1]);
var angle = Math.min((b1 - b2 + 360) % 360, (b2 - b1 + 360) % 360);
sumAngles += angle;
if ((90 - threshold) <= angle && angle <= (90 + threshold)) rightAngles ++;
};
return rightAngles == 4 && ((360 - threshold) <= sumAngles && sumAngles <= (360 + threshold));
};
This is just a quick hack for demonstration, but in principle this should work. The function
- explodes the (Multi)Polygon feature into Point features
- appends the second feature to the end of the point feature array
(cheap trick to be able to calculate the last angle) - calculates the angles between two adjacent lines made from two consecutive points
- returns
true
if there are 4 right angles and the sum of all angles is 360°
(both calculations can be adjusted in sensitivity for exact values by thethreshold
value)
Update:
Since I just realzed you also want the corner points; this function returns those features that sit on the 90° angles if the shape is found to be rectangular, or false
if not:
function getCornerPts(turfInputPolygon, threshold) {
var threshold = threshold || 1;
var rightAngles = 0;
var sumAngles = 0;
var cornerPts = [];
var turfPolygon = turfInputPolygon;
if (turf.booleanClockwise(turf.polygonToLine(turfPolygon).features[0])) {
turfPolygon = turf.rewind(turfPolygon);
};
var turfPolygonPts = turf.explode(turfPolygon);
turfPolygonPts.features.push(turfPolygonPts.features[1]);
for (var i = 1, len = turfPolygonPts.features.length; i < len - 1; i++) {
var b1 = turf.bearing(turfPolygonPts.features[i - 1], turfPolygonPts.features[i]);
var b2 = turf.bearing(turfPolygonPts.features[i], turfPolygonPts.features[i + 1]);
var angle = Math.min((b1 - b2 + 360) % 360, (b2 - b1 + 360) % 360);
sumAngles += angle;
if ((90 - threshold) <= angle && angle <= (90 + threshold)) {
rightAngles ++;
cornerPts.push(turfPolygonPts.features[i]);
};
};
return (rightAngles == 4 && ((360 - threshold) <= sumAngles && sumAngles <= (360 + threshold))) ? cornerPts : false;
};
No comments:
Post a Comment