package.geom.flat.orient.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ol Show documentation
Show all versions of ol Show documentation
OpenLayers mapping library
The newest version!
/**
* @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} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {number} end End.
* @param {number} stride Stride.
* @return {boolean|undefined} 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 `right` argument.
*
* @param {Array} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {Array} ends Array of end indexes.
* @param {number} stride Stride.
* @param {boolean} [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,
right,
) {
right = right !== undefined ? 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 `right` argument.
*
* @param {Array} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {Array>} endss Array of array of end indexes.
* @param {number} stride Stride.
* @param {boolean} [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,
right,
) {
for (let i = 0, ii = endss.length; i < ii; ++i) {
const ends = endss[i];
if (!linearRingsAreOriented(flatCoordinates, offset, ends, stride, 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 `right` argument.
*
* @param {Array} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {Array} ends Ends.
* @param {number} stride Stride.
* @param {boolean} [right] Follow the right-hand rule for orientation.
* @return {number} End.
*/
export function orientLinearRings(
flatCoordinates,
offset,
ends,
stride,
right,
) {
right = right !== undefined ? 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 `right` argument.
*
* @param {Array} flatCoordinates Flat coordinates.
* @param {number} offset Offset.
* @param {Array>} endss Array of array of end indexes.
* @param {number} stride Stride.
* @param {boolean} [right] Follow the right-hand rule for orientation.
* @return {number} End.
*/
export function orientLinearRingsArray(
flatCoordinates,
offset,
endss,
stride,
right,
) {
for (let i = 0, ii = endss.length; i < ii; ++i) {
offset = orientLinearRings(
flatCoordinates,
offset,
endss[i],
stride,
right,
);
}
return offset;
}
/**
* Return a two-dimensional endss
* @param {Array} flatCoordinates Flat coordinates
* @param {Array} ends Linear ring end indexes
* @return {Array>} Two dimensional endss array that can
* be used to construct a MultiPolygon
*/
export function inflateEnds(flatCoordinates, ends) {
const endss = [];
let offset = 0;
let prevEndIndex = 0;
let startOrientation;
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
const orientation = linearRingIsClockwise(flatCoordinates, offset, end, 2);
if (startOrientation === undefined) {
startOrientation = orientation;
}
if (orientation === startOrientation) {
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;
}