leaflet-draw
Version:
Vector drawing plugin for Leaflet
1,906 lines (1,598 loc) • 125 kB
JavaScript
/*
Leaflet.draw 1.0.4, a plugin that adds drawing and editing tools to Leaflet powered maps.
(c) 2012-2017, Jacob Toye, Jon West, Smartrak, Leaflet
https://github.com/Leaflet/Leaflet.draw
http://leafletjs.com
*/
(function (window, document, undefined) {/**
* Leaflet.draw assumes that you have already included the Leaflet library.
*/
L.drawVersion = "1.0.4";
/**
* @class L.Draw
* @aka Draw
*
*
* To add the draw toolbar set the option drawControl: true in the map options.
*
* @example
* ```js
* var map = L.map('map', {drawControl: true}).setView([51.505, -0.09], 13);
*
* L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
* attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
* }).addTo(map);
* ```
*
* ### Adding the edit toolbar
* To use the edit toolbar you must initialise the Leaflet.draw control and manually add it to the map.
*
* ```js
* var map = L.map('map').setView([51.505, -0.09], 13);
*
* L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
* attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
* }).addTo(map);
*
* // FeatureGroup is to store editable layers
* var drawnItems = new L.FeatureGroup();
* map.addLayer(drawnItems);
*
* var drawControl = new L.Control.Draw({
* edit: {
* featureGroup: drawnItems
* }
* });
* map.addControl(drawControl);
* ```
*
* The key here is the featureGroup option. This tells the plugin which FeatureGroup contains the layers that
* should be editable. The featureGroup can contain 0 or more features with geometry types Point, LineString, and Polygon.
* Leaflet.draw does not work with multigeometry features such as MultiPoint, MultiLineString, MultiPolygon,
* or GeometryCollection. If you need to add multigeometry features to the draw plugin, convert them to a
* FeatureCollection of non-multigeometries (Points, LineStrings, or Polygons).
*/
L.Draw = {};
/**
* @class L.drawLocal
* @aka L.drawLocal
*
* The core toolbar class of the API — it is used to create the toolbar ui
*
* @example
* ```js
* var modifiedDraw = L.drawLocal.extend({
* draw: {
* toolbar: {
* buttons: {
* polygon: 'Draw an awesome polygon'
* }
* }
* }
* });
* ```
*
* The default state for the control is the draw toolbar just below the zoom control.
* This will allow map users to draw vectors and markers.
* **Please note the edit toolbar is not enabled by default.**
*/
L.drawLocal = {
// format: {
// numeric: {
// delimiters: {
// thousands: ',',
// decimal: '.'
// }
// }
// },
draw: {
toolbar: {
// #TODO: this should be reorganized where actions are nested in actions
// ex: actions.undo or actions.cancel
actions: {
title: 'Cancel drawing',
text: 'Cancel'
},
finish: {
title: 'Finish drawing',
text: 'Finish'
},
undo: {
title: 'Delete last point drawn',
text: 'Delete last point'
},
buttons: {
polyline: 'Draw a polyline',
polygon: 'Draw a polygon',
rectangle: 'Draw a rectangle',
circle: 'Draw a circle',
marker: 'Draw a marker',
circlemarker: 'Draw a circlemarker'
}
},
handlers: {
circle: {
tooltip: {
start: 'Click and drag to draw circle.'
},
radius: 'Radius'
},
circlemarker: {
tooltip: {
start: 'Click map to place circle marker.'
}
},
marker: {
tooltip: {
start: 'Click map to place marker.'
}
},
polygon: {
tooltip: {
start: 'Click to start drawing shape.',
cont: 'Click to continue drawing shape.',
end: 'Click first point to close this shape.'
}
},
polyline: {
error: '<strong>Error:</strong> shape edges cannot cross!',
tooltip: {
start: 'Click to start drawing line.',
cont: 'Click to continue drawing line.',
end: 'Click last point to finish line.'
}
},
rectangle: {
tooltip: {
start: 'Click and drag to draw rectangle.'
}
},
simpleshape: {
tooltip: {
end: 'Release mouse to finish drawing.'
}
}
}
},
edit: {
toolbar: {
actions: {
save: {
title: 'Save changes',
text: 'Save'
},
cancel: {
title: 'Cancel editing, discards all changes',
text: 'Cancel'
},
clearAll: {
title: 'Clear all layers',
text: 'Clear All'
}
},
buttons: {
edit: 'Edit layers',
editDisabled: 'No layers to edit',
remove: 'Delete layers',
removeDisabled: 'No layers to delete'
}
},
handlers: {
edit: {
tooltip: {
text: 'Drag handles or markers to edit features.',
subtext: 'Click cancel to undo changes.'
}
},
remove: {
tooltip: {
text: 'Click on a feature to remove.'
}
}
}
}
};
/**
* ### Events
* Once you have successfully added the Leaflet.draw plugin to your map you will want to respond to the different
* actions users can initiate. The following events will be triggered on the map:
*
* @class L.Draw.Event
* @aka Draw.Event
*
* Use `L.Draw.Event.EVENTNAME` constants to ensure events are correct.
*
* @example
* ```js
* map.on(L.Draw.Event.CREATED; function (e) {
* var type = e.layerType,
* layer = e.layer;
*
* if (type === 'marker') {
* // Do marker specific actions
* }
*
* // Do whatever else you need to. (save to db; add to map etc)
* map.addLayer(layer);
*});
* ```
*/
L.Draw.Event = {};
/**
* @event draw:created: PolyLine; Polygon; Rectangle; Circle; Marker | String
*
* Layer that was just created.
* The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker`
* Triggered when a new vector or marker has been created.
*
*/
L.Draw.Event.CREATED = 'draw:created';
/**
* @event draw:edited: LayerGroup
*
* List of all layers just edited on the map.
*
*
* Triggered when layers in the FeatureGroup; initialised with the plugin; have been edited and saved.
*
* @example
* ```js
* map.on('draw:edited', function (e) {
* var layers = e.layers;
* layers.eachLayer(function (layer) {
* //do whatever you want; most likely save back to db
* });
* });
* ```
*/
L.Draw.Event.EDITED = 'draw:edited';
/**
* @event draw:deleted: LayerGroup
*
* List of all layers just removed from the map.
*
* Triggered when layers have been removed (and saved) from the FeatureGroup.
*/
L.Draw.Event.DELETED = 'draw:deleted';
/**
* @event draw:drawstart: String
*
* The type of layer this is. One of:`polyline`; `polygon`; `rectangle`; `circle`; `marker`
*
* Triggered when the user has chosen to draw a particular vector or marker.
*/
L.Draw.Event.DRAWSTART = 'draw:drawstart';
/**
* @event draw:drawstop: String
*
* The type of layer this is. One of: `polyline`; `polygon`; `rectangle`; `circle`; `marker`
*
* Triggered when the user has finished a particular vector or marker.
*/
L.Draw.Event.DRAWSTOP = 'draw:drawstop';
/**
* @event draw:drawvertex: LayerGroup
*
* List of all layers just being added from the map.
*
* Triggered when a vertex is created on a polyline or polygon.
*/
L.Draw.Event.DRAWVERTEX = 'draw:drawvertex';
/**
* @event draw:editstart: String
*
* The type of edit this is. One of: `edit`
*
* Triggered when the user starts edit mode by clicking the edit tool button.
*/
L.Draw.Event.EDITSTART = 'draw:editstart';
/**
* @event draw:editmove: ILayer
*
* Layer that was just moved.
*
* Triggered as the user moves a rectangle; circle or marker.
*/
L.Draw.Event.EDITMOVE = 'draw:editmove';
/**
* @event draw:editresize: ILayer
*
* Layer that was just moved.
*
* Triggered as the user resizes a rectangle or circle.
*/
L.Draw.Event.EDITRESIZE = 'draw:editresize';
/**
* @event draw:editvertex: LayerGroup
*
* List of all layers just being edited from the map.
*
* Triggered when a vertex is edited on a polyline or polygon.
*/
L.Draw.Event.EDITVERTEX = 'draw:editvertex';
/**
* @event draw:editstop: String
*
* The type of edit this is. One of: `edit`
*
* Triggered when the user has finshed editing (edit mode) and saves edits.
*/
L.Draw.Event.EDITSTOP = 'draw:editstop';
/**
* @event draw:deletestart: String
*
* The type of edit this is. One of: `remove`
*
* Triggered when the user starts remove mode by clicking the remove tool button.
*/
L.Draw.Event.DELETESTART = 'draw:deletestart';
/**
* @event draw:deletestop: String
*
* The type of edit this is. One of: `remove`
*
* Triggered when the user has finished removing shapes (remove mode) and saves.
*/
L.Draw.Event.DELETESTOP = 'draw:deletestop';
/**
* @event draw:toolbaropened: String
*
* Triggered when a toolbar is opened.
*/
L.Draw.Event.TOOLBAROPENED = 'draw:toolbaropened';
/**
* @event draw:toolbarclosed: String
*
* Triggered when a toolbar is closed.
*/
L.Draw.Event.TOOLBARCLOSED = 'draw:toolbarclosed';
/**
* @event draw:markercontext: String
*
* Triggered when a marker is right clicked.
*/
L.Draw.Event.MARKERCONTEXT = 'draw:markercontext';
L.Draw = L.Draw || {};
/**
* @class L.Draw.Feature
* @aka Draw.Feature
*/
L.Draw.Feature = L.Handler.extend({
// @method initialize(): void
initialize: function (map, options) {
this._map = map;
this._container = map._container;
this._overlayPane = map._panes.overlayPane;
this._popupPane = map._panes.popupPane;
// Merge default shapeOptions options with custom shapeOptions
if (options && options.shapeOptions) {
options.shapeOptions = L.Util.extend({}, this.options.shapeOptions, options.shapeOptions);
}
L.setOptions(this, options);
var version = L.version.split('.');
//If Version is >= 1.2.0
if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {
L.Draw.Feature.include(L.Evented.prototype);
} else {
L.Draw.Feature.include(L.Mixin.Events);
}
},
// @method enable(): void
// Enables this handler
enable: function () {
if (this._enabled) {
return;
}
L.Handler.prototype.enable.call(this);
this.fire('enabled', {handler: this.type});
this._map.fire(L.Draw.Event.DRAWSTART, {layerType: this.type});
},
// @method disable(): void
disable: function () {
if (!this._enabled) {
return;
}
L.Handler.prototype.disable.call(this);
this._map.fire(L.Draw.Event.DRAWSTOP, {layerType: this.type});
this.fire('disabled', {handler: this.type});
},
// @method addHooks(): void
// Add's event listeners to this handler
addHooks: function () {
var map = this._map;
if (map) {
L.DomUtil.disableTextSelection();
map.getContainer().focus();
this._tooltip = new L.Draw.Tooltip(this._map);
L.DomEvent.on(this._container, 'keyup', this._cancelDrawing, this);
}
},
// @method removeHooks(): void
// Removes event listeners from this handler
removeHooks: function () {
if (this._map) {
L.DomUtil.enableTextSelection();
this._tooltip.dispose();
this._tooltip = null;
L.DomEvent.off(this._container, 'keyup', this._cancelDrawing, this);
}
},
// @method setOptions(object): void
// Sets new options to this handler
setOptions: function (options) {
L.setOptions(this, options);
},
_fireCreatedEvent: function (layer) {
this._map.fire(L.Draw.Event.CREATED, {layer: layer, layerType: this.type});
},
// Cancel drawing when the escape key is pressed
_cancelDrawing: function (e) {
if (e.keyCode === 27) {
this._map.fire('draw:canceled', {layerType: this.type});
this.disable();
}
}
});
/**
* @class L.Draw.Polyline
* @aka Draw.Polyline
* @inherits L.Draw.Feature
*/
L.Draw.Polyline = L.Draw.Feature.extend({
statics: {
TYPE: 'polyline'
},
Poly: L.Polyline,
options: {
allowIntersection: true,
repeatMode: false,
drawError: {
color: '#b00b00',
timeout: 2500
},
icon: new L.DivIcon({
iconSize: new L.Point(8, 8),
className: 'leaflet-div-icon leaflet-editing-icon'
}),
touchIcon: new L.DivIcon({
iconSize: new L.Point(20, 20),
className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'
}),
guidelineDistance: 20,
maxGuideLineLength: 4000,
shapeOptions: {
stroke: true,
color: '#3388ff',
weight: 4,
opacity: 0.5,
fill: false,
clickable: true
},
metric: true, // Whether to use the metric measurement system or imperial
feet: true, // When not metric, to use feet instead of yards for display.
nautic: false, // When not metric, not feet use nautic mile for display
showLength: true, // Whether to display distance in the tooltip
zIndexOffset: 2000, // This should be > than the highest z-index any map layers
factor: 1, // To change distance calculation
maxPoints: 0 // Once this number of points are placed, finish shape
},
// @method initialize(): void
initialize: function (map, options) {
// if touch, switch to touch icon
if (L.Browser.touch) {
this.options.icon = this.options.touchIcon;
}
// Need to set this here to ensure the correct message is used.
this.options.drawError.message = L.drawLocal.draw.handlers.polyline.error;
// Merge default drawError options with custom options
if (options && options.drawError) {
options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
}
// Save the type so super can fire, need to do this as cannot do this.TYPE :(
this.type = L.Draw.Polyline.TYPE;
L.Draw.Feature.prototype.initialize.call(this, map, options);
},
// @method addHooks(): void
// Add listener hooks to this handler
addHooks: function () {
L.Draw.Feature.prototype.addHooks.call(this);
if (this._map) {
this._markers = [];
this._markerGroup = new L.LayerGroup();
this._map.addLayer(this._markerGroup);
this._poly = new L.Polyline([], this.options.shapeOptions);
this._tooltip.updateContent(this._getTooltipText());
// Make a transparent marker that will used to catch click events. These click
// events will create the vertices. We need to do this so we can ensure that
// we can create vertices over other map layers (markers, vector layers). We
// also do not want to trigger any click handlers of objects we are clicking on
// while drawing.
if (!this._mouseMarker) {
this._mouseMarker = L.marker(this._map.getCenter(), {
icon: L.divIcon({
className: 'leaflet-mouse-marker',
iconAnchor: [20, 20],
iconSize: [40, 40]
}),
opacity: 0,
zIndexOffset: this.options.zIndexOffset
});
}
this._mouseMarker
.on('mouseout', this._onMouseOut, this)
.on('mousemove', this._onMouseMove, this) // Necessary to prevent 0.8 stutter
.on('mousedown', this._onMouseDown, this)
.on('mouseup', this._onMouseUp, this) // Necessary for 0.8 compatibility
.addTo(this._map);
this._map
.on('mouseup', this._onMouseUp, this) // Necessary for 0.7 compatibility
.on('mousemove', this._onMouseMove, this)
.on('zoomlevelschange', this._onZoomEnd, this)
.on('touchstart', this._onTouch, this)
.on('zoomend', this._onZoomEnd, this);
}
},
// @method removeHooks(): void
// Remove listener hooks from this handler.
removeHooks: function () {
L.Draw.Feature.prototype.removeHooks.call(this);
this._clearHideErrorTimeout();
this._cleanUpShape();
// remove markers from map
this._map.removeLayer(this._markerGroup);
delete this._markerGroup;
delete this._markers;
this._map.removeLayer(this._poly);
delete this._poly;
this._mouseMarker
.off('mousedown', this._onMouseDown, this)
.off('mouseout', this._onMouseOut, this)
.off('mouseup', this._onMouseUp, this)
.off('mousemove', this._onMouseMove, this);
this._map.removeLayer(this._mouseMarker);
delete this._mouseMarker;
// clean up DOM
this._clearGuides();
this._map
.off('mouseup', this._onMouseUp, this)
.off('mousemove', this._onMouseMove, this)
.off('zoomlevelschange', this._onZoomEnd, this)
.off('zoomend', this._onZoomEnd, this)
.off('touchstart', this._onTouch, this)
.off('click', this._onTouch, this);
},
// @method deleteLastVertex(): void
// Remove the last vertex from the polyline, removes polyline from map if only one point exists.
deleteLastVertex: function () {
if (this._markers.length <= 1) {
return;
}
var lastMarker = this._markers.pop(),
poly = this._poly,
// Replaces .spliceLatLngs()
latlngs = poly.getLatLngs(),
latlng = latlngs.splice(-1, 1)[0];
this._poly.setLatLngs(latlngs);
this._markerGroup.removeLayer(lastMarker);
if (poly.getLatLngs().length < 2) {
this._map.removeLayer(poly);
}
this._vertexChanged(latlng, false);
},
// @method addVertex(): void
// Add a vertex to the end of the polyline
addVertex: function (latlng) {
var markersLength = this._markers.length;
// markersLength must be greater than or equal to 2 before intersections can occur
if (markersLength >= 2 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) {
this._showErrorTooltip();
return;
}
else if (this._errorShown) {
this._hideErrorTooltip();
}
this._markers.push(this._createMarker(latlng));
this._poly.addLatLng(latlng);
if (this._poly.getLatLngs().length === 2) {
this._map.addLayer(this._poly);
}
this._vertexChanged(latlng, true);
},
// @method completeShape(): void
// Closes the polyline between the first and last points
completeShape: function () {
if (this._markers.length <= 1 || !this._shapeIsValid()) {
return;
}
this._fireCreatedEvent();
this.disable();
if (this.options.repeatMode) {
this.enable();
}
},
_finishShape: function () {
var latlngs = this._poly._defaultShape ? this._poly._defaultShape() : this._poly.getLatLngs();
var intersects = this._poly.newLatLngIntersects(latlngs[latlngs.length - 1]);
if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) {
this._showErrorTooltip();
return;
}
this._fireCreatedEvent();
this.disable();
if (this.options.repeatMode) {
this.enable();
}
},
// Called to verify the shape is valid when the user tries to finish it
// Return false if the shape is not valid
_shapeIsValid: function () {
return true;
},
_onZoomEnd: function () {
if (this._markers !== null) {
this._updateGuide();
}
},
_onMouseMove: function (e) {
var newPos = this._map.mouseEventToLayerPoint(e.originalEvent);
var latlng = this._map.layerPointToLatLng(newPos);
// Save latlng
// should this be moved to _updateGuide() ?
this._currentLatLng = latlng;
this._updateTooltip(latlng);
// Update the guide line
this._updateGuide(newPos);
// Update the mouse marker position
this._mouseMarker.setLatLng(latlng);
L.DomEvent.preventDefault(e.originalEvent);
},
_vertexChanged: function (latlng, added) {
this._map.fire(L.Draw.Event.DRAWVERTEX, {layers: this._markerGroup});
this._updateFinishHandler();
this._updateRunningMeasure(latlng, added);
this._clearGuides();
this._updateTooltip();
},
_onMouseDown: function (e) {
if (!this._clickHandled && !this._touchHandled && !this._disableMarkers) {
this._onMouseMove(e);
this._clickHandled = true;
this._disableNewMarkers();
var originalEvent = e.originalEvent;
var clientX = originalEvent.clientX;
var clientY = originalEvent.clientY;
this._startPoint.call(this, clientX, clientY);
}
},
_startPoint: function (clientX, clientY) {
this._mouseDownOrigin = L.point(clientX, clientY);
},
_onMouseUp: function (e) {
var originalEvent = e.originalEvent;
var clientX = originalEvent.clientX;
var clientY = originalEvent.clientY;
this._endPoint.call(this, clientX, clientY, e);
this._clickHandled = null;
},
_endPoint: function (clientX, clientY, e) {
if (this._mouseDownOrigin) {
var dragCheckDistance = L.point(clientX, clientY)
.distanceTo(this._mouseDownOrigin);
var lastPtDistance = this._calculateFinishDistance(e.latlng);
if (this.options.maxPoints > 1 && this.options.maxPoints == this._markers.length + 1) {
this.addVertex(e.latlng);
this._finishShape();
} else if (lastPtDistance < 10 && L.Browser.touch) {
this._finishShape();
} else if (Math.abs(dragCheckDistance) < 9 * (window.devicePixelRatio || 1)) {
this.addVertex(e.latlng);
}
this._enableNewMarkers(); // after a short pause, enable new markers
}
this._mouseDownOrigin = null;
},
// ontouch prevented by clickHandled flag because some browsers fire both click/touch events,
// causing unwanted behavior
_onTouch: function (e) {
var originalEvent = e.originalEvent;
var clientX;
var clientY;
if (originalEvent.touches && originalEvent.touches[0] && !this._clickHandled && !this._touchHandled && !this._disableMarkers) {
clientX = originalEvent.touches[0].clientX;
clientY = originalEvent.touches[0].clientY;
this._disableNewMarkers();
this._touchHandled = true;
this._startPoint.call(this, clientX, clientY);
this._endPoint.call(this, clientX, clientY, e);
this._touchHandled = null;
}
this._clickHandled = null;
},
_onMouseOut: function () {
if (this._tooltip) {
this._tooltip._onMouseOut.call(this._tooltip);
}
},
// calculate if we are currently within close enough distance
// of the closing point (first point for shapes, last point for lines)
// this is semi-ugly code but the only reliable way i found to get the job done
// note: calculating point.distanceTo between mouseDownOrigin and last marker did NOT work
_calculateFinishDistance: function (potentialLatLng) {
var lastPtDistance;
if (this._markers.length > 0) {
var finishMarker;
if (this.type === L.Draw.Polyline.TYPE) {
finishMarker = this._markers[this._markers.length - 1];
} else if (this.type === L.Draw.Polygon.TYPE) {
finishMarker = this._markers[0];
} else {
return Infinity;
}
var lastMarkerPoint = this._map.latLngToContainerPoint(finishMarker.getLatLng()),
potentialMarker = new L.Marker(potentialLatLng, {
icon: this.options.icon,
zIndexOffset: this.options.zIndexOffset * 2
});
var potentialMarkerPint = this._map.latLngToContainerPoint(potentialMarker.getLatLng());
lastPtDistance = lastMarkerPoint.distanceTo(potentialMarkerPint);
} else {
lastPtDistance = Infinity;
}
return lastPtDistance;
},
_updateFinishHandler: function () {
var markerCount = this._markers.length;
// The last marker should have a click handler to close the polyline
if (markerCount > 1) {
this._markers[markerCount - 1].on('click', this._finishShape, this);
}
// Remove the old marker click handler (as only the last point should close the polyline)
if (markerCount > 2) {
this._markers[markerCount - 2].off('click', this._finishShape, this);
}
},
_createMarker: function (latlng) {
var marker = new L.Marker(latlng, {
icon: this.options.icon,
zIndexOffset: this.options.zIndexOffset * 2
});
this._markerGroup.addLayer(marker);
return marker;
},
_updateGuide: function (newPos) {
var markerCount = this._markers ? this._markers.length : 0;
if (markerCount > 0) {
newPos = newPos || this._map.latLngToLayerPoint(this._currentLatLng);
// draw the guide line
this._clearGuides();
this._drawGuide(
this._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()),
newPos
);
}
},
_updateTooltip: function (latLng) {
var text = this._getTooltipText();
if (latLng) {
this._tooltip.updatePosition(latLng);
}
if (!this._errorShown) {
this._tooltip.updateContent(text);
}
},
_drawGuide: function (pointA, pointB) {
var length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))),
guidelineDistance = this.options.guidelineDistance,
maxGuideLineLength = this.options.maxGuideLineLength,
// Only draw a guideline with a max length
i = length > maxGuideLineLength ? length - maxGuideLineLength : guidelineDistance,
fraction,
dashPoint,
dash;
//create the guides container if we haven't yet
if (!this._guidesContainer) {
this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane);
}
//draw a dash every GuildeLineDistance
for (; i < length; i += this.options.guidelineDistance) {
//work out fraction along line we are
fraction = i / length;
//calculate new x,y point
dashPoint = {
x: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)),
y: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y))
};
//add guide dash to guide container
dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer);
dash.style.backgroundColor =
!this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color;
L.DomUtil.setPosition(dash, dashPoint);
}
},
_updateGuideColor: function (color) {
if (this._guidesContainer) {
for (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) {
this._guidesContainer.childNodes[i].style.backgroundColor = color;
}
}
},
// removes all child elements (guide dashes) from the guides container
_clearGuides: function () {
if (this._guidesContainer) {
while (this._guidesContainer.firstChild) {
this._guidesContainer.removeChild(this._guidesContainer.firstChild);
}
}
},
_getTooltipText: function () {
var showLength = this.options.showLength,
labelText, distanceStr;
if (this._markers.length === 0) {
labelText = {
text: L.drawLocal.draw.handlers.polyline.tooltip.start
};
} else {
distanceStr = showLength ? this._getMeasurementString() : '';
if (this._markers.length === 1) {
labelText = {
text: L.drawLocal.draw.handlers.polyline.tooltip.cont,
subtext: distanceStr
};
} else {
labelText = {
text: L.drawLocal.draw.handlers.polyline.tooltip.end,
subtext: distanceStr
};
}
}
return labelText;
},
_updateRunningMeasure: function (latlng, added) {
var markersLength = this._markers.length,
previousMarkerIndex, distance;
if (this._markers.length === 1) {
this._measurementRunningTotal = 0;
} else {
previousMarkerIndex = markersLength - (added ? 2 : 1);
// Calculate the distance based on the version
if (L.GeometryUtil.isVersion07x()) {
distance = latlng.distanceTo(this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1);
} else {
distance = this._map.distance(latlng, this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1);
}
this._measurementRunningTotal += distance * (added ? 1 : -1);
}
},
_getMeasurementString: function () {
var currentLatLng = this._currentLatLng,
previousLatLng = this._markers[this._markers.length - 1].getLatLng(),
distance;
// Calculate the distance from the last fixed point to the mouse position based on the version
if (L.GeometryUtil.isVersion07x()) {
distance = previousLatLng && currentLatLng && currentLatLng.distanceTo ? this._measurementRunningTotal + currentLatLng.distanceTo(previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0;
} else {
distance = previousLatLng && currentLatLng ? this._measurementRunningTotal + this._map.distance(currentLatLng, previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0;
}
return L.GeometryUtil.readableDistance(distance, this.options.metric, this.options.feet, this.options.nautic, this.options.precision);
},
_showErrorTooltip: function () {
this._errorShown = true;
// Update tooltip
this._tooltip
.showAsError()
.updateContent({text: this.options.drawError.message});
// Update shape
this._updateGuideColor(this.options.drawError.color);
this._poly.setStyle({color: this.options.drawError.color});
// Hide the error after 2 seconds
this._clearHideErrorTimeout();
this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout);
},
_hideErrorTooltip: function () {
this._errorShown = false;
this._clearHideErrorTimeout();
// Revert tooltip
this._tooltip
.removeError()
.updateContent(this._getTooltipText());
// Revert shape
this._updateGuideColor(this.options.shapeOptions.color);
this._poly.setStyle({color: this.options.shapeOptions.color});
},
_clearHideErrorTimeout: function () {
if (this._hideErrorTimeout) {
clearTimeout(this._hideErrorTimeout);
this._hideErrorTimeout = null;
}
},
// disable new markers temporarily;
// this is to prevent duplicated touch/click events in some browsers
_disableNewMarkers: function () {
this._disableMarkers = true;
},
// see _disableNewMarkers
_enableNewMarkers: function () {
setTimeout(function () {
this._disableMarkers = false;
}.bind(this), 50);
},
_cleanUpShape: function () {
if (this._markers.length > 1) {
this._markers[this._markers.length - 1].off('click', this._finishShape, this);
}
},
_fireCreatedEvent: function () {
var poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions);
L.Draw.Feature.prototype._fireCreatedEvent.call(this, poly);
}
});
/**
* @class L.Draw.Polygon
* @aka Draw.Polygon
* @inherits L.Draw.Polyline
*/
L.Draw.Polygon = L.Draw.Polyline.extend({
statics: {
TYPE: 'polygon'
},
Poly: L.Polygon,
options: {
showArea: false,
showLength: false,
shapeOptions: {
stroke: true,
color: '#3388ff',
weight: 4,
opacity: 0.5,
fill: true,
fillColor: null, //same as color by default
fillOpacity: 0.2,
clickable: true
},
// Whether to use the metric measurement system (truthy) or not (falsy).
// Also defines the units to use for the metric system as an array of
// strings (e.g. `['ha', 'm']`).
metric: true,
feet: true, // When not metric, to use feet instead of yards for display.
nautic: false, // When not metric, not feet use nautic mile for display
// Defines the precision for each type of unit (e.g. {km: 2, ft: 0}
precision: {}
},
// @method initialize(): void
initialize: function (map, options) {
L.Draw.Polyline.prototype.initialize.call(this, map, options);
// Save the type so super can fire, need to do this as cannot do this.TYPE :(
this.type = L.Draw.Polygon.TYPE;
},
_updateFinishHandler: function () {
var markerCount = this._markers.length;
// The first marker should have a click handler to close the polygon
if (markerCount === 1) {
this._markers[0].on('click', this._finishShape, this);
}
// Add and update the double click handler
if (markerCount > 2) {
this._markers[markerCount - 1].on('dblclick', this._finishShape, this);
// Only need to remove handler if has been added before
if (markerCount > 3) {
this._markers[markerCount - 2].off('dblclick', this._finishShape, this);
}
}
},
_getTooltipText: function () {
var text, subtext;
if (this._markers.length === 0) {
text = L.drawLocal.draw.handlers.polygon.tooltip.start;
} else if (this._markers.length < 3) {
text = L.drawLocal.draw.handlers.polygon.tooltip.cont;
subtext = this._getMeasurementString();
} else {
text = L.drawLocal.draw.handlers.polygon.tooltip.end;
subtext = this._getMeasurementString();
}
return {
text: text,
subtext: subtext
};
},
_getMeasurementString: function () {
var area = this._area,
measurementString = '';
if (!area && !this.options.showLength) {
return null;
}
if (this.options.showLength) {
measurementString = L.Draw.Polyline.prototype._getMeasurementString.call(this);
}
if (area) {
measurementString += '<br>' + L.GeometryUtil.readableArea(area, this.options.metric, this.options.precision);
}
return measurementString;
},
_shapeIsValid: function () {
return this._markers.length >= 3;
},
_vertexChanged: function (latlng, added) {
var latLngs;
// Check to see if we should show the area
if (!this.options.allowIntersection && this.options.showArea) {
latLngs = this._poly.getLatLngs();
this._area = L.GeometryUtil.geodesicArea(latLngs);
}
L.Draw.Polyline.prototype._vertexChanged.call(this, latlng, added);
},
_cleanUpShape: function () {
var markerCount = this._markers.length;
if (markerCount > 0) {
this._markers[0].off('click', this._finishShape, this);
if (markerCount > 2) {
this._markers[markerCount - 1].off('dblclick', this._finishShape, this);
}
}
}
});
L.SimpleShape = {};
/**
* @class L.Draw.SimpleShape
* @aka Draw.SimpleShape
* @inherits L.Draw.Feature
*/
L.Draw.SimpleShape = L.Draw.Feature.extend({
options: {
repeatMode: false
},
// @method initialize(): void
initialize: function (map, options) {
this._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end;
L.Draw.Feature.prototype.initialize.call(this, map, options);
},
// @method addHooks(): void
// Add listener hooks to this handler.
addHooks: function () {
L.Draw.Feature.prototype.addHooks.call(this);
if (this._map) {
this._mapDraggable = this._map.dragging.enabled();
if (this._mapDraggable) {
this._map.dragging.disable();
}
//TODO refactor: move cursor to styles
this._container.style.cursor = 'crosshair';
this._tooltip.updateContent({text: this._initialLabelText});
this._map
.on('mousedown', this._onMouseDown, this)
.on('mousemove', this._onMouseMove, this)
.on('touchstart', this._onMouseDown, this)
.on('touchmove', this._onMouseMove, this);
// we should prevent default, otherwise default behavior (scrolling) will fire,
// and that will cause document.touchend to fire and will stop the drawing
// (circle, rectangle) in touch mode.
// (update): we have to send passive now to prevent scroll, because by default it is {passive: true} now, which means,
// handler can't event.preventDefault
// check the news https://developers.google.com/web/updates/2016/06/passive-event-listeners
document.addEventListener('touchstart', L.DomEvent.preventDefault, {passive: false});
}
},
// @method removeHooks(): void
// Remove listener hooks from this handler.
removeHooks: function () {
L.Draw.Feature.prototype.removeHooks.call(this);
if (this._map) {
if (this._mapDraggable) {
this._map.dragging.enable();
}
//TODO refactor: move cursor to styles
this._container.style.cursor = '';
this._map
.off('mousedown', this._onMouseDown, this)
.off('mousemove', this._onMouseMove, this)
.off('touchstart', this._onMouseDown, this)
.off('touchmove', this._onMouseMove, this);
L.DomEvent.off(document, 'mouseup', this._onMouseUp, this);
L.DomEvent.off(document, 'touchend', this._onMouseUp, this);
document.removeEventListener('touchstart', L.DomEvent.preventDefault);
// If the box element doesn't exist they must not have moved the mouse, so don't need to destroy/return
if (this._shape) {
this._map.removeLayer(this._shape);
delete this._shape;
}
}
this._isDrawing = false;
},
_getTooltipText: function () {
return {
text: this._endLabelText
};
},
_onMouseDown: function (e) {
this._isDrawing = true;
this._startLatLng = e.latlng;
L.DomEvent
.on(document, 'mouseup', this._onMouseUp, this)
.on(document, 'touchend', this._onMouseUp, this)
.preventDefault(e.originalEvent);
},
_onMouseMove: function (e) {
var latlng = e.latlng;
this._tooltip.updatePosition(latlng);
if (this._isDrawing) {
this._tooltip.updateContent(this._getTooltipText());
this._drawShape(latlng);
}
},
_onMouseUp: function () {
if (this._shape) {
this._fireCreatedEvent();
}
this.disable();
if (this.options.repeatMode) {
this.enable();
}
}
});
/**
* @class L.Draw.Rectangle
* @aka Draw.Rectangle
* @inherits L.Draw.SimpleShape
*/
L.Draw.Rectangle = L.Draw.SimpleShape.extend({
statics: {
TYPE: 'rectangle'
},
options: {
shapeOptions: {
stroke: true,
color: '#3388ff',
weight: 4,
opacity: 0.5,
fill: true,
fillColor: null, //same as color by default
fillOpacity: 0.2,
clickable: true
},
showArea: true, //Whether to show the area in the tooltip
metric: true // Whether to use the metric measurement system or imperial
},
// @method initialize(): void
initialize: function (map, options) {
// Save the type so super can fire, need to do this as cannot do this.TYPE :(
this.type = L.Draw.Rectangle.TYPE;
this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start;
L.Draw.SimpleShape.prototype.initialize.call(this, map, options);
},
// @method disable(): void
disable: function () {
if (!this._enabled) {
return;
}
this._isCurrentlyTwoClickDrawing = false;
L.Draw.SimpleShape.prototype.disable.call(this);
},
_onMouseUp: function (e) {
if (!this._shape && !this._isCurrentlyTwoClickDrawing) {
this._isCurrentlyTwoClickDrawing = true;
return;
}
// Make sure closing click is on map
if (this._isCurrentlyTwoClickDrawing && !_hasAncestor(e.target, 'leaflet-pane')) {
return;
}
L.Draw.SimpleShape.prototype._onMouseUp.call(this);
},
_drawShape: function (latlng) {
if (!this._shape) {
this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, latlng), this.options.shapeOptions);
this._map.addLayer(this._shape);
} else {
this._shape.setBounds(new L.LatLngBounds(this._startLatLng, latlng));
}
},
_fireCreatedEvent: function () {
var rectangle = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions);
L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, rectangle);
},
_getTooltipText: function () {
var tooltipText = L.Draw.SimpleShape.prototype._getTooltipText.call(this),
shape = this._shape,
showArea = this.options.showArea,
latLngs, area, subtext;
if (shape) {
latLngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs();
area = L.GeometryUtil.geodesicArea(latLngs);
subtext = showArea ? L.GeometryUtil.readableArea(area, this.options.metric) : '';
}
return {
text: tooltipText.text,
subtext: subtext
};
}
});
function _hasAncestor(el, cls) {
while ((el = el.parentElement) && !el.classList.contains(cls)) {
;
}
return el;
}
/**
* @class L.Draw.Marker
* @aka Draw.Marker
* @inherits L.Draw.Feature
*/
L.Draw.Marker = L.Draw.Feature.extend({
statics: {
TYPE: 'marker'
},
options: {
icon: new L.Icon.Default(),
repeatMode: false,
zIndexOffset: 2000 // This should be > than the highest z-index any markers
},
// @method initialize(): void
initialize: function (map, options) {
// Save the type so super can fire, need to do this as cannot do this.TYPE :(
this.type = L.Draw.Marker.TYPE;
this._initialLabelText = L.drawLocal.draw.handlers.marker.tooltip.start;
L.Draw.Feature.prototype.initialize.call(this, map, options);
},
// @method addHooks(): void
// Add listener hooks to this handler.
addHooks: function () {
L.Draw.Feature.prototype.addHooks.call(this);
if (this._map) {
this._tooltip.updateContent({text: this._initialLabelText});
// Same mouseMarker as in Draw.Polyline
if (!this._mouseMarker) {
this._mouseMarker = L.marker(this._map.getCenter(), {
icon: L.divIcon({
className: 'leaflet-mouse-marker',
iconAnchor: [20, 20],
iconSize: [40, 40]
}),
opacity: 0,
zIndexOffset: this.options.zIndexOffset
});
}
this._mouseMarker
.on('click', this._onClick, this)
.addTo(this._map);
this._map.on('mousemove', this._onMouseMove, this);
this._map.on('click', this._onTouch, this);
}
},
// @method removeHooks(): void
// Remove listener hooks from this handler.
removeHooks: function () {
L.Draw.Feature.prototype.removeHooks.call(this);
if (this._map) {
this._map
.off('click', this._onClick, this)
.off('click', this._onTouch, this);
if (this._marker) {
this._marker.off('click', this._onClick, this);
this._map
.removeLayer(this._marker);
delete this._marker;
}
this._mouseMarker.off('click', this._onClick, this);
this._map.removeLayer(this._mouseMarker);
delete this._mouseMarker;
this._map.off('mousemove', this._onMouseMove, this);
}
},
_onMouseMove: function (e) {
var latlng = e.latlng;
this._tooltip.updatePosition(latlng);
this._mouseMarker.setLatLng(latlng);
if (!this._marker) {
this._marker = this._createMarker(latlng);
// Bind to both marker and map to make sure we get the click event.
this._marker.on('click', this._onClick, this);
this._map
.on('click', this._onClick, this)
.addLayer(this._marker);
}
else {
latlng = this._mouseMarker.getLatLng();
this._marker.setLatLng(latlng);
}
},
_createMarker: function (latlng) {
return new L.Marker(latlng, {
icon: this.options.icon,
zIndexOffset: this.options.zIndexOffset
});
},
_onClick: function () {
this._fireCreatedEvent();
this.disable();
if (this.options.repeatMode) {
this.enable();
}
},
_onTouch: function (e) {
// called on click & tap, only really does any thing on tap
this._onMouseMove(e); // creates & places marker
this._onClick(); // permanently places marker & ends interaction
},
_fireCreatedEvent: function () {
var marker = new L.Marker.Touch(this._marker.getLatLng(), {icon: this.options.icon});
L.Draw.Feature.prototype._fireCreatedEvent.call(this, marker);
}
});
/**
* @class L.Draw.CircleMarker
* @aka Draw.CircleMarker
* @inherits L.Draw.Marker
*/
L.Draw.CircleMarker = L.Draw.Marker.extend({
statics: {
TYPE: 'circlemarker'
},
options: {
stroke: true,
color: '#3388ff',
weight: 4,
opacity: 0.5,
fill: true,
fillColor: null, //same as color by default
fillOpacity: 0.2,
clickable: true,
zIndexOffset: 2000 // This should be > than the highest z-index any markers
},
// @method initialize(): void
initialize: function (map, options) {
// Save the type so super can fire, need to do this as cannot do this.TYPE :(
this.type = L.Draw.CircleMarker.TYPE;
this._initialLabelText = L.drawLocal.draw.handlers.circlemarker.tooltip.start;
L.Draw.Feature.prototype.initialize.call(this, map, options);
},
_fireCreatedEvent: function () {
var circleMarker = new L.CircleMarker(this._marker.getLatLng(), this.options);
L.Draw.Feature.prototype._fireCreatedEvent.call(this, circleMarker);
},
_createMarker: function (latlng) {
return new L.CircleMarker(latlng, this.options);
}
});
/**
* @class L.Draw.Circle
* @aka Draw.Circle
* @inherits L.Draw.SimpleShape
*/
L.Draw.Circle = L.Draw.SimpleShape.extend({
statics: {
TYPE: 'circle'
},
options: {
shapeOptions: {
stroke: true,
color: '#3388ff',
weight: 4,
opacity: 0.5,
fill: true,
fillColor: null, //same as color by default
fillOpacity: 0.2,
clickable: true
},
showRadius: true,
metric: true, // Whether to use the metric measurement system or imperial
feet: true, // When not metric, use feet instead of yards for display
nautic: false // When not metric, not feet use nautic mile for display
},
// @method initialize(): void
initialize: function (map, options) {
// Save the type so super can fire, need to do this as cannot do this.TYPE :(
this.type = L.Draw.Circle.TYPE;
this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start;
L.Draw.SimpleShape.prototype.initialize.call(this, map, options);
},
_drawShape: function (latlng) {
// Calculate the distance based on the version
if (L.GeometryUtil.isVersion07x()) {
var distance = this._startLatLng.distanceTo(latlng);
} else {
var distance = this._map.distance(this._startLatLng, latlng);
}
if (!this._shape) {
this._shape = new L.Circle(this._startLatLng, distance, this.options.shapeOptions);
this._map.addLayer(this._shape);
} else {
this._shape.setRadius(distance);
}
},
_fireCreatedEvent: function () {
var circle = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions);
L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, circle);
},
_onMouseMove: function (e) {
var latlng = e.latlng,
showRadius = this.options.showRadius,
useMetric = this.options.metric,
radius;
this._tooltip.updatePosition(latlng);
if (this._isDrawing) {
this._drawShape(latlng);
// Get the new radius (rounded to 1 dp)
radius = this._shape.getRadius().toFixed(1);
var subtext = '';
if (showRadius) {
subtext = L.drawLocal.draw.handlers.circle.radius + ': ' +
L.GeometryUtil.readableDistance(radius, useMetric, this.options.feet, this.options.nautic);
}
this._tooltip.updateContent({
text: this._endLabelText,
subtext: subtext
});
}
}
});
L.Edit = L.Edit || {};
/**
* @class L.Edit.Marker
* @aka Edit.Marker
*/
L.Edit.Marker = L.Handler.extend({
// @method initialize(): void
initialize: function (marker, options) {
this._marker = marker;
L.setOptions(this, options);
},
// @method addHooks(): void
// Add listener hooks to this handler
addHooks: function () {
var marker = this._marker;
marker.dragging.enable();
marker.on('dragend', this._onDragEnd, marker);
this._toggleMarkerHighlight();
},
// @method removeHooks(): void
// Remove listener hooks from this handler
removeHooks: function () {
var marker = this._marker;
marker.dragging.disable();
marker.off('dragend', this._onDragEnd, marker);
this._toggleMarkerHighlight();
},
_onDragEnd: function (e) {
var layer = e.target;
layer.edited = true;
this._map.fire(L.Draw.Event.EDITMOVE, {layer: layer});
},
_toggleMarkerHighlight: function () {
var icon = this._marker._icon;
// Don't do anything if this layer is a marker but doesn't have an icon. Markers
// should usually have icons. If using Leaflet.draw with Leaflet.markercluster there
// is a chance that a marker doesn't.
if (!icon) {
return;
}
// This is quite naughty, but I don't see another way of doing it. (short of setting a new icon)
icon.style.display = 'none';
if (L.DomUtil.hasClass(icon, 'leaflet-edit-marker-selected')) {
L.DomUtil.removeClass(icon, 'leaflet-edit-marker-selected');
// Offset as the border will make the icon move.
this._offsetMarker(icon, -4);
} else {
L.DomUtil.addClass(icon, 'leaflet-edit-marker-selected');
// Offset as the border will make the icon move.
this._offsetMarker(icon, 4);
}
icon.style.display = '';
},
_offsetMarker: function (icon, offset) {
var iconMarginTop = parseInt(icon.style.marginTop, 10) - offset,
iconMarginLeft = parseInt(icon.style.marginLeft, 10) - offset;
icon.style.marginTop = iconMarginTop + 'px';
icon.style.marginLeft = iconMarginLeft + 'px';
}
});
L.Marker.addInitHook(function () {
if (L.Edit.Marker) {
this.editing = new L.Edit.Marker(this);
if (this.options.editable) {
this.editing.enable();
}
}
});
L.Edit = L.Edit || {};
/**
* @class L.Edit.Polyline
* @aka L.Edit.Poly
* @aka Edit.Poly
*/
L.Edit.Poly = L.Handler.extend({
// @method initialize(): void
initialize: function (poly) {
this.latlngs = [poly._latlngs];
if (poly._holes) {
this.latlngs = this.latlngs.concat(poly._holes);
}
this._poly = poly;
this._poly.on('revert-edited', this._updateLatLngs, this);
},
// Compatibility method to normalize Poly* objects
// between 0.7.x and 1.0+
_defaultShape: function () {
if (!L.Polyline._flat) {
return this._poly._latlngs;
}
return L.Polyline._flat(this._poly._latlngs) ? this._poly._latlngs : this._poly._latlngs[0];
},
_eachVertexHandler: function (callback) {
for (var i = 0; i < this._verticesHandlers.length; i++) {
callback(this._verticesHandlers[i]);
}
},
// @method addHooks(): void
// Add listener hooks to this handler
addHooks: function () {
this._initHandlers();
this._eachVertexHandler(function (handler) {
handler.addHooks();
});
},
// @method removeHooks(): void
// Remove listener hooks from this handler
removeHooks: function () {
this._eachVertexHandler(function (handler) {
handler.removeHooks();
});
},
// @method updateMarkers(): void
// Fire an update for each vertex handler
updateMarkers: function () {
this._eachVertexHandler(function (handler) {
handler.updateMarkers();
});
},
_initHandlers: function () {
this._verticesHandlers = [];
for (var i = 0; i < this.latlngs.length; i++) {
this._verticesHandlers.push(new L.Edit.PolyVerticesEdit(this._poly, this.latlngs[i], this._poly.options.poly));
}
},
_updateLatLngs: function (e) {
this.latlngs = [e.layer._latlngs];
if (e.layer._holes) {
this.latlngs = this.latlngs.concat(e.layer._holes);
}
}
});
/**
* @class L.Edit.PolyVerticesEdit
* @aka Edit.PolyVerticesEdit
*/
L.Edit.PolyVerticesEdit = L.Handler.extend({
options: {
icon: new L.DivIcon({
iconSize: new L.Point(8, 8),
className: 'leaflet-div-icon leaflet-editing-icon'
}),
touchIcon: new L.DivIcon({
iconSize: new L.Point(20, 20),
className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'
}),
drawError: {
color: '#b00b00',
timeout: 1000
}
},
// @method intialize(): void
initialize: function (poly, latlngs, options) {
// if touch, switch to touch icon
if (L.Browser.touch) {
this.options.icon = this.options.touchIcon;
}
this._poly = poly;
if (options && options.drawError) {
options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
}
this._latlngs = latlngs;
L.setOptions(this, options);
},
// Compatibility method to normalize Poly* objects
// between 0.7.x and 1.0+
_defaultShape: function () {
if (!L.Polyline._flat) {
return this._latlngs;
}
return