PageSourceSearch

https://dubaienergy.netlify.app/src/draw/handler/Draw.Polyline.js

js dubaienergy.netlify.app collected 2026-10-03 10:40:35 UTC 17,648 bytes, 592 lines download raw bytes

vendor: 17,648 bytes, lines 1-592
1/**
2 * @class L.Draw.Polyline
3 * @aka Draw.Polyline
4 * @inherits L.Draw.Feature
5 */
6L.Draw.Polyline = L.Draw.Feature.extend({
7	statics: {
8		TYPE: 'polyline'
9	},
10
11	Poly: L.Polyline,
12
13	options: {
14		allowIntersection: true,
15		repeatMode: false,
16		drawError: {
17			color: '#b00b00',
18			timeout: 2500
19		},
20		icon: new L.DivIcon({
21			iconSize: new L.Point(8, 8),
22			className: 'leaflet-div-icon leaflet-editing-icon'
23		}),
24		touchIcon: new L.DivIcon({
25			iconSize: new L.Point(20, 20),
26			className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'
27		}),
28		guidelineDistance: 20,
29		maxGuideLineLength: 4000,
30		shapeOptions: {
31			stroke: true,
32			color: '#3388ff',
33			weight: 4,
34			opacity: 0.5,
35			fill: false,
36			clickable: true
37		},
38		metric: true, // Whether to use the metric measurement system or imperial
39		feet: true, // When not metric, to use feet instead of yards for display.
40		nautic: false, // When not metric, not feet use nautic mile for display
41		showLength: true, // Whether to display distance in the tooltip
42		zIndexOffset: 2000, // This should be > than the highest z-index any map layers
43		factor: 1, // To change distance calculation
44		maxPoints: 0 // Once this number of points are placed, finish shape
45	},
46
47	// @method initialize(): void
48	initialize: function (map, options) {
49		// if touch, switch to touch icon
50		if (L.Browser.touch) {
51			this.options.icon = this.options.touchIcon;
52		}
53
54		// Need to set this here to ensure the correct message is used.
55		this.options.drawError.message = L.drawLocal.draw.handlers.polyline.error;
56
57		// Merge default drawError options with custom options
58		if (options && options.drawError) {
59			options.drawError = L.Util.extend({}, this.options.drawError, options.drawError);
60		}
61
62		// Save the type so super can fire, need to do this as cannot do this.TYPE :(
63		this.type = L.Draw.Polyline.TYPE;
64
65		L.Draw.Feature.prototype.initialize.call(this, map, options);
66	},
67
68	// @method addHooks(): void
69	// Add listener hooks to this handler
70	addHooks: function () {
71		L.Draw.Feature.prototype.addHooks.call(this);
72		if (this._map) {
73			this._markers = [];
74
75			this._markerGroup = new L.LayerGroup();
76			this._map.addLayer(this._markerGroup);
77
78			this._poly = new L.Polyline([], this.options.shapeOptions);
79
80			this._tooltip.updateContent(this._getTooltipText());
81
82			// Make a transparent marker that will used to catch click events. These click
83			// events will create the vertices. We need to do this so we can ensure that
84			// we can create vertices over other map layers (markers, vector layers). We
85			// also do not want to trigger any click handlers of objects we are clicking on
86			// while drawing.
87			if (!this._mouseMarker) {
88				this._mouseMarker = L.marker(this._map.getCenter(), {
89					icon: L.divIcon({
90						className: 'leaflet-mouse-marker',
91						iconAnchor: [20, 20],
92						iconSize: [40, 40]
93					}),
94					opacity: 0,
95					zIndexOffset: this.options.zIndexOffset
96				});
97			}
98
99			this._mouseMarker
100				.on('mouseout', this._onMouseOut, this)
101				.on('mousemove', this._onMouseMove, this) // Necessary to prevent 0.8 stutter
102				.on('mousedown', this._onMouseDown, this)
103				.on('mouseup', this._onMouseUp, this) // Necessary for 0.8 compatibility
104				.addTo(this._map);
105
106			this._map
107				.on('mouseup', this._onMouseUp, this) // Necessary for 0.7 compatibility
108				.on('mousemove', this._onMouseMove, this)
109				.on('zoomlevelschange', this._onZoomEnd, this)
110				.on('touchstart', this._onTouch, this)
111				.on('zoomend', this._onZoomEnd, this);
112
113		}
114	},
115
116	// @method removeHooks(): void
117	// Remove listener hooks from this handler.
118	removeHooks: function () {
119		L.Draw.Feature.prototype.removeHooks.call(this);
120
121		this._clearHideErrorTimeout();
122
123		this._cleanUpShape();
124
125		// remove markers from map
126		this._map.removeLayer(this._markerGroup);
127		delete this._markerGroup;
128		delete this._markers;
129
130		this._map.removeLayer(this._poly);
131		delete this._poly;
132
133		this._mouseMarker
134			.off('mousedown', this._onMouseDown, this)
135			.off('mouseout', this._onMouseOut, this)
136			.off('mouseup', this._onMouseUp, this)
137			.off('mousemove', this._onMouseMove, this);
138		this._map.removeLayer(this._mouseMarker);
139		delete this._mouseMarker;
140
141		// clean up DOM
142		this._clearGuides();
143
144		this._map
145			.off('mouseup', this._onMouseUp, this)
146			.off('mousemove', this._onMouseMove, this)
147			.off('zoomlevelschange', this._onZoomEnd, this)
148			.off('zoomend', this._onZoomEnd, this)
149			.off('touchstart', this._onTouch, this)
150			.off('click', this._onTouch, this);
151	},
152
153	// @method deleteLastVertex(): void
154	// Remove the last vertex from the polyline, removes polyline from map if only one point exists.
155	deleteLastVertex: function () {
156		if (this._markers.length <= 1) {
157			return;
158		}
159
160		var lastMarker = this._markers.pop(),
161			poly = this._poly,
162			// Replaces .spliceLatLngs()
163			latlngs = poly.getLatLngs(),
164			latlng = latlngs.splice(-1, 1)[0];
165		this._poly.setLatLngs(latlngs);
166
167		this._markerGroup.removeLayer(lastMarker);
168
169		if (poly.getLatLngs().length < 2) {
170			this._map.removeLayer(poly);
171		}
172
173		this._vertexChanged(latlng, false);
174	},
175
176	// @method addVertex(): void
177	// Add a vertex to the end of the polyline
178	addVertex: function (latlng) {
179		var markersLength = this._markers.length;
180		// markersLength must be greater than or equal to 2 before intersections can occur
181		if (markersLength >= 2 && !this.options.allowIntersection && this._poly.newLatLngIntersects(latlng)) {
182			this._showErrorTooltip();
183			return;
184		}
185		else if (this._errorShown) {
186			this._hideErrorTooltip();
187		}
188
189		this._markers.push(this._createMarker(latlng));
190
191		this._poly.addLatLng(latlng);
192
193		if (this._poly.getLatLngs().length === 2) {
194			this._map.addLayer(this._poly);
195		}
196
197		this._vertexChanged(latlng, true);
198	},
199
200	// @method completeShape(): void
201	// Closes the polyline between the first and last points
202	completeShape: function () {
203		if (this._markers.length <= 1 || !this._shapeIsValid()) {
204			return;
205		}
206
207		this._fireCreatedEvent();
208		this.disable();
209
210		if (this.options.repeatMode) {
211			this.enable();
212		}
213	},
214
215	_finishShape: function () {
216		var latlngs = this._poly._defaultShape ? this._poly._defaultShape() : this._poly.getLatLngs();
217		var intersects = this._poly.newLatLngIntersects(latlngs[latlngs.length - 1]);
218
219		if ((!this.options.allowIntersection && intersects) || !this._shapeIsValid()) {
220			this._showErrorTooltip();
221			return;
222		}
223
224		this._fireCreatedEvent();
225		this.disable();
226		if (this.options.repeatMode) {
227			this.enable();
228		}
229	},
230
231	// Called to verify the shape is valid when the user tries to finish it
232	// Return false if the shape is not valid
233	_shapeIsValid: function () {
234		return true;
235	},
236
237	_onZoomEnd: function () {
238		if (this._markers !== null) {
239			this._updateGuide();
240		}
241	},
242
243	_onMouseMove: function (e) {
244		var newPos = this._map.mouseEventToLayerPoint(e.originalEvent);
245		var latlng = this._map.layerPointToLatLng(newPos);
246
247		// Save latlng
248		// should this be moved to _updateGuide() ?
249		this._currentLatLng = latlng;
250
251		this._updateTooltip(latlng);
252
253		// Update the guide line
254		this._updateGuide(newPos);
255
256		// Update the mouse marker position
257		this._mouseMarker.setLatLng(latlng);
258
259		L.DomEvent.preventDefault(e.originalEvent);
260	},
261
262	_vertexChanged: function (latlng, added) {
263		this._map.fire(L.Draw.Event.DRAWVERTEX, {layers: this._markerGroup});
264		this._updateFinishHandler();
265
266		this._updateRunningMeasure(latlng, added);
267
268		this._clearGuides();
269
270		this._updateTooltip();
271	},
272
273	_onMouseDown: function (e) {
274		if (!this._clickHandled && !this._touchHandled && !this._disableMarkers) {
275			this._onMouseMove(e);
276			this._clickHandled = true;
277			this._disableNewMarkers();
278			var originalEvent = e.originalEvent;
279			var clientX = originalEvent.clientX;
280			var clientY = originalEvent.clientY;
281			this._startPoint.call(this, clientX, clientY);
282		}
283	},
284
285	_startPoint: function (clientX, clientY) {
286		this._mouseDownOrigin = L.point(clientX, clientY);
287	},
288
289	_onMouseUp: function (e) {
290		var originalEvent = e.originalEvent;
291		var clientX = originalEvent.clientX;
292		var clientY = originalEvent.clientY;
293		this._endPoint.call(this, clientX, clientY, e);
294		this._clickHandled = null;
295	},
296
297	_endPoint: function (clientX, clientY, e) {
298		if (this._mouseDownOrigin) {
299			var dragCheckDistance = L.point(clientX, clientY)
300				.distanceTo(this._mouseDownOrigin);
301			var lastPtDistance = this._calculateFinishDistance(e.latlng);
302			if (this.options.maxPoints > 1 && this.options.maxPoints == this._markers.length + 1) {
303				this.addVertex(e.latlng);
304				this._finishShape();
305			} else if (lastPtDistance < 10 && L.Browser.touch) {
306				this._finishShape();
307			} else if (Math.abs(dragCheckDistance) < 9 * (window.devicePixelRatio || 1)) {
308				this.addVertex(e.latlng);
309			}
310			this._enableNewMarkers(); // after a short pause, enable new markers
311		}
312		this._mouseDownOrigin = null;
313	},
314
315	// ontouch prevented by clickHandled flag because some browsers fire both click/touch events,
316	// causing unwanted behavior
317	_onTouch: function (e) {
318		var originalEvent = e.originalEvent;
319		var clientX;
320		var clientY;
321		if (originalEvent.touches && originalEvent.touches[0] && !this._clickHandled && !this._touchHandled && !this._disableMarkers) {
322			clientX = originalEvent.touches[0].clientX;
323			clientY = originalEvent.touches[0].clientY;
324			this._disableNewMarkers();
325			this._touchHandled = true;
326			this._startPoint.call(this, clientX, clientY);
327			this._endPoint.call(this, clientX, clientY, e);
328			this._touchHandled = null;
329		}
330		this._clickHandled = null;
331	},
332
333	_onMouseOut: function () {
334		if (this._tooltip) {
335			this._tooltip._onMouseOut.call(this._tooltip);
336		}
337	},
338
339	// calculate if we are currently within close enough distance
340	// of the closing point (first point for shapes, last point for lines)
341	// this is semi-ugly code but the only reliable way i found to get the job done
342	// note: calculating point.distanceTo between mouseDownOrigin and last marker did NOT work
343	_calculateFinishDistance: function (potentialLatLng) {
344		var lastPtDistance;
345		if (this._markers.length > 0) {
346			var finishMarker;
347			if (this.type === L.Draw.Polyline.TYPE) {
348				finishMarker = this._markers[this._markers.length - 1];
349			} else if (this.type === L.Draw.Polygon.TYPE) {
350				finishMarker = this._markers[0];
351			} else {
352				return Infinity;
353			}
354			var lastMarkerPoint = this._map.latLngToContainerPoint(finishMarker.getLatLng()),
355				potentialMarker = new L.Marker(potentialLatLng, {
356					icon: this.options.icon,
357					zIndexOffset: this.options.zIndexOffset * 2
358				});
359			var potentialMarkerPint = this._map.latLngToContainerPoint(potentialMarker.getLatLng());
360			lastPtDistance = lastMarkerPoint.distanceTo(potentialMarkerPint);
361		} else {
362			lastPtDistance = Infinity;
363		}
364		return lastPtDistance;
365	},
366
367	_updateFinishHandler: function () {
368		var markerCount = this._markers.length;
369		// The last marker should have a click handler to close the polyline
370		if (markerCount > 1) {
371			this._markers[markerCount - 1].on('click', this._finishShape, this);
372		}
373
374		// Remove the old marker click handler (as only the last point should close the polyline)
375		if (markerCount > 2) {
376			this._markers[markerCount - 2].off('click', this._finishShape, this);
377		}
378	},
379
380	_createMarker: function (latlng) {
381		var marker = new L.Marker(latlng, {
382			icon: this.options.icon,
383			zIndexOffset: this.options.zIndexOffset * 2
384		});
385
386		this._markerGroup.addLayer(marker);
387
388		return marker;
389	},
390
391	_updateGuide: function (newPos) {
392		var markerCount = this._markers ? this._markers.length : 0;
393
394		if (markerCount > 0) {
395			newPos = newPos || this._map.latLngToLayerPoint(this._currentLatLng);
396
397			// draw the guide line
398			this._clearGuides();
399			this._drawGuide(
400				this._map.latLngToLayerPoint(this._markers[markerCount - 1].getLatLng()),
401				newPos
402			);
403		}
404	},
405
406	_updateTooltip: function (latLng) {
407		var text = this._getTooltipText();
408
409		if (latLng) {
410			this._tooltip.updatePosition(latLng);
411		}
412
413		if (!this._errorShown) {
414			this._tooltip.updateContent(text);
415		}
416	},
417
418	_drawGuide: function (pointA, pointB) {
419		var length = Math.floor(Math.sqrt(Math.pow((pointB.x - pointA.x), 2) + Math.pow((pointB.y - pointA.y), 2))),
420			guidelineDistance = this.options.guidelineDistance,
421			maxGuideLineLength = this.options.maxGuideLineLength,
422			// Only draw a guideline with a max length
423			i = length > maxGuideLineLength ? length - maxGuideLineLength : guidelineDistance,
424			fraction,
425			dashPoint,
426			dash;
427
428		//create the guides container if we haven't yet
429		if (!this._guidesContainer) {
430			this._guidesContainer = L.DomUtil.create('div', 'leaflet-draw-guides', this._overlayPane);
431		}
432
433		//draw a dash every GuildeLineDistance
434		for (; i < length; i += this.options.guidelineDistance) {
435			//work out fraction along line we are
436			fraction = i / length;
437
438			//calculate new x,y point
439			dashPoint = {
440				x: Math.floor((pointA.x * (1 - fraction)) + (fraction * pointB.x)),
441				y: Math.floor((pointA.y * (1 - fraction)) + (fraction * pointB.y))
442			};
443
444			//add guide dash to guide container
445			dash = L.DomUtil.create('div', 'leaflet-draw-guide-dash', this._guidesContainer);
446			dash.style.backgroundColor =
447				!this._errorShown ? this.options.shapeOptions.color : this.options.drawError.color;
448
449			L.DomUtil.setPosition(dash, dashPoint);
450		}
451	},
452
453	_updateGuideColor: function (color) {
454		if (this._guidesContainer) {
455			for (var i = 0, l = this._guidesContainer.childNodes.length; i < l; i++) {
456				this._guidesContainer.childNodes[i].style.backgroundColor = color;
457			}
458		}
459	},
460
461	// removes all child elements (guide dashes) from the guides container
462	_clearGuides: function () {
463		if (this._guidesContainer) {
464			while (this._guidesContainer.firstChild) {
465				this._guidesContainer.removeChild(this._guidesContainer.firstChild);
466			}
467		}
468	},
469
470	_getTooltipText: function () {
471		var showLength = this.options.showLength,
472			labelText, distanceStr;
473		if (this._markers.length === 0) {
474			labelText = {
475				text: L.drawLocal.draw.handlers.polyline.tooltip.start
476			};
477		} else {
478			distanceStr = showLength ? this._getMeasurementString() : '';
479
480			if (this._markers.length === 1) {
481				labelText = {
482					text: L.drawLocal.draw.handlers.polyline.tooltip.cont,
483					subtext: distanceStr
484				};
485			} else {
486				labelText = {
487					text: L.drawLocal.draw.handlers.polyline.tooltip.end,
488					subtext: distanceStr
489				};
490			}
491		}
492		return labelText;
493	},
494
495	_updateRunningMeasure: function (latlng, added) {
496		var markersLength = this._markers.length,
497			previousMarkerIndex, distance;
498
499		if (this._markers.length === 1) {
500			this._measurementRunningTotal = 0;
501		} else {
502			previousMarkerIndex = markersLength - (added ? 2 : 1);
503
504			// Calculate the distance based on the version
505			if (L.GeometryUtil.isVersion07x()) {
506				distance = latlng.distanceTo(this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1);
507			} else {
508				distance = this._map.distance(latlng, this._markers[previousMarkerIndex].getLatLng()) * (this.options.factor || 1);
509			}
510
511			this._measurementRunningTotal += distance * (added ? 1 : -1);
512		}
513	},
514
515	_getMeasurementString: function () {
516		var currentLatLng = this._currentLatLng,
517			previousLatLng = this._markers[this._markers.length - 1].getLatLng(),
518			distance;
519
520		// Calculate the distance from the last fixed point to the mouse position based on the version
521		if (L.GeometryUtil.isVersion07x()) {
522			distance = previousLatLng && currentLatLng && currentLatLng.distanceTo ? this._measurementRunningTotal + currentLatLng.distanceTo(previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0;
523		} else {
524			distance = previousLatLng && currentLatLng ? this._measurementRunningTotal + this._map.distance(currentLatLng, previousLatLng) * (this.options.factor || 1) : this._measurementRunningTotal || 0;
525		}
526
527		return L.GeometryUtil.readableDistance(distance, this.options.metric, this.options.feet, this.options.nautic, this.options.precision);
528	},
529
530	_showErrorTooltip: function () {
531		this._errorShown = true;
532
533		// Update tooltip
534		this._tooltip
535			.showAsError()
536			.updateContent({text: this.options.drawError.message});
537
538		// Update shape
539		this._updateGuideColor(this.options.drawError.color);
540		this._poly.setStyle({color: this.options.drawError.color});
541
542		// Hide the error after 2 seconds
543		this._clearHideErrorTimeout();
544		this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout);
545	},
546
547	_hideErrorTooltip: function () {
548		this._errorShown = false;
549
550		this._clearHideErrorTimeout();
551
552		// Revert tooltip
553		this._tooltip
554			.removeError()
555			.updateContent(this._getTooltipText());
556
557		// Revert shape
558		this._updateGuideColor(this.options.shapeOptions.color);
559		this._poly.setStyle({color: this.options.shapeOptions.color});
560	},
561
562	_clearHideErrorTimeout: function () {
563		if (this._hideErrorTimeout) {
564			clearTimeout(this._hideErrorTimeout);
565			this._hideErrorTimeout = null;
566		}
567	},
568
569	// disable new markers temporarily;
570	// this is to prevent duplicated touch/click events in some browsers
571	_disableNewMarkers: function () {
572		this._disableMarkers = true;
573	},
574
575	// see _disableNewMarkers
576	_enableNewMarkers: function () {
577		setTimeout(function () {
578			this._disableMarkers = false;
579		}.bind(this), 50);
580	},
581
582	_cleanUpShape: function () {
583		if (this._markers.length > 1) {
584			this._markers[this._markers.length - 1].off('click', this._finishShape, this);
585		}
586	},
587
588	_fireCreatedEvent: function () {
589		var poly = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions);
590		L.Draw.Feature.prototype._fireCreatedEvent.call(this, poly);
591	}
592});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.