nakarte

Source code of https://map.sikmir.ru (fork)
git clone git://git.sikmir.ru/nakarte
Log | Files | Refs | LICENSE

index.js (16628B)


      1 import L from 'leaflet';
      2 import './edit_line.css';
      3 import {wrapLatLngToTarget} from '~/lib/leaflet.fixes/fixWorldCopyJump';
      4 
      5 L.Polyline.EditMixinOptions = {
      6     className: 'leaflet-editable-line'
      7 };
      8 
      9 L.Polyline.EditMixin = {
     10     _nodeMarkersZOffset: 10000,
     11 
     12     startEdit: function() {
     13         if (this._map && !this._editing) {
     14             this._editing = true;
     15             this._drawingDirection = 0;
     16             this.setupMarkers();
     17             this.on('remove', this.stopEdit.bind(this));
     18             this._map
     19                 .on('click', this.onMapClick, this)
     20                 .on('dragend', this.onMapEndDrag, this);
     21             L.DomEvent.on(document, 'keyup', this.onKeyPress, this);
     22             this._storedStyle = {weight: this.options.weight, opacity: this.options.opacity};
     23             this.setStyle({weight: 1.5, opacity: 1});
     24             L.DomUtil.addClass(this._map._container, 'leaflet-line-editing');
     25             this.fire('editstart', {target: this});
     26         }
     27     },
     28 
     29     stopEdit: function(userCancelled) {
     30         if (this._editing) {
     31             this.stopDrawingLine();
     32             this._editing = false;
     33             this.removeMarkers();
     34             L.DomEvent.off(document, 'keyup', this.onKeyPress, this);
     35             this.off('remove', this.stopEdit.bind(this));
     36             this._map
     37                 .off('click', this.onMapClick, this)
     38                 .off('dragend', this.onMapEndDrag, this);
     39             this.setStyle(this._storedStyle);
     40             L.DomUtil.removeClass(this._map._container, 'leaflet-line-editing');
     41             this.fire('editend', {target: this, userCancelled});
     42         }
     43     },
     44 
     45     removeMarkers: function() {
     46         this.getLatLngs().forEach(function(node) {
     47                 if (node._nodeMarker) {
     48                     this.nodeMarkers.removeLayer(node._nodeMarker);
     49                     delete node._nodeMarker._lineNode;
     50                     delete node._nodeMarker;
     51                 }
     52                 if (node._segmentOverlay) {
     53                     this.segmentOverlays.removeLayer(node._segmentOverlay);
     54                     delete node._segmentOverlay._lineNode;
     55                     delete node._segmentOverlay;
     56                 }
     57             }.bind(this)
     58         );
     59     },
     60 
     61     getMarkerIndex: function(marker) {
     62         return this.getLatLngs().indexOf(marker._lineNode);
     63     },
     64 
     65     getSegmentOverlayIndex: function(segmentOverlay) {
     66         return this.getLatLngs().indexOf(segmentOverlay._lineNode);
     67     },
     68 
     69     onNodeMarkerDragEnd: function(e) {
     70         var marker = e.target,
     71             nodeIndex = this.getMarkerIndex(marker);
     72         this.replaceNode(nodeIndex, marker.getLatLng());
     73         this._setupEndMarkers();
     74     },
     75 
     76     onNodeMarkerMovedChangeNode: function(e) {
     77         var marker = e.target,
     78             latlng = marker.getLatLng(),
     79             node = marker._lineNode;
     80         node.lat = latlng.lat;
     81         node.lng = latlng.lng;
     82         this.redraw();
     83         this.fire('nodeschanged');
     84     },
     85 
     86     onNodeMarkerDblClickedRemoveNode: function(e) {
     87         if (this._disableEditOnLeftClick) {
     88             return;
     89         }
     90         if (this.getLatLngs().length < 2 || (this._drawingDirection && this.getLatLngs().length === 2)) {
     91             return;
     92         }
     93         var marker = e.target,
     94             nodeIndex = this.getMarkerIndex(marker);
     95         this.removeNode(nodeIndex);
     96         this._setupEndMarkers();
     97     },
     98 
     99     onMapClick: function(e) {
    100         if (this._drawingDirection) {
    101             let newNodeIndex,
    102                 refNodeIndex;
    103             if (this._drawingDirection === -1) {
    104                 newNodeIndex = 1;
    105                 refNodeIndex = 0;
    106             } else {
    107                 newNodeIndex = this._latlngs.length - 1;
    108                 refNodeIndex = this._latlngs.length - 1;
    109             }
    110             this.addNode(newNodeIndex, wrapLatLngToTarget(e.latlng, this._latlngs[refNodeIndex]));
    111         } else {
    112             if (!this.preventStopEdit) {
    113                 this.stopEdit(true);
    114             }
    115         }
    116     },
    117 
    118     onMapEndDrag: function(e) {
    119         if (e.distance < 15) {
    120             // get mouse position from map drag handler
    121             var handler = e.target.dragging._draggable;
    122             var mousePos = handler._startPoint.add(handler._newPos).subtract(handler._startPos);
    123             var latlng = e.target.mouseEventToLatLng({clientX: mousePos.x, clientY: mousePos.y});
    124             this.onMapClick({latlng: latlng});
    125         }
    126     },
    127 
    128     startDrawingLine: function(direction, e) {
    129         if (!this._editing) {
    130             return;
    131         }
    132         if (direction === undefined) {
    133             direction = 1;
    134         }
    135         if (this._drawingDirection === direction) {
    136             return;
    137         }
    138         this.stopDrawingLine();
    139         this._drawingDirection = direction;
    140         if (e) {
    141             var newNodeIndex = this._drawingDirection === -1 ? 0 : this.getLatLngs().length;
    142             this.spliceLatLngs(newNodeIndex, 0, e.latlng);
    143             this._setupEndMarkers();
    144         }
    145 
    146         this._map.on('mousemove', this.onMouseMoveFollowEndNode, this);
    147         L.DomUtil.addClass(this._map._container, 'leaflet-line-drawing');
    148         this._map.clickLocked = true;
    149     },
    150 
    151     stopDrawingLine: function() {
    152         if (!this._drawingDirection) {
    153             return;
    154         }
    155         this._map.off('mousemove', this.onMouseMoveFollowEndNode, this);
    156         var nodeIndex = this._drawingDirection === -1 ? 0 : this.getLatLngs().length - 1;
    157         this.spliceLatLngs(nodeIndex, 1);
    158         this._drawingDirection = 0;
    159         L.DomUtil.removeClass(this._map._container, 'leaflet-line-drawing');
    160         this._map.clickLocked = false;
    161         this._setupEndMarkers();
    162         this.fire('drawend');
    163     },
    164 
    165     onKeyPress: function(e) {
    166         const code = e.keyCode;
    167         const targetTag = e.target.tagName.toLowerCase();
    168         const isTargetTextInput = (targetTag === 'input' && e.target.type.toLowerCase() === 'text') ||
    169             targetTag === 'textarea';
    170         if (code !== 27 && isTargetTextInput) {
    171             return;
    172         }
    173 
    174         switch (code) {
    175             case 27: // Escape
    176             case 13: // Enter
    177                 if (this._drawingDirection) {
    178                     this.stopDrawingLine();
    179                 } else {
    180                     if (!this.preventStopEdit) {
    181                         this.stopEdit(true);
    182                     }
    183                 }
    184                 L.DomEvent.stop(e);
    185                 break;
    186             case 8: // Backspace
    187             case 46: // Delete
    188                 if (this._drawingDirection && this.getLatLngs().length > 2) {
    189                     const nodeIndex = this._drawingDirection === 1 ? this.getLatLngs().length - 2 : 1;
    190                     this.removeNode(nodeIndex);
    191                     L.DomEvent.preventDefault(e);
    192                 }
    193                 break;
    194 
    195             default:
    196         }
    197     },
    198 
    199     onMouseMoveFollowEndNode: function(e) {
    200         var nodeIndex = this._drawingDirection === -1 ? 0 : this.getLatLngs().length - 1;
    201         let latlng = e.latlng;
    202         if (this._latlngs.length > 0) {
    203             latlng = wrapLatLngToTarget(latlng, this._latlngs[nodeIndex]);
    204         }
    205         this.spliceLatLngs(nodeIndex, 1, latlng);
    206     },
    207 
    208     makeNodeMarker: function(nodeIndex) {
    209         var node = this.getLatLngs()[nodeIndex],
    210             marker = L.marker(node.clone(), {
    211                     icon: L.divIcon({
    212                         className: 'line-editor-node-marker-halo',
    213                         html: '<div class="line-editor-node-marker"></div>'
    214                     }),
    215                     draggable: true,
    216                     zIndexOffset: this._nodeMarkersZOffset,
    217                     projectedShift: () => this.shiftProjectedFitMapView()
    218                 }
    219             );
    220         marker
    221             .on('drag', this.onNodeMarkerMovedChangeNode, this)
    222             // .on('dragstart', this.fire.bind(this, 'editingstart'))
    223             .on('dragend', this.onNodeMarkerDragEnd, this)
    224             .on('dblclick', this.onNodeMarkerDblClickedRemoveNode, this)
    225             .on('click', this.onNodeMarkerClickStartStopDrawing, this)
    226             .on('contextmenu', function(e) {
    227                     this.stopDrawingLine();
    228                     this.fire('noderightclick', {
    229                             nodeIndex: this.getMarkerIndex(marker),
    230                             line: this,
    231                             mouseEvent: e
    232                         }
    233                     );
    234                 }, this
    235             );
    236         marker._lineNode = node;
    237         node._nodeMarker = marker;
    238         marker.addTo(this.nodeMarkers);
    239     },
    240 
    241     onNodeMarkerClickStartStopDrawing: function(e) {
    242         if (this._disableEditOnLeftClick) {
    243             return;
    244         }
    245         var marker = e.target,
    246             latlngs = this.getLatLngs(),
    247             latlngs_n = latlngs.length,
    248             nodeIndex = this.getMarkerIndex(marker);
    249         if ((this._drawingDirection === -1 && nodeIndex === 1) ||
    250             ((this._drawingDirection === 1 && nodeIndex === latlngs_n - 2))) {
    251             this.stopDrawingLine();
    252         } else if (nodeIndex === this.getLatLngs().length - 1) {
    253             this.startDrawingLine(1, e);
    254         } else if (nodeIndex === 0) {
    255             this.startDrawingLine(-1, e);
    256         }
    257     },
    258 
    259     makeSegmentOverlay: function(nodeIndex) {
    260         const latlngs = this.getLatLngs(),
    261             p1 = latlngs[nodeIndex],
    262             p2 = latlngs[nodeIndex + 1];
    263         if (!p2) {
    264             return;
    265         }
    266         const segmentOverlay = L.polyline([p1, p2], {
    267             weight: 10,
    268             opacity: 0.0,
    269             projectedShift: () => this.shiftProjectedFitMapView()
    270         });
    271         segmentOverlay.on('mousedown', this.onSegmentMouseDownAddNode, this);
    272         segmentOverlay.on('contextmenu', function(e) {
    273                 this.stopDrawingLine();
    274                 this.fire('segmentrightclick', {
    275                         nodeIndex: this.getSegmentOverlayIndex(segmentOverlay),
    276                         mouseEvent: e,
    277                         line: this
    278                     }
    279                 );
    280             }, this
    281         );
    282         segmentOverlay._lineNode = p1;
    283         p1._segmentOverlay = segmentOverlay;
    284         segmentOverlay.addTo(this.segmentOverlays);
    285     },
    286 
    287     onSegmentMouseDownAddNode: function(e) {
    288         if (e.originalEvent.button !== 0 || this._disableEditOnLeftClick) {
    289             return;
    290         }
    291         var segmentOverlay = e.target,
    292             latlngs = this.getLatLngs(),
    293             nodeIndex = this.getSegmentOverlayIndex(segmentOverlay) + 1;
    294         const midPoint = L.latLngBounds(latlngs[nodeIndex], latlngs[nodeIndex - 1]).getCenter();
    295         this.addNode(nodeIndex, wrapLatLngToTarget(e.latlng, midPoint));
    296         if (L.Draggable._dragging) {
    297             L.Draggable._dragging.finishDrag();
    298         }
    299         latlngs[nodeIndex]._nodeMarker.dragging._draggable._onDown(e.originalEvent);
    300     },
    301 
    302     addNode: function(index, latlng) {
    303         var nodes = this.getLatLngs(),
    304             isAddingLeft = (index === 1 && this._drawingDirection === -1),
    305             isAddingRight = (index === nodes.length - 1 && this._drawingDirection === 1);
    306         latlng = latlng.clone();
    307         this.spliceLatLngs(index, 0, latlng);
    308         this.makeNodeMarker(index);
    309         if (!isAddingLeft && (index >= 1)) {
    310             if (!isAddingRight) {
    311                 var prevNode = nodes[index - 1];
    312                 this.segmentOverlays.removeLayer(prevNode._segmentOverlay);
    313                 delete prevNode._segmentOverlay._lineNode;
    314                 delete prevNode._segmentOverlay;
    315             }
    316             this.makeSegmentOverlay(index - 1);
    317         }
    318         if (!isAddingRight) {
    319             this.makeSegmentOverlay(index);
    320         }
    321         if (nodes.length < 3) {
    322             this._setupEndMarkers();
    323         }
    324     },
    325 
    326     removeNode: function(index) {
    327         var nodes = this.getLatLngs(),
    328             node = nodes[index],
    329             marker = node._nodeMarker;
    330         delete node._nodeMarker;
    331         delete marker._lineNode;
    332         this.spliceLatLngs(index, 1);
    333         this.nodeMarkers.removeLayer(marker);
    334         if (node._segmentOverlay) {
    335             this.segmentOverlays.removeLayer(node._segmentOverlay);
    336             delete node._segmentOverlay._lineNode;
    337             delete node._segmentOverlay;
    338         }
    339         var prevNode = nodes[index - 1];
    340         if (prevNode && prevNode._segmentOverlay) {
    341             this.segmentOverlays.removeLayer(prevNode._segmentOverlay);
    342             delete prevNode._segmentOverlay._lineNode;
    343             delete prevNode._segmentOverlay;
    344             if ((index < nodes.length - 1) || (index < nodes.length && this._drawingDirection !== 1)) {
    345                 this.makeSegmentOverlay(index - 1);
    346             }
    347         }
    348     },
    349 
    350     replaceNode: function(index, latlng) {
    351         var nodes = this.getLatLngs(),
    352             oldNode = nodes[index],
    353             oldMarker = oldNode._nodeMarker;
    354         this.nodeMarkers.removeLayer(oldNode._nodeMarker);
    355         delete oldNode._nodeMarker;
    356         delete oldMarker._lineNode;
    357         latlng = latlng.clone();
    358         this.spliceLatLngs(index, 1, latlng);
    359         this.makeNodeMarker(index);
    360         if (oldNode._segmentOverlay) {
    361             this.segmentOverlays.removeLayer(oldNode._segmentOverlay);
    362             delete oldNode._segmentOverlay._lineNode;
    363             delete oldNode._segmentOverlay;
    364             this.makeSegmentOverlay(index);
    365         }
    366         var prevNode = nodes[index - 1];
    367         if (prevNode && prevNode._segmentOverlay) {
    368             this.segmentOverlays.removeLayer(prevNode._segmentOverlay);
    369             delete prevNode._segmentOverlay._lineNode;
    370             delete prevNode._segmentOverlay;
    371             this.makeSegmentOverlay(index - 1);
    372         }
    373     },
    374 
    375     _setupEndMarkers: function() {
    376         const nodesCount = this._latlngs.length;
    377         if (nodesCount === 0) {
    378             return;
    379         }
    380         const startIndex = this._drawingDirection === -1 ? 1 : 0;
    381         const endIndex = this._drawingDirection === 1 ? nodesCount - 2 : nodesCount - 1;
    382         const startIcon = this._latlngs[startIndex]._nodeMarker._icon;
    383         L.DomUtil[this._drawingDirection === -1 ? 'removeClass' : 'addClass'](
    384             startIcon, 'line-editor-node-marker-start'
    385         );
    386         if (endIndex >= 0) {
    387             const endIcon = this._latlngs[endIndex]._nodeMarker._icon;
    388             let func;
    389             if (this._drawingDirection !== 1 && endIndex > 0) {
    390                 func = L.DomUtil.addClass;
    391             } else {
    392                 func = L.DomUtil.removeClass;
    393             }
    394             func(endIcon, 'line-editor-node-marker-end');
    395         }
    396     },
    397 
    398     setupMarkers: function() {
    399         if (!this.segmentOverlays) {
    400             this.segmentOverlays = L.featureGroup().addTo(this._map);
    401         }
    402         if (!this.nodeMarkers) {
    403             this.nodeMarkers = L.featureGroup().addTo(this._map);
    404         }
    405         this.removeMarkers();
    406         var latlngs = this.getLatLngs(),
    407             startNode = 0,
    408             endNode = latlngs.length - 1;
    409         if (this._drawingDirection === -1) {
    410             startNode += 1;
    411         }
    412         if (this._drawingDirection === 1) {
    413             endNode -= 1;
    414         }
    415         for (var i = startNode; i <= endNode; i++) {
    416             this.makeNodeMarker(i);
    417             if (i < endNode) {
    418                 this.makeSegmentOverlay(i);
    419             }
    420         }
    421         this._setupEndMarkers();
    422     },
    423 
    424     spliceLatLngs: function(...args) {
    425         const latlngs = this.getLatLngs();
    426         const res = latlngs.splice(...args);
    427         this.setLatLngs(latlngs);
    428         this.fire('nodeschanged');
    429         return res;
    430         // this._latlngs.splice(...args);
    431         // this.redraw();
    432     },
    433 
    434     getFixedLatLngs: function() {
    435         const start = this._drawingDirection === -1 ? 1 : 0;
    436         let end = this._latlngs.length;
    437         if (this._drawingDirection === 1) {
    438             end -= 1;
    439         }
    440         return this._latlngs.slice(start, end);
    441     },
    442 
    443     disableEditOnLeftClick: function(disable) {
    444         this._disableEditOnLeftClick = disable;
    445     },
    446 
    447     highlighNodesForDeletion: function(startNodeIndex, endNodeIndex) {
    448         if (!this._editing) {
    449             return;
    450         }
    451         const nodes = this.getLatLngs();
    452         for (let i = 0; i < nodes.length; i++) {
    453             const node = nodes[i];
    454             const icon = node._nodeMarker._icon;
    455             L.DomUtil[i >= startNodeIndex && i <= endNodeIndex ? 'addClass' : 'removeClass'](icon, 'highlight-delete');
    456         }
    457     }
    458 };