maplair
Version:
JavaScript library for mobile-friendly interactive maps
152 lines (151 loc) • 4.68 kB
JavaScript
import { setOptions } from "../../core/Util.js";
import { Point } from "../../geometry/Point.js";
import { Bounds } from "../../geometry/Bounds.js";
import { LatLngBounds } from "../../geo/LatLngBounds.js";
import { LatLng } from "../../geo/LatLng.js";
import { _sqClosestPointOnSegment, clipSegment, isFlat, pointToSegmentDistance, polylineCenter, simplify } from "../../geometry/LineUtil.js";
import { Path } from "./Path.js";
//#region src/layer/vector/Polyline.js
var Polyline = class Polyline extends Path {
static {
this.setDefaultOptions({
smoothFactor: .5,
noClip: false
});
}
constructor(latlngs, options) {
super();
setOptions(this, options);
this._setLatLngs(latlngs);
this.callInitHooks(Polyline);
}
getLatLngs() {
return this._latlngs;
}
setLatLngs(latlngs) {
this._setLatLngs(latlngs);
return this.redraw();
}
isEmpty() {
return !this._latlngs.length;
}
closestLayerPoint(p) {
p = new Point(p);
let minDistance = Infinity, minPoint = null, p1, p2;
const closest = _sqClosestPointOnSegment;
for (const points of this._parts) for (let i = 1, len = points.length; i < len; i++) {
p1 = points[i - 1];
p2 = points[i];
const sqDist = closest(p, p1, p2, true);
if (sqDist < minDistance) {
minDistance = sqDist;
minPoint = closest(p, p1, p2);
}
}
if (minPoint) minPoint.distance = Math.sqrt(minDistance);
return minPoint;
}
getCenter() {
if (!this._map) throw new Error("Must add layer to map before using getCenter()");
return polylineCenter(this._defaultShape(), this._map.options.crs);
}
getBounds() {
return this._bounds;
}
addLatLng(latlng, latlngs) {
latlngs ??= this._defaultShape();
latlng = new LatLng(latlng);
latlngs.push(latlng);
this._bounds.extend(latlng);
return this.redraw();
}
_setLatLngs(latlngs) {
this._bounds = new LatLngBounds();
this._latlngs = this._convertLatLngs(latlngs);
}
_defaultShape() {
return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
}
_convertLatLngs(latlngs) {
const result = [], flat = isFlat(latlngs);
for (let i = 0, len = latlngs.length; i < len; i++) if (flat) {
result[i] = new LatLng(latlngs[i]);
this._bounds.extend(result[i]);
} else result[i] = this._convertLatLngs(latlngs[i]);
return result;
}
_project() {
const pxBounds = new Bounds();
this._rings = [];
this._projectLatlngs(this._latlngs, this._rings, pxBounds);
if (this._bounds.isValid() && pxBounds.isValid()) {
this._rawPxBounds = pxBounds;
this._updateBounds();
}
}
_updateBounds() {
const w = this._clickTolerance(), p = new Point(w, w);
if (!this._rawPxBounds) return;
this._pxBounds = new Bounds([this._rawPxBounds.min.subtract(p), this._rawPxBounds.max.add(p)]);
}
_projectLatlngs(latlngs, result, projectedBounds) {
if (latlngs[0] instanceof LatLng) {
const ring = latlngs.map((latlng) => this._map.latLngToLayerPoint(latlng));
ring.forEach((r) => projectedBounds.extend(r));
result.push(ring);
} else latlngs.forEach((latlng) => this._projectLatlngs(latlng, result, projectedBounds));
}
_clipPoints() {
const bounds = this._renderer._bounds;
this._parts = [];
if (!this._pxBounds || !this._pxBounds.intersects(bounds)) return;
if (this.options.noClip) {
this._parts = this._rings;
return;
}
const parts = this._parts;
let i, j, k, len, len2, segment, points;
for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
points = this._rings[i];
for (j = 0, len2 = points.length; j < len2 - 1; j++) {
segment = clipSegment(points[j], points[j + 1], bounds, j, true);
if (!segment) continue;
parts[k] ??= [];
parts[k].push(segment[0]);
if (segment[1] !== points[j + 1] || j === len2 - 2) {
parts[k].push(segment[1]);
k++;
}
}
}
}
_simplifyPoints() {
const parts = this._parts, tolerance = this.options.smoothFactor;
for (let i = 0, len = parts.length; i < len; i++) parts[i] = simplify(parts[i], tolerance);
}
_update() {
if (!this._map) return;
this._clipPoints();
this._simplifyPoints();
this._updatePath();
}
_updatePath() {
this._renderer._updatePoly(this);
}
_containsPoint(p, closed) {
let i, j, k, len, len2, part;
const w = this._clickTolerance();
if (!this._pxBounds || !this._pxBounds.contains(p)) return false;
for (i = 0, len = this._parts.length; i < len; i++) {
part = this._parts[i];
for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
if (!closed && j === 0) continue;
if (pointToSegmentDistance(p, part[k], part[j]) <= w) return true;
}
}
return false;
}
};
//#endregion
export { Polyline };
//# sourceMappingURL=Polyline.js.map