/* * L.Polyline implements polyline vector layer (a set of points connected with lines) */ L.Polyline = L.Path.extend({ options: { // how much to simplify the polyline on each zoom level // more = better performance and smoother look, less = more accurate smoothFactor: 1.0 // noClip: false }, initialize: function (latlngs, options) { L.setOptions(this, options); this._setLatLngs(latlngs); }, getLatLngs: function () { // TODO rings return this._latlngs; }, setLatLngs: function (latlngs) { this._setLatLngs(latlngs); return this.redraw(); }, addLatLng: function (latlng) { // TODO rings latlng = L.latLng(latlng); this._latlngs.push(latlng); this._bounds.extend(latlng); return this.redraw(); }, spliceLatLngs: function () { // TODO rings var removed = [].splice.apply(this._latlngs, arguments); this._setLatLngs(this._latlngs); this.redraw(); return removed; }, closestLayerPoint: function (p) { var minDistance = Infinity, minPoint = null, closest = L.LineUtil._sqClosestPointOnSegment, p1, p2; for (var j = 0, jLen = this._parts.length; j < jLen; j++) { var points = this._parts[j]; for (var i = 1, len = points.length; i < len; i++) { p1 = points[i - 1]; p2 = points[i]; var 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: function () { var i, halfDist, segDist, dist, p1, p2, ratio, points = this._rings[0], len = points.length; // polyline centroid algorithm; only uses the first ring if there are multiple for (i = 0, halfDist = 0; i < len - 1; i++) { halfDist += points[i].distanceTo(points[i + 1]) / 2; } 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; return this._map.layerPointToLatLng([ p2.x - ratio * (p2.x - p1.x), p2.y - ratio * (p2.y - p1.y) ]); } } }, getBounds: function () { return this._bounds; }, _setLatLngs: function (latlngs) { this._bounds = new L.LatLngBounds(); this._latlngs = this._convertLatLngs(latlngs); }, // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way _convertLatLngs: function (latlngs) { var result = [], flat = this._flat(latlngs); for (var i = 0, len = latlngs.length; i < len; i++) { if (flat) { result[i] = L.latLng(latlngs[i]); this._bounds.extend(result[i]); } else { result[i] = this._convertLatLngs(latlngs[i]); } } return result; }, _flat: function (latlngs) { // true if it's a flat array of latlngs; false if nested return !L.Util.isArray(latlngs[0]) || typeof latlngs[0][0] !== 'object'; }, _project: function () { this._rings = []; this._projectLatlngs(this._latlngs, this._rings); // project bounds as well to use later for Canvas hit detection/etc. var w = this._clickTolerance(), p = new L.Point(w, -w); if (this._latlngs.length) { this._pxBounds = new L.Bounds( this._map.latLngToLayerPoint(this._bounds.getSouthWest())._subtract(p), this._map.latLngToLayerPoint(this._bounds.getNorthEast())._add(p)); } }, // recursively turns latlngs into a set of rings with projected coordinates _projectLatlngs: function (latlngs, result) { var flat = latlngs[0] instanceof L.LatLng, len = latlngs.length, i, ring; if (flat) { ring = []; for (i = 0; i < len; i++) { ring[i] = this._map.latLngToLayerPoint(latlngs[i]); } result.push(ring); } else { for (i = 0; i < len; i++) { this._projectLatlngs(latlngs[i], result); } } }, // clip polyline by renderer bounds so that we have less to render for performance _clipPoints: function () { if (this.options.noClip) { this._parts = this._rings; return; } this._parts = []; var parts = this._parts, bounds = this._renderer._bounds, 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 = L.LineUtil.clipSegment(points[j], points[j + 1], bounds, j); if (!segment) { continue; } parts[k] = parts[k] || []; parts[k].push(segment[0]); // if segment goes out of screen, or it's the last one, it's the end of the line part if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) { parts[k].push(segment[1]); k++; } } } }, // simplify each clipped part of the polyline for performance _simplifyPoints: function () { var parts = this._parts, tolerance = this.options.smoothFactor; for (var i = 0, len = parts.length; i < len; i++) { parts[i] = L.LineUtil.simplify(parts[i], tolerance); } }, _update: function () { if (!this._map) { return; } this._clipPoints(); this._simplifyPoints(); this._updatePath(); }, _updatePath: function () { this._renderer._updatePoly(this); } }); L.polyline = function (latlngs, options) { return new L.Polyline(latlngs, options); };