';
767 | currentTooltip._icon.innerHTML = textCurrent;
768 | if ((this.options.showBearings === true) && (prevTooltip)) {
769 | var textPrev = prevTooltip._icon.innerHTML;
770 | var regExp = new RegExp(this.options.bearingTextOut + '.*\°');
771 | var textReplace = textPrev.replace(regExp, this.options.bearingTextOut + ': ' + angleOut + "°");
772 | prevTooltip._icon.innerHTML = textReplace;
773 | }
774 | },
775 |
776 | _drawArrow: function (arcLine) {
777 | // center of Great-circle distance, NOT of the arc on a Mercator map! reason: a) to complicated b) map not always Mercator c) good optical feature to see where real center of distance is not the "virtual" warped arc center due to Mercator projection
778 | // differ between even and odd pointed Arcs. If even the arrow is in the center of the middle line-segment, if odd it is on the middle point
779 | var midpoint = Math.trunc(arcLine.length/2);
780 | if (arcLine.length % 2 == 0) {
781 | var P1 = arcLine[midpoint-1];
782 | var P2 = arcLine[midpoint];
783 | var diffLng12 = P2[1] - P1[1];
784 | var diffLat12 = P2[0] - P1[0];
785 | var center = [P1[0] + diffLat12/2, P1[1] + diffLng12/2];
786 | } else {
787 | var P1 = arcLine[midpoint-1];
788 | var P2 = arcLine[midpoint+1];
789 | var diffLng12 = P2[1] - P1[1];
790 | var diffLat12 = P2[0] - P1[0];
791 | var center = arcLine[midpoint];
792 | }
793 | // angle just an aprroximation, which could be somewhat off if Line runs near high latitudes. Use of *geographical coords* for line segment P1 to P2 is best method. Use of *Pixel coords* for just one arc segement P1 to P2 could create for short lines unexact rotation angles, and the use Use of Pixel coords between endpoints [0] to [98] (in case of 99-point-arc) results in even more rotation difference for high latitudes as with geogrpaphical coords-method
794 | var cssAngle = -Math.atan2(diffLat12, diffLng12)*57.29578 // convert radiant to degree as needed for use as CSS value; cssAngle is opposite to mathematical angle.
795 | var iconArrow = L.divIcon ({
796 | className: "", // to avoid getting a default class with paddings and borders assigned by Leaflet
797 | iconSize: [16, 16],
798 | iconAnchor: [8, 8],
799 | // html : "" <<=== alternative method by the use of an image instead of a Unicode symbol.
800 | html : "
➤
" // best results if iconSize = font-size = line-height and iconAnchor font-size/2 .both values needed to position symbol in center of L.divIcon for all font-sizes.
801 | });
802 | var newArrowMarker = L.marker (center, {icon: iconArrow, zIndexOffset:-50}).addTo(this._layerPaint); // zIndexOffset to draw arrows below tooltips
803 | if (!this._currentLine){ // just bind tooltip if not drawing line anymore, cause following the instruction of tooltip is just possible when not drawing a line
804 | newArrowMarker.bindTooltip (this.options.tooltipTextAdd, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
805 | }
806 | newArrowMarker.on ('click', this._clickedArrow, this);
807 | return newArrowMarker;
808 | },
809 |
810 | /**
811 | * Event to fire on mouse move
812 | * @param {Object} e Event
813 | * @private
814 | */
815 | _mouseMove: function (e) {
816 | var mouseCoords = e.latlng;
817 | this._map.on ('click', this._mouseClick, this); // necassary for _dragCircle. If switched on already within _dragCircle an unwanted click is fired at the end of the drag.
818 | if(!mouseCoords || !this._currentLine) {
819 | return;
820 | }
821 | var lastCircleCoords = this._currentLine.circleCoords.last();
822 | this._rubberlinePath.setLatLngs (this._polylineArc (lastCircleCoords, mouseCoords));
823 | var currentTooltip = this._currentLine.tooltips.last();
824 | var prevTooltip = this._currentLine.tooltips.slice(-2,-1)[0];
825 | currentTooltip.setLatLng (mouseCoords);
826 | var distanceSegment = mouseCoords.distanceTo (lastCircleCoords);
827 | this._updateTooltip (currentTooltip, prevTooltip, this._currentLine.distance + distanceSegment, distanceSegment, lastCircleCoords, mouseCoords);
828 | },
829 |
830 | _startLine: function (clickCoords) {
831 | var icon = L.divIcon({
832 | className: 'polyline-measure-tooltip',
833 | iconAnchor: [-4, -4]
834 | });
835 | var last = function() {
836 | return this.slice(-1)[0];
837 | };
838 | this._rubberlinePath = L.polyline ([], {
839 | // Style of temporary, dashed line while moving the mouse
840 | color: this.options.tempLine.color,
841 | weight: this.options.tempLine.weight,
842 | interactive: false,
843 | dashArray: '8,8'
844 | }).addTo(this._layerPaint).bringToBack();
845 |
846 | var polylineState = this; // use "polylineState" instead of "this" to allow measuring on 2 different maps the same time
847 |
848 | this._currentLine = {
849 | id: 0,
850 | circleCoords: [],
851 | circleMarkers: [],
852 | arrowMarkers: [],
853 | tooltips: [],
854 | distance: 0,
855 |
856 | polylinePath: L.polyline([], {
857 | // Style of fixed, polyline after mouse is clicked
858 | color: this.options.fixedLine.color,
859 | weight: this.options.fixedLine.weight,
860 | interactive: false
861 | }).addTo(this._layerPaint).bringToBack(),
862 |
863 | handleMarkers: function (latlng) {
864 | // update style on previous marker
865 | var lastCircleMarker = this.circleMarkers.last();
866 | if (lastCircleMarker) {
867 | lastCircleMarker.bindTooltip (polylineState.options.tooltipTextDelete, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
868 | lastCircleMarker.off ('click', polylineState._finishPolylinePath, polylineState);
869 | if (this.circleMarkers.length === 1) {
870 | lastCircleMarker.setStyle (polylineState.options.startCircle);
871 | } else {
872 | lastCircleMarker.setStyle (polylineState.options.intermedCircle);
873 | }
874 | }
875 | var newCircleMarker = new L.CircleMarker (latlng, polylineState.options.currentCircle).addTo(polylineState._layerPaint);
876 | newCircleMarker.bindTooltip (polylineState.options.tooltipTextFinish + polylineState.options.tooltipTextDelete, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
877 | newCircleMarker.cntLine = polylineState._currentLine.id;
878 | newCircleMarker.cntCircle = polylineState._cntCircle;
879 | polylineState._cntCircle++;
880 | newCircleMarker.on ('mousedown', polylineState._dragCircle, polylineState);
881 | newCircleMarker.on ('click', polylineState._finishPolylinePath, polylineState);
882 | this.circleMarkers.push (newCircleMarker);
883 | },
884 |
885 | getNewToolTip: function(latlng) {
886 | return L.marker (latlng, {
887 | icon: icon,
888 | interactive: false
889 | });
890 | },
891 |
892 | addPoint: function (mouseCoords) {
893 | var lastCircleCoords = this.circleCoords.last();
894 | if (lastCircleCoords && lastCircleCoords.equals (mouseCoords)) { // don't add a new circle if the click was onto the last circle
895 | return;
896 | }
897 | this.circleCoords.push (mouseCoords);
898 | // update polyline
899 | if (this.circleCoords.length > 1) {
900 | var arc = polylineState._polylineArc (lastCircleCoords, mouseCoords);
901 | var arrowMarker = polylineState._drawArrow (arc);
902 | if (this.circleCoords.length > 2) {
903 | arc.shift(); // remove first coordinate of the arc, cause it is identical with last coordinate of previous arc
904 | }
905 | this.polylinePath.setLatLngs (this.polylinePath.getLatLngs().concat(arc));
906 | // following lines needed especially for Mobile Browsers where we just use mouseclicks. No mousemoves, no tempLine.
907 | arrowMarker.cntLine = polylineState._currentLine.id;
908 | arrowMarker.cntArrow = polylineState._cntCircle - 1;
909 | polylineState._currentLine.arrowMarkers.push (arrowMarker);
910 | var distanceSegment = lastCircleCoords.distanceTo (mouseCoords);
911 | this.distance += distanceSegment;
912 | var currentTooltip = polylineState._currentLine.tooltips.last();
913 | var prevTooltip = polylineState._currentLine.tooltips.slice(-1,-2)[0];
914 | polylineState._updateTooltip (currentTooltip, prevTooltip, this.distance, distanceSegment, lastCircleCoords, mouseCoords);
915 | }
916 | // update last tooltip with final value
917 | if (currentTooltip) {
918 | currentTooltip.setLatLng (mouseCoords);
919 | }
920 | // add new tooltip to update on mousemove
921 | var tooltipNew = this.getNewToolTip(mouseCoords);
922 | tooltipNew.addTo(polylineState._layerPaint);
923 | this.tooltips.push (tooltipNew);
924 | this.handleMarkers (mouseCoords);
925 | },
926 |
927 | finalize: function() {
928 | // remove tooltip created by last click
929 | polylineState._layerPaint.removeLayer (this.tooltips.last());
930 | this.tooltips.pop();
931 | // remove temporary rubberline
932 | polylineState._layerPaint.removeLayer (polylineState._rubberlinePath);
933 | if (this.circleCoords.length > 1) {
934 | this.tooltips.last()._icon.classList.add('polyline-measure-tooltip-end'); // add Class e.g. another background-color to the Previous Tooltip (which is the last fixed tooltip, cause the moving tooltip is being deleted later)
935 | var lastCircleMarker = this.circleMarkers.last()
936 | lastCircleMarker.setStyle (polylineState.options.endCircle);
937 | // use Leaflet's own tooltip method to shwo a popuo tooltip if user hovers the last circle of a polyline
938 | lastCircleMarker.unbindTooltip (); // to close the opened Tooltip after it's been opened after click onto point to finish the line
939 | polylineState._currentLine.circleMarkers.map (function (circle) {circle.bindTooltip (polylineState.options.tooltipTextMove + polylineState.options.tooltipTextDelete, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'})});
940 | polylineState._currentLine.circleMarkers[0].bindTooltip (polylineState.options.tooltipTextMove + polylineState.options.tooltipTextDelete + polylineState.options.tooltipTextResume, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
941 | lastCircleMarker.bindTooltip (polylineState.options.tooltipTextMove + polylineState.options.tooltipTextDelete + polylineState.options.tooltipTextResume, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
942 | polylineState._currentLine.arrowMarkers.map (function (arrow) {arrow.bindTooltip (polylineState.options.tooltipTextAdd, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'})});
943 | lastCircleMarker.off ('click', polylineState._finishPolylinePath, polylineState);
944 | lastCircleMarker.on ('click', polylineState._resumePolylinePath, polylineState);
945 | polylineState._arrPolylines [this.id] = this;
946 | } else {
947 | // if there is only one point, just clean it up
948 | polylineState._layerPaint.removeLayer (this.circleMarkers.last());
949 | polylineState._layerPaint.removeLayer (this.tooltips.last());
950 | }
951 | polylineState._resetPathVariables();
952 | }
953 | };
954 |
955 | var firstTooltip = L.marker (clickCoords, {
956 | icon: icon,
957 | interactive: false
958 | })
959 | firstTooltip.addTo(this._layerPaint);
960 | var text = '';
961 | if (this.options.showBearings === true) {
962 | text = this.options.bearingTextIn+':---° '+this.options.bearingTextOut+':---°';
963 | }
964 | text = text + '
+' + '0
';
965 | text = text + '
' + '0
';
966 | firstTooltip._icon.innerHTML = text;
967 | this._currentLine.tooltips.push (firstTooltip);
968 | this._currentLine.circleCoords.last = last;
969 | this._currentLine.tooltips.last = last;
970 | this._currentLine.circleMarkers.last = last;
971 | this._currentLine.id = this._arrPolylines.length;
972 | },
973 |
974 | /**
975 | * Event to fire on mouse click
976 | * @param {Object} e Event
977 | * @private
978 | */
979 | _mouseClick: function (e) {
980 | // skip if there are no coords provided by the event, or this event's screen coordinates match those of finishing CircleMarker for the line we just completed
981 | if (!e.latlng || (this._finishCircleScreencoords && this._finishCircleScreencoords.equals(e.containerPoint))) {
982 | return;
983 | }
984 |
985 | if (!this._currentLine && !this._mapdragging) {
986 | this._startLine (e.latlng);
987 | this._map.fire('polylinemeasure:start', this._currentLine);
988 | }
989 | // just create a point if the map isn't dragged during the mouseclick.
990 | if (!this._mapdragging) {
991 | this._currentLine.addPoint (e.latlng);
992 | this._map.fire('polylinemeasure:add', e);
993 | this._map.fire('polylinemeasure:change', this._currentLine);
994 | } else {
995 | this._mapdragging = false; // this manual setting to "false" needed, instead of a "moveend"-Event. Cause the mouseclick of a "moveend"-event immediately would create a point too the same time.
996 | }
997 | },
998 |
999 | /**
1000 | * Finish the drawing of the path by clicking onto the last circle or pressing ESC-Key
1001 | * @private
1002 | */
1003 | _finishPolylinePath: function (e) {
1004 | this._map.fire('polylinemeasure:finish', this._currentLine);
1005 | this._currentLine.finalize();
1006 | if (e) {
1007 | this._finishCircleScreencoords = e.containerPoint;
1008 | }
1009 | },
1010 |
1011 | /**
1012 | * Resume the drawing of a polyline by pressing CTRL-Key and clicking onto the last circle
1013 | * @private
1014 | */
1015 | _resumePolylinePath: function (e) {
1016 | if (e.originalEvent.ctrlKey === true || e.originalEvent.metaKey === true) { // just resume if user pressed the CTRL-Key (or metaKey on Mac) while clicking onto the last circle
1017 | this._currentLine = this._arrPolylines [e.target.cntLine];
1018 | this._rubberlinePath = L.polyline ([], {
1019 | // Style of temporary, rubberline while moving the mouse
1020 | color: this.options.tempLine.color,
1021 | weight: this.options.tempLine.weight,
1022 | interactive: false,
1023 | dashArray: '8,8'
1024 | }).addTo(this._layerPaint).bringToBack();
1025 | this._currentLine.tooltips.last()._icon.classList.remove ('polyline-measure-tooltip-end'); // remove extra CSS-class of previous, last tooltip
1026 | var tooltipNew = this._currentLine.getNewToolTip (e.latlng);
1027 | tooltipNew.addTo (this._layerPaint);
1028 | this._currentLine.tooltips.push(tooltipNew);
1029 | this._currentLine.circleMarkers.last().unbindTooltip(); // remove popup-tooltip of previous, last circleMarker
1030 | this._currentLine.circleMarkers.last().bindTooltip (this.options.tooltipTextMove + this.options.tooltipTextDelete, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
1031 | this._currentLine.circleMarkers.last().setStyle (this.options.currentCircle);
1032 | this._cntCircle = this._currentLine.circleCoords.length;
1033 | this._map.fire('polylinemeasure:resume', this._currentLine);
1034 | }
1035 | },
1036 |
1037 | /**
1038 | * After completing a path, reset all the values to prepare in creating the next polyline measurement
1039 | * @private
1040 | */
1041 | _resetPathVariables: function() {
1042 | this._cntCircle = 0;
1043 | this._currentLine = null;
1044 | },
1045 |
1046 | _clickedArrow: function(e) {
1047 | if (e.originalEvent.ctrlKey || e.originalEvent.metaKey) { // (metaKey for Mac)
1048 | var lineNr = e.target.cntLine;
1049 | var arrowNr = e.target.cntArrow;
1050 | this._arrPolylines[lineNr].arrowMarkers [arrowNr].removeFrom (this._layerPaint);
1051 | var newCircleMarker = new L.CircleMarker (e.latlng, this.options.intermedCircle).addTo(this._layerPaint);
1052 | newCircleMarker.cntLine = lineNr;
1053 | newCircleMarker.on ('mousedown', this._dragCircle, this);
1054 | newCircleMarker.bindTooltip (this.options.tooltipTextMove + this.options.tooltipTextDelete, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
1055 | this._arrPolylines[lineNr].circleMarkers.splice (arrowNr+1, 0, newCircleMarker);
1056 | this._arrPolylines[lineNr].circleMarkers.map (function (item, index) {
1057 | item.cntCircle = index;
1058 | });
1059 | this._arrPolylines[lineNr].circleCoords.splice (arrowNr+1, 0, e.latlng);
1060 | var lineCoords = this._arrPolylines[lineNr].polylinePath.getLatLngs(); // get Coords of each Point of the current Polyline
1061 | var arc1 = this._polylineArc (this._arrPolylines[lineNr].circleCoords[arrowNr], e.latlng);
1062 | arc1.pop();
1063 | var arc2 = this._polylineArc (e.latlng, this._arrPolylines[lineNr].circleCoords[arrowNr+2]);
1064 | Array.prototype.splice.apply (lineCoords, [(arrowNr)*(this._arcpoints-1), this._arcpoints].concat (arc1, arc2));
1065 | this._arrPolylines[lineNr].polylinePath.setLatLngs (lineCoords);
1066 | var arrowMarker = this._drawArrow (arc1);
1067 | this._arrPolylines[lineNr].arrowMarkers[arrowNr] = arrowMarker;
1068 | arrowMarker = this._drawArrow (arc2);
1069 | this._arrPolylines[lineNr].arrowMarkers.splice(arrowNr+1,0,arrowMarker);
1070 | this._arrPolylines[lineNr].arrowMarkers.map (function (item, index) {
1071 | item.cntLine = lineNr;
1072 | item.cntArrow = index;
1073 | });
1074 | this._tooltipNew = L.marker (e.latlng, {
1075 | icon: L.divIcon({
1076 | className: 'polyline-measure-tooltip',
1077 | iconAnchor: [-4, -4]
1078 | }),
1079 | interactive: false
1080 | });
1081 | this._tooltipNew.addTo(this._layerPaint);
1082 | this._arrPolylines[lineNr].tooltips.splice (arrowNr+1, 0, this._tooltipNew);
1083 | var totalDistance = 0;
1084 | this._arrPolylines[lineNr].tooltips.map (function (item, index) {
1085 | if (index >= 1) {
1086 | var distance = this._arrPolylines[lineNr].circleCoords[index-1].distanceTo (this._arrPolylines[lineNr].circleCoords[index]);
1087 | var lastCircleCoords = this._arrPolylines[lineNr].circleCoords[index - 1];
1088 | var mouseCoords = this._arrPolylines[lineNr].circleCoords[index];
1089 | totalDistance += distance;
1090 | var prevTooltip = this._arrPolylines[lineNr].tooltips[index-1]
1091 | this._updateTooltip (item, prevTooltip, totalDistance, distance, lastCircleCoords, mouseCoords);
1092 | }
1093 | }.bind(this));
1094 | this._map.fire('polylinemeasure:insert', e);
1095 | this._map.fire('polylinemeasure:change', this._arrPolylines[this._lineNr]);
1096 | }
1097 | },
1098 |
1099 | _dragCircleMouseup: function () {
1100 | // bind new popup-tooltip to the last CircleMArker if dragging finished
1101 | if ((this._circleNr === 0) || (this._circleNr === this._arrPolylines[this._lineNr].circleCoords.length-1)) {
1102 | this._e1.target.bindTooltip (this.options.tooltipTextMove + this.options.tooltipTextDelete + this.options.tooltipTextResume, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
1103 | } else {
1104 | this._e1.target.bindTooltip (this.options.tooltipTextMove + this.options.tooltipTextDelete, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
1105 | }
1106 | this._resetPathVariables();
1107 | this._map.off ('mousemove', this._dragCircleMousemove, this);
1108 | this._map.dragging.enable();
1109 | this._map.on ('mousemove', this._mouseMove, this);
1110 | this._map.off ('mouseup', this._dragCircleMouseup, this);
1111 | this._map.fire('polylinemeasure:move', this._e1);
1112 | this._map.fire('polylinemeasure:change', this._arrPolylines[this._lineNr]);
1113 | },
1114 |
1115 | _dragCircleMousemove: function (e2) {
1116 | var mouseNewLat = e2.latlng.lat;
1117 | var mouseNewLng = e2.latlng.lng;
1118 | var latDifference = mouseNewLat - this._mouseStartingLat;
1119 | var lngDifference = mouseNewLng - this._mouseStartingLng;
1120 | var currentCircleCoords = L.latLng (this._circleStartingLat + latDifference, this._circleStartingLng + lngDifference);
1121 | var arcpoints = this._arcpoints;
1122 | var lineNr = this._e1.target.cntLine;
1123 | this._lineNr = lineNr;
1124 | var circleNr = this._e1.target.cntCircle;
1125 | this._circleNr = circleNr;
1126 | this._e1.target.setLatLng (currentCircleCoords);
1127 | this._e1.target.unbindTooltip(); // unbind popup-tooltip cause otherwise it would be annoying during dragging, or popup instantly again if it's just closed
1128 | this._arrPolylines[lineNr].circleCoords[circleNr] = currentCircleCoords;
1129 | var lineCoords = this._arrPolylines[lineNr].polylinePath.getLatLngs(); // get Coords of each Point of the current Polyline
1130 | if (circleNr >= 1) { // redraw previous arc just if circle is not starting circle of polyline
1131 | var newLineSegment1 = this._polylineArc(this._arrPolylines[lineNr].circleCoords[circleNr-1], currentCircleCoords);
1132 | // the next line's syntax has to be used since Internet Explorer doesn't know new spread operator (...) for inserting the individual elements of an array as 3rd argument of the splice method; Otherwise we could write: lineCoords.splice (circleNr*(arcpoints-1), arcpoints, ...newLineSegment1);
1133 | Array.prototype.splice.apply (lineCoords, [(circleNr-1)*(arcpoints-1), arcpoints].concat (newLineSegment1));
1134 | var arrowMarker = this._drawArrow (newLineSegment1);
1135 | arrowMarker.cntLine = lineNr;
1136 | arrowMarker.cntArrow = circleNr-1;
1137 | this._arrPolylines[lineNr].arrowMarkers [circleNr-1].removeFrom (this._layerPaint);
1138 | this._arrPolylines[lineNr].arrowMarkers [circleNr-1] = arrowMarker;
1139 | }
1140 | if (circleNr < this._arrPolylines[lineNr].circleCoords.length-1) { // redraw following arc just if circle is not end circle of polyline
1141 | var newLineSegment2 = this._polylineArc (currentCircleCoords, this._arrPolylines[lineNr].circleCoords[circleNr+1]);
1142 | Array.prototype.splice.apply (lineCoords, [circleNr*(arcpoints-1), arcpoints].concat (newLineSegment2));
1143 | arrowMarker = this._drawArrow (newLineSegment2);
1144 | arrowMarker.cntLine = lineNr;
1145 | arrowMarker.cntArrow = circleNr;
1146 | this._arrPolylines[lineNr].arrowMarkers [circleNr].removeFrom (this._layerPaint);
1147 | this._arrPolylines[lineNr].arrowMarkers [circleNr] = arrowMarker;
1148 | }
1149 | this._arrPolylines[lineNr].polylinePath.setLatLngs (lineCoords);
1150 | if (circleNr >= 0) { // just update tooltip position if moved circle is 2nd, 3rd, 4th etc. circle of a polyline
1151 | this._arrPolylines[lineNr].tooltips[circleNr].setLatLng (currentCircleCoords);
1152 | }
1153 | var totalDistance = 0;
1154 | // update tooltip texts of each tooltip
1155 | this._arrPolylines[lineNr].tooltips.map (function (item, index) {
1156 | if (index >= 1) {
1157 | var distance = this._arrPolylines[lineNr].circleCoords[index-1].distanceTo (this._arrPolylines[lineNr].circleCoords[index]);
1158 | var lastCircleCoords = this._arrPolylines[lineNr].circleCoords[index - 1];
1159 | var mouseCoords = this._arrPolylines[lineNr].circleCoords[index];
1160 | totalDistance += distance;
1161 | this._arrPolylines[lineNr].distance = totalDistance;
1162 | var prevTooltip = this._arrPolylines[lineNr].tooltips[index-1]
1163 | this._updateTooltip (item, prevTooltip, totalDistance, distance, lastCircleCoords, mouseCoords);
1164 | }
1165 | }.bind(this));
1166 | this._map.on ('mouseup', this._dragCircleMouseup, this);
1167 | },
1168 |
1169 | _resumeFirstpointMousemove: function (e) {
1170 | var lineNr = this._lineNr;
1171 | this._map.on ('click', this._resumeFirstpointClick, this); // necassary for _dragCircle. If switched on already within _dragCircle an unwanted click is fired at the end of the drag.
1172 | var mouseCoords = e.latlng;
1173 | this._rubberlinePath2.setLatLngs (this._polylineArc (mouseCoords, currentCircleCoords));
1174 | this._tooltipNew.setLatLng (mouseCoords);
1175 | var totalDistance = 0;
1176 | var distance = mouseCoords.distanceTo (this._arrPolylines[lineNr].circleCoords[0]);
1177 | var lastCircleCoords = mouseCoords;
1178 | var currentCoords = this._arrPolylines[lineNr].circleCoords[0];
1179 | totalDistance += distance;
1180 | var prevTooltip = this._tooltipNew;
1181 | var currentTooltip = this._arrPolylines[lineNr].tooltips[0]
1182 | this._updateTooltip (currentTooltip, prevTooltip, totalDistance, distance, lastCircleCoords, currentCoords);
1183 | this._arrPolylines[lineNr].tooltips.map (function (item, index) {
1184 | if (index >= 1) {
1185 | var distance = this._arrPolylines[lineNr].circleCoords[index-1].distanceTo (this._arrPolylines[lineNr].circleCoords[index]);
1186 | var lastCircleCoords = this._arrPolylines[lineNr].circleCoords[index - 1];
1187 | var mouseCoords = this._arrPolylines[lineNr].circleCoords[index];
1188 | totalDistance += distance;
1189 | var prevTooltip = this._arrPolylines[lineNr].tooltips[index-1]
1190 | this._updateTooltip (item, prevTooltip, totalDistance, distance, lastCircleCoords, mouseCoords);
1191 | }
1192 | }.bind (this));
1193 | },
1194 |
1195 | _resumeFirstpointClick: function (e) {
1196 | var lineNr = this._lineNr;
1197 | this._resumeFirstpointFlag = false;
1198 | this._map.off ('mousemove', this._resumeFirstpointMousemove, this);
1199 | this._map.off ('click', this._resumeFirstpointClick, this);
1200 | this._layerPaint.removeLayer (this._rubberlinePath2);
1201 | this._arrPolylines[lineNr].circleMarkers [0].setStyle (this.options.intermedCircle);
1202 | this._arrPolylines[lineNr].circleMarkers [0].unbindTooltip();
1203 | this._arrPolylines[lineNr].circleMarkers [0].bindTooltip (this.options.tooltipTextMove + this.options.tooltipTextDelete, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
1204 | var newCircleMarker = new L.CircleMarker (e.latlng, this.options.startCircle).addTo(this._layerPaint);
1205 | newCircleMarker.cntLine = lineNr;
1206 | newCircleMarker.cntCircle = 0;
1207 | newCircleMarker.on ('mousedown', this._dragCircle, this);
1208 | newCircleMarker.bindTooltip (this.options.tooltipTextMove + this.options.tooltipTextDelete + this.options.tooltipTextResume, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
1209 | this._arrPolylines[lineNr].circleMarkers.unshift(newCircleMarker);
1210 | this._arrPolylines[lineNr].circleMarkers.map (function (item, index) {
1211 | item.cntCircle = index;
1212 | });
1213 | this._arrPolylines[lineNr].circleCoords.unshift(e.latlng);
1214 | var arc = this._polylineArc (e.latlng, currentCircleCoords);
1215 | var arrowMarker = this._drawArrow (arc);
1216 | this._arrPolylines[lineNr].arrowMarkers.unshift(arrowMarker);
1217 | this._arrPolylines[lineNr].arrowMarkers.map (function (item, index) {
1218 | item.cntLine = lineNr;
1219 | item.cntArrow = index;
1220 | });
1221 | arc.pop(); // remove last coordinate of arc, cause it's already part of the next arc.
1222 | this._arrPolylines[lineNr].polylinePath.setLatLngs (arc.concat(this._arrPolylines[lineNr].polylinePath.getLatLngs()));
1223 | this._arrPolylines[lineNr].tooltips.unshift(this._tooltipNew);
1224 | this._map.on ('mousemove', this._mouseMove, this);
1225 | },
1226 |
1227 |
1228 | // not just used for dragging Cirles but also for deleting circles and resuming line at its starting point.
1229 | _dragCircle: function (e1) {
1230 | var arcpoints = this._arcpoints;
1231 | if (e1.originalEvent.ctrlKey || e1.originalEvent.metaKey) { // if user wants to resume drawing a line. metaKey for Mac
1232 | this._map.off ('click', this._mouseClick, this); // to avoid unwanted creation of a new line if CTRL-clicked onto a point
1233 | // if user wants resume the line at its starting point
1234 | if (e1.target.cntCircle === 0) {
1235 | this._resumeFirstpointFlag = true;
1236 | this._lineNr = e1.target.cntLine;
1237 | var lineNr = this._lineNr;
1238 | this._circleNr = e1.target.cntCircle;
1239 | currentCircleCoords = e1.latlng;
1240 | this._arrPolylines[lineNr].circleMarkers [0].setStyle (this.options.currentCircle);
1241 | this._rubberlinePath2 = L.polyline ([], {
1242 | // Style of temporary, rubberline while moving the mouse
1243 | color: this.options.tempLine.color,
1244 | weight: this.options.tempLine.weight,
1245 | interactive: false,
1246 | dashArray: '8,8'
1247 | }).addTo(this._layerPaint).bringToBack();
1248 | this._tooltipNew = L.marker (currentCircleCoords, {
1249 | icon: L.divIcon({
1250 | className: 'polyline-measure-tooltip',
1251 | iconAnchor: [-4, -4]
1252 | }),
1253 | interactive: false
1254 | });
1255 | this._tooltipNew.addTo(this._layerPaint);
1256 | var text='';
1257 | if (this.options.showBearings === true) {
1258 | text = text + this.options.bearingTextIn+':---° '+this.options.bearingTextOut+':---°';
1259 | }
1260 | text = text + '
+' + '0
';
1261 | text = text + '
' + '0
';
1262 | this._tooltipNew._icon.innerHTML = text;
1263 | this._map.off ('mousemove', this._mouseMove, this);
1264 | this._map.on ('mousemove', this._resumeFirstpointMousemove, this);
1265 | }
1266 | return;
1267 | }
1268 |
1269 | // if user wants to delete a circle
1270 | if (e1.originalEvent.shiftKey) { // it's not possible to use "ALT-Key" instead, cause this won't work in some Linux distributions (there it's the default hotkey for moving windows)
1271 | this._lineNr = e1.target.cntLine;
1272 | var lineNr = this._lineNr;
1273 | this._circleNr = e1.target.cntCircle;
1274 | var circleNr = this._circleNr;
1275 |
1276 | // if there is a rubberlinePath-layer and rubberline-id = clicked line-id of point meaning user is deleting a point of current line being drawn
1277 | if ((this._layerPaint.hasLayer (this._rubberlinePath)) && (lineNr === this._currentLine.id)) {
1278 | // when you're drawing and deleting point you need to take it into account by decreasing _cntCircle
1279 | this._cntCircle--;
1280 | // if the last Circle in polyline is being removed
1281 | if(this._currentLine.circleMarkers.length === 1) {
1282 | this._currentLine.finalize();
1283 | return;
1284 | }
1285 |
1286 | this._currentLine.circleCoords.splice(circleNr,1);
1287 | this._currentLine.circleMarkers [circleNr].removeFrom (this._layerPaint);
1288 | this._currentLine.circleMarkers.splice(circleNr,1);
1289 | this._currentLine.circleMarkers.map (function (item, index) {
1290 | item.cntCircle = index;
1291 | });
1292 | lineCoords = this._currentLine.polylinePath.getLatLngs();
1293 | this._currentLine.tooltips [circleNr].removeFrom (this._layerPaint);
1294 | this._currentLine.tooltips.splice(circleNr,1);
1295 |
1296 | // if first Circle is being removed
1297 | if (circleNr === 0) {
1298 | if(this._currentLine.circleMarkers.length === 1) {
1299 | this._currentLine.circleMarkers [0].setStyle (this.options.currentCircle);
1300 | } else {
1301 | this._currentLine.circleMarkers [0].setStyle (this.options.startCircle);
1302 | }
1303 | lineCoords.splice (0, arcpoints-1);
1304 | this._currentLine.circleMarkers [0].bindTooltip (this.options.tooltipTextMove + this.options.tooltipTextDelete + this.options.tooltipTextResume, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
1305 | this._currentLine.arrowMarkers [circleNr].removeFrom (this._layerPaint);
1306 | this._currentLine.arrowMarkers.splice(0,1);
1307 | var text='';
1308 | if (this.options.showBearings === true) {
1309 | text = this.options.bearingTextIn+':---° '+this.options.bearingTextOut+':---°';
1310 | }
1311 | text = text + '
+' + '0
';
1312 | text = text + '
' + '0
';
1313 | this._currentLine.tooltips [0]._icon.innerHTML = text;
1314 | // if last Circle is being removed
1315 | } else if (circleNr === this._currentLine.circleCoords.length) {
1316 | this._currentLine.circleMarkers [circleNr-1].on ('click', this._resumePolylinePath, this);
1317 | this._currentLine.circleMarkers [circleNr-1].bindTooltip (this.options.tooltipTextMove + this.options.tooltipTextDelete + this.options.tooltipTextResume, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
1318 | this._currentLine.circleMarkers.slice(-1)[0].setStyle (this.options.currentCircle); // get last element of the array
1319 | lineCoords.splice (-(arcpoints-1), arcpoints-1);
1320 | this._currentLine.arrowMarkers [circleNr-1].removeFrom (this._layerPaint);
1321 | this._currentLine.arrowMarkers.splice(-1,1);
1322 | // if intermediate Circle is being removed
1323 | } else {
1324 | newLineSegment = this._polylineArc (this._currentLine.circleCoords[circleNr-1], this._currentLine.circleCoords[circleNr]);
1325 | Array.prototype.splice.apply (lineCoords, [(circleNr-1)*(arcpoints-1), (2*arcpoints-1)].concat (newLineSegment));
1326 | this._currentLine.arrowMarkers [circleNr-1].removeFrom (this._layerPaint);
1327 | this._currentLine.arrowMarkers [circleNr].removeFrom (this._layerPaint);
1328 | arrowMarker = this._drawArrow (newLineSegment);
1329 | this._currentLine.arrowMarkers.splice(circleNr-1,2,arrowMarker);
1330 | }
1331 | this._currentLine.polylinePath.setLatLngs (lineCoords);
1332 | this._currentLine.arrowMarkers.map (function (item, index) {
1333 | item.cntLine = lineNr;
1334 | item.cntArrow = index;
1335 | });
1336 | var totalDistanceUnfinishedLine = 0;
1337 | this._currentLine.tooltips.map (function (item, index, arr) {
1338 | if (index >= 1) {
1339 | var distance, mouseCoords;
1340 | var prevTooltip = this._currentLine.tooltips[index-1];
1341 | var lastCircleCoords = this._currentLine.circleCoords[index - 1];
1342 | if(index === arr.length - 1) {
1343 | distance = this._currentLine.circleCoords[index-1].distanceTo (e1.latlng);
1344 | mouseCoords = e1.latlng;
1345 | // if this is the last Circle (mouse cursor) then don't sum the distance, but update tooltip like it was summed
1346 | this._updateTooltip (item, prevTooltip, totalDistanceUnfinishedLine + distance, distance, lastCircleCoords, mouseCoords);
1347 | } else {
1348 | distance = this._currentLine.circleCoords[index-1].distanceTo (this._currentLine.circleCoords[index]);
1349 | mouseCoords = this._currentLine.circleCoords[index];
1350 | // if this is not the last Circle (mouse cursor) then sum the distance
1351 | totalDistanceUnfinishedLine += distance;
1352 | this._updateTooltip (item, prevTooltip, totalDistanceUnfinishedLine, distance, lastCircleCoords, mouseCoords);
1353 | }
1354 | }
1355 | }.bind (this));
1356 |
1357 | // update _currentLine distance after point deletion
1358 | this._currentLine.distance = totalDistanceUnfinishedLine;
1359 | } else {
1360 | if (this._arrPolylines[lineNr].circleMarkers.length === 2) { // if there are just 2 remaining points, delete all these points and the remaining line, since there should not stay a lonely point the map
1361 | this._layerPaint.removeLayer (this._arrPolylines[lineNr].circleMarkers [1]);
1362 | this._layerPaint.removeLayer (this._arrPolylines[lineNr].tooltips [1]);
1363 | this._layerPaint.removeLayer (this._arrPolylines[lineNr].circleMarkers [0]);
1364 | this._layerPaint.removeLayer (this._arrPolylines[lineNr].tooltips [0]);
1365 | this._layerPaint.removeLayer (this._arrPolylines[lineNr].arrowMarkers [0]);
1366 | this._layerPaint.removeLayer (this._arrPolylines[lineNr].polylinePath);
1367 | this._map.fire('polylinemeasure:remove', e1);
1368 | this._map.fire('polylinemeasure:change', this._arrPolylines[this._lineNr]);
1369 | return;
1370 | }
1371 |
1372 | this._arrPolylines[lineNr].circleCoords.splice(circleNr,1);
1373 | this._arrPolylines[lineNr].circleMarkers [circleNr].removeFrom (this._layerPaint);
1374 | this._arrPolylines[lineNr].circleMarkers.splice(circleNr,1);
1375 | this._arrPolylines[lineNr].circleMarkers.map (function (item, index) {
1376 | item.cntCircle = index;
1377 | });
1378 | var lineCoords = this._arrPolylines[lineNr].polylinePath.getLatLngs();
1379 | this._arrPolylines[lineNr].tooltips [circleNr].removeFrom (this._layerPaint);
1380 | this._arrPolylines[lineNr].tooltips.splice(circleNr,1);
1381 |
1382 | // if the last Circle in polyline is being removed (in the code above, so length will be equal 0)
1383 | if(!this._arrPolylines[lineNr].circleMarkers.length) {
1384 | this._arrPolylines.splice(lineNr, 1);
1385 | // when you delete the line in the middle of array, other lines indexes change, so you need to update line number of markers and circles
1386 | this._arrPolylines.forEach(function(line, index) {
1387 | line.circleMarkers.map(function (item) {
1388 | item.cntLine = index;
1389 | });
1390 | line.arrowMarkers.map(function (item) {
1391 | item.cntLine = index;
1392 | });
1393 | });
1394 |
1395 | return;
1396 | }
1397 | // if first Circle is being removed
1398 | if (circleNr === 0) {
1399 | this._arrPolylines[lineNr].circleMarkers [0].setStyle (this.options.startCircle);
1400 | lineCoords.splice (0, arcpoints-1);
1401 | this._arrPolylines[lineNr].circleMarkers [0].bindTooltip (this.options.tooltipTextMove + this.options.tooltipTextDelete + this.options.tooltipTextResume, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
1402 | this._arrPolylines[lineNr].arrowMarkers [circleNr].removeFrom (this._layerPaint);
1403 | this._arrPolylines[lineNr].arrowMarkers.splice(0,1);
1404 | var text='';
1405 | if (this.options.showBearings === true) {
1406 | text = this.options.bearingTextIn+':---° '+this.options.bearingTextOut+':---°';
1407 | }
1408 | text = text + '
+' + '0
';
1409 | text = text + '
' + '0
';
1410 | this._arrPolylines[lineNr].tooltips [0]._icon.innerHTML = text;
1411 | // if last Circle is being removed
1412 | } else if (circleNr === this._arrPolylines[lineNr].circleCoords.length) {
1413 | this._arrPolylines[lineNr].circleMarkers [circleNr-1].on ('click', this._resumePolylinePath, this);
1414 | this._arrPolylines[lineNr].circleMarkers [circleNr-1].bindTooltip (this.options.tooltipTextMove + this.options.tooltipTextDelete + this.options.tooltipTextResume, {direction:'top', opacity:0.7, className:'polyline-measure-popupTooltip'});
1415 | this._arrPolylines[lineNr].circleMarkers.slice(-1)[0].setStyle (this.options.endCircle); // get last element of the array
1416 | this._arrPolylines[lineNr].tooltips.slice(-1)[0]._icon.classList.add('polyline-measure-tooltip-end');
1417 | lineCoords.splice (-(arcpoints-1), arcpoints-1);
1418 | this._arrPolylines[lineNr].arrowMarkers [circleNr-1].removeFrom (this._layerPaint);
1419 | this._arrPolylines[lineNr].arrowMarkers.splice(-1,1);
1420 | // if intermediate Circle is being removed
1421 | } else {
1422 | var newLineSegment = this._polylineArc (this._arrPolylines[lineNr].circleCoords[circleNr-1], this._arrPolylines[lineNr].circleCoords[circleNr]);
1423 | Array.prototype.splice.apply (lineCoords, [(circleNr-1)*(arcpoints-1), (2*arcpoints-1)].concat (newLineSegment));
1424 | this._arrPolylines[lineNr].arrowMarkers [circleNr-1].removeFrom (this._layerPaint);
1425 | this._arrPolylines[lineNr].arrowMarkers [circleNr].removeFrom (this._layerPaint);
1426 | var arrowMarker = this._drawArrow (newLineSegment);
1427 | this._arrPolylines[lineNr].arrowMarkers.splice(circleNr-1,2,arrowMarker);
1428 | }
1429 | this._arrPolylines[lineNr].polylinePath.setLatLngs (lineCoords);
1430 | this._arrPolylines[lineNr].arrowMarkers.map (function (item, index) {
1431 | item.cntLine = lineNr;
1432 | item.cntArrow = index;
1433 | });
1434 | var totalDistance = 0;
1435 | this._arrPolylines[lineNr].tooltips.map (function (item, index) {
1436 | if (index >= 1) {
1437 | var distance = this._arrPolylines[lineNr].circleCoords[index-1].distanceTo (this._arrPolylines[lineNr].circleCoords[index]);
1438 | var lastCircleCoords = this._arrPolylines[lineNr].circleCoords[index - 1];
1439 | var mouseCoords = this._arrPolylines[lineNr].circleCoords[index];
1440 | totalDistance += distance;
1441 | this._arrPolylines[lineNr].distance = totalDistance;
1442 | var prevTooltip = this._arrPolylines[lineNr].tooltips[index-1];
1443 | this._updateTooltip (item, prevTooltip, totalDistance, distance, lastCircleCoords, mouseCoords);
1444 | }
1445 | }.bind (this));
1446 | // if user is deleting a point of a line not finished yet (= rubbberline still present)
1447 | }
1448 |
1449 | this._map.fire('polylinemeasure:remove', e1);
1450 | this._map.fire('polylinemeasure:change', this._arrPolylines[this._lineNr]);
1451 | return;
1452 | }
1453 | this._e1 = e1;
1454 | if ((this._measuring) && (this._cntCircle === 0)) { // just execute drag-function if Measuring tool is active but no line is being drawn at the moment.
1455 | this._map.dragging.disable(); // turn of moving of the map during drag of a circle
1456 | this._map.off ('mousemove', this._mouseMove, this);
1457 | this._map.off ('click', this._mouseClick, this);
1458 | this._mouseStartingLat = e1.latlng.lat;
1459 | this._mouseStartingLng = e1.latlng.lng;
1460 | this._circleStartingLat = e1.target._latlng.lat;
1461 | this._circleStartingLng = e1.target._latlng.lng;
1462 | this._map.on ('mousemove', this._dragCircleMousemove, this);
1463 | }
1464 | },
1465 |
1466 | /**
1467 | * Takes in a dataset and programatically draws the polylines and measurements to the map
1468 | * Dataset must be in the form of an array of LatLng[], which allows for multiple discontinuous
1469 | * polylines to be seeded
1470 | * @param {L.LatLng[][]} polylinesArray | Array of array of points
1471 | */
1472 | seed: function(polylinesArray){
1473 | // Hijack user actions to manually draw polylines
1474 | polylinesArray.forEach((polyline) => {
1475 | // toggle draw state on:
1476 | this._toggleMeasure();
1477 | // start line with first point of each polyline
1478 | this._startLine(polyline[0]);
1479 | // add subsequent points:
1480 | polyline.forEach((point, ind) => {
1481 | const latLng = L.latLng(point);
1482 | this._mouseMove({ latLng });
1483 | this._currentLine.addPoint(latLng);
1484 | // on last point,
1485 | if (ind === polyline.length - 1) {
1486 | this._finishPolylinePath();
1487 | this._toggleMeasure();
1488 | }
1489 | });
1490 | });
1491 | }
1492 | });
1493 |
1494 | //======================================================================================
1495 |
1496 | L.Map.mergeOptions({
1497 | PolylineMeasureControl: false
1498 | });
1499 |
1500 | L.Map.addInitHook(function () {
1501 | if (this.options.polylineMeasureControl) {
1502 | this.PMControl = new L.Control.PolylineMeasure();
1503 | this.addControl(this.PMControl);
1504 | }
1505 | });
1506 |
1507 | L.control.polylineMeasure = function (options) {
1508 | return new L.Control.PolylineMeasure (options);
1509 | };
1510 |
1511 | return L.Control.PolylineMeasure;
1512 | // to allow
1513 | // import PolylineMeasure from 'leaflet.polylinemeasure';
1514 | // const measureControl = new PolylineMeasure();
1515 | // together with
1516 | // import 'leaflet.polylinemeasure';
1517 | // const measureControl = new L.Control.PolylineMeasure();
1518 |
1519 | }));
1520 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Leaflet.PolylineMeasure
4 | * Leaflet Plugin to **measure distances** of simple lines as well as of complex polylines.
5 | * Measuring in **metric system** (metres, kilometres), in **imperial system** (feet, landmiles), or in **nautical miles**.
6 | * Lines are drawn as realistic arcs. **Bearings** and **distances** are calculated considering [**Great-circle distance**](https://en.wikipedia.org/wiki/Great-circle_distance) which is the shortest path between 2 points on Earth.
7 | * **Arrows** indicating the **real midways** of the line's great-circle **distances**, not their optical middle which is different due to projection, especially in high latitudes.
8 | * To **finish** drawing a line just *doubleclick*, or *singleclick* onto the last (=orange) point, or *press "ESC"-key*.
9 | * **Moving** of line's points afterwards is possible by clicking and draging them. *(This feature can not be guaranteed to work on every **mobile** browser using touch input, e.g. with Chrome Mobile it isn't working right now)*
10 | * To **continue** a line after it has been finished, hold the *Ctrl-Key* while clicking onto the first or last point of a line.
11 | * To **add** points, hold the *Ctrl-Key* while clicking onto an arrow.
12 | * To **delete** points, hold the *Shift-Key* while clicking onto a point.
13 | * It is an evolution of jtreml's Plugin [leaflet.measure](https://github.com/jtreml/leaflet.measure) since the original plugin hasn't been bugfixed for years. I modified it to work again with **Leaflet v1.0 and newer** (still runs with Leaflet v0.7) and added functional and optical improvements.
14 |
15 | ## Demos
16 | * Please take a look at these demos:
17 | - [**Demo 1**](https://ppete2.github.io/Leaflet.PolylineMeasure/demo1.html) (kilometre units, bearings, with Clear Control and Unit Control buttons)
18 | - [**Demo 2**](https://ppete2.github.io/Leaflet.PolylineMeasure/demo2.html) (landmile units, without bearings, without Unit Control button)
19 | - [**Demo 3**](https://ppete2.github.io/Leaflet.PolylineMeasure/demo3.html) (nautical mile units, bearings, without Unit Control and Clear Control buttons)
20 | - [**Demo 4**](https://ppete2.github.io/Leaflet.PolylineMeasure/demo4.html) (two maps)
21 | - [**Demo 5**](https://ppete2.github.io/Leaflet.PolylineMeasure/demo5.html) (programatically providing polyline points - "Seeding Data")
22 |
23 | 
24 |
25 | ## Usage
26 |
27 | Add 2 code lines within your **HTML-file** to load the .css and .js files of the plugin:
28 | ```html
29 |
30 |
31 | ```
32 |
33 | Add 1 code line within your **Javascript-file** to add the plugin's control into your Leaflet map.
34 | ```js
35 | L.control.polylineMeasure(options).addTo(map);
36 | ```
37 |
38 | ## Package manager install
39 |
40 | It's possible to install and update the Plugin using package managers like `npm`. This feature has been added by other users. I'm not familiar nor responsible to keep these package manager installs up-to-date. If you notice such installs being outdated, feel free to provide a Pull request or contact one of the persons who introduced package manager installs, thanks.
41 |
42 | ## Default options
43 |
44 | ```js
45 | options = {
46 | position: 'topleft', // Position to show the control. Values: 'topright', 'topleft', 'bottomright', 'bottomleft'
47 | unit: 'kilometres', // Default unit the distances are displayed in. Values: 'kilometres', 'landmiles', 'nauticalmiles'
48 | useSubunits: true, // Use subunits (metres/feet) in tooltips if distances are less than 1 kilometre/landmile
49 | clearMeasurementsOnStop: true, // Clear all measurements when Measure Control is switched off
50 | showBearings: false, // Whether bearings are displayed within the tooltips
51 | bearingTextIn: 'In', // language dependend label for inbound bearings
52 | bearingTextOut: 'Out', // language dependend label for outbound bearings
53 | tooltipTextFinish: 'Click to finish line ',
54 | tooltipTextDelete: 'Press SHIFT-key and click to delete point',
55 | tooltipTextMove: 'Click and drag to move point ',
56 | tooltipTextResume: ' Press CTRL-key and click to resume line',
57 | tooltipTextAdd: 'Press CTRL-key and click to add point',
58 | // language dependend labels for point's tooltips
59 | measureControlTitleOn: 'Turn on PolylineMeasure', // Title for the Measure Control going to be switched on
60 | measureControlTitleOff: 'Turn off PolylineMeasure', // Title for the Measure Control going to be switched off
61 | measureControlLabel: '↦', // Label of the Measure Control (Unicode symbols are possible)
62 | measureControlClasses: [], // Classes to apply to the Measure Control
63 | showClearControl: false, // Show a control to clear all the measurements
64 | clearControlTitle: 'Clear Measurements', // Title text to show on the Clear Control
65 | clearControlLabel: '×', // Label of the Clear Control (Unicode symbols are possible)
66 | clearControlClasses: [], // Classes to apply to Clear Control
67 | showUnitControl: false, // Show a control to change the units of measurements
68 | unitControlUnits: ["kilometres", "landmiles", "nauticalmiles"],
69 | // measurement units being cycled through by using the Unit Control
70 | unitControlTitle: { // Title texts to show on the Unit Control
71 | text: 'Change Units',
72 | kilometres: 'kilometres',
73 | landmiles: 'land miles',
74 | nauticalmiles: 'nautical miles'
75 | },
76 | unitControlLabel: { // Unit symbols to show in the Unit Control and measurement labels
77 | metres: 'm',
78 | kilometres: 'km',
79 | feet: 'ft',
80 | landmiles: 'mi',
81 | nauticalmiles: 'nm'
82 | },
83 | unitControlClasses: [], // Classes to apply to the Unit Control
84 | tempLine: { // Styling settings for the temporary dashed line
85 | color: '#00f', // Dashed line color
86 | weight: 2 // Dashed line weight
87 | },
88 | fixedLine: { // Styling for the solid line
89 | color: '#006', // Solid line color
90 | weight: 2 // Solid line weight
91 | },
92 | arrow: { // Styling of the midway arrow
93 | color: '#000', // Color of the arrow
94 | },
95 | startCircle: { // Style settings for circle marker indicating the starting point of the polyline
96 | color: '#000', // Color of the border of the circle
97 | weight: 1, // Weight of the circle
98 | fillColor: '#0f0', // Fill color of the circle
99 | fillOpacity: 1, // Fill opacity of the circle
100 | radius: 3 // Radius of the circle
101 | },
102 | intermedCircle: { // Style settings for all circle markers between startCircle and endCircle
103 | color: '#000', // Color of the border of the circle
104 | weight: 1, // Weight of the circle
105 | fillColor: '#ff0', // Fill color of the circle
106 | fillOpacity: 1, // Fill opacity of the circle
107 | radius: 3 // Radius of the circle
108 | },
109 | currentCircle: { // Style settings for circle marker indicating the latest point of the polyline during drawing a line
110 | color: '#000', // Color of the border of the circle
111 | weight: 1, // Weight of the circle
112 | fillColor: '#f0f', // Fill color of the circle
113 | fillOpacity: 1, // Fill opacity of the circle
114 | radius: 6 // Radius of the circle
115 | },
116 | endCircle: { // Style settings for circle marker indicating the last point of the polyline
117 | color: '#000', // Color of the border of the circle
118 | weight: 1, // Weight of the circle
119 | fillColor: '#f00', // Fill color of the circle
120 | fillOpacity: 1, // Fill opacity of the circle
121 | radius: 3 // Radius of the circle
122 | },
123 | };
124 | ```
125 |
126 | ## Events
127 | Several Events are fired during the use of the Plugin in order to offer interactivity outside the Plugin.
128 | Subscribe to events with:
129 |
130 | ```js
131 | map.on('polylinemeasure:toogle', e => { /* e.sttus */ });
132 | map.on('polylinemeasure:start', currentLine => {...});
133 | map.on('polylinemeasure:resume', currentLine => {...});
134 | map.on('polylinemeasure:finish', currentLine => {...});
135 | map.on('polylinemeasure:change', currentLine => {...});
136 | map.on('polylinemeasure:clear', e => {...});
137 | map.on('polylinemeasure:add', e => { /* e.latlng */ });
138 | map.on('polylinemeasure:insert', e => { /* e.latlng */ });
139 | map.on('polylinemeasure:move', e => { /* e.latlng ; e.sourceTarget._latlng */ });
140 | map.on('polylinemeasure:remove', e => { /* e.latlng ; e.sourceTarget._latlng */ });
141 | ```
142 | * Please take a look at [**Demo 1**](https://ppete2.github.io/Leaflet.PolylineMeasure/demo1.html), where those events get listed in the JS console of your browser
143 |
144 | ## Seeding Data
145 | You can programatically draw measured polylines using the `.seed` method. It takes an array of arrays of `L.LatLng`, which enables drawing multiple, discontinuous polylines:
146 |
147 | ```js
148 | let polylineMeasure = L.control.polylineMeasure(options);
149 | polylineMeasure.addTo (map);
150 |
151 | const line1coords = [
152 | { lat: 22.156883186860703, lng: -158.95019531250003 },
153 | { lat: 22.01436065310322, lng: -157.33520507812503 },
154 | { lat: 21.391704731036587, lng: -156.17065429687503 },
155 | { lat: 20.64306554672647, lng: -155.56640625000003 }
156 | ];
157 | const line2coords = [
158 | { lat: 19.880391767822505, lng: -159.67529296875003 },
159 | { lat: 17.90556881196468, lng: -156.39038085937503 }
160 | ];
161 |
162 | polylineMeasure.seed([line1coords, line2coords])
163 | ```
164 |
165 | * Please take a look at [**Demo 5**](https://ppete2.github.io/Leaflet.PolylineMeasure/demo5.html), where multiple polylines are drawn and measured programatically.
166 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/demo1.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Demo 1 of Leaflet.PolylineMeasure
7 |
8 |
9 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/demo2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Demo 2 of Leaflet.PolylineMeasure
7 |
8 |
9 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/demo3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Demo 3 of Leaflet.PolylineMeasure
7 |
8 |
9 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/demo4.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Two Map Demo of Leaflet.PolylineMeasure
7 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
20 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/demo5.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Demo 5 of Leaflet.PolylineMeasure
7 |
8 |
9 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "leaflet.polylinemeasure",
3 | "version": "1.0.0",
4 | "description": "Leaflet Plugin to measure distances of simple lines as well as of complex polylines",
5 | "main": "Leaflet.PolylineMeasure.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/ppete2/Leaflet.PolylineMeasure.git"
12 | },
13 | "author": "",
14 | "license": "BSD-2-Clause",
15 | "bugs": {
16 | "url": "https://github.com/ppete2/Leaflet.PolylineMeasure/issues"
17 | },
18 | "homepage": "https://github.com/ppete2/Leaflet.PolylineMeasure#readme"
19 | }
20 |
--------------------------------------------------------------------------------
/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ppete2/Leaflet.PolylineMeasure/9908e5a2cbd2753c335ac758f3559ba9da6f99e9/screenshot.jpg
--------------------------------------------------------------------------------