maplair
Version:
JavaScript library for mobile-friendly interactive maps
169 lines (168 loc) • 5.76 kB
JavaScript
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
import { Point } from "./Point.js";
import { LatLngBounds } from "../geo/LatLngBounds.js";
import { LatLng } from "../geo/LatLng.js";
import { centroid } from "./PolyUtil.js";
//#region src/geometry/LineUtil.js
var LineUtil_exports = /* @__PURE__ */ __exportAll({
_getBitCode: () => _getBitCode,
_getEdgeIntersection: () => _getEdgeIntersection,
_sqClosestPointOnSegment: () => _sqClosestPointOnSegment,
clipSegment: () => clipSegment,
closestPointOnSegment: () => closestPointOnSegment,
isFlat: () => isFlat,
pointToSegmentDistance: () => pointToSegmentDistance,
polylineCenter: () => polylineCenter,
simplify: () => simplify
});
function simplify(points, tolerance) {
if (!tolerance || !points.length) return points.slice();
const sqTolerance = tolerance * tolerance;
points = _reducePoints(points, sqTolerance);
points = _simplifyDP(points, sqTolerance);
return points;
}
function pointToSegmentDistance(p, p1, p2) {
return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
}
function closestPointOnSegment(p, p1, p2) {
return _sqClosestPointOnSegment(p, p1, p2);
}
function _simplifyDP(points, sqTolerance) {
const len = points.length, markers = new Uint8Array(len);
markers[0] = markers[len - 1] = 1;
_simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
let i;
const newPoints = [];
for (i = 0; i < len; i++) if (markers[i]) newPoints.push(points[i]);
return newPoints;
}
function _simplifyDPStep(points, markers, sqTolerance, first, last) {
let maxSqDist = 0, index, i, sqDist;
for (i = first + 1; i <= last - 1; i++) {
sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
if (sqDist > maxSqDist) {
index = i;
maxSqDist = sqDist;
}
}
if (maxSqDist > sqTolerance) {
markers[index] = 1;
_simplifyDPStep(points, markers, sqTolerance, first, index);
_simplifyDPStep(points, markers, sqTolerance, index, last);
}
}
function _reducePoints(points, sqTolerance) {
const reducedPoints = [points[0]];
let prev = 0;
for (let i = 1; i < points.length; i++) if (_sqDist(points[i], points[prev]) > sqTolerance) {
reducedPoints.push(points[i]);
prev = i;
}
if (prev < points.length - 1) reducedPoints.push(points[points.length - 1]);
return reducedPoints;
}
let _lastCode;
function clipSegment(a, b, bounds, useLastCode, round) {
let codeA = useLastCode ? _lastCode : _getBitCode(a, bounds), codeB = _getBitCode(b, bounds), codeOut, p, newCode;
_lastCode = codeB;
while (true) {
if (!(codeA | codeB)) return [a, b];
if (codeA & codeB) return false;
codeOut = codeA || codeB;
p = _getEdgeIntersection(a, b, codeOut, bounds, round);
newCode = _getBitCode(p, bounds);
if (codeOut === codeA) {
a = p;
codeA = newCode;
} else {
b = p;
codeB = newCode;
}
}
}
function _getEdgeIntersection(a, b, code, bounds, round) {
const dx = b.x - a.x, dy = b.y - a.y, min = bounds.min, max = bounds.max;
let x, y;
if (code & 8) {
x = a.x + dx * (max.y - a.y) / dy;
y = max.y;
} else if (code & 4) {
x = a.x + dx * (min.y - a.y) / dy;
y = min.y;
} else if (code & 2) {
x = max.x;
y = a.y + dy * (max.x - a.x) / dx;
} else if (code & 1) {
x = min.x;
y = a.y + dy * (min.x - a.x) / dx;
}
return new Point(x, y, round);
}
function _getBitCode(p, bounds) {
let code = 0;
if (p.x < bounds.min.x) code |= 1;
else if (p.x > bounds.max.x) code |= 2;
if (p.y < bounds.min.y) code |= 4;
else if (p.y > bounds.max.y) code |= 8;
return code;
}
function _sqDist(p1, p2) {
const dx = p2.x - p1.x, dy = p2.y - p1.y;
return dx * dx + dy * dy;
}
function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
let x = p1.x, y = p1.y, dx = p2.x - x, dy = p2.y - y, t;
const dot = dx * dx + dy * dy;
if (dot > 0) {
t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
if (t > 1) {
x = p2.x;
y = p2.y;
} else if (t > 0) {
x += dx * t;
y += dy * t;
}
}
dx = p.x - x;
dy = p.y - y;
return sqDist ? dx * dx + dy * dy : new Point(x, y);
}
function isFlat(latlngs) {
return !Array.isArray(latlngs[0]) || typeof latlngs[0][0] !== "object" && typeof latlngs[0][0] !== "undefined";
}
function polylineCenter(latlngs, crs) {
let i, halfDist, segDist, dist, p1, p2, ratio, center;
if (!latlngs || latlngs.length === 0) throw new Error("latlngs not passed");
if (!isFlat(latlngs)) {
console.warn("latlngs are not flat! Only the first ring will be used");
latlngs = latlngs[0];
}
let centroidLatLng = new LatLng([0, 0]);
const bounds = new LatLngBounds(latlngs);
if (bounds.getNorthWest().distanceTo(bounds.getSouthWest()) * bounds.getNorthEast().distanceTo(bounds.getNorthWest()) < 1700) centroidLatLng = centroid(latlngs);
const len = latlngs.length;
const points = [];
for (i = 0; i < len; i++) {
const latlng = new LatLng(latlngs[i]);
points.push(crs.project(new LatLng([latlng.lat - centroidLatLng.lat, latlng.lng - centroidLatLng.lng])));
}
for (i = 0, halfDist = 0; i < len - 1; i++) halfDist += points[i].distanceTo(points[i + 1]) / 2;
if (halfDist === 0) center = points[0];
else for (i = 0, dist = 0; i < len - 1; i++) {
p1 = points[i];
p2 = points[i + 1];
segDist = p1.distanceTo(p2);
dist += segDist;
if (dist > halfDist) {
ratio = (dist - halfDist) / segDist;
center = [p2.x - ratio * (p2.x - p1.x), p2.y - ratio * (p2.y - p1.y)];
break;
}
}
const latlngCenter = crs.unproject(new Point(center));
return new LatLng([latlngCenter.lat + centroidLatLng.lat, latlngCenter.lng + centroidLatLng.lng]);
}
//#endregion
export { LineUtil_exports, _getBitCode, _getEdgeIntersection, _sqClosestPointOnSegment, clipSegment, closestPointOnSegment, isFlat, pointToSegmentDistance, polylineCenter, simplify };
//# sourceMappingURL=LineUtil.js.map