UNPKG

maplair

Version:

JavaScript library for mobile-friendly interactive maps

169 lines (168 loc) • 5.76 kB
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