Files
openlayers/src/ol/geom/flat/orient.js
2022-01-29 17:52:07 +01:00

209 lines
6.1 KiB
JavaScript

/**
* @module ol/geom/flat/orient
*/
import {coordinates as reverseCoordinates} from './reverse.js';
/**
* Is the linear ring oriented clockwise in a coordinate system with a bottom-left
* coordinate origin? For a coordinate system with a top-left coordinate origin,
* the ring's orientation is clockwise when this function returns false.
* @param {Array<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {number} end End.
* @param {number} stride Stride.
* @return {boolean} Is clockwise.
*/
export function linearRingIsClockwise(flatCoordinates, offset, end, stride) {
// https://stackoverflow.com/q/1165647/clockwise-method#1165943
// https://github.com/OSGeo/gdal/blob/master/gdal/ogr/ogrlinearring.cpp
let edge = 0;
let x1 = flatCoordinates[end - stride];
let y1 = flatCoordinates[end - stride + 1];
for (; offset < end; offset += stride) {
const x2 = flatCoordinates[offset];
const y2 = flatCoordinates[offset + 1];
edge += (x2 - x1) * (y2 + y1);
x1 = x2;
y1 = y2;
}
return edge === 0 ? undefined : edge > 0;
}
/**
* Determines if linear rings are oriented. By default, left-hand orientation
* is tested (first ring must be clockwise, remaining rings counter-clockwise).
* To test for right-hand orientation, use the `opt_right` argument.
*
* @param {Array<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {Array<number>} ends Array of end indexes.
* @param {number} stride Stride.
* @param {boolean} [opt_right] Test for right-hand orientation
* (counter-clockwise exterior ring and clockwise interior rings).
* @return {boolean} Rings are correctly oriented.
*/
export function linearRingsAreOriented(
flatCoordinates,
offset,
ends,
stride,
opt_right
) {
const right = opt_right !== undefined ? opt_right : false;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const isClockwise = linearRingIsClockwise(
flatCoordinates,
offset,
end,
stride
);
if (i === 0) {
if ((right && isClockwise) || (!right && !isClockwise)) {
return false;
}
} else {
if ((right && !isClockwise) || (!right && isClockwise)) {
return false;
}
}
offset = end;
}
return true;
}
/**
* Determines if linear rings are oriented. By default, left-hand orientation
* is tested (first ring must be clockwise, remaining rings counter-clockwise).
* To test for right-hand orientation, use the `opt_right` argument.
*
* @param {Array<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {Array<Array<number>>} endss Array of array of end indexes.
* @param {number} stride Stride.
* @param {boolean} [opt_right] Test for right-hand orientation
* (counter-clockwise exterior ring and clockwise interior rings).
* @return {boolean} Rings are correctly oriented.
*/
export function linearRingssAreOriented(
flatCoordinates,
offset,
endss,
stride,
opt_right
) {
for (let i = 0, ii = endss.length; i < ii; ++i) {
const ends = endss[i];
if (
!linearRingsAreOriented(flatCoordinates, offset, ends, stride, opt_right)
) {
return false;
}
if (ends.length) {
offset = ends[ends.length - 1];
}
}
return true;
}
/**
* Orient coordinates in a flat array of linear rings. By default, rings
* are oriented following the left-hand rule (clockwise for exterior and
* counter-clockwise for interior rings). To orient according to the
* right-hand rule, use the `opt_right` argument.
*
* @param {Array<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {Array<number>} ends Ends.
* @param {number} stride Stride.
* @param {boolean} [opt_right] Follow the right-hand rule for orientation.
* @return {number} End.
*/
export function orientLinearRings(
flatCoordinates,
offset,
ends,
stride,
opt_right
) {
const right = opt_right !== undefined ? opt_right : false;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
const isClockwise = linearRingIsClockwise(
flatCoordinates,
offset,
end,
stride
);
const reverse =
i === 0
? (right && isClockwise) || (!right && !isClockwise)
: (right && !isClockwise) || (!right && isClockwise);
if (reverse) {
reverseCoordinates(flatCoordinates, offset, end, stride);
}
offset = end;
}
return offset;
}
/**
* Orient coordinates in a flat array of linear rings. By default, rings
* are oriented following the left-hand rule (clockwise for exterior and
* counter-clockwise for interior rings). To orient according to the
* right-hand rule, use the `opt_right` argument.
*
* @param {Array<number>} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {Array<Array<number>>} endss Array of array of end indexes.
* @param {number} stride Stride.
* @param {boolean} [opt_right] Follow the right-hand rule for orientation.
* @return {number} End.
*/
export function orientLinearRingsArray(
flatCoordinates,
offset,
endss,
stride,
opt_right
) {
for (let i = 0, ii = endss.length; i < ii; ++i) {
offset = orientLinearRings(
flatCoordinates,
offset,
endss[i],
stride,
opt_right
);
}
return offset;
}
/**
* Return a two-dimensional endss
* @param {Array<number>} flatCoordinates Flat coordinates
* @param {Array<number>} ends Linear ring end indexes
* @return {Array<Array<number>>} Two dimensional endss array that can
* be used to contruct a MultiPolygon
*/
export function inflateEnds(flatCoordinates, ends) {
const endss = [];
let offset = 0;
let prevEndIndex = 0;
for (let i = 0, ii = ends.length; i < ii; ++i) {
const end = ends[i];
// classifies an array of rings into polygons with outer rings and holes
if (!linearRingIsClockwise(flatCoordinates, offset, end, 2)) {
endss.push(ends.slice(prevEndIndex, i + 1));
} else {
if (endss.length === 0) {
continue;
}
endss[endss.length - 1].push(ends[prevEndIndex]);
}
prevEndIndex = i + 1;
offset = end;
}
return endss;
}