L.Util.extend(L.LineUtil, { /** * Snap to all layers * * @param latlng - original position * @param id - leaflet unique id * @param opts - snapping options * * @return closest point */ snapToLayers: function (latlng, id, opts) { var i, j, keys, feature, res, sensitivity, vertexonly, layers, minDist, minPoint, map; sensitivity = opts.sensitivity || 10; vertexonly = opts.vertexonly || false; layers = opts.layers || []; minDist = Infinity; minPoint = latlng; minPoint._feature = null; // containing layer if (!opts || !opts.layers || !opts.layers.length) { return minPoint; } map = opts.layers[0]._map; // @todo check for undef for (i = 0; i < opts.layers.length; i++) { keys = Object.keys(opts.layers[i]._layers); for (j = 0; j < keys.length; j++) { feature = opts.layers[i]._layers[keys[j]]; // Don't even try snapping to itself! if (id === feature._leaflet_id) { continue; } // GeometryCollection if (feature._layers) { var newLatlng = this.snapToLayers(latlng, id, { 'sensitivity': sensitivity, 'vertexonly': vertexonly, 'layers': [feature] }); // What if this is the same? res = {'minDist': latlng.distanceTo(newLatlng), 'minPoint': newLatlng}; // Marker } else if (feature instanceof L.Marker) { res = this._snapToLatlngs(latlng, [feature.getLatLng()], map, sensitivity, vertexonly, minDist); // Polyline } else if (feature instanceof L.Polyline) { res = this._snapToLatlngs(latlng, feature.getLatLngs(), map, sensitivity, vertexonly, minDist); // MultiPolyline } else if (feature instanceof L.MultiPolyline) { console.error('Snapping to MultiPolyline is currently unsupported', feature); res = {'minDist': minDist, 'minPoint': minPoint}; // Polygon } else if (feature instanceof L.Polygon) { res = this._snapToPolygon(latlng, feature, map, sensitivity, vertexonly, minDist); // MultiPolygon } else if (feature instanceof L.MultiPolygon) { res = this._snapToMultiPolygon(latlng, feature, map, sensitivity, vertexonly, minDist); // Unknown } else { console.error('Unsupported snapping feature', feature); res = {'minDist': minDist, 'minPoint': minPoint}; } if (res.minDist < minDist) { minDist = res.minDist; minPoint = res.minPoint; minPoint._feature = feature; } } } return minPoint; }, /** * Snap to Polygon * * @param latlng - original position * @param feature - * @param map - * @param sensitivity - * @param vertexonly - * @param minDist - * * @return minDist and minPoint */ _snapToPolygon: function (latlng, polygon, map, sensitivity, vertexonly, minDist) { var res, keys, latlngs, i, minPoint; minPoint = null; latlngs = polygon.getLatLngs(); latlngs.push(latlngs[0]); res = this._snapToLatlngs(latlng, polygon.getLatLngs(), map, sensitivity, vertexonly, minDist); if (res.minDist < minDist) { minDist = res.minDist; minPoint = res.minPoint; } keys = Object.keys(polygon._holes); for (i = 0; i < keys.length; i++) { latlngs = polygon._holes[keys[i]]; latlngs.push(latlngs[0]); res = this._snapToLatlngs(latlng, polygon._holes[keys[i]], map, sensitivity, vertexonly, minDist); if (res.minDist < minDist) { minDist = res.minDist; minPoint = res.minPoint; } } return {'minDist': minDist, 'minPoint': minPoint}; }, /** * Snap to MultiPolygon * * @param latlng - original position * @param feature - * @param map - * @param sensitivity - * @param vertexonly - * @param minDist - * * @return minDist and minPoint */ _snapToMultiPolygon: function (latlng, multipolygon, map, sensitivity, vertexonly, minDist) { var i, keys, res, minPoint; minPoint = null; keys = Object.keys(multipolygon._layers); for (i = 0; i < keys.length; i++) { res = this._snapToPolygon(latlng, multipolygon._layers[keys[i]], map, sensitivity, vertexonly, minDist); if (res.minDist < minDist) { minDist = res.minDist; minPoint = res.minPoint; } } return {'minDist': minDist, 'minPoint': minPoint}; }, /** * Snap to of * * @param latlng - cursor click * @param latlngs - array of to snap to * @param opts - snapping options * @param isPolygon - if feature is a polygon * * @return minDist and minPoint */ _snapToLatlngs: function (latlng, latlngs, map, sensitivity, vertexonly, minDist) { var i, tmpDist, minPoint, p, p1, p2, d2; p = map.latLngToLayerPoint(latlng); p1 = minPoint = null; for (i = 0; i < latlngs.length; i++) { p2 = map.latLngToLayerPoint(latlngs[i]); if (!vertexonly && p1 !== null) { tmpDist = L.LineUtil.pointToSegmentDistance(p, p1, p2); if (tmpDist < minDist && tmpDist <= sensitivity) { minDist = tmpDist; minPoint = map.layerPointToLatLng(L.LineUtil.closestPointOnSegment(p, p1, p2)); } } else if ((d2 = p.distanceTo(p2)) && d2 <= sensitivity && d2 < minDist) { minDist = d2; minPoint = latlngs[i]; } p1 = p2; } return {'minDist': minDist, 'minPoint': minPoint}; } });