Commit f4fadffb authored by JOE XMG's avatar JOE XMG
Browse files

update

parent abe537c0
Pipeline #6174 passed with stage
in 6 seconds
L.Draw = L.Draw || {};
/**
* @class L.Draw.Tooltip
* @aka Tooltip
*
* The tooltip class — it is used to display the tooltip while drawing
* This will be depreciated
*
* @example
*
* ```js
* var tooltip = L.Draw.Tooltip();
* ```
*
*/
L.Draw.Tooltip = L.Class.extend({
// @section Methods for modifying draw state
// @method initialize(map): void
// Tooltip constructor
initialize: function (map) {
this._map = map;
this._popupPane = map._panes.popupPane;
this._visible = false;
this._container = map.options.drawControlTooltips ?
L.DomUtil.create('div', 'leaflet-draw-tooltip', this._popupPane) : null;
this._singleLineLabel = false;
this._map.on('mouseout', this._onMouseOut, this);
},
// @method dispose(): void
// Remove Tooltip DOM and unbind events
dispose: function () {
this._map.off('mouseout', this._onMouseOut, this);
if (this._container) {
this._popupPane.removeChild(this._container);
this._container = null;
}
},
// @method updateContent(labelText): this
// Changes the tooltip text to string in function call
updateContent: function (labelText) {
if (!this._container) {
return this;
}
labelText.subtext = labelText.subtext || '';
// update the vertical position (only if changed)
if (labelText.subtext.length === 0 && !this._singleLineLabel) {
L.DomUtil.addClass(this._container, 'leaflet-draw-tooltip-single');
this._singleLineLabel = true;
}
else if (labelText.subtext.length > 0 && this._singleLineLabel) {
L.DomUtil.removeClass(this._container, 'leaflet-draw-tooltip-single');
this._singleLineLabel = false;
}
this._container.innerHTML =
(labelText.subtext.length > 0 ?
'<span class="leaflet-draw-tooltip-subtext">' + labelText.subtext + '</span>' + '<br />' : '') +
'<span>' + labelText.text + '</span>';
if (!labelText.text && !labelText.subtext) {
this._visible = false;
this._container.style.visibility = 'hidden';
} else {
this._visible = true;
this._container.style.visibility = 'inherit';
}
return this;
},
// @method updatePosition(latlng): this
// Changes the location of the tooltip
updatePosition: function (latlng) {
var pos = this._map.latLngToLayerPoint(latlng),
tooltipContainer = this._container;
if (this._container) {
if (this._visible) {
tooltipContainer.style.visibility = 'inherit';
}
L.DomUtil.setPosition(tooltipContainer, pos);
}
return this;
},
// @method showAsError(): this
// Applies error class to tooltip
showAsError: function () {
if (this._container) {
L.DomUtil.addClass(this._container, 'leaflet-error-draw-tooltip');
}
return this;
},
// @method removeError(): this
// Removes the error class from the tooltip
removeError: function () {
if (this._container) {
L.DomUtil.removeClass(this._container, 'leaflet-error-draw-tooltip');
}
return this;
},
_onMouseOut: function () {
if (this._container) {
this._container.style.visibility = 'hidden';
}
}
});
/*
Leaflet.draw {VERSION}, 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
*/
/**
* @class L.DrawToolbar
* @aka Toolbar
*/
L.DrawToolbar = L.Toolbar.extend({
statics: {
TYPE: 'draw'
},
options: {
polyline: {},
polygon: {},
rectangle: {},
circle: {},
marker: {},
circlemarker: {}
},
// @method initialize(): void
initialize: function (options) {
// Ensure that the options are merged correctly since L.extend is only shallow
for (var type in this.options) {
if (this.options.hasOwnProperty(type)) {
if (options[type]) {
options[type] = L.extend({}, this.options[type], options[type]);
}
}
}
this._toolbarClass = 'leaflet-draw-draw';
L.Toolbar.prototype.initialize.call(this, options);
},
// @method getModeHandlers(): object
// Get mode handlers information
getModeHandlers: function (map) {
return [
{
enabled: this.options.polyline,
handler: new L.Draw.Polyline(map, this.options.polyline),
title: L.drawLocal.draw.toolbar.buttons.polyline
},
{
enabled: this.options.polygon,
handler: new L.Draw.Polygon(map, this.options.polygon),
title: L.drawLocal.draw.toolbar.buttons.polygon
},
{
enabled: this.options.rectangle,
handler: new L.Draw.Rectangle(map, this.options.rectangle),
title: L.drawLocal.draw.toolbar.buttons.rectangle
},
{
enabled: this.options.circle,
handler: new L.Draw.Circle(map, this.options.circle),
title: L.drawLocal.draw.toolbar.buttons.circle
},
{
enabled: this.options.marker,
handler: new L.Draw.Marker(map, this.options.marker),
title: L.drawLocal.draw.toolbar.buttons.marker
},
{
enabled: this.options.circlemarker,
handler: new L.Draw.CircleMarker(map, this.options.circlemarker),
title: L.drawLocal.draw.toolbar.buttons.circlemarker
}
];
},
// @method getActions(): object
// Get action information
getActions: function (handler) {
return [
{
enabled: handler.completeShape,
title: L.drawLocal.draw.toolbar.finish.title,
text: L.drawLocal.draw.toolbar.finish.text,
callback: handler.completeShape,
context: handler
},
{
enabled: handler.deleteLastVertex,
title: L.drawLocal.draw.toolbar.undo.title,
text: L.drawLocal.draw.toolbar.undo.text,
callback: handler.deleteLastVertex,
context: handler
},
{
title: L.drawLocal.draw.toolbar.actions.title,
text: L.drawLocal.draw.toolbar.actions.text,
callback: this.disable,
context: this
}
];
},
// @method setOptions(): void
// Sets the options to the toolbar
setOptions: function (options) {
L.setOptions(this, options);
for (var type in this._modes) {
if (this._modes.hasOwnProperty(type) && options.hasOwnProperty(type)) {
this._modes[type].handler.setOptions(options[type]);
}
}
}
});
/**
* @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
});
}
}
});
/**
* @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);
}
});
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.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.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);
}
}
}
});
/**
* @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.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;
}
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();
}
}
});
/*L.Map.mergeOptions({
editControl: true
});*/
/**
* @class L.EditToolbar
* @aka EditToolbar
*/
L.EditToolbar = L.Toolbar.extend({
statics: {
TYPE: 'edit'
},
options: {
edit: {
selectedPathOptions: {
dashArray: '10, 10',
fill: true,
fillColor: '#fe57a1',
fillOpacity: 0.1,
// Whether to user the existing layers color
maintainColor: false
}
},
remove: {},
poly: null,
featureGroup: null /* REQUIRED! TODO: perhaps if not set then all layers on the map are selectable? */
},
// @method intialize(): void
initialize: function (options) {
// Need to set this manually since null is an acceptable value here
if (options.edit) {
if (typeof options.edit.selectedPathOptions === 'undefined') {
options.edit.selectedPathOptions = this.options.edit.selectedPathOptions;
}
options.edit.selectedPathOptions = L.extend({}, this.options.edit.selectedPathOptions, options.edit.selectedPathOptions);
}
if (options.remove) {
options.remove = L.extend({}, this.options.remove, options.remove);
}
if (options.poly) {
options.poly = L.extend({}, this.options.poly, options.poly);
}
this._toolbarClass = 'leaflet-draw-edit';
L.Toolbar.prototype.initialize.call(this, options);
this._selectedFeatureCount = 0;
},
// @method getModeHandlers(): object
// Get mode handlers information
getModeHandlers: function (map) {
var featureGroup = this.options.featureGroup;
return [
{
enabled: this.options.edit,
handler: new L.EditToolbar.Edit(map, {
featureGroup: featureGroup,
selectedPathOptions: this.options.edit.selectedPathOptions,
poly: this.options.poly
}),
title: L.drawLocal.edit.toolbar.buttons.edit
},
{
enabled: this.options.remove,
handler: new L.EditToolbar.Delete(map, {
featureGroup: featureGroup
}),
title: L.drawLocal.edit.toolbar.buttons.remove
}
];
},
// @method getActions(): object
// Get actions information
getActions: function (handler) {
var actions = [
{
title: L.drawLocal.edit.toolbar.actions.save.title,
text: L.drawLocal.edit.toolbar.actions.save.text,
callback: this._save,
context: this
},
{
title: L.drawLocal.edit.toolbar.actions.cancel.title,
text: L.drawLocal.edit.toolbar.actions.cancel.text,
callback: this.disable,
context: this
}
];
if (handler.removeAllLayers) {
actions.push({
title: L.drawLocal.edit.toolbar.actions.clearAll.title,
text: L.drawLocal.edit.toolbar.actions.clearAll.text,
callback: this._clearAllLayers,
context: this
});
}
return actions;
},
// @method addToolbar(map): L.DomUtil
// Adds the toolbar to the map
addToolbar: function (map) {
var container = L.Toolbar.prototype.addToolbar.call(this, map);
this._checkDisabled();
this.options.featureGroup.on('layeradd layerremove', this._checkDisabled, this);
return container;
},
// @method removeToolbar(): void
// Removes the toolbar from the map
removeToolbar: function () {
this.options.featureGroup.off('layeradd layerremove', this._checkDisabled, this);
L.Toolbar.prototype.removeToolbar.call(this);
},
// @method disable(): void
// Disables the toolbar
disable: function () {
if (!this.enabled()) {
return;
}
this._activeMode.handler.revertLayers();
L.Toolbar.prototype.disable.call(this);
},
_save: function () {
this._activeMode.handler.save();
if (this._activeMode) {
this._activeMode.handler.disable();
}
},
_clearAllLayers: function () {
this._activeMode.handler.removeAllLayers();
if (this._activeMode) {
this._activeMode.handler.disable();
}
},
_checkDisabled: function () {
var featureGroup = this.options.featureGroup,
hasLayers = featureGroup.getLayers().length !== 0,
button;
if (this.options.edit) {
button = this._modes[L.EditToolbar.Edit.TYPE].button;
if (hasLayers) {
L.DomUtil.removeClass(button, 'leaflet-disabled');
} else {
L.DomUtil.addClass(button, 'leaflet-disabled');
}
button.setAttribute(
'title',
hasLayers ?
L.drawLocal.edit.toolbar.buttons.edit
: L.drawLocal.edit.toolbar.buttons.editDisabled
);
}
if (this.options.remove) {
button = this._modes[L.EditToolbar.Delete.TYPE].button;
if (hasLayers) {
L.DomUtil.removeClass(button, 'leaflet-disabled');
} else {
L.DomUtil.addClass(button, 'leaflet-disabled');
}
button.setAttribute(
'title',
hasLayers ?
L.drawLocal.edit.toolbar.buttons.remove
: L.drawLocal.edit.toolbar.buttons.removeDisabled
);
}
}
});
L.Edit = L.Edit || {};
/**
* @class L.Edit.Circle
* @aka Edit.Circle
* @inherits L.Edit.CircleMarker
*/
L.Edit.Circle = L.Edit.CircleMarker.extend({
_createResizeMarker: function () {
var center = this._shape.getLatLng(),
resizemarkerPoint = this._getResizeMarkerPoint(center);
this._resizeMarkers = [];
this._resizeMarkers.push(this._createMarker(resizemarkerPoint, this.options.resizeIcon));
},
_getResizeMarkerPoint: function (latlng) {
// From L.shape.getBounds()
var delta = this._shape._radius * Math.cos(Math.PI / 4),
point = this._map.project(latlng);
return this._map.unproject([point.x + delta, point.y - delta]);
},
_resize: function (latlng) {
var moveLatLng = this._moveMarker.getLatLng();
// Calculate the radius based on the version
if (L.GeometryUtil.isVersion07x()) {
radius = moveLatLng.distanceTo(latlng);
} else {
radius = this._map.distance(moveLatLng, latlng);
}
this._shape.setRadius(radius);
if (this._map.editTooltip) {
this._map._editTooltip.updateContent({
text: L.drawLocal.edit.handlers.edit.tooltip.subtext + '<br />' + L.drawLocal.edit.handlers.edit.tooltip.text,
subtext: L.drawLocal.draw.handlers.circle.radius + ': ' +
L.GeometryUtil.readableDistance(radius, true, this.options.feet, this.options.nautic)
});
}
this._shape.setRadius(radius);
this._map.fire(L.Draw.Event.EDITRESIZE, {layer: this._shape});
}
});
L.Circle.addInitHook(function () {
if (L.Edit.Circle) {
this.editing = new L.Edit.Circle(this);
if (this.options.editable) {
this.editing.enable();
}
}
});
L.Edit = L.Edit || {};
/**
* @class L.Edit.CircleMarker
* @aka Edit.Circle
* @inherits L.Edit.SimpleShape
*/
L.Edit.CircleMarker = L.Edit.SimpleShape.extend({
_createMoveMarker: function () {
var center = this._shape.getLatLng();
this._moveMarker = this._createMarker(center, this.options.moveIcon);
},
_createResizeMarker: function () {
// To avoid an undefined check in L.Edit.SimpleShape.removeHooks
this._resizeMarkers = [];
},
_move: function (latlng) {
if (this._resizeMarkers.length) {
var resizemarkerPoint = this._getResizeMarkerPoint(latlng);
// Move the resize marker
this._resizeMarkers[0].setLatLng(resizemarkerPoint);
}
// Move the circle
this._shape.setLatLng(latlng);
this._map.fire(L.Draw.Event.EDITMOVE, {layer: this._shape});
},
});
L.CircleMarker.addInitHook(function () {
if (L.Edit.CircleMarker) {
this.editing = new L.Edit.CircleMarker(this);
if (this.options.editable) {
this.editing.enable();
}
}
this.on('add', function () {
if (this.editing && this.editing.enabled()) {
this.editing.addHooks();
}
});
this.on('remove', function () {
if (this.editing && this.editing.enabled()) {
this.editing.removeHooks();
}
});
});
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 L.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0];
},
// @method addHooks(): void
// Add listener hooks to this handler.
addHooks: function () {
var poly = this._poly;
var path = poly._path;
if (!(poly instanceof L.Polygon)) {
poly.options.fill = false;
if (poly.options.editing) {
poly.options.editing.fill = false;
}
}
if (path) {
if (poly.options.editing && poly.options.editing.className) {
if (poly.options.original.className) {
poly.options.original.className.split(' ').forEach(function (className) {
L.DomUtil.removeClass(path, className);
});
}
poly.options.editing.className.split(' ').forEach(function (className) {
L.DomUtil.addClass(path, className);
});
}
}
poly.setStyle(poly.options.editing);
if (this._poly._map) {
this._map = this._poly._map; // Set map
if (!this._markerGroup) {
this._initMarkers();
}
this._poly._map.addLayer(this._markerGroup);
}
},
// @method removeHooks(): void
// Remove listener hooks from this handler.
removeHooks: function () {
var poly = this._poly;
var path = poly._path;
if (path) {
if (poly.options.editing && poly.options.editing.className) {
poly.options.editing.className.split(' ').forEach(function (className) {
L.DomUtil.removeClass(path, className);
});
if (poly.options.original.className) {
poly.options.original.className.split(' ').forEach(function (className) {
L.DomUtil.addClass(path, className);
});
}
}
}
poly.setStyle(poly.options.original);
if (poly._map) {
poly._map.removeLayer(this._markerGroup);
delete this._markerGroup;
delete this._markers;
}
},
// @method updateMarkers(): void
// Clear markers and update their location
updateMarkers: function () {
this._markerGroup.clearLayers();
this._initMarkers();
},
_initMarkers: function () {
if (!this._markerGroup) {
this._markerGroup = new L.LayerGroup();
}
this._markers = [];
var latlngs = this._defaultShape(),
i, j, len, marker;
for (i = 0, len = latlngs.length; i < len; i++) {
marker = this._createMarker(latlngs[i], i);
marker.on('click', this._onMarkerClick, this);
marker.on('contextmenu', this._onContextMenu, this);
this._markers.push(marker);
}
var markerLeft, markerRight;
for (i = 0, j = len - 1; i < len; j = i++) {
if (i === 0 && !(L.Polygon && (this._poly instanceof L.Polygon))) {
continue;
}
markerLeft = this._markers[j];
markerRight = this._markers[i];
this._createMiddleMarker(markerLeft, markerRight);
this._updatePrevNext(markerLeft, markerRight);
}
},
_createMarker: function (latlng, index) {
// Extending L.Marker in TouchEvents.js to include touch.
var marker = new L.Marker.Touch(latlng, {
draggable: true,
icon: this.options.icon,
});
marker._origLatLng = latlng;
marker._index = index;
marker
.on('dragstart', this._onMarkerDragStart, this)
.on('drag', this._onMarkerDrag, this)
.on('dragend', this._fireEdit, this)
.on('touchmove', this._onTouchMove, this)
.on('touchend', this._fireEdit, this)
.on('MSPointerMove', this._onTouchMove, this)
.on('MSPointerUp', this._fireEdit, this);
this._markerGroup.addLayer(marker);
return marker;
},
_onMarkerDragStart: function () {
this._poly.fire('editstart');
},
_spliceLatLngs: function () {
var latlngs = this._defaultShape();
var removed = [].splice.apply(latlngs, arguments);
this._poly._convertLatLngs(latlngs, true);
this._poly.redraw();
return removed;
},
_removeMarker: function (marker) {
var i = marker._index;
this._markerGroup.removeLayer(marker);
this._markers.splice(i, 1);
this._spliceLatLngs(i, 1);
this._updateIndexes(i, -1);
marker
.off('dragstart', this._onMarkerDragStart, this)
.off('drag', this._onMarkerDrag, this)
.off('dragend', this._fireEdit, this)
.off('touchmove', this._onMarkerDrag, this)
.off('touchend', this._fireEdit, this)
.off('click', this._onMarkerClick, this)
.off('MSPointerMove', this._onTouchMove, this)
.off('MSPointerUp', this._fireEdit, this);
},
_fireEdit: function () {
this._poly.edited = true;
this._poly.fire('edit');
this._poly._map.fire(L.Draw.Event.EDITVERTEX, {layers: this._markerGroup, poly: this._poly});
},
_onMarkerDrag: function (e) {
var marker = e.target;
var poly = this._poly;
var oldOrigLatLng = L.LatLngUtil.cloneLatLng(marker._origLatLng);
L.extend(marker._origLatLng, marker._latlng);
if (poly.options.poly) {
var tooltip = poly._map._editTooltip; // Access the tooltip
// If we don't allow intersections and the polygon intersects
if (!poly.options.poly.allowIntersection && poly.intersects()) {
L.extend(marker._origLatLng, oldOrigLatLng);
marker.setLatLng(oldOrigLatLng);
var originalColor = poly.options.color;
poly.setStyle({color: this.options.drawError.color});
if (tooltip) {
tooltip.updateContent({
text: L.drawLocal.draw.handlers.polyline.error
});
}
// Reset everything back to normal after a second
setTimeout(function () {
poly.setStyle({color: originalColor});
if (tooltip) {
tooltip.updateContent({
text: L.drawLocal.edit.handlers.edit.tooltip.text,
subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
});
}
}, 1000);
}
}
if (marker._middleLeft) {
marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
}
if (marker._middleRight) {
marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
}
//refresh the bounds when draging
this._poly._bounds._southWest = L.latLng(Infinity, Infinity);
this._poly._bounds._northEast = L.latLng(-Infinity, -Infinity);
var latlngs = this._poly.getLatLngs();
this._poly._convertLatLngs(latlngs, true);
this._poly.redraw();
this._poly.fire('editdrag');
},
_onMarkerClick: function (e) {
var minPoints = L.Polygon && (this._poly instanceof L.Polygon) ? 4 : 3,
marker = e.target;
// If removing this point would create an invalid polyline/polygon don't remove
if (this._defaultShape().length < minPoints) {
return;
}
// remove the marker
this._removeMarker(marker);
// update prev/next links of adjacent markers
this._updatePrevNext(marker._prev, marker._next);
// remove ghost markers near the removed marker
if (marker._middleLeft) {
this._markerGroup.removeLayer(marker._middleLeft);
}
if (marker._middleRight) {
this._markerGroup.removeLayer(marker._middleRight);
}
// create a ghost marker in place of the removed one
if (marker._prev && marker._next) {
this._createMiddleMarker(marker._prev, marker._next);
} else if (!marker._prev) {
marker._next._middleLeft = null;
} else if (!marker._next) {
marker._prev._middleRight = null;
}
this._fireEdit();
},
_onContextMenu: function (e) {
var marker = e.target;
var poly = this._poly;
this._poly._map.fire(L.Draw.Event.MARKERCONTEXT, {marker: marker, layers: this._markerGroup, poly: this._poly});
L.DomEvent.stopPropagation;
},
_onTouchMove: function (e) {
var layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]),
latlng = this._map.layerPointToLatLng(layerPoint),
marker = e.target;
L.extend(marker._origLatLng, latlng);
if (marker._middleLeft) {
marker._middleLeft.setLatLng(this._getMiddleLatLng(marker._prev, marker));
}
if (marker._middleRight) {
marker._middleRight.setLatLng(this._getMiddleLatLng(marker, marker._next));
}
this._poly.redraw();
this.updateMarkers();
},
_updateIndexes: function (index, delta) {
this._markerGroup.eachLayer(function (marker) {
if (marker._index > index) {
marker._index += delta;
}
});
},
_createMiddleMarker: function (marker1, marker2) {
var latlng = this._getMiddleLatLng(marker1, marker2),
marker = this._createMarker(latlng),
onClick,
onDragStart,
onDragEnd;
marker.setOpacity(0.6);
marker1._middleRight = marker2._middleLeft = marker;
onDragStart = function () {
marker.off('touchmove', onDragStart, this);
var i = marker2._index;
marker._index = i;
marker
.off('click', onClick, this)
.on('click', this._onMarkerClick, this);
latlng.lat = marker.getLatLng().lat;
latlng.lng = marker.getLatLng().lng;
this._spliceLatLngs(i, 0, latlng);
this._markers.splice(i, 0, marker);
marker.setOpacity(1);
this._updateIndexes(i, 1);
marker2._index++;
this._updatePrevNext(marker1, marker);
this._updatePrevNext(marker, marker2);
this._poly.fire('editstart');
};
onDragEnd = function () {
marker.off('dragstart', onDragStart, this);
marker.off('dragend', onDragEnd, this);
marker.off('touchmove', onDragStart, this);
this._createMiddleMarker(marker1, marker);
this._createMiddleMarker(marker, marker2);
};
onClick = function () {
onDragStart.call(this);
onDragEnd.call(this);
this._fireEdit();
};
marker
.on('click', onClick, this)
.on('dragstart', onDragStart, this)
.on('dragend', onDragEnd, this)
.on('touchmove', onDragStart, this);
this._markerGroup.addLayer(marker);
},
_updatePrevNext: function (marker1, marker2) {
if (marker1) {
marker1._next = marker2;
}
if (marker2) {
marker2._prev = marker1;
}
},
_getMiddleLatLng: function (marker1, marker2) {
var map = this._poly._map,
p1 = map.project(marker1.getLatLng()),
p2 = map.project(marker2.getLatLng());
return map.unproject(p1._add(p2)._divideBy(2));
}
});
L.Polyline.addInitHook(function () {
// Check to see if handler has already been initialized. This is to support versions of Leaflet that still have L.Handler.PolyEdit
if (this.editing) {
return;
}
if (L.Edit.Poly) {
this.editing = new L.Edit.Poly(this);
if (this.options.editable) {
this.editing.enable();
}
}
this.on('add', function () {
if (this.editing && this.editing.enabled()) {
this.editing.addHooks();
}
});
this.on('remove', function () {
if (this.editing && this.editing.enabled()) {
this.editing.removeHooks();
}
});
});
L.Edit = L.Edit || {};
/**
* @class L.Edit.Rectangle
* @aka Edit.Rectangle
* @inherits L.Edit.SimpleShape
*/
L.Edit.Rectangle = L.Edit.SimpleShape.extend({
_createMoveMarker: function () {
var bounds = this._shape.getBounds(),
center = bounds.getCenter();
this._moveMarker = this._createMarker(center, this.options.moveIcon);
},
_createResizeMarker: function () {
var corners = this._getCorners();
this._resizeMarkers = [];
for (var i = 0, l = corners.length; i < l; i++) {
this._resizeMarkers.push(this._createMarker(corners[i], this.options.resizeIcon));
// Monkey in the corner index as we will need to know this for dragging
this._resizeMarkers[i]._cornerIndex = i;
}
},
_onMarkerDragStart: function (e) {
L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e);
// Save a reference to the opposite point
var corners = this._getCorners(),
marker = e.target,
currentCornerIndex = marker._cornerIndex;
this._oppositeCorner = corners[(currentCornerIndex + 2) % 4];
this._toggleCornerMarkers(0, currentCornerIndex);
},
_onMarkerDragEnd: function (e) {
var marker = e.target,
bounds, center;
// Reset move marker position to the center
if (marker === this._moveMarker) {
bounds = this._shape.getBounds();
center = bounds.getCenter();
marker.setLatLng(center);
}
this._toggleCornerMarkers(1);
this._repositionCornerMarkers();
L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, e);
},
_move: function (newCenter) {
var latlngs = this._shape._defaultShape ? this._shape._defaultShape() : this._shape.getLatLngs(),
bounds = this._shape.getBounds(),
center = bounds.getCenter(),
offset, newLatLngs = [];
// Offset the latlngs to the new center
for (var i = 0, l = latlngs.length; i < l; i++) {
offset = [latlngs[i].lat - center.lat, latlngs[i].lng - center.lng];
newLatLngs.push([newCenter.lat + offset[0], newCenter.lng + offset[1]]);
}
this._shape.setLatLngs(newLatLngs);
// Reposition the resize markers
this._repositionCornerMarkers();
this._map.fire(L.Draw.Event.EDITMOVE, {layer: this._shape});
},
_resize: function (latlng) {
var bounds;
// Update the shape based on the current position of this corner and the opposite point
this._shape.setBounds(L.latLngBounds(latlng, this._oppositeCorner));
// Reposition the move marker
bounds = this._shape.getBounds();
this._moveMarker.setLatLng(bounds.getCenter());
this._map.fire(L.Draw.Event.EDITRESIZE, {layer: this._shape});
},
_getCorners: function () {
var bounds = this._shape.getBounds(),
nw = bounds.getNorthWest(),
ne = bounds.getNorthEast(),
se = bounds.getSouthEast(),
sw = bounds.getSouthWest();
return [nw, ne, se, sw];
},
_toggleCornerMarkers: function (opacity) {
for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
this._resizeMarkers[i].setOpacity(opacity);
}
},
_repositionCornerMarkers: function () {
var corners = this._getCorners();
for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
this._resizeMarkers[i].setLatLng(corners[i]);
}
}
});
L.Rectangle.addInitHook(function () {
if (L.Edit.Rectangle) {
this.editing = new L.Edit.Rectangle(this);
if (this.options.editable) {
this.editing.enable();
}
}
});
L.Edit = L.Edit || {};
/**
* @class L.Edit.SimpleShape
* @aka Edit.SimpleShape
*/
L.Edit.SimpleShape = L.Handler.extend({
options: {
moveIcon: new L.DivIcon({
iconSize: new L.Point(8, 8),
className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move'
}),
resizeIcon: new L.DivIcon({
iconSize: new L.Point(8, 8),
className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize'
}),
touchMoveIcon: new L.DivIcon({
iconSize: new L.Point(20, 20),
className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-move leaflet-touch-icon'
}),
touchResizeIcon: new L.DivIcon({
iconSize: new L.Point(20, 20),
className: 'leaflet-div-icon leaflet-editing-icon leaflet-edit-resize leaflet-touch-icon'
}),
},
// @method intialize(): void
initialize: function (shape, options) {
// if touch, switch to touch icon
if (L.Browser.touch) {
this.options.moveIcon = this.options.touchMoveIcon;
this.options.resizeIcon = this.options.touchResizeIcon;
}
this._shape = shape;
L.Util.setOptions(this, options);
},
// @method addHooks(): void
// Add listener hooks to this handler
addHooks: function () {
var shape = this._shape;
if (this._shape._map) {
this._map = this._shape._map;
shape.setStyle(shape.options.editing);
if (shape._map) {
this._map = shape._map;
if (!this._markerGroup) {
this._initMarkers();
}
this._map.addLayer(this._markerGroup);
}
}
},
// @method removeHooks(): void
// Remove listener hooks from this handler
removeHooks: function () {
var shape = this._shape;
shape.setStyle(shape.options.original);
if (shape._map) {
this._unbindMarker(this._moveMarker);
for (var i = 0, l = this._resizeMarkers.length; i < l; i++) {
this._unbindMarker(this._resizeMarkers[i]);
}
this._resizeMarkers = null;
this._map.removeLayer(this._markerGroup);
delete this._markerGroup;
}
this._map = null;
},
// @method updateMarkers(): void
// Remove the edit markers from this layer
updateMarkers: function () {
this._markerGroup.clearLayers();
this._initMarkers();
},
_initMarkers: function () {
if (!this._markerGroup) {
this._markerGroup = new L.LayerGroup();
}
// Create center marker
this._createMoveMarker();
// Create edge marker
this._createResizeMarker();
},
_createMoveMarker: function () {
// Children override
},
_createResizeMarker: function () {
// Children override
},
_createMarker: function (latlng, icon) {
// Extending L.Marker in TouchEvents.js to include touch.
var marker = new L.Marker.Touch(latlng, {
draggable: true,
icon: icon,
zIndexOffset: 10
});
this._bindMarker(marker);
this._markerGroup.addLayer(marker);
return marker;
},
_bindMarker: function (marker) {
marker
.on('dragstart', this._onMarkerDragStart, this)
.on('drag', this._onMarkerDrag, this)
.on('dragend', this._onMarkerDragEnd, this)
.on('touchstart', this._onTouchStart, this)
.on('touchmove', this._onTouchMove, this)
.on('MSPointerMove', this._onTouchMove, this)
.on('touchend', this._onTouchEnd, this)
.on('MSPointerUp', this._onTouchEnd, this);
},
_unbindMarker: function (marker) {
marker
.off('dragstart', this._onMarkerDragStart, this)
.off('drag', this._onMarkerDrag, this)
.off('dragend', this._onMarkerDragEnd, this)
.off('touchstart', this._onTouchStart, this)
.off('touchmove', this._onTouchMove, this)
.off('MSPointerMove', this._onTouchMove, this)
.off('touchend', this._onTouchEnd, this)
.off('MSPointerUp', this._onTouchEnd, this);
},
_onMarkerDragStart: function (e) {
var marker = e.target;
marker.setOpacity(0);
this._shape.fire('editstart');
},
_fireEdit: function () {
this._shape.edited = true;
this._shape.fire('edit');
},
_onMarkerDrag: function (e) {
var marker = e.target,
latlng = marker.getLatLng();
if (marker === this._moveMarker) {
this._move(latlng);
} else {
this._resize(latlng);
}
this._shape.redraw();
this._shape.fire('editdrag');
},
_onMarkerDragEnd: function (e) {
var marker = e.target;
marker.setOpacity(1);
this._fireEdit();
},
_onTouchStart: function (e) {
L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, e);
if (typeof(this._getCorners) === 'function') {
// Save a reference to the opposite point
var corners = this._getCorners(),
marker = e.target,
currentCornerIndex = marker._cornerIndex;
marker.setOpacity(0);
// Copyed from Edit.Rectangle.js line 23 _onMarkerDragStart()
// Latlng is null otherwise.
this._oppositeCorner = corners[(currentCornerIndex + 2) % 4];
this._toggleCornerMarkers(0, currentCornerIndex);
}
this._shape.fire('editstart');
},
_onTouchMove: function (e) {
var layerPoint = this._map.mouseEventToLayerPoint(e.originalEvent.touches[0]),
latlng = this._map.layerPointToLatLng(layerPoint),
marker = e.target;
if (marker === this._moveMarker) {
this._move(latlng);
} else {
this._resize(latlng);
}
this._shape.redraw();
// prevent touchcancel in IOS
// e.preventDefault();
return false;
},
_onTouchEnd: function (e) {
var marker = e.target;
marker.setOpacity(1);
this.updateMarkers();
this._fireEdit();
},
_move: function () {
// Children override
},
_resize: function () {
// Children override
}
});
/**
* @class L.EditToolbar.Delete
* @aka EditToolbar.Delete
*/
L.EditToolbar.Delete = L.Handler.extend({
statics: {
TYPE: 'remove' // not delete as delete is reserved in js
},
// @method intialize(): void
initialize: function (map, options) {
L.Handler.prototype.initialize.call(this, map);
L.Util.setOptions(this, options);
// Store the selectable layer group for ease of access
this._deletableLayers = this.options.featureGroup;
if (!(this._deletableLayers instanceof L.FeatureGroup)) {
throw new Error('options.featureGroup must be a L.FeatureGroup');
}
// Save the type so super can fire, need to do this as cannot do this.TYPE :(
this.type = L.EditToolbar.Delete.TYPE;
var version = L.version.split('.');
//If Version is >= 1.2.0
if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {
L.EditToolbar.Delete.include(L.Evented.prototype);
} else {
L.EditToolbar.Delete.include(L.Mixin.Events);
}
},
// @method enable(): void
// Enable the delete toolbar
enable: function () {
if (this._enabled || !this._hasAvailableLayers()) {
return;
}
this.fire('enabled', {handler: this.type});
this._map.fire(L.Draw.Event.DELETESTART, {handler: this.type});
L.Handler.prototype.enable.call(this);
this._deletableLayers
.on('layeradd', this._enableLayerDelete, this)
.on('layerremove', this._disableLayerDelete, this);
},
// @method disable(): void
// Disable the delete toolbar
disable: function () {
if (!this._enabled) {
return;
}
this._deletableLayers
.off('layeradd', this._enableLayerDelete, this)
.off('layerremove', this._disableLayerDelete, this);
L.Handler.prototype.disable.call(this);
this._map.fire(L.Draw.Event.DELETESTOP, {handler: this.type});
this.fire('disabled', {handler: this.type});
},
// @method addHooks(): void
// Add listener hooks to this handler
addHooks: function () {
var map = this._map;
if (map) {
map.getContainer().focus();
this._deletableLayers.eachLayer(this._enableLayerDelete, this);
this._deletedLayers = new L.LayerGroup();
this._tooltip = new L.Draw.Tooltip(this._map);
this._tooltip.updateContent({text: L.drawLocal.edit.handlers.remove.tooltip.text});
this._map.on('mousemove', this._onMouseMove, this);
}
},
// @method removeHooks(): void
// Remove listener hooks from this handler
removeHooks: function () {
if (this._map) {
this._deletableLayers.eachLayer(this._disableLayerDelete, this);
this._deletedLayers = null;
this._tooltip.dispose();
this._tooltip = null;
this._map.off('mousemove', this._onMouseMove, this);
}
},
// @method revertLayers(): void
// Revert the deleted layers back to their prior state.
revertLayers: function () {
// Iterate of the deleted layers and add them back into the featureGroup
this._deletedLayers.eachLayer(function (layer) {
this._deletableLayers.addLayer(layer);
layer.fire('revert-deleted', {layer: layer});
}, this);
},
// @method save(): void
// Save deleted layers
save: function () {
this._map.fire(L.Draw.Event.DELETED, {layers: this._deletedLayers});
},
// @method removeAllLayers(): void
// Remove all delateable layers
removeAllLayers: function () {
// Iterate of the delateable layers and add remove them
this._deletableLayers.eachLayer(function (layer) {
this._removeLayer({layer: layer});
}, this);
this.save();
},
_enableLayerDelete: function (e) {
var layer = e.layer || e.target || e;
layer.on('click', this._removeLayer, this);
},
_disableLayerDelete: function (e) {
var layer = e.layer || e.target || e;
layer.off('click', this._removeLayer, this);
// Remove from the deleted layers so we can't accidentally revert if the user presses cancel
this._deletedLayers.removeLayer(layer);
},
_removeLayer: function (e) {
var layer = e.layer || e.target || e;
this._deletableLayers.removeLayer(layer);
this._deletedLayers.addLayer(layer);
layer.fire('deleted');
},
_onMouseMove: function (e) {
this._tooltip.updatePosition(e.latlng);
},
_hasAvailableLayers: function () {
return this._deletableLayers.getLayers().length !== 0;
}
});
/**
* @class L.EditToolbar.Edit
* @aka EditToolbar.Edit
*/
L.EditToolbar.Edit = L.Handler.extend({
statics: {
TYPE: 'edit'
},
// @method intialize(): void
initialize: function (map, options) {
L.Handler.prototype.initialize.call(this, map);
L.setOptions(this, options);
// Store the selectable layer group for ease of access
this._featureGroup = options.featureGroup;
if (!(this._featureGroup instanceof L.FeatureGroup)) {
throw new Error('options.featureGroup must be a L.FeatureGroup');
}
this._uneditedLayerProps = {};
// Save the type so super can fire, need to do this as cannot do this.TYPE :(
this.type = L.EditToolbar.Edit.TYPE;
var version = L.version.split('.');
//If Version is >= 1.2.0
if (parseInt(version[0], 10) === 1 && parseInt(version[1], 10) >= 2) {
L.EditToolbar.Edit.include(L.Evented.prototype);
} else {
L.EditToolbar.Edit.include(L.Mixin.Events);
}
},
// @method enable(): void
// Enable the edit toolbar
enable: function () {
if (this._enabled || !this._hasAvailableLayers()) {
return;
}
this.fire('enabled', {handler: this.type});
//this disable other handlers
this._map.fire(L.Draw.Event.EDITSTART, {handler: this.type});
//allow drawLayer to be updated before beginning edition.
L.Handler.prototype.enable.call(this);
this._featureGroup
.on('layeradd', this._enableLayerEdit, this)
.on('layerremove', this._disableLayerEdit, this);
},
// @method disable(): void
// Disable the edit toolbar
disable: function () {
if (!this._enabled) {
return;
}
this._featureGroup
.off('layeradd', this._enableLayerEdit, this)
.off('layerremove', this._disableLayerEdit, this);
L.Handler.prototype.disable.call(this);
this._map.fire(L.Draw.Event.EDITSTOP, {handler: this.type});
this.fire('disabled', {handler: this.type});
},
// @method addHooks(): void
// Add listener hooks for this handler
addHooks: function () {
var map = this._map;
if (map) {
map.getContainer().focus();
this._featureGroup.eachLayer(this._enableLayerEdit, this);
this._tooltip = new L.Draw.Tooltip(this._map);
this._tooltip.updateContent({
text: L.drawLocal.edit.handlers.edit.tooltip.text,
subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
});
// Quickly access the tooltip to update for intersection checking
map._editTooltip = this._tooltip;
this._updateTooltip();
this._map
.on('mousemove', this._onMouseMove, this)
.on('touchmove', this._onMouseMove, this)
.on('MSPointerMove', this._onMouseMove, this)
.on(L.Draw.Event.EDITVERTEX, this._updateTooltip, this);
}
},
// @method removeHooks(): void
// Remove listener hooks for this handler
removeHooks: function () {
if (this._map) {
// Clean up selected layers.
this._featureGroup.eachLayer(this._disableLayerEdit, this);
// Clear the backups of the original layers
this._uneditedLayerProps = {};
this._tooltip.dispose();
this._tooltip = null;
this._map
.off('mousemove', this._onMouseMove, this)
.off('touchmove', this._onMouseMove, this)
.off('MSPointerMove', this._onMouseMove, this)
.off(L.Draw.Event.EDITVERTEX, this._updateTooltip, this);
}
},
// @method revertLayers(): void
// Revert each layer's geometry changes
revertLayers: function () {
this._featureGroup.eachLayer(function (layer) {
this._revertLayer(layer);
}, this);
},
// @method save(): void
// Save the layer geometries
save: function () {
var editedLayers = new L.LayerGroup();
this._featureGroup.eachLayer(function (layer) {
if (layer.edited) {
editedLayers.addLayer(layer);
layer.edited = false;
}
});
this._map.fire(L.Draw.Event.EDITED, {layers: editedLayers});
},
_backupLayer: function (layer) {
var id = L.Util.stamp(layer);
if (!this._uneditedLayerProps[id]) {
// Polyline, Polygon or Rectangle
if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {
this._uneditedLayerProps[id] = {
latlngs: L.LatLngUtil.cloneLatLngs(layer.getLatLngs())
};
} else if (layer instanceof L.Circle) {
this._uneditedLayerProps[id] = {
latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng()),
radius: layer.getRadius()
};
} else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker
this._uneditedLayerProps[id] = {
latlng: L.LatLngUtil.cloneLatLng(layer.getLatLng())
};
}
}
},
_getTooltipText: function () {
return ({
text: L.drawLocal.edit.handlers.edit.tooltip.text,
subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext
});
},
_updateTooltip: function () {
this._tooltip.updateContent(this._getTooltipText());
},
_revertLayer: function (layer) {
var id = L.Util.stamp(layer);
layer.edited = false;
if (this._uneditedLayerProps.hasOwnProperty(id)) {
// Polyline, Polygon or Rectangle
if (layer instanceof L.Polyline || layer instanceof L.Polygon || layer instanceof L.Rectangle) {
layer.setLatLngs(this._uneditedLayerProps[id].latlngs);
} else if (layer instanceof L.Circle) {
layer.setLatLng(this._uneditedLayerProps[id].latlng);
layer.setRadius(this._uneditedLayerProps[id].radius);
} else if (layer instanceof L.Marker || layer instanceof L.CircleMarker) { // Marker or CircleMarker
layer.setLatLng(this._uneditedLayerProps[id].latlng);
}
layer.fire('revert-edited', {layer: layer});
}
},
_enableLayerEdit: function (e) {
var layer = e.layer || e.target || e,
pathOptions, poly;
// Back up this layer (if haven't before)
this._backupLayer(layer);
if (this.options.poly) {
poly = L.Util.extend({}, this.options.poly);
layer.options.poly = poly;
}
// Set different style for editing mode
if (this.options.selectedPathOptions) {
pathOptions = L.Util.extend({}, this.options.selectedPathOptions);
// Use the existing color of the layer
if (pathOptions.maintainColor) {
pathOptions.color = layer.options.color;
pathOptions.fillColor = layer.options.fillColor;
}
layer.options.original = L.extend({}, layer.options);
layer.options.editing = pathOptions;
}
if (layer instanceof L.Marker) {
if (layer.editing) {
layer.editing.enable();
}
layer.dragging.enable();
layer
.on('dragend', this._onMarkerDragEnd)
// #TODO: remove when leaflet finally fixes their draggable so it's touch friendly again.
.on('touchmove', this._onTouchMove, this)
.on('MSPointerMove', this._onTouchMove, this)
.on('touchend', this._onMarkerDragEnd, this)
.on('MSPointerUp', this._onMarkerDragEnd, this);
} else {
layer.editing.enable();
}
},
_disableLayerEdit: function (e) {
var layer = e.layer || e.target || e;
layer.edited = false;
if (layer.editing) {
layer.editing.disable();
}
delete layer.options.editing;
delete layer.options.original;
// Reset layer styles to that of before select
if (this._selectedPathOptions) {
if (layer instanceof L.Marker) {
this._toggleMarkerHighlight(layer);
} else {
// reset the layer style to what is was before being selected
layer.setStyle(layer.options.previousOptions);
// remove the cached options for the layer object
delete layer.options.previousOptions;
}
}
if (layer instanceof L.Marker) {
layer.dragging.disable();
layer
.off('dragend', this._onMarkerDragEnd, this)
.off('touchmove', this._onTouchMove, this)
.off('MSPointerMove', this._onTouchMove, this)
.off('touchend', this._onMarkerDragEnd, this)
.off('MSPointerUp', this._onMarkerDragEnd, this);
} else {
layer.editing.disable();
}
},
_onMouseMove: function (e) {
this._tooltip.updatePosition(e.latlng);
},
_onMarkerDragEnd: function (e) {
var layer = e.target;
layer.edited = true;
this._map.fire(L.Draw.Event.EDITMOVE, {layer: layer});
},
_onTouchMove: function (e) {
var touchEvent = e.originalEvent.changedTouches[0],
layerPoint = this._map.mouseEventToLayerPoint(touchEvent),
latlng = this._map.layerPointToLatLng(layerPoint);
e.target.setLatLng(latlng);
},
_hasAvailableLayers: function () {
return this._featureGroup.getLayers().length !== 0;
}
});
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment