From 009dcf9cfa5ef4073553dbbf9eef7bc52bf7262f Mon Sep 17 00:00:00 2001 From: Olivia Marie Valmin Date: Thu, 1 Oct 2020 09:26:34 +0200 Subject: [PATCH 01/30] Added Danish translations (#670) (minor) * Added danish translations * Added translation file --- src/assets/translations/da.json | 32 ++++++++++++++++++++++++++++++++ src/assets/translations/index.js | 4 +++- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 src/assets/translations/da.json diff --git a/src/assets/translations/da.json b/src/assets/translations/da.json new file mode 100644 index 00000000..b4a469dd --- /dev/null +++ b/src/assets/translations/da.json @@ -0,0 +1,32 @@ +{ + "tooltips": { + "placeMarker": "Tryk for at placere en markør", + "firstVertex": "Tryk for at placere det første punkt", + "continueLine": "Tryk for at fortsætte linjen", + "finishLine": "Tryk på et eksisterende punkt for at afslutte", + "finishPoly": "Tryk på det første punkt for at afslutte", + "finishRect": "Tryk for at afslutte", + "startCircle": "Tryk for at placere cirklens center", + "finishCircle": "Tryk for at afslutte cirklen", + "placeCircleMarker": "Tryk for at placere en cirkelmarkør" + }, + "actions": { + "finish": "Afslut", + "cancel": "Afbryd", + "removeLastVertex": "Fjern sidste punkt" + }, + "buttonTitles": { + "drawMarkerButton": "Placer markør", + "drawPolyButton": "Tegn polygon", + "drawLineButton": "Tegn linje", + "drawCircleButton": "Tegn cirkel", + "drawRectButton": "Tegn firkant", + "editButton": "Rediger", + "dragButton": "Træk", + "cutButton": "Klip", + "deleteButton": "Fjern", + "drawCircleMarkerButton": "Tegn cirkelmarkør", + "snappingButton": "Fastgør trukket markør til andre elementer", + "pinningButton": "Sammenlæg delte elementer" + } +} \ No newline at end of file diff --git a/src/assets/translations/index.js b/src/assets/translations/index.js index 96538bea..93f8dd42 100644 --- a/src/assets/translations/index.js +++ b/src/assets/translations/index.js @@ -14,6 +14,7 @@ import pl from './pl.json'; import sv from './sv.json'; import el from './el.json'; import hu from './hu.json'; +import da from './da.json'; export default { en, @@ -30,5 +31,6 @@ export default { pl, sv, el, - hu + hu, + da }; From 08bfa689d7b6691904ffbebc7343e835e079390f Mon Sep 17 00:00:00 2001 From: Falke Design Date: Thu, 1 Oct 2020 09:28:52 +0200 Subject: [PATCH 02/30] fix _hiddenPolyCircle Bug with pmIgnore: true (#668) (patch) --- cypress/integration/circlemarker.spec.js | 21 +++++++++++++++++++++ src/js/Mixins/Snapping.js | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/cypress/integration/circlemarker.spec.js b/cypress/integration/circlemarker.spec.js index c7ee36c5..3e86b4d7 100644 --- a/cypress/integration/circlemarker.spec.js +++ b/cypress/integration/circlemarker.spec.js @@ -141,4 +141,25 @@ describe('Draw Circle Marker', () => { cy.hasVertexMarkers(2); }); + it('snapping to CircleMarker with pmIgnore:true', () => { + cy.window().then(({ map, L}) => { + L.circleMarker(map.getCenter(),{pmIgnore: true}).addTo(map); + }); + + cy.toolbarButton('rectangle') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + cy.get(mapSelector) + .click(200, 200) + .click(400, 350); + + cy.toolbarButton('edit') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + cy.hasVertexMarkers(4); + }); }); diff --git a/src/js/Mixins/Snapping.js b/src/js/Mixins/Snapping.js index 4663a658..9f620b07 100644 --- a/src/js/Mixins/Snapping.js +++ b/src/js/Mixins/Snapping.js @@ -234,7 +234,7 @@ const SnapMixin = { ) { // adds a hidden polygon which matches the border of the circle - if ((layer instanceof L.Circle || layer instanceof L.CircleMarker) && layer.pm._hiddenPolyCircle) { + if ((layer instanceof L.Circle || layer instanceof L.CircleMarker) && layer.pm && layer.pm._hiddenPolyCircle) { layers.push(layer.pm._hiddenPolyCircle); } layers.push(layer); From 1b8bff4345354c35b4c9fd02a076b6ec07d2d78b Mon Sep 17 00:00:00 2001 From: Falke Design Date: Thu, 1 Oct 2020 09:30:05 +0200 Subject: [PATCH 03/30] check if layer has coords (deep level) before add it to snap list (#667) (patch) * check if layer has coords (deep level) before add it to snap list * add test --- cypress/integration/polygon.spec.js | 24 ++++++++++++++++++++++++ src/js/Mixins/Snapping.js | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/cypress/integration/polygon.spec.js b/cypress/integration/polygon.spec.js index 50d54e44..e19318aa 100644 --- a/cypress/integration/polygon.spec.js +++ b/cypress/integration/polygon.spec.js @@ -633,4 +633,28 @@ describe('Draw & Edit Poly', () => { }) }); }); + + it('no snapping to polygon with no coords', () => { + cy.window().then(({ map, L }) => { + L.polygon([]).addTo(map); + }); + + // activate line drawing + cy.toolbarButton('polyline') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + // draw a line + cy.get(mapSelector) + .click(150, 250) + .click(160, 50) + .click(160, 50); + + + cy.toolbarButton('edit') + .click(); + + cy.hasVertexMarkers(2); + }); }); diff --git a/src/js/Mixins/Snapping.js b/src/js/Mixins/Snapping.js index 9f620b07..2339d8a3 100644 --- a/src/js/Mixins/Snapping.js +++ b/src/js/Mixins/Snapping.js @@ -1,4 +1,5 @@ import Utils from '../L.PM.Utils'; +import {isEmptyDeep} from "../helpers"; const SnapMixin = { _initSnappableMarkers() { @@ -257,7 +258,7 @@ const SnapMixin = { // also remove everything that has no coordinates yet layers = layers.filter( - layer => layer._latlng || (layer._latlngs && layer._latlngs.length > 0) + layer => layer._latlng || (layer._latlngs && !isEmptyDeep(layer._latlngs)) ); // finally remove everything that's leaflet-geoman specific temporary stuff From 5a4f28fdb335eebe00fc83f6ef058e1c924c589f Mon Sep 17 00:00:00 2001 From: Falke Design Date: Thu, 1 Oct 2020 09:31:18 +0200 Subject: [PATCH 04/30] reset of intersection-red not worked (#666) (patch) * reset of intersection-red not worked * Add test --- cypress/integration/rectangle.spec.js | 34 +++++++++++++++++++++++++++ src/js/Edit/L.PM.Edit.Line.js | 12 ++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/cypress/integration/rectangle.spec.js b/cypress/integration/rectangle.spec.js index dd180522..6bc85af7 100644 --- a/cypress/integration/rectangle.spec.js +++ b/cypress/integration/rectangle.spec.js @@ -97,5 +97,39 @@ describe('Draw Rectangle', () => { cy.toolbarButton('edit').click(); cy.hasVertexMarkers(16); + }); + + it('goes back to blue after self-intersection removed', ()=>{ + cy.toolbarButton('rectangle').click(); + cy.get(mapSelector) + .click(100,50) + .click(700,400); + + cy.toolbarButton('cut').click(); + cy.get(mapSelector) + .click(200,200) + .click(250,230) + .click(300,250) + .click(370,200) + .click(200,200); + + cy.toolbarButton('cut').click(); + cy.get(mapSelector) + .click(200,300) + .click(250,270) + .click(300,250) + .click(370,300) + .click(200,300); + + cy.toolbarButton('edit').click(); + cy.hasVertexMarkers(12); + + cy.get(mapSelector).rightclick(300,250); + + cy.window().then(({ map, L }) => { + const rect = map.pm.getGeomanDrawLayers()[0]; + expect(rect.options.color).to.not.equal('red'); + + }) }) }); diff --git a/src/js/Edit/L.PM.Edit.Line.js b/src/js/Edit/L.PM.Edit.Line.js index 1dd2a8bb..1e81a8dc 100644 --- a/src/js/Edit/L.PM.Edit.Line.js +++ b/src/js/Edit/L.PM.Edit.Line.js @@ -78,12 +78,16 @@ Edit.Line = Edit.extend({ ); } - this.cachedColor = undefined; if (!this.options.allowSelfIntersection) { - this.cachedColor = this._layer.options.color; - - this.isRed = false; + if(this._layer.options.color !== 'red') { + this.cachedColor = this._layer.options.color; + this.isRed = false; + }else{ + this.isRed = true; + } this._handleLayerStyle(); + }else{ + this.cachedColor = undefined; } }, From d64ec774c26c1e5dd7f280318f330e92428aea1a Mon Sep 17 00:00:00 2001 From: Falke Design Date: Thu, 1 Oct 2020 09:39:12 +0200 Subject: [PATCH 05/30] Re-Designed the optIn (#596) (minor) * Update README.md typo globaldrawmodetoggled --> globaldragmodetoggled * Add reInitLayer function: reinit layer which was excluded with pmIgnore * Add reInitLayer function to Readme * New design of opt-in * Add tests --- README.md | 15 +++++++- cypress/integration/polygon.spec.js | 59 +++++++++++++++++++++++++++-- cypress/support/commands.js | 1 + src/js/L.PM.js | 56 ++++++++++++++++++++++----- 4 files changed, 116 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index d06465e4..fe1531be 100644 --- a/README.md +++ b/README.md @@ -111,12 +111,25 @@ their options when creating them. Example: L.marker([51.50915, -0.096112], { pmIgnore: true }).addTo(map); ``` +Enable leaflet-geoman on an ignored layer: +```js +layer.options.pmIgnore = false; +L.PM.reInitLayer(layer); +``` +If `Opt-In` (look below) is `true`, a layers `pmIgnore` property has to be set to `false` to get initiated. + + ##### Opt-In If you want to use leaflet-geoman as opt-in, call the following function right after importing: ```js -L.PM.initialize({ optIn: true }); +L.PM.setOptIn(true); +``` + +And to disable it: +```js +L.PM.setOptIn(false); ``` All layers will be ignored by leaflet-geoman, unless you specify `pmIgnore: false` on a layer: diff --git a/cypress/integration/polygon.spec.js b/cypress/integration/polygon.spec.js index e19318aa..3ac0b6cf 100644 --- a/cypress/integration/polygon.spec.js +++ b/cypress/integration/polygon.spec.js @@ -22,7 +22,7 @@ describe('Draw & Edit Poly', () => { it('works without pmIgnore', () => { cy.window().then(({ L }) => { - L.PM.initialize({ optIn: false }); + L.PM.setOptIn(false); cy.drawShape('MultiPolygon'); }); @@ -33,7 +33,7 @@ describe('Draw & Edit Poly', () => { it('respects pmIgnore', () => { cy.window().then(({ L }) => { - L.PM.initialize({ optIn: false }); + L.PM.setOptIn(false); cy.drawShape('MultiPolygon', true); }); @@ -44,7 +44,7 @@ describe('Draw & Edit Poly', () => { it('respects optIn', () => { cy.window().then(({ L }) => { - L.PM.initialize({ optIn: true }); + L.PM.setOptIn(true); cy.drawShape('MultiPolygon'); }); @@ -55,7 +55,7 @@ describe('Draw & Edit Poly', () => { it('respects pmIgnore with optIn', () => { cy.window().then(({ L }) => { - L.PM.initialize({ optIn: true }); + L.PM.setOptIn(true); cy.drawShape('MultiPolygon', false); }); @@ -64,6 +64,57 @@ describe('Draw & Edit Poly', () => { cy.hasVertexMarkers(8); }); + it('respects optIn and reinit layer', () => { + cy.window().then(({ L }) => { + L.PM.setOptIn(true); + cy.drawShape('MultiPolygon').then((poly)=>{ + cy.hasVertexMarkers(0); //Not allowed because optIn + L.PM.setOptIn(false); + L.PM.reInitLayer(poly); + }) + }); + cy.toolbarButton('edit').click(); + + cy.hasVertexMarkers(8); + }); + + it('respects optIn and reinit layer with pmIgnore', () => { + cy.window().then(({ L }) => { + L.PM.setOptIn(true); + cy.drawShape('MultiPolygon',true).then((poly)=>{ + cy.hasVertexMarkers(0); //Not allowed because optIn + L.PM.reInitLayer(poly);//Not allowed because pmIgnore is not false + cy.hasVertexMarkers(0); + L.PM.setOptIn(false); + L.PM.reInitLayer(poly);//Not allowed because pmIgnore is true + cy.hasVertexMarkers(0); + poly.options.pmIgnore = false; + poly.eachLayer((layer)=>{ + layer.options.pmIgnore = false; + }); + L.PM.reInitLayer(poly);//Allowed because pmIgnore is not true + }) + }); + cy.toolbarButton('edit').click(); + + cy.hasVertexMarkers(8); + }); + + it('respects optIn and disable optIn', () => { + cy.window().then(({ L }) => { + L.PM.setOptIn(true); + cy.drawShape('MultiPolygon'); + cy.drawShape('MultiPolygon',false).then(()=>{ + L.PM.setOptIn(false); + cy.drawShape('MultiPolygon'); + }) + }); + + cy.toolbarButton('edit').click(); + + cy.hasVertexMarkers(16); + }); + it('doesnt finish single point polys', () => { cy.toolbarButton('polygon').click(); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index bbb742aa..d6624bdd 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -136,6 +136,7 @@ Cypress.Commands.add('drawShape', (shape, ignore) => { const layer = L.geoJson(json, { pmIgnore: ignore }).addTo(map); const bounds = layer.getBounds(); map.fitBounds(bounds); + return layer; }); } diff --git a/src/js/L.PM.js b/src/js/L.PM.js index a4c45cb6..16b8bf7f 100644 --- a/src/js/L.PM.js +++ b/src/js/L.PM.js @@ -45,15 +45,19 @@ L.PM = L.PM || { Edit, Utils, activeLang: 'en', + optIn: false, initialize(options) { this.addInitHooks(options); }, + setOptIn(value){ + this.optIn = !!value; + }, addInitHooks(options = {}) { function initMap() { this.pm = undefined; - if (options.optIn) { + if (L.PM.optIn) { if (this.options.pmIgnore === false) { this.pm = new L.PM.Map(this); } @@ -65,8 +69,14 @@ L.PM = L.PM || { L.Map.addInitHook(initMap); function initLayerGroup() { - // doesn't need pmIgnore condition as the init hook of the individual layers will check it - this.pm = new L.PM.Edit.LayerGroup(this); + this.pm = undefined; + if (L.PM.optIn) { + if (this.options.pmIgnore === false) { + this.pm = new L.PM.Edit.LayerGroup(this); + } + } else if (!this.options.pmIgnore) { + this.pm = new L.PM.Edit.LayerGroup(this); + } } L.LayerGroup.addInitHook(initLayerGroup); @@ -74,7 +84,7 @@ L.PM = L.PM || { function initMarker() { this.pm = undefined; - if (options.optIn) { + if (L.PM.optIn) { if (this.options.pmIgnore === false) { this.pm = new L.PM.Edit.Marker(this); } @@ -82,13 +92,12 @@ L.PM = L.PM || { this.pm = new L.PM.Edit.Marker(this); } } - L.Marker.addInitHook(initMarker); function initCircleMarker() { this.pm = undefined; - if (options.optIn) { + if (L.PM.optIn) { if (this.options.pmIgnore === false) { this.pm = new L.PM.Edit.CircleMarker(this); } @@ -102,7 +111,7 @@ L.PM = L.PM || { function initPolyline() { this.pm = undefined; - if (options.optIn) { + if (L.PM.optIn) { if (this.options.pmIgnore === false) { this.pm = new L.PM.Edit.Line(this); } @@ -116,7 +125,7 @@ L.PM = L.PM || { function initPolygon() { this.pm = undefined; - if (options.optIn) { + if (L.PM.optIn) { if (this.options.pmIgnore === false) { this.pm = new L.PM.Edit.Polygon(this); } @@ -131,7 +140,7 @@ L.PM = L.PM || { function initRectangle() { this.pm = undefined; - if (options.optIn) { + if (L.PM.optIn) { if (this.options.pmIgnore === false) { this.pm = new L.PM.Edit.Rectangle(this); } @@ -145,7 +154,7 @@ L.PM = L.PM || { function initCircle() { this.pm = undefined; - if (options.optIn) { + if (L.PM.optIn) { if (this.options.pmIgnore === false) { this.pm = new L.PM.Edit.Circle(this); } @@ -156,6 +165,33 @@ L.PM = L.PM || { L.Circle.addInitHook(initCircle); }, + reInitLayer(layer){ + if(layer instanceof L.LayerGroup){ + layer.eachLayer((_layer)=>{ + this.reInitLayer(_layer); + }) + }else if(layer.pm){ + // PM is already added to the layer + }else if(L.PM.optIn && layer.options.pmIgnore !== false){ + // Opt-In is true and pmIgnore is not false + }else if(layer.options.pmIgnore){ + // pmIgnore is true + }else if(layer instanceof L.Map){ + layer.pm = new L.PM.Map(layer); + }else if(layer instanceof L.Marker){ + layer.pm = new L.PM.Edit.Marker(layer); + }else if(layer instanceof L.Circle){ + layer.pm = new L.PM.Edit.Circle(layer); + }else if(layer instanceof L.CircleMarker){ + layer.pm = new L.PM.Edit.CircleMarker(layer); + }else if(layer instanceof L.Rectangle){ + layer.pm = new L.PM.Edit.Rectangle(layer); + }else if(layer instanceof L.Polygon){ + layer.pm = new L.PM.Edit.Polygon(layer); + }else if(layer instanceof L.Polyline){ + layer.pm = new L.PM.Edit.Line(layer); + } + } }; // initialize leaflet-geoman From 6f8d440f24dd85a41de26676b0fbb5283950d8f1 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Sat, 3 Oct 2020 21:02:35 +0200 Subject: [PATCH 06/30] Restructure (#639) (ignore) * Change point of fire enable/disable events * Fix test * Restructure Code --- cypress/integration/events.spec.js | 5 +- src/js/Draw/L.PM.Draw.Circle.js | 33 +-- src/js/Draw/L.PM.Draw.CircleMarker.js | 26 +-- src/js/Draw/L.PM.Draw.Cut.js | 93 ++++---- src/js/Draw/L.PM.Draw.Line.js | 88 ++++---- src/js/Draw/L.PM.Draw.Marker.js | 47 ++-- src/js/Draw/L.PM.Draw.Polygon.js | 82 +++---- src/js/Draw/L.PM.Draw.Rectangle.js | 41 ++-- src/js/Edit/L.PM.Edit.Circle.js | 195 ++++++++-------- src/js/Edit/L.PM.Edit.CircleMarker.js | 150 ++++++------- src/js/Edit/L.PM.Edit.LayerGroup.js | 72 +++--- src/js/Edit/L.PM.Edit.Line.js | 307 ++++++++++++-------------- src/js/Edit/L.PM.Edit.Marker.js | 64 +++--- src/js/Mixins/Dragging.js | 100 ++++----- src/js/Mixins/Modes/Mode.Drag.js | 22 +- src/js/Mixins/Modes/Mode.Edit.js | 35 ++- src/js/Mixins/Modes/Mode.Removal.js | 75 ++++--- src/js/Mixins/Snapping.js | 130 ++++++----- 18 files changed, 760 insertions(+), 805 deletions(-) diff --git a/cypress/integration/events.spec.js b/cypress/integration/events.spec.js index d0b96b8f..75d7afee 100644 --- a/cypress/integration/events.spec.js +++ b/cypress/integration/events.spec.js @@ -345,12 +345,14 @@ describe('Events', () => { it('Events while editing: pm:edit,pm:update,pm:enable,pm:disable,pm:vertexadded,pm:vertexremoved', () => { let calledevent = ""; + let calledeventArr = []; cy.window().then(({map}) => { function logEvent(e){ console.log(e.type) calledevent = e.type; + calledeventArr[e.type] = e.type; } map.on("pm:create",({layer}) => { @@ -414,8 +416,9 @@ describe('Events', () => { map.pm.disableGlobalEditMode(); }).then(()=>{ cy.wait(100); - expect(calledevent).to.equal("pm:update"); + expect(calledeventArr["pm:update"]).to.equal("pm:update"); calledevent = ""; + calledeventArr = []; }); cy.window().then(({map}) => { diff --git a/src/js/Draw/L.PM.Draw.Circle.js b/src/js/Draw/L.PM.Draw.Circle.js index c847b073..8cfbeb8f 100644 --- a/src/js/Draw/L.PM.Draw.Circle.js +++ b/src/js/Draw/L.PM.Draw.Circle.js @@ -75,12 +75,6 @@ Draw.Circle = Draw.extend({ // sync hint marker with mouse cursor this._map.on('mousemove', this._syncHintMarker, this); - // fire drawstart event - this._map.fire('pm:drawstart', { - shape: this._shape, - workingLayer: this._layer, - }); - this._setGlobalDrawMode(); // toggle the draw button of the Toolbar in case drawing mode got enabled without the button this._map.pm.Toolbar.toggleButton(this.toolbarButtonName, true); @@ -88,6 +82,13 @@ Draw.Circle = Draw.extend({ // an array used in the snapping mixin. // TODO: think about moving this somewhere else? this._otherSnapLayers = []; + + // fire drawstart event + this._map.fire('pm:drawstart', { + shape: this._shape, + workingLayer: this._layer, + }); + this._setGlobalDrawMode(); }, disable() { // disable drawing mode @@ -110,9 +111,6 @@ Draw.Circle = Draw.extend({ // remove helping layers this._map.removeLayer(this._layerGroup); - // fire drawend event - this._map.fire('pm:drawend', { shape: this._shape }); - this._setGlobalDrawMode(); // toggle the draw button of the Toolbar in case drawing mode got disabled without the button this._map.pm.Toolbar.toggleButton(this.toolbarButtonName, false); @@ -121,6 +119,10 @@ Draw.Circle = Draw.extend({ if (this.options.snappable) { this._cleanupSnapping(); } + + // fire drawend event + this._map.fire('pm:drawend', { shape: this._shape }); + this._setGlobalDrawMode(); }, enabled() { return this._enabled; @@ -225,17 +227,4 @@ Draw.Circle = Draw.extend({ layer: circleLayer, }); }, - _createMarker(latlng) { - // create the new marker - const marker = new L.Marker(latlng, { - draggable: false, - icon: L.divIcon({ className: 'marker-icon' }), - }); - marker._pmTempLayer = true; - - // add it to the map - this._layerGroup.addLayer(marker); - - return marker; - }, }); diff --git a/src/js/Draw/L.PM.Draw.CircleMarker.js b/src/js/Draw/L.PM.Draw.CircleMarker.js index a2c4d2b5..8b47d025 100644 --- a/src/js/Draw/L.PM.Draw.CircleMarker.js +++ b/src/js/Draw/L.PM.Draw.CircleMarker.js @@ -99,17 +99,9 @@ Draw.CircleMarker = Draw.Marker.extend({ } } - // sync hint marker with mouse cursor this._map.on('mousemove', this._syncHintMarker, this); - // fire drawstart event - this._map.fire('pm:drawstart', { - shape: this._shape, - workingLayer: this._layer, - }); - this._setGlobalDrawMode(); - if (!this.options.editable) { // enable edit mode for existing markers this._map.eachLayer(layer => { @@ -118,12 +110,22 @@ Draw.CircleMarker = Draw.Marker.extend({ } }); } + + // fire drawstart event + this._map.fire('pm:drawstart', { + shape: this._shape, + workingLayer: this._layer, + }); + this._setGlobalDrawMode(); }, disable() { // cancel, if drawing mode isn't even enabled if (!this._enabled) { return; } + // change enabled state + this._enabled = false; + // disable when drawing like a Circle if (this.options.editable) { // reset cursor @@ -153,9 +155,6 @@ Draw.CircleMarker = Draw.Marker.extend({ // remove event listener to sync hint marker this._map.off('mousemove', this._syncHintMarker, this); - // fire drawend event - this._map.fire('pm:drawend', { shape: this._shape }); - // toggle the draw button of the Toolbar in case drawing mode got disabled without the button this._map.pm.Toolbar.toggleButton(this.toolbarButtonName, false); @@ -164,8 +163,9 @@ Draw.CircleMarker = Draw.Marker.extend({ this._cleanupSnapping(); } - // change enabled state - this._enabled = false; + // fire drawend event + this._map.fire('pm:drawend', { shape: this._shape }); + this._setGlobalDrawMode(); }, _placeCenterMarker(e) { // assign the coordinate of the click to the hintMarker, that's necessary for diff --git a/src/js/Draw/L.PM.Draw.Cut.js b/src/js/Draw/L.PM.Draw.Cut.js index 9b683999..c8f4f55f 100644 --- a/src/js/Draw/L.PM.Draw.Cut.js +++ b/src/js/Draw/L.PM.Draw.Cut.js @@ -7,12 +7,57 @@ Draw.Cut = Draw.Polygon.extend({ this._shape = 'Cut'; this.toolbarButtonName = 'cutPolygon'; }, + _finishShape() { + this._editedLayers = []; + // if self intersection is not allowed, do not finish the shape! + if (!this.options.allowSelfIntersection) { + this._handleSelfIntersection(false); + + if (this._doesSelfIntersect) { + return; + } + } + + const coords = this._layer.getLatLngs(); + const polygonLayer = L.polygon(coords, this.options.pathOptions); + this._cut(polygonLayer); + + // disable drawing + this.disable(); + + // clean up snapping states + this._cleanupSnapping(); + + // remove the first vertex from "other snapping layers" + this._otherSnapLayers.splice(this._tempSnapLayerIndex, 1); + delete this._tempSnapLayerIndex; + + this._editedLayers.forEach(({layer, originalLayer}) =>{ + // fire pm:cut on the cutted layer + originalLayer.fire('pm:cut', { + shape: this._shape, + layer, + originalLayer, + }); + + // fire pm:cut on the map + this._map.fire('pm:cut', { + shape: this._shape, + layer, + originalLayer, + }); + + // fire edit event after cut + originalLayer.fire('pm:edit', { layer: originalLayer, shape: originalLayer.pm.getShape()}); + }); + this._editedLayers = []; + }, _cut(layer) { const all = this._map._layers; // find all layers that intersect with `layer`, the just drawn cutting layer const layers = Object.keys(all) - // convert object to array + // convert object to array .map(l => all[l]) // only layers handled by leaflet-geoman .filter(l => l.pm) @@ -66,52 +111,6 @@ Draw.Cut = Draw.Polygon.extend({ layer: resultingLayer, originalLayer: l }); - }); }, - _finishShape() { - this._editedLayers = []; - // if self intersection is not allowed, do not finish the shape! - if (!this.options.allowSelfIntersection) { - this._handleSelfIntersection(false); - - if (this._doesSelfIntersect) { - return; - } - } - - const coords = this._layer.getLatLngs(); - const polygonLayer = L.polygon(coords, this.options.pathOptions); - this._cut(polygonLayer); - - // disable drawing - this.disable(); - - // clean up snapping states - this._cleanupSnapping(); - - // remove the first vertex from "other snapping layers" - this._otherSnapLayers.splice(this._tempSnapLayerIndex, 1); - delete this._tempSnapLayerIndex; - - this._editedLayers.forEach(({layer, originalLayer}) =>{ - // fire pm:cut on the cutted layer - originalLayer.fire('pm:cut', { - shape: this._shape, - layer, - originalLayer, - }); - - // fire pm:cut on the map - this._map.fire('pm:cut', { - shape: this._shape, - layer, - originalLayer, - }); - - // fire edit event after cut - originalLayer.fire('pm:edit', { layer: originalLayer, shape: originalLayer.pm.getShape()}); - }); - this._editedLayers = []; - }, }); diff --git a/src/js/Draw/L.PM.Draw.Line.js b/src/js/Draw/L.PM.Draw.Line.js index fa852f54..92fa48a5 100644 --- a/src/js/Draw/L.PM.Draw.Line.js +++ b/src/js/Draw/L.PM.Draw.Line.js @@ -89,19 +89,20 @@ Draw.Line = Draw.extend({ // sync the hintline with hint marker this._hintMarker.on('move', this._syncHintLine, this); - // fire drawstart event - this._map.fire('pm:drawstart', { - shape: this._shape, - workingLayer: this._layer, - }); - this._setGlobalDrawMode(); - // toggle the draw button of the Toolbar in case drawing mode got enabled without the button this._map.pm.Toolbar.toggleButton(this.toolbarButtonName, true); // an array used in the snapping mixin. // TODO: think about moving this somewhere else? this._otherSnapLayers = []; + + + // fire drawstart event + this._map.fire('pm:drawstart', { + shape: this._shape, + workingLayer: this._layer, + }); + this._setGlobalDrawMode(); }, disable() { // disable draw mode @@ -130,10 +131,6 @@ Draw.Line = Draw.extend({ // remove layer this._map.removeLayer(this._layerGroup); - // fire drawend event - this._map.fire('pm:drawend', { shape: this._shape }); - this._setGlobalDrawMode(); - // toggle the draw button of the Toolbar in case drawing mode got disabled without the button this._map.pm.Toolbar.toggleButton(this.toolbarButtonName, false); @@ -141,6 +138,11 @@ Draw.Line = Draw.extend({ if (this.options.snappable) { this._cleanupSnapping(); } + + // fire drawend event + this._map.fire('pm:drawend', { shape: this._shape }); + this._setGlobalDrawMode(); + }, enabled() { return this._enabled; @@ -152,11 +154,6 @@ Draw.Line = Draw.extend({ this.enable(options); } }, - hasSelfIntersection() { - // check for self intersection of the layer and return true/false - const selfIntersection = kinks(this._layer.toGeoJSON(15)); - return selfIntersection.features.length > 0; - }, _syncHintLine() { const polyPoints = this._layer.getLatLngs(); @@ -186,6 +183,11 @@ Draw.Line = Draw.extend({ this._handleSelfIntersection(true, e.latlng); } }, + hasSelfIntersection() { + // check for self intersection of the layer and return true/false + const selfIntersection = kinks(this._layer.toGeoJSON(15)); + return selfIntersection.features.length > 0; + }, _handleSelfIntersection(addVertex, latlng) { // ok we need to check the self intersection here // problem: during draw, the marker on the cursor is not yet part @@ -219,33 +221,6 @@ Draw.Line = Draw.extend({ this._hintline.setStyle(this.options.hintlineStyle); } }, - _removeLastVertex() { - // remove last coords - const coords = this._layer.getLatLngs(); - const removedCoord = coords.pop(); - - // if all coords are gone, cancel drawing - if (coords.length < 1) { - this.disable(); - return; - } - - // find corresponding marker - const marker = this._layerGroup - .getLayers() - .filter(l => l instanceof L.Marker) - .filter(l => !L.DomUtil.hasClass(l._icon, 'cursor-marker')) - .find(l => l.getLatLng() === removedCoord); - - // remove that marker - this._layerGroup.removeLayer(marker); - - // update layer with new coords - this._layer.setLatLngs(coords); - - // sync the hintline again - this._syncHintLine(); - }, _createVertex(e) { // don't create a vertex if we have a selfIntersection and it is not allowed if (!this.options.allowSelfIntersection) { @@ -291,6 +266,33 @@ Draw.Line = Draw.extend({ latlng, }); }, + _removeLastVertex() { + // remove last coords + const coords = this._layer.getLatLngs(); + const removedCoord = coords.pop(); + + // if all coords are gone, cancel drawing + if (coords.length < 1) { + this.disable(); + return; + } + + // find corresponding marker + const marker = this._layerGroup + .getLayers() + .filter(l => l instanceof L.Marker) + .filter(l => !L.DomUtil.hasClass(l._icon, 'cursor-marker')) + .find(l => l.getLatLng() === removedCoord); + + // remove that marker + this._layerGroup.removeLayer(marker); + + // update layer with new coords + this._layer.setLatLngs(coords); + + // sync the hintline again + this._syncHintLine(); + }, _finishShape() { // if self intersection is not allowed, do not finish the shape! if (!this.options.allowSelfIntersection) { diff --git a/src/js/Draw/L.PM.Draw.Marker.js b/src/js/Draw/L.PM.Draw.Marker.js index b2cd8f99..5c691991 100644 --- a/src/js/Draw/L.PM.Draw.Marker.js +++ b/src/js/Draw/L.PM.Draw.Marker.js @@ -46,12 +46,6 @@ Draw.Marker = Draw.extend({ // sync hint marker with mouse cursor this._map.on('mousemove', this._syncHintMarker, this); - // fire drawstart event - this._map.fire('pm:drawstart', { - shape: this._shape, - workingLayer: this._layer, - }); - this._setGlobalDrawMode(); // enable edit mode for existing markers this._map.eachLayer(layer => { @@ -59,6 +53,13 @@ Draw.Marker = Draw.extend({ layer.pm.enable(); } }); + + // fire drawstart event + this._map.fire('pm:drawstart', { + shape: this._shape, + workingLayer: this._layer, + }); + this._setGlobalDrawMode(); }, disable() { // cancel, if drawing mode isn't even enabled @@ -85,9 +86,6 @@ Draw.Marker = Draw.extend({ } }); - // fire drawend event - this._map.fire('pm:drawend', { shape: this._shape }); - this._setGlobalDrawMode(); // toggle the draw button of the Toolbar in case drawing mode got disabled without the button this._map.pm.Toolbar.toggleButton(this.toolbarButtonName, false); @@ -97,9 +95,9 @@ Draw.Marker = Draw.extend({ this._cleanupSnapping(); } - }, - isRelevantMarker(layer) { - return layer instanceof L.Marker && layer.pm && !layer._pmTempLayer; + // fire drawend event + this._map.fire('pm:drawend', { shape: this._shape }); + this._setGlobalDrawMode(); }, enabled() { return this._enabled; @@ -111,6 +109,20 @@ Draw.Marker = Draw.extend({ this.enable(options); } }, + isRelevantMarker(layer) { + return layer instanceof L.Marker && layer.pm && !layer._pmTempLayer; + }, + _syncHintMarker(e) { + // move the cursor marker + this._hintMarker.setLatLng(e.latlng); + + // if snapping is enabled, do it + if (this.options.snappable) { + const fakeDragEvent = e; + fakeDragEvent.target = this._hintMarker; + this._handleSnapping(fakeDragEvent); + } + }, _createMarker(e) { if (!e.latlng) { return; @@ -145,15 +157,4 @@ Draw.Marker = Draw.extend({ this._cleanupSnapping(); }, - _syncHintMarker(e) { - // move the cursor marker - this._hintMarker.setLatLng(e.latlng); - - // if snapping is enabled, do it - if (this.options.snappable) { - const fakeDragEvent = e; - fakeDragEvent.target = this._hintMarker; - this._handleSnapping(fakeDragEvent); - } - }, }); diff --git a/src/js/Draw/L.PM.Draw.Polygon.js b/src/js/Draw/L.PM.Draw.Polygon.js index 1320d28a..e601d227 100644 --- a/src/js/Draw/L.PM.Draw.Polygon.js +++ b/src/js/Draw/L.PM.Draw.Polygon.js @@ -8,47 +8,6 @@ Draw.Polygon = Draw.Line.extend({ this._shape = 'Polygon'; this.toolbarButtonName = 'drawPolygon'; }, - _finishShape(e) { - // if self intersection is not allowed, do not finish the shape! - if (!this.options.allowSelfIntersection) { - // Check if polygon intersects when is completed and the line between the last and the first point is drawn - this._handleSelfIntersection(true, this._layer.getLatLngs()[0]); - - if (this._doesSelfIntersect) { - return; - } - } - - // get coordinates - const coords = this._layer.getLatLngs(); - - // only finish the shape if there are 3 or more vertices - if (coords.length <= 2) { - return; - } - - const polygonLayer = L.polygon(coords, this.options.pathOptions).addTo( - this._map - ); - this._setShapeForFinishLayer(polygonLayer); - this._addDrawnLayerProp(polygonLayer); - - // disable drawing - this.disable(); - - // fire the pm:create event and pass shape and layer - this._map.fire('pm:create', { - shape: this._shape, - layer: polygonLayer, - }); - - // clean up snapping states - this._cleanupSnapping(); - - // remove the first vertex from "other snapping layers" - this._otherSnapLayers.splice(this._tempSnapLayerIndex, 1); - delete this._tempSnapLayerIndex; - }, _createMarker(latlng, first) { // create the new marker const marker = new L.Marker(latlng, { @@ -93,4 +52,45 @@ Draw.Polygon = Draw.Line.extend({ return marker; }, + _finishShape(e) { + // if self intersection is not allowed, do not finish the shape! + if (!this.options.allowSelfIntersection) { + // Check if polygon intersects when is completed and the line between the last and the first point is drawn + this._handleSelfIntersection(true, this._layer.getLatLngs()[0]); + + if (this._doesSelfIntersect) { + return; + } + } + + // get coordinates + const coords = this._layer.getLatLngs(); + + // only finish the shape if there are 3 or more vertices + if (coords.length <= 2) { + return; + } + + const polygonLayer = L.polygon(coords, this.options.pathOptions).addTo( + this._map + ); + this._setShapeForFinishLayer(polygonLayer); + this._addDrawnLayerProp(polygonLayer); + + // disable drawing + this.disable(); + + // fire the pm:create event and pass shape and layer + this._map.fire('pm:create', { + shape: this._shape, + layer: polygonLayer, + }); + + // clean up snapping states + this._cleanupSnapping(); + + // remove the first vertex from "other snapping layers" + this._otherSnapLayers.splice(this._tempSnapLayerIndex, 1); + delete this._tempSnapLayerIndex; + }, }); diff --git a/src/js/Draw/L.PM.Draw.Rectangle.js b/src/js/Draw/L.PM.Draw.Rectangle.js index f32dab6e..7076278a 100644 --- a/src/js/Draw/L.PM.Draw.Rectangle.js +++ b/src/js/Draw/L.PM.Draw.Rectangle.js @@ -86,6 +86,13 @@ Draw.Rectangle = Draw.extend({ // sync hint marker with mouse cursor this._map.on('mousemove', this._syncHintMarker, this); + // toggle the draw button of the Toolbar in case drawing mode got enabled without the button + this._map.pm.Toolbar.toggleButton(this.toolbarButtonName, true); + + // an array used in the snapping mixin. + // TODO: think about moving this somewhere else? + this._otherSnapLayers = []; + // fire drawstart event this._map.fire('pm:drawstart', { shape: this._shape, @@ -93,12 +100,6 @@ Draw.Rectangle = Draw.extend({ }); this._setGlobalDrawMode(); - // toggle the draw button of the Toolbar in case drawing mode got enabled without the button - this._map.pm.Toolbar.toggleButton(this.toolbarButtonName, true); - - // an array used in the snapping mixin. - // TODO: think about moving this somewhere else? - this._otherSnapLayers = []; }, disable() { // disable drawing mode @@ -121,10 +122,6 @@ Draw.Rectangle = Draw.extend({ // remove helping layers this._map.removeLayer(this._layerGroup); - // fire drawend event - this._map.fire('pm:drawend', { shape: this._shape }); - this._setGlobalDrawMode(); - // toggle the draw button of the Toolbar in case drawing mode got disabled without the button this._map.pm.Toolbar.toggleButton(this.toolbarButtonName, false); @@ -132,6 +129,10 @@ Draw.Rectangle = Draw.extend({ if (this.options.snappable) { this._cleanupSnapping(); } + // fire drawend event + this._map.fire('pm:drawend', { shape: this._shape }); + this._setGlobalDrawMode(); + }, enabled() { return this._enabled; @@ -224,6 +225,16 @@ Draw.Rectangle = Draw.extend({ }); } }, + _findCorners() { + const corners = this._layer.getBounds(); + + const northwest = corners.getNorthWest(); + const northeast = corners.getNorthEast(); + const southeast = corners.getSouthEast(); + const southwest = corners.getSouthWest(); + + return [northwest, northeast, southeast, southwest]; + }, _finishShape(e) { // assign the coordinate of the click to the hintMarker, that's necessary for // mobile where the marker can't follow a cursor @@ -253,14 +264,4 @@ Draw.Rectangle = Draw.extend({ layer: rectangleLayer, }); }, - _findCorners() { - const corners = this._layer.getBounds(); - - const northwest = corners.getNorthWest(); - const northeast = corners.getNorthEast(); - const southeast = corners.getSouthEast(); - const southwest = corners.getSouthWest(); - - return [northwest, northeast, southeast, southwest]; - }, }); diff --git a/src/js/Edit/L.PM.Edit.Circle.js b/src/js/Edit/L.PM.Edit.Circle.js index 0fc9988c..cc680128 100644 --- a/src/js/Edit/L.PM.Edit.Circle.js +++ b/src/js/Edit/L.PM.Edit.Circle.js @@ -9,37 +9,6 @@ Edit.Circle = Edit.extend({ // create polygon around the circle border this._updateHiddenPolyCircle(); }, - applyOptions() { - if (this.options.snappable) { - this._initSnappableMarkers(); - // sync the hintline with hint marker - this._outerMarker.on('move', this._syncHintLine, this); - this._outerMarker.on('move', this._syncCircleRadius, this); - this._centerMarker.on('move', this._moveCircle, this); - } else { - this._disableSnapping(); - } - }, - _disableSnapping() { - this._markers.forEach(marker => { - marker.off('move', this._syncHintLine, this); - marker.off('move', this._syncCircleRadius, this); - marker.off('drag', this._handleSnapping, this); - marker.off('dragend', this._cleanupSnapping, this); - }); - - this._layer.off('pm:dragstart', this._unsnap, this); - }, - toggleEdit(options) { - if (!this.enabled()) { - this.enable(options); - } else { - this.disable(); - } - }, - enabled() { - return this._enabled; - }, enable(options) { L.Util.setOptions(this, options); @@ -59,8 +28,6 @@ Edit.Circle = Edit.extend({ this.applyOptions(); - this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); - // if polygon gets removed from map, disable edit mode this._layer.on('remove', e => { this.disable(e.target); @@ -68,6 +35,7 @@ Edit.Circle = Edit.extend({ // create polygon around the circle border this._updateHiddenPolyCircle(); + this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); }, disable(layer = this._layer) { // if it's not enabled, it doesn't need to be disabled @@ -94,15 +62,25 @@ Edit.Circle = Edit.extend({ const el = layer._path ? layer._path : this._layer._renderer._container; L.DomUtil.removeClass(el, 'leaflet-pm-draggable'); - this._layer.fire('pm:disable', { layer: this._layer, shape: this.getShape() }); if (this._layerEdited) { this._layer.fire('pm:update', { layer: this._layer, shape: this.getShape() }); } this._layerEdited = false; + this._layer.fire('pm:disable', { layer: this._layer, shape: this.getShape() }); return true; }, + enabled() { + return this._enabled; + }, + toggleEdit(options) { + if (!this.enabled()) { + this.enable(options); + } else { + this.disable(); + } + }, _initMarkers() { const map = this._map; @@ -126,14 +104,62 @@ Edit.Circle = Edit.extend({ this._outerMarker = this._createOuterMarker(outer); this._markers = [this._centerMarker, this._outerMarker]; this._createHintLine(this._centerMarker, this._outerMarker); + }, + applyOptions() { + if (this.options.snappable) { + this._initSnappableMarkers(); + // sync the hintline with hint marker + this._outerMarker.on('move', this._syncHintLine, this); + this._outerMarker.on('move', this._syncCircleRadius, this); + this._centerMarker.on('move', this._moveCircle, this); + } else { + this._disableSnapping(); + } + }, + _createHintLine(markerA, markerB) { + const A = markerA.getLatLng(); + const B = markerB.getLatLng(); + this._hintline = L.polyline([A, B], this.options.hintlineStyle); + this._hintline._pmTempLayer = true; + this._helperLayers.addLayer(this._hintline); + }, + _createCenterMarker(latlng) { + const marker = this._createMarker(latlng); + + L.DomUtil.addClass(marker._icon, 'leaflet-pm-draggable'); + // TODO: switch back to move event once this leaflet issue is solved: + // https://github.com/Leaflet/Leaflet/issues/6492 + marker.on('drag', this._moveCircle, this); + + marker.on('dragstart', this._fireDragStart, this); + marker.on('drag', this._fireDrag, this); + marker.on('dragend', this._fireDragEnd, this); + // marker.on('contextmenu', this._removeMarker, this); + + return marker; + }, + _createOuterMarker(latlng) { + const marker = this._createMarker(latlng); + marker.on('drag', this._resizeCircle, this); + return marker; }, - _getLatLngOnCircle(center, radius) { - const pointA = this._map.project(center); - const pointB = L.point(pointA.x + radius, pointA.y); + _createMarker(latlng) { + const marker = new L.Marker(latlng, { + draggable: true, + icon: L.divIcon({ className: 'marker-icon' }), + }); - return this._map.unproject(pointB); + marker._origLatLng = latlng; + marker._pmTempLayer = true; + + marker.on('dragstart', this._onMarkerDragStart, this); + marker.on('dragend', this._onMarkerDragEnd, this); + + this._helperLayers.addLayer(marker); + + return marker; }, _resizeCircle() { this._syncHintLine(); @@ -157,26 +183,6 @@ Edit.Circle = Edit.extend({ shape: this.getShape() }); }, - _onMarkerDragStart(e) { - this._layer.fire('pm:markerdragstart', { - layer: this._layer, - markerEvent: e, - shape: this.getShape(), - indexPath: undefined - }); - }, - _onMarkerDragEnd(e) { - // fire edit event - this._fireEdit(); - - // fire markerdragend event - this._layer.fire('pm:markerdragend', { - layer: this._layer, - markerEvent: e, - shape: this.getShape(), - indexPath: undefined - }); - }, _syncCircleRadius() { const A = this._centerMarker.getLatLng(); const B = this._outerMarker.getLatLng(); @@ -193,50 +199,35 @@ Edit.Circle = Edit.extend({ // set coords for hintline from marker to last vertex of drawin polyline this._hintline.setLatLngs([A, B]); }, - _createHintLine(markerA, markerB) { - const A = markerA.getLatLng(); - const B = markerB.getLatLng(); - this._hintline = L.polyline([A, B], this.options.hintlineStyle); - this._hintline._pmTempLayer = true; - this._helperLayers.addLayer(this._hintline); - }, - _createCenterMarker(latlng) { - const marker = this._createMarker(latlng); - - L.DomUtil.addClass(marker._icon, 'leaflet-pm-draggable'); - // TODO: switch back to move event once this leaflet issue is solved: - // https://github.com/Leaflet/Leaflet/issues/6492 - marker.on('drag', this._moveCircle, this); - - marker.on('dragstart', this._fireDragStart, this); - marker.on('drag', this._fireDrag, this); - marker.on('dragend', this._fireDragEnd, this); - // marker.on('contextmenu', this._removeMarker, this); - - return marker; - }, - _createOuterMarker(latlng) { - const marker = this._createMarker(latlng); - - marker.on('drag', this._resizeCircle, this); + _disableSnapping() { + this._markers.forEach(marker => { + marker.off('move', this._syncHintLine, this); + marker.off('move', this._syncCircleRadius, this); + marker.off('drag', this._handleSnapping, this); + marker.off('dragend', this._cleanupSnapping, this); + }); - return marker; + this._layer.off('pm:dragstart', this._unsnap, this); }, - _createMarker(latlng) { - const marker = new L.Marker(latlng, { - draggable: true, - icon: L.divIcon({ className: 'marker-icon' }), + _onMarkerDragStart(e) { + this._layer.fire('pm:markerdragstart', { + layer: this._layer, + markerEvent: e, + shape: this.getShape(), + indexPath: undefined }); + }, + _onMarkerDragEnd(e) { + // fire edit event + this._fireEdit(); - marker._origLatLng = latlng; - marker._pmTempLayer = true; - - marker.on('dragstart', this._onMarkerDragStart, this); - marker.on('dragend', this._onMarkerDragEnd, this); - - this._helperLayers.addLayer(marker); - - return marker; + // fire markerdragend event + this._layer.fire('pm:markerdragend', { + layer: this._layer, + markerEvent: e, + shape: this.getShape(), + indexPath: undefined + }); }, _fireEdit() { // fire edit event @@ -262,5 +253,11 @@ Edit.Circle = Edit.extend({ if (!this._hiddenPolyCircle._parentCopy) { this._hiddenPolyCircle._parentCopy = this._layer } - } + }, + _getLatLngOnCircle(center, radius) { + const pointA = this._map.project(center); + const pointB = L.point(pointA.x + radius, pointA.y); + + return this._map.unproject(pointB); + }, }); diff --git a/src/js/Edit/L.PM.Edit.CircleMarker.js b/src/js/Edit/L.PM.Edit.CircleMarker.js index c2667c0b..abda6377 100644 --- a/src/js/Edit/L.PM.Edit.CircleMarker.js +++ b/src/js/Edit/L.PM.Edit.CircleMarker.js @@ -9,54 +9,6 @@ Edit.CircleMarker = Edit.extend({ // create polygon around the circle border this._updateHiddenPolyCircle(); }, - applyOptions() { - // Use the not editable and only draggable version - if (!this.options.editable && this.options.draggable) { - this.enableLayerDrag(); - } else { - this.disableLayerDrag(); - } - - // Make it editable like a Circle - if (this.options.editable) { - this._initMarkers(); - this._map.on('move', this._syncMarkers, this); - } else { - // only update the circle border poly - this._map.on('move', this._updateHiddenPolyCircle, this); - } - - // init snapping in different ways - if (this.options.snappable) { - if (this.options.editable) { - this._initSnappableMarkers(); - // sync the hintline with hint marker - this._outerMarker.on('move', this._syncHintLine, this); - this._outerMarker.on('move', this._syncCircleRadius, this); - } else { - this._initSnappableMarkersDrag(); - } - } else if (this.options.editable) { - this._disableSnapping(); - } else { - this._disableSnappingDrag(); - } - - // enable removal for the marker - if (!this.options.preventMarkerRemoval) { - this._layer.on('contextmenu', this._removeMarker, this); - } - }, - toggleEdit(options) { - if (!this.enabled()) { - this.enable(options); - } else { - this.disable(); - } - }, - enabled() { - return this._enabled; - }, enable(options = { draggable: true, snappable: true }) { L.Util.setOptions(this, options); @@ -74,7 +26,6 @@ Edit.CircleMarker = Edit.extend({ } this.applyOptions(); - this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); // change state this._enabled = true; @@ -82,6 +33,8 @@ Edit.CircleMarker = Edit.extend({ // create polygon around the circle border this._updateHiddenPolyCircle(); + + this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); }, disable(layer = this._layer) { // prevent disabling if layer is being dragged @@ -106,22 +59,69 @@ Edit.CircleMarker = Edit.extend({ // disable dragging, as this could have been active even without being enabled this.disableLayerDrag(); + this._layer.off('contextmenu', this._removeMarker, this); + // only fire events if it was enabled before if (!this.enabled()) { - this._layer.fire('pm:disable', { layer: this._layer, shape: this.getShape() }); - if (this._layerEdited) { this._layer.fire('pm:update', { layer: this._layer, shape: this.getShape() }); } this._layerEdited = false; + this._layer.fire('pm:disable', { layer: this._layer, shape: this.getShape() }); } - this._layer.off('contextmenu', this._removeMarker, this); - layer.pm._enabled = false; return true; }, + enabled() { + return this._enabled; + }, + toggleEdit(options) { + if (!this.enabled()) { + this.enable(options); + } else { + this.disable(); + } + }, + applyOptions() { + // Use the not editable and only draggable version + if (!this.options.editable && this.options.draggable) { + this.enableLayerDrag(); + } else { + this.disableLayerDrag(); + } + + // Make it editable like a Circle + if (this.options.editable) { + this._initMarkers(); + this._map.on('move', this._syncMarkers, this); + } else { + // only update the circle border poly + this._map.on('move', this._updateHiddenPolyCircle, this); + } + + // init snapping in different ways + if (this.options.snappable) { + if (this.options.editable) { + this._initSnappableMarkers(); + // sync the hintline with hint marker + this._outerMarker.on('move', this._syncHintLine, this); + this._outerMarker.on('move', this._syncCircleRadius, this); + } else { + this._initSnappableMarkersDrag(); + } + } else if (this.options.editable) { + this._disableSnapping(); + } else { + this._disableSnappingDrag(); + } + + // enable removal for the marker + if (!this.options.preventMarkerRemoval) { + this._layer.on('contextmenu', this._removeMarker, this); + } + }, _initMarkers() { const map = this._map; @@ -170,21 +170,6 @@ Edit.CircleMarker = Edit.extend({ } return marker; }, - _moveCircle(e) { - const center = e.latlng; - this._layer.setLatLng(center); - - const radius = this._layer._radius; - const outer = this._getLatLngOnCircle(center, radius); - this._outerMarker.setLatLng(outer); - this._syncHintLine(); - - this._layer.fire('pm:centerplaced', { - layer: this._layer, - latlng: center, - shape: this.getShape() - }); - }, _createOuterMarker(latlng) { const marker = this._createMarker(latlng); marker.on('drag', this._resizeCircle, this); @@ -206,6 +191,21 @@ Edit.CircleMarker = Edit.extend({ return marker; }, + _moveCircle(e) { + const center = e.latlng; + this._layer.setLatLng(center); + + const radius = this._layer._radius; + const outer = this._getLatLngOnCircle(center, radius); + this._outerMarker.setLatLng(outer); + this._syncHintLine(); + + this._layer.fire('pm:centerplaced', { + layer: this._layer, + latlng: center, + shape: this.getShape() + }); + }, _syncMarkers() { const center = this._layer.getLatLng(); const radius = this._layer._radius; @@ -235,10 +235,6 @@ Edit.CircleMarker = Edit.extend({ // set coords for hintline from marker to last vertex of drawin polyline this._hintline.setLatLngs([A, B]); }, - _moveMarker(e) { - const center = e.latlng; - this._layer.setLatLng(center).redraw(); - }, _removeMarker() { if (this.options.editable) { this.disable(); @@ -255,11 +251,6 @@ Edit.CircleMarker = Edit.extend({ indexPath: undefined }); }, - _fireEdit() { - // fire edit event - this._layer.fire('pm:edit', { layer: this._layer, shape: this.getShape() }); - this._layerEdited = true; - }, _onMarkerDragEnd(e) { this._layer.fire('pm:markerdragend', { layer: this._layer, @@ -268,6 +259,11 @@ Edit.CircleMarker = Edit.extend({ indexPath: undefined }); }, + _fireEdit() { + // fire edit event + this._layer.fire('pm:edit', { layer: this._layer, shape: this.getShape() }); + this._layerEdited = true; + }, // _initSnappableMarkers when option editable is not true _initSnappableMarkersDrag() { const marker = this._layer; diff --git a/src/js/Edit/L.PM.Edit.LayerGroup.js b/src/js/Edit/L.PM.Edit.LayerGroup.js index 79e6f282..c7999e69 100644 --- a/src/js/Edit/L.PM.Edit.LayerGroup.js +++ b/src/js/Edit/L.PM.Edit.LayerGroup.js @@ -40,21 +40,26 @@ Edit.LayerGroup = L.Class.extend({ this._layers = this.findLayers(); }) }, - findLayers() { - // get all layers of the layer group - let layers = this._layerGroup.getLayers(); - - // filter out layers that are no layerGroup - layers = layers.filter(layer => !(layer instanceof L.LayerGroup)); - - // filter out layers that don't have leaflet-geoman - layers = layers.filter(layer => !!layer.pm); - - // filter out everything that's leaflet-geoman specific temporary stuff - layers = layers.filter(layer => !layer._pmTempLayer); - - // return them - return layers; + enable(options) { + this._options = options; + this._layers.forEach(layer => { + layer.pm.enable(options); + }); + }, + disable() { + this._layers.forEach(layer => { + layer.pm.disable(); + }); + }, + enabled() { + const enabled = this._layers.find(layer => layer.pm.enabled()); + return !!enabled; + }, + toggleEdit(options) { + this._options = options; + this._layers.forEach(layer => { + layer.pm.toggleEdit(options); + }); }, _initLayer(layer) { // available events @@ -86,30 +91,25 @@ Edit.LayerGroup = L.Class.extend({ // add reference for the group to each layer inside said group layer.pm._layerGroup = this._layerGroup; }, + findLayers() { + // get all layers of the layer group + let layers = this._layerGroup.getLayers(); + + // filter out layers that are no layerGroup + layers = layers.filter(layer => !(layer instanceof L.LayerGroup)); + + // filter out layers that don't have leaflet-geoman + layers = layers.filter(layer => !!layer.pm); + + // filter out everything that's leaflet-geoman specific temporary stuff + layers = layers.filter(layer => !layer._pmTempLayer); + + // return them + return layers; + }, _fireEvent(e) { this._layerGroup.fireEvent(e.type, e); }, - toggleEdit(options) { - this._options = options; - this._layers.forEach(layer => { - layer.pm.toggleEdit(options); - }); - }, - enable(options) { - this._options = options; - this._layers.forEach(layer => { - layer.pm.enable(options); - }); - }, - disable() { - this._layers.forEach(layer => { - layer.pm.disable(); - }); - }, - enabled() { - const enabled = this._layers.find(layer => layer.pm.enabled()); - return !!enabled; - }, dragging() { const dragging = this._layers.find(layer => layer.pm.dragging()); return !!dragging; diff --git a/src/js/Edit/L.PM.Edit.Line.js b/src/js/Edit/L.PM.Edit.Line.js index 1e81a8dc..a79c9031 100644 --- a/src/js/Edit/L.PM.Edit.Line.js +++ b/src/js/Edit/L.PM.Edit.Line.js @@ -3,7 +3,7 @@ import lineIntersect from '@turf/line-intersect'; import get from 'lodash/get'; import Edit from './L.PM.Edit'; import Utils from '../L.PM.Utils'; -import { isEmptyDeep } from '../helpers'; +import {isEmptyDeep} from '../helpers'; import MarkerLimits from '../Mixins/MarkerLimits'; @@ -22,25 +22,6 @@ Edit.Line = Edit.extend({ this._layer = layer; this._enabled = false; }, - - applyOptions() { - if (this.options.snappable) { - this._initSnappableMarkers(); - } else { - this._disableSnapping(); - } - }, - - toggleEdit(options) { - if (!this.enabled()) { - this.enable(options); - } else { - this.disable(); - } - - return this.enabled(); - }, - enable(options) { L.Util.setOptions(this, options); @@ -65,8 +46,6 @@ Edit.Line = Edit.extend({ this.applyOptions(); - this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); - // if polygon gets removed from map, disable edit mode this._layer.on('remove', this._onLayerRemove, this); @@ -89,16 +68,8 @@ Edit.Line = Edit.extend({ }else{ this.cachedColor = undefined; } + this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); }, - - _onLayerRemove(e) { - this.disable(e.target); - }, - - enabled() { - return this._enabled; - }, - disable(poly = this._layer) { // if it's not enabled, it doesn't need to be disabled if (!this.enabled()) { @@ -121,7 +92,6 @@ Edit.Line = Edit.extend({ this._layer.off('remove', this._onLayerRemove, this); - if (!this.options.allowSelfIntersection) { this._layer.off( 'pm:vertexremoved', @@ -139,93 +109,34 @@ Edit.Line = Edit.extend({ L.DomUtil.removeClass(el, 'leaflet-pm-invalid'); } - this._layer.fire('pm:disable', { layer: this._layer, shape: this.getShape() }); - if (this._layerEdited) { this._layer.fire('pm:update', { layer: this._layer, shape: this.getShape() }); } this._layerEdited = false; - + this._layer.fire('pm:disable', { layer: this._layer, shape: this.getShape() }); return true; }, - - hasSelfIntersection() { - // check for self intersection of the layer and return true/false - const selfIntersection = kinks(this._layer.toGeoJSON(15)); - return selfIntersection.features.length > 0; + enabled() { + return this._enabled; }, - - _handleSelfIntersectionOnVertexRemoval() { - // check for selfintersection again (mainly to reset the style) - this._handleLayerStyle(true); - - if (this.hasSelfIntersection()) { - // reset coordinates - this._layer.setLatLngs(this._coordsBeforeEdit); - this._coordsBeforeEdit = null; - - // re-enable markers for the new coords - this._initMarkers(); + toggleEdit(options) { + if (!this.enabled()) { + this.enable(options); + } else { + this.disable(); } + return this.enabled(); }, - - _handleLayerStyle(flash) { - const layer = this._layer; - - if (this.hasSelfIntersection()) { - if (!this.options.allowSelfIntersection && this.options.allowSelfIntersectionEdit) { - this._updateDisabledMarkerStyle(this._markers, true); - } - - if (this.isRed) { - return; - } - - // if it does self-intersect, mark or flash it red - if (flash) { - layer.setStyle({ color: 'red' }); - this.isRed = true; - - window.setTimeout(() => { - layer.setStyle({ color: this.cachedColor }); - this.isRed = false; - }, 200); - } else { - layer.setStyle({ color: 'red' }); - this.isRed = true; - } - - // fire intersect event - this._layer.fire('pm:intersect', { - layer: this._layer, - intersection: kinks(this._layer.toGeoJSON(15)), - shape: this.getShape() - }); + applyOptions() { + if (this.options.snappable) { + this._initSnappableMarkers(); } else { - // if not, reset the style to the default color - layer.setStyle({ color: this.cachedColor }); - this.isRed = false; - if (!this.options.allowSelfIntersection && this.options.allowSelfIntersectionEdit) { - this._updateDisabledMarkerStyle(this._markers, false); - } + this._disableSnapping(); } }, - _updateDisabledMarkerStyle(markers, disabled) { - markers.forEach((marker) => { - if (Array.isArray(marker)) { - return this._updateDisabledMarkerStyle(marker, disabled); - } - - if (marker._icon) { - if (disabled && !this._checkMarkerAllowedToDrag(marker)) { - L.DomUtil.addClass(marker._icon, "vertexmarker-disabled"); - } else { - L.DomUtil.removeClass(marker._icon, "vertexmarker-disabled"); - } - } - }); + _onLayerRemove(e) { + this.disable(e.target); }, - _initMarkers() { const map = this._map; const coords = this._layer.getLatLngs(); @@ -399,6 +310,83 @@ Edit.Line = Edit.extend({ } }, + hasSelfIntersection() { + // check for self intersection of the layer and return true/false + const selfIntersection = kinks(this._layer.toGeoJSON(15)); + return selfIntersection.features.length > 0; + }, + + _handleSelfIntersectionOnVertexRemoval() { + // check for selfintersection again (mainly to reset the style) + this._handleLayerStyle(true); + + if (this.hasSelfIntersection()) { + // reset coordinates + this._layer.setLatLngs(this._coordsBeforeEdit); + this._coordsBeforeEdit = null; + + // re-enable markers for the new coords + this._initMarkers(); + } + }, + + _handleLayerStyle(flash) { + const layer = this._layer; + + if (this.hasSelfIntersection()) { + if (!this.options.allowSelfIntersection && this.options.allowSelfIntersectionEdit) { + this._updateDisabledMarkerStyle(this._markers, true); + } + + if (this.isRed) { + return; + } + + // if it does self-intersect, mark or flash it red + if (flash) { + layer.setStyle({ color: 'red' }); + this.isRed = true; + + window.setTimeout(() => { + layer.setStyle({ color: this.cachedColor }); + this.isRed = false; + }, 200); + } else { + layer.setStyle({ color: 'red' }); + this.isRed = true; + } + + // fire intersect event + this._layer.fire('pm:intersect', { + layer: this._layer, + intersection: kinks(this._layer.toGeoJSON(15)), + shape: this.getShape() + }); + } else { + // if not, reset the style to the default color + layer.setStyle({ color: this.cachedColor }); + this.isRed = false; + if (!this.options.allowSelfIntersection && this.options.allowSelfIntersectionEdit) { + this._updateDisabledMarkerStyle(this._markers, false); + } + } + }, + _updateDisabledMarkerStyle(markers, disabled) { + markers.forEach((marker) => { + if (Array.isArray(marker)) { + return this._updateDisabledMarkerStyle(marker, disabled); + } + + if (marker._icon) { + if (disabled && !this._checkMarkerAllowedToDrag(marker)) { + L.DomUtil.addClass(marker._icon, "vertexmarker-disabled"); + } else { + L.DomUtil.removeClass(marker._icon, "vertexmarker-disabled"); + } + } + }); + }, + _removeMarker(e) { // if self intersection isn't allowed, save the coords upon dragstart // in case we need to reset the layer @@ -580,6 +568,58 @@ Edit.Line = Edit.extend({ return { prevMarker, nextMarker }; }, + _checkMarkerAllowedToDrag(marker) { + const { prevMarker, nextMarker } = this._getNeighborMarkers(marker); + + const prevLine = L.polyline([prevMarker.getLatLng(), marker.getLatLng()]); + const nextLine = L.polyline([marker.getLatLng(), nextMarker.getLatLng()]); + + let prevLineIntersectionLen = lineIntersect(this._layer.toGeoJSON(15), prevLine.toGeoJSON(15)).features.length; + let nextLineIntersectionLen = lineIntersect(this._layer.toGeoJSON(15), nextLine.toGeoJSON(15)).features.length; + + // The first and last line has one intersection fewer because they are not connected + if (marker.getLatLng() === this._markers[0][0].getLatLng()) { + nextLineIntersectionLen += 1; + } else if (marker.getLatLng() === this._markers[0][this._markers[0].length - 1].getLatLng()) { + prevLineIntersectionLen += 1; + } + + // <= 2 the start and end point of the line always intersecting because they have the same coords. + if (prevLineIntersectionLen <= 2 && nextLineIntersectionLen <= 2) { + return false; + } + return true; + + }, + _onMarkerDragStart(e) { + const marker = e.target; + const { indexPath } = this.findDeepMarkerIndex(this._markers, marker); + + this._layer.fire('pm:markerdragstart', { + layer: this._layer, + markerEvent: e, + indexPath, + shape: this.getShape() + }); + + // if self intersection isn't allowed, save the coords upon dragstart + // in case we need to reset the layer + if (!this.options.allowSelfIntersection) { + this._coordsBeforeEdit = this._layer.getLatLngs(); + } + + // When intersection is true while calling enable(), the cachedColor is already set + if (!this.cachedColor) { + this.cachedColor = this._layer.options.color; + } + + + if (!this.options.allowSelfIntersection && this.options.allowSelfIntersectionEdit && this.hasSelfIntersection()) { + this._markerAllowedToDrag = this._checkMarkerAllowedToDrag(marker); + } else { + this._markerAllowedToDrag = null; + } + }, _onMarkerDrag(e) { // dragged marker const marker = e.target; @@ -644,7 +684,6 @@ Edit.Line = Edit.extend({ this._handleLayerStyle(); } }, - _onMarkerDragEnd(e) { const marker = e.target; const { indexPath } = this.findDeepMarkerIndex(this._markers, marker); @@ -678,63 +717,9 @@ Edit.Line = Edit.extend({ if (!this.options.allowSelfIntersection && this.options.allowSelfIntersectionEdit) { this._handleLayerStyle(); } - - // fire edit event this._fireEdit(); }, - _onMarkerDragStart(e) { - const marker = e.target; - const { indexPath } = this.findDeepMarkerIndex(this._markers, marker); - - this._layer.fire('pm:markerdragstart', { - layer: this._layer, - markerEvent: e, - indexPath, - shape: this.getShape() - }); - - // if self intersection isn't allowed, save the coords upon dragstart - // in case we need to reset the layer - if (!this.options.allowSelfIntersection) { - this._coordsBeforeEdit = this._layer.getLatLngs(); - } - - // When intersection is true while calling enable(), the cachedColor is already set - if (!this.cachedColor) { - this.cachedColor = this._layer.options.color; - } - - - if (!this.options.allowSelfIntersection && this.options.allowSelfIntersectionEdit && this.hasSelfIntersection()) { - this._markerAllowedToDrag = this._checkMarkerAllowedToDrag(marker); - } else { - this._markerAllowedToDrag = null; - } - }, - _checkMarkerAllowedToDrag(marker) { - const { prevMarker, nextMarker } = this._getNeighborMarkers(marker); - - const prevLine = L.polyline([prevMarker.getLatLng(), marker.getLatLng()]); - const nextLine = L.polyline([marker.getLatLng(), nextMarker.getLatLng()]); - - let prevLineIntersectionLen = lineIntersect(this._layer.toGeoJSON(15), prevLine.toGeoJSON(15)).features.length; - let nextLineIntersectionLen = lineIntersect(this._layer.toGeoJSON(15), nextLine.toGeoJSON(15)).features.length; - - // The first and last line has one intersection fewer because they are not connected - if (marker.getLatLng() === this._markers[0][0].getLatLng()) { - nextLineIntersectionLen += 1; - } else if (marker.getLatLng() === this._markers[0][this._markers[0].length - 1].getLatLng()) { - prevLineIntersectionLen += 1; - } - - // <= 2 the start and end point of the line always intersecting because they have the same coords. - if (prevLineIntersectionLen <= 2 && nextLineIntersectionLen <= 2) { - return false; - } - return true; - - }, _fireEdit() { // fire edit event this._layerEdited = true; diff --git a/src/js/Edit/L.PM.Edit.Marker.js b/src/js/Edit/L.PM.Edit.Marker.js index 9d01d971..17c779e6 100644 --- a/src/js/Edit/L.PM.Edit.Marker.js +++ b/src/js/Edit/L.PM.Edit.Marker.js @@ -10,35 +10,6 @@ Edit.Marker = Edit.extend({ // register dragend event e.g. to fire pm:edit this._layer.on('dragend', this._onDragEnd, this); }, - - applyOptions() { - // console.log('apply options', this.options) - - if (this.options.snappable) { - this._initSnappableMarkers(); - } else { - this._disableSnapping(); - } - - if (this.options.draggable) { - this.enableLayerDrag(); - } else { - this.disableLayerDrag(); - } - // enable removal for the marker - if (!this.options.preventMarkerRemoval) { - this._layer.on('contextmenu', this._removeMarker, this); - } - }, - - toggleEdit(options) { - if (!this.enabled()) { - this.enable(options); - } else { - this.disable(); - } - }, - enable(options = { draggable: true }) { L.Util.setOptions(this, options); @@ -49,15 +20,9 @@ Edit.Marker = Edit.extend({ } this._enabled = true; - this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); - this.applyOptions(); + this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); }, - - enabled() { - return this._enabled; - }, - disable() { this._enabled = false; @@ -73,6 +38,33 @@ Edit.Marker = Edit.extend({ } this._layerEdited = false; }, + enabled() { + return this._enabled; + }, + toggleEdit(options) { + if (!this.enabled()) { + this.enable(options); + } else { + this.disable(); + } + }, + applyOptions() { + if (this.options.snappable) { + this._initSnappableMarkers(); + } else { + this._disableSnapping(); + } + + if (this.options.draggable) { + this.enableLayerDrag(); + } else { + this.disableLayerDrag(); + } + // enable removal for the marker + if (!this.options.preventMarkerRemoval) { + this._layer.on('contextmenu', this._removeMarker, this); + } + }, _removeMarker(e) { const marker = e.target; marker.remove(); diff --git a/src/js/Mixins/Dragging.js b/src/js/Mixins/Dragging.js index 56e9ce57..366ddf02 100644 --- a/src/js/Mixins/Dragging.js +++ b/src/js/Mixins/Dragging.js @@ -68,6 +68,55 @@ const DragMixin = { // disable mousedown event this._layer.off('mousedown', this._dragMixinOnMouseDown, this); }, + dragging() { + return this._dragging; + }, + _dragMixinOnMouseDown(e) { + // cancel if mouse button is NOT the left button + if (e.originalEvent.button > 0) { + return; + } + // save current map dragging state + if (this._safeToCacheDragState) { + this._originalMapDragState = this._layer._map.dragging._enabled; + + // don't cache the state again until another mouse up is registered + this._safeToCacheDragState = false; + } + + // save for delta calculation + this._tempDragCoord = e.latlng; + + this._layer._map.on('mouseup', this._dragMixinOnMouseUp, this); + + // listen to mousemove on map (instead of polygon), + // otherwise fast mouse movements stop the drag + this._layer._map.on('mousemove', this._dragMixinOnMouseMove, this); + }, + _dragMixinOnMouseMove(e) { + const el = this._layer._path + ? this._layer._path + : this._layer._renderer._container; + + if (!this._dragging) { + // set state + this._dragging = true; + L.DomUtil.addClass(el, 'leaflet-pm-dragging'); + + // bring it to front to prevent drag interception + this._layer.bringToFront(); + + // disbale map drag + if (this._originalMapDragState) { + this._layer._map.dragging.disable(); + } + + // fire pm:dragstart event + this._fireDragStart(); + } + + this._onLayerDrag(e); + }, _dragMixinOnMouseUp() { const el = this._layer._path ? this._layer._path @@ -92,7 +141,6 @@ const DragMixin = { return false; } - // update the hidden circle border after dragging if(this._layer instanceof L.CircleMarker){ this._layer.pm._updateHiddenPolyCircle(); @@ -114,56 +162,6 @@ const DragMixin = { return true; }, - _dragMixinOnMouseMove(e) { - const el = this._layer._path - ? this._layer._path - : this._layer._renderer._container; - - if (!this._dragging) { - // set state - this._dragging = true; - L.DomUtil.addClass(el, 'leaflet-pm-dragging'); - - // bring it to front to prevent drag interception - this._layer.bringToFront(); - - // disbale map drag - if (this._originalMapDragState) { - this._layer._map.dragging.disable(); - } - - - // fire pm:dragstart event - this._fireDragStart(); - } - - this._onLayerDrag(e); - }, - _dragMixinOnMouseDown(e) { - // cancel if mouse button is NOT the left button - if (e.originalEvent.button > 0) { - return; - } - // save current map dragging state - if (this._safeToCacheDragState) { - this._originalMapDragState = this._layer._map.dragging._enabled; - - // don't cache the state again until another mouse up is registered - this._safeToCacheDragState = false; - } - - // save for delta calculation - this._tempDragCoord = e.latlng; - - this._layer._map.on('mouseup', this._dragMixinOnMouseUp, this); - - // listen to mousemove on map (instead of polygon), - // otherwise fast mouse movements stop the drag - this._layer._map.on('mousemove', this._dragMixinOnMouseMove, this); - }, - dragging() { - return this._dragging; - }, _onLayerDrag(e) { // latLng of mouse event const { latlng } = e; diff --git a/src/js/Mixins/Modes/Mode.Drag.js b/src/js/Mixins/Modes/Mode.Drag.js index 4352f0f4..97561872 100644 --- a/src/js/Mixins/Modes/Mode.Drag.js +++ b/src/js/Mixins/Modes/Mode.Drag.js @@ -1,11 +1,8 @@ import Utils from '../../L.PM.Utils' -const { findLayers } = Utils +const { findLayers } = Utils; const GlobalDragMode = { - globalDragModeEnabled() { - return !!this._globalDragMode; - }, enableGlobalDragMode() { const layers = findLayers(this.map); @@ -44,11 +41,8 @@ const GlobalDragMode = { this._fireDragModeEvent(false); }, - _fireDragModeEvent(enabled) { - this.map.fire('pm:globaldragmodetoggled', { - enabled, - map: this.map, - }); + globalDragModeEnabled() { + return !!this._globalDragMode; }, toggleGlobalDragMode() { if (this.globalDragModeEnabled()) { @@ -58,7 +52,7 @@ const GlobalDragMode = { } }, reinitGlobalDragMode({ layer }) { - // do nothing if layer is not handled by leaflet so it doesn't fire unnecessarily + // do nothing if layer is not handled by leaflet so it doesn't fire unnecessarily const isRelevant = !!layer.pm && !layer._pmTempLayer; if (!isRelevant) { return; @@ -70,6 +64,12 @@ const GlobalDragMode = { this.enableGlobalDragMode(); } }, + _fireDragModeEvent(enabled) { + this.map.fire('pm:globaldragmodetoggled', { + enabled, + map: this.map, + }); + }, } -export default GlobalDragMode \ No newline at end of file +export default GlobalDragMode diff --git a/src/js/Mixins/Modes/Mode.Edit.js b/src/js/Mixins/Modes/Mode.Edit.js index fd4427a7..f98de979 100644 --- a/src/js/Mixins/Modes/Mode.Edit.js +++ b/src/js/Mixins/Modes/Mode.Edit.js @@ -1,24 +1,10 @@ // this mixin adds a global edit mode to the map import Utils from '../../L.PM.Utils' -const { findLayers } = Utils +const { findLayers } = Utils; const GlobalEditMode = { _globalEditMode: false, - // TODO: Remove in the next major release - globalEditEnabled() { - return this.globalEditModeEnabled(); - }, - globalEditModeEnabled() { - return this._globalEditMode; - }, - setGlobalEditStatus(status) { - // set status - this._globalEditMode = status; - - // fire event - this._fireEditModeEvent(this._globalEditMode); - }, enableGlobalEditMode(o) { const options = { snappable: this._globalSnappingEnabled, @@ -66,9 +52,20 @@ const GlobalEditMode = { this.setGlobalEditStatus(status); }, + // TODO: Remove in the next major release + globalEditEnabled() { + return this.globalEditModeEnabled(); + }, + globalEditModeEnabled() { + return this._globalEditMode; + }, + setGlobalEditStatus(status) { + // set status + this._globalEditMode = status; + // fire event + this._fireEditModeEvent(this._globalEditMode); + }, toggleGlobalEditMode(options = this.globalOptions) { - // console.log('toggle global edit mode', options); - if (this.globalEditModeEnabled()) { // disable this.disableGlobalEditMode(); @@ -88,6 +85,7 @@ const GlobalEditMode = { } if (this.globalEditModeEnabled()) { + // TODO: this._globalSnappingEnabled is a Pro Feature. Remove this option from OSS? layer.pm.enable({ ...this.globalOptions, snappable: this._globalSnappingEnabled }); } }, @@ -97,7 +95,6 @@ const GlobalEditMode = { map: this.map, }); }, - -} +}; export default GlobalEditMode diff --git a/src/js/Mixins/Modes/Mode.Removal.js b/src/js/Mixins/Modes/Mode.Removal.js index b22f99b5..9dfe84fa 100644 --- a/src/js/Mixins/Modes/Mode.Removal.js +++ b/src/js/Mixins/Modes/Mode.Removal.js @@ -1,18 +1,4 @@ const GlobalRemovalMode = { - disableGlobalRemovalMode() { - this._globalRemovalMode = false; - this.map.eachLayer(layer => { - layer.off('click', this.removeLayer, this); - }); - - // remove map handler - this.map.off('layeradd', this.throttledReInitRemoval, this); - - // toogle the button in the toolbar if this is called programatically - this.Toolbar.toggleButton('deleteLayer', this._globalRemovalMode); - - this._fireRemovalModeEvent(false); - }, enableGlobalRemovalMode() { const isRelevant = layer => layer.pm && @@ -39,11 +25,26 @@ const GlobalRemovalMode = { this._fireRemovalModeEvent(true); }, - _fireRemovalModeEvent(enabled) { - this.map.fire('pm:globalremovalmodetoggled', { - enabled, - map: this.map, + disableGlobalRemovalMode() { + this._globalRemovalMode = false; + this.map.eachLayer(layer => { + layer.off('click', this.removeLayer, this); }); + + // remove map handler + this.map.off('layeradd', this.throttledReInitRemoval, this); + + // toogle the button in the toolbar if this is called programatically + this.Toolbar.toggleButton('deleteLayer', this._globalRemovalMode); + + this._fireRemovalModeEvent(false); + }, + // TODO: Remove in the next major release + globalRemovalEnabled() { + return this.globalRemovalModeEnabled(); + }, + globalRemovalModeEnabled() { + return !!this._globalRemovalMode; }, toggleGlobalRemovalMode() { // toggle global edit mode @@ -53,15 +54,26 @@ const GlobalRemovalMode = { this.enableGlobalRemovalMode(); } }, - // TODO: Remove in the next major release - globalRemovalEnabled() { - return this.globalRemovalModeEnabled(); + reinitGlobalRemovalMode({ layer }) { + // do nothing if layer is not handled by leaflet so it doesn't fire unnecessarily + const isRelevant = !!layer.pm && !layer._pmTempLayer; + if (!isRelevant) { + return; + } + + // re-enable global removal mode if it's enabled already + if (this.globalRemovalModeEnabled()) { + this.disableGlobalRemovalMode(); + this.enableGlobalRemovalMode(); + } }, - globalRemovalModeEnabled() { - return !!this._globalRemovalMode; + _fireRemovalModeEvent(enabled) { + this.map.fire('pm:globalremovalmodetoggled', { + enabled, + map: this.map, + }); }, removeLayer(e) { - const layer = e.target; // only remove layer, if it's handled by leaflet-geoman, // not a tempLayer and not currently being dragged @@ -80,19 +92,6 @@ const GlobalRemovalMode = { } }, - reinitGlobalRemovalMode({ layer }) { - // do nothing if layer is not handled by leaflet so it doesn't fire unnecessarily - const isRelevant = !!layer.pm && !layer._pmTempLayer; - if (!isRelevant) { - return; - } - - // re-enable global removal mode if it's enabled already - if (this.globalRemovalModeEnabled()) { - this.disableGlobalRemovalMode(); - this.enableGlobalRemovalMode(); - } - }, -} +}; export default GlobalRemovalMode diff --git a/src/js/Mixins/Snapping.js b/src/js/Mixins/Snapping.js index 2339d8a3..a9f1b47c 100644 --- a/src/js/Mixins/Snapping.js +++ b/src/js/Mixins/Snapping.js @@ -31,10 +31,6 @@ const SnapMixin = { marker.on('dragend', this._cleanupSnapping, this); }); }, - _unsnap() { - // delete the last snap - delete this._snapLatLng; - }, _cleanupSnapping() { // delete it, we need to refresh this with each start of a drag because // meanwhile, new layers could've been added to the map @@ -49,14 +45,6 @@ const SnapMixin = { }); } }, - _handleSnapLayerRemoval({ layer }) { - // find the layers index in snaplist - const index = this._snapList.findIndex( - e => e._leaflet_id === layer._leaflet_id - ); - // remove it from the snaplist - this._snapList.splice(index, 1); - }, _handleSnapping(e) { function throttledList() { return L.Util.throttle(this._createSnapList, 100, this); @@ -162,60 +150,6 @@ const SnapMixin = { return true; }, - - // we got the point we want to snap to (C), but we need to check if a coord of the polygon - // receives priority over C as the snapping point. Let's check this here - _checkPrioritiySnapping(closestLayer) { - const map = this._map; - - // A and B are the points of the closest segment to P (the marker position we want to snap) - const A = closestLayer.segment[0]; - const B = closestLayer.segment[1]; - - // C is the point we would snap to on the segment. - // The closest point on the closest segment of the closest polygon to P. That's right. - const C = closestLayer.latlng; - - // distances from A to C and B to C to check which one is closer to C - const distanceAC = this._getDistance(map, A, C); - const distanceBC = this._getDistance(map, B, C); - - // closest latlng of A and B to C - let closestVertexLatLng = distanceAC < distanceBC ? A : B; - - // distance between closestVertexLatLng and C - let shortestDistance = distanceAC < distanceBC ? distanceAC : distanceBC; - - // snap to middle (M) of segment if option is enabled - if (this.options.snapMiddle) { - const M = Utils.calcMiddleLatLng(map, A, B); - const distanceMC = this._getDistance(map, M, C); - - if (distanceMC < distanceAC && distanceMC < distanceBC) { - // M is the nearest vertex - closestVertexLatLng = M; - shortestDistance = distanceMC; - } - } - - // the distance that needs to be undercut to trigger priority - const priorityDistance = this.options.snapDistance; - - // the latlng we ultemately want to snap to - let snapLatlng; - - // if C is closer to the closestVertexLatLng (A, B or M) than the snapDistance, - // the closestVertexLatLng has priority over C as the snapping point. - if (shortestDistance < priorityDistance) { - snapLatlng = closestVertexLatLng; - } else { - snapLatlng = C; - } - - // return the copy of snapping point - return Object.assign({}, snapLatlng); - }, - _createSnapList() { let layers = []; const debugIndicatorLines = []; @@ -273,6 +207,14 @@ const SnapMixin = { this.debugIndicatorLines = debugIndicatorLines; }, + _handleSnapLayerRemoval({ layer }) { + // find the layers index in snaplist + const index = this._snapList.findIndex( + e => e._leaflet_id === layer._leaflet_id + ); + // remove it from the snaplist + this._snapList.splice(index, 1); + }, _calcClosestLayer(latlng, layers) { // the closest polygon to our dragged marker latlng let closestLayer = {}; @@ -303,7 +245,6 @@ const SnapMixin = { // if there is no closest layer, return undefined return closestLayer; }, - _calcLayerDistances(latlng, layer) { const map = this._map; @@ -385,7 +326,62 @@ const SnapMixin = { distance: shortestDistance, }; }, + // we got the point we want to snap to (C), but we need to check if a coord of the polygon + // receives priority over C as the snapping point. Let's check this here + _checkPrioritiySnapping(closestLayer) { + const map = this._map; + // A and B are the points of the closest segment to P (the marker position we want to snap) + const A = closestLayer.segment[0]; + const B = closestLayer.segment[1]; + + // C is the point we would snap to on the segment. + // The closest point on the closest segment of the closest polygon to P. That's right. + const C = closestLayer.latlng; + + // distances from A to C and B to C to check which one is closer to C + const distanceAC = this._getDistance(map, A, C); + const distanceBC = this._getDistance(map, B, C); + + // closest latlng of A and B to C + let closestVertexLatLng = distanceAC < distanceBC ? A : B; + + // distance between closestVertexLatLng and C + let shortestDistance = distanceAC < distanceBC ? distanceAC : distanceBC; + + // snap to middle (M) of segment if option is enabled + if (this.options.snapMiddle) { + const M = Utils.calcMiddleLatLng(map, A, B); + const distanceMC = this._getDistance(map, M, C); + + if (distanceMC < distanceAC && distanceMC < distanceBC) { + // M is the nearest vertex + closestVertexLatLng = M; + shortestDistance = distanceMC; + } + } + + // the distance that needs to be undercut to trigger priority + const priorityDistance = this.options.snapDistance; + + // the latlng we ultemately want to snap to + let snapLatlng; + + // if C is closer to the closestVertexLatLng (A, B or M) than the snapDistance, + // the closestVertexLatLng has priority over C as the snapping point. + if (shortestDistance < priorityDistance) { + snapLatlng = closestVertexLatLng; + } else { + snapLatlng = C; + } + + // return the copy of snapping point + return Object.assign({}, snapLatlng); + }, + _unsnap() { + // delete the last snap + delete this._snapLatLng; + }, _getClosestPointOnSegment(map, latlng, latlngA, latlngB) { let maxzoom = map.getMaxZoom(); if (maxzoom === Infinity) { From 01328e897f442ef0ee7c19eb004e5a4de7ecb673 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Sat, 3 Oct 2020 21:03:56 +0200 Subject: [PATCH 07/30] Remove empty coord rings (#665). Fixes #653 (patch) * remove empty CoordRings * remove empty markers array * Add test * remove .only * remove only Co-authored-by: Sumit Kumar --- README.md | 2 +- cypress/integration/rectangle.spec.js | 27 ++++++++++++ src/js/Edit/L.PM.Edit.Line.js | 60 ++++++++++++++++----------- src/js/Toolbar/L.PM.Toolbar.js | 4 +- src/js/helpers/index.js | 9 ++++ 5 files changed, 75 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index fe1531be..d77435ec 100644 --- a/README.md +++ b/README.md @@ -563,7 +563,7 @@ Change the language of user-facing copy in leaflet-geoman map.pm.setLang('de'); ``` -Currently available languages are `en`, `de`, `it`, `ru`, `ro`, `es`, `fr`, `pt_br`, `id`, `zh`, `nl`, `el`, `pl`, `sv` and `hu`. +Currently available languages are `en`, `de`, `it`, `ru`, `ro`, `es`, `fr`, `pt_br`, `id`, `zh`, `nl`, `el`, `pl`, `sv`, `da` and `hu`. To add translations to the plugin, you can add [a translation file](src/assets/translations) via Pull Request. You can also provide your own custom translations. diff --git a/cypress/integration/rectangle.spec.js b/cypress/integration/rectangle.spec.js index 6bc85af7..2a7f0e20 100644 --- a/cypress/integration/rectangle.spec.js +++ b/cypress/integration/rectangle.spec.js @@ -131,5 +131,32 @@ describe('Draw Rectangle', () => { expect(rect.options.color).to.not.equal('red'); }) + }); + + it('remove empty coord rings', ()=>{ + cy.toolbarButton('rectangle').click(); + cy.get(mapSelector) + .click(100,50) + .click(700,400); + + cy.toolbarButton('cut').click(); + cy.get(mapSelector) + .click(200,200) + .click(300,250) + .click(370,200) + .click(200,200); + + + cy.toolbarButton('edit').click(); + cy.hasVertexMarkers(7); + + cy.get(mapSelector).rightclick(300,250); + + cy.window().then(({ map}) => { + const rect = map.pm.getGeomanDrawLayers()[0]; + const geojson = rect.toGeoJSON(); + const coords = geojson.geometry.coordinates; + expect(coords.length).to.equal(1); + }) }) }); diff --git a/src/js/Edit/L.PM.Edit.Line.js b/src/js/Edit/L.PM.Edit.Line.js index a79c9031..412d578e 100644 --- a/src/js/Edit/L.PM.Edit.Line.js +++ b/src/js/Edit/L.PM.Edit.Line.js @@ -3,7 +3,7 @@ import lineIntersect from '@turf/line-intersect'; import get from 'lodash/get'; import Edit from './L.PM.Edit'; import Utils from '../L.PM.Utils'; -import {isEmptyDeep} from '../helpers'; +import { isEmptyDeep, removeEmptyCoordRings } from '../helpers'; import MarkerLimits from '../Mixins/MarkerLimits'; @@ -32,6 +32,7 @@ Edit.Line = Edit.extend({ return; } + // TODO: this is wrong: if already enable then go into the if if (!this.enabled()) { // if it was already enabled, disable first // we don't block enabling again because new options might be passed @@ -399,7 +400,7 @@ Edit.Line = Edit.extend({ const marker = e.target; // coords of the layer - const coords = this._layer.getLatLngs(); + let coords = this._layer.getLatLngs(); // the index path to the marker inside the multidimensional marker array const { indexPath, index, parentPath } = this.findDeepMarkerIndex( @@ -416,7 +417,7 @@ Edit.Line = Edit.extend({ const coordsRing = indexPath.length > 1 ? get(coords, parentPath) : coords; // define the markers array that is edited - const markerArr = + let markerArr = indexPath.length > 1 ? get(this._markers, parentPath) : this._markers; // remove coordinate @@ -441,15 +442,22 @@ Edit.Line = Edit.extend({ // TODO: kind of an ugly workaround maybe do it better? this.disable(); this.enable(this.options); - } - // TODO: we may should remove all empty coord-rings here as well. + } // if no coords are left, remove the layer if (isEmptyDeep(coords)) { this._layer.remove(); } + // remove all empty coord-rings + coords = removeEmptyCoordRings(coords); + this._layer.setLatLngs(coords); + // remove empty marker arrays + this._markers = removeEmptyCoordRings(this._markers); + // get new markerArr because we cleaned up coords and markers array + markerArr = indexPath.length > 1 ? get(this._markers, parentPath) : this._markers; + // now handle the middle markers // remove the marker and the middlemarkers next to it from the map if (marker._middleMarkerPrev) { @@ -462,30 +470,32 @@ Edit.Line = Edit.extend({ // remove the marker from the map this._markerGroup.removeLayer(marker); - let rightMarkerIndex; - let leftMarkerIndex; + if(markerArr) { + let rightMarkerIndex; + let leftMarkerIndex; - if (this.isPolygon()) { - // find neighbor marker-indexes - rightMarkerIndex = (index + 1) % markerArr.length; - leftMarkerIndex = (index + (markerArr.length - 1)) % markerArr.length; - } else { - // find neighbor marker-indexes - leftMarkerIndex = index - 1 < 0 ? undefined : index - 1; - rightMarkerIndex = index + 1 >= markerArr.length ? undefined : index + 1; - } + if (this.isPolygon()) { + // find neighbor marker-indexes + rightMarkerIndex = (index + 1) % markerArr.length; + leftMarkerIndex = (index + (markerArr.length - 1)) % markerArr.length; + } else { + // find neighbor marker-indexes + leftMarkerIndex = index - 1 < 0 ? undefined : index - 1; + rightMarkerIndex = index + 1 >= markerArr.length ? undefined : index + 1; + } - // don't create middlemarkers if there is only one marker left - if (rightMarkerIndex !== leftMarkerIndex) { - const leftM = markerArr[leftMarkerIndex]; - const rightM = markerArr[rightMarkerIndex]; - if (this.options.hideMiddleMarkers !== true) { - this._createMiddleMarker(leftM, rightM); + // don't create middlemarkers if there is only one marker left + if (rightMarkerIndex !== leftMarkerIndex) { + const leftM = markerArr[leftMarkerIndex]; + const rightM = markerArr[rightMarkerIndex]; + if (this.options.hideMiddleMarkers !== true) { + this._createMiddleMarker(leftM, rightM); + } } - } - // remove the marker from the markers array - markerArr.splice(index, 1); + // remove the marker from the markers array + markerArr.splice(index, 1); + } // fire edit event this._fireEdit(); diff --git a/src/js/Toolbar/L.PM.Toolbar.js b/src/js/Toolbar/L.PM.Toolbar.js index ffb44060..fbc6db17 100644 --- a/src/js/Toolbar/L.PM.Toolbar.js +++ b/src/js/Toolbar/L.PM.Toolbar.js @@ -492,7 +492,9 @@ const Toolbar = L.Class.extend({ options.block = ""; } - if (options.className.indexOf('control-icon') === -1) { + if (!options.className){ + options.className = 'control-icon'; + }else if(options.className.indexOf('control-icon') === -1) { options.className = `control-icon ${options.className}`; } diff --git a/src/js/helpers/index.js b/src/js/helpers/index.js index d15014bb..f5e0abf7 100644 --- a/src/js/helpers/index.js +++ b/src/js/helpers/index.js @@ -22,6 +22,15 @@ export function isEmptyDeep(l) { return !flatten(l).length; } +export function removeEmptyCoordRings(arr) { + return arr.reduce((result, item)=>{ + if(item.length !== 0){ + result.push( Array.isArray(item) ? removeEmptyCoordRings(item) : item ); + } + return result; + }, []); +} + // Code from https://stackoverflow.com/a/24153998/8283938 function destinationVincenty(lonlat, brng, dist) { // rewritten to work with leaflet const VincentyConstants = { From efbc9af4c75029813bf2469b3baf7b51c7a19436 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Tue, 13 Oct 2020 17:11:06 +0200 Subject: [PATCH 08/30] Fix missing pm instance for OptIn (#676) (patch) --- src/js/Draw/L.PM.Draw.Circle.js | 6 ++++-- src/js/Draw/L.PM.Draw.CircleMarker.js | 12 ++++++++---- src/js/Draw/L.PM.Draw.Marker.js | 9 +++++++-- src/js/Draw/L.PM.Draw.js | 4 +++- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/js/Draw/L.PM.Draw.Circle.js b/src/js/Draw/L.PM.Draw.Circle.js index 8cfbeb8f..4d283101 100644 --- a/src/js/Draw/L.PM.Draw.Circle.js +++ b/src/js/Draw/L.PM.Draw.Circle.js @@ -215,8 +215,10 @@ Draw.Circle = Draw.extend({ this._setShapeForFinishLayer(circleLayer); this._addDrawnLayerProp(circleLayer); - // create polygon around the circle border - circleLayer.pm._updateHiddenPolyCircle(); + if(circleLayer.pm) { + // create polygon around the circle border + circleLayer.pm._updateHiddenPolyCircle(); + } // disable drawing this.disable(); diff --git a/src/js/Draw/L.PM.Draw.CircleMarker.js b/src/js/Draw/L.PM.Draw.CircleMarker.js index 8b47d025..59a3ca9b 100644 --- a/src/js/Draw/L.PM.Draw.CircleMarker.js +++ b/src/js/Draw/L.PM.Draw.CircleMarker.js @@ -254,8 +254,10 @@ Draw.CircleMarker = Draw.Marker.extend({ // add marker to the map marker.addTo(this._map); - // enable editing for the marker - marker.pm.enable(); + if(marker.pm) { + // enable editing for the marker + marker.pm.enable(); + } // fire the pm:create event and pass shape and marker this._map.fire('pm:create', { @@ -283,8 +285,10 @@ Draw.CircleMarker = Draw.Marker.extend({ const circleLayer = L.circleMarker(center, options).addTo(this._map); this._setShapeForFinishLayer(circleLayer); this._addDrawnLayerProp(circleLayer); - // create polygon around the circle border - circleLayer.pm._updateHiddenPolyCircle(); + if(circleLayer.pm) { + // create polygon around the circle border + circleLayer.pm._updateHiddenPolyCircle(); + } // disable drawing this.disable(); diff --git a/src/js/Draw/L.PM.Draw.Marker.js b/src/js/Draw/L.PM.Draw.Marker.js index 5c691991..4ccd5f13 100644 --- a/src/js/Draw/L.PM.Draw.Marker.js +++ b/src/js/Draw/L.PM.Draw.Marker.js @@ -142,11 +142,16 @@ Draw.Marker = Draw.extend({ this._setShapeForFinishLayer(marker); this._addDrawnLayerProp(marker); + if(!marker.pm) { + marker.options.draggable = false; + } // add marker to the map marker.addTo(this._map); - // enable editing for the marker - marker.pm.enable(); + if(marker.pm) { + // enable editing for the marker + marker.pm.enable(); + } // fire the pm:create event and pass shape and marker this._map.fire('pm:create', { diff --git a/src/js/Draw/L.PM.Draw.js b/src/js/Draw/L.PM.Draw.js index eabdd1bf..1cab0def 100644 --- a/src/js/Draw/L.PM.Draw.js +++ b/src/js/Draw/L.PM.Draw.js @@ -146,7 +146,9 @@ const Draw = L.Class.extend({ layer._drawnByGeoman = true; }, _setShapeForFinishLayer(layer){ - layer.pm._shape = this._shape; + if(layer.pm) { + layer.pm._shape = this._shape; + } } }); From 3a0e9ec562424f4e35a4750bdaaa30b4ecc8c6d8 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Tue, 13 Oct 2020 17:13:16 +0200 Subject: [PATCH 09/30] Don't cut if cutting-layer has self-intersection (#681). Fixes #680 (patch) --- cypress/integration/polygon.spec.js | 30 +++++++++++++++++++++++++++++ src/js/Draw/L.PM.Draw.Cut.js | 3 ++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/cypress/integration/polygon.spec.js b/cypress/integration/polygon.spec.js index 3ac0b6cf..63247c34 100644 --- a/cypress/integration/polygon.spec.js +++ b/cypress/integration/polygon.spec.js @@ -708,4 +708,34 @@ describe('Draw & Edit Poly', () => { cy.hasVertexMarkers(2); }); + + it('don\'t Cut if it has selfIntersection on finish', () => { + cy.toolbarButton('polygon') + .click(); + + cy.get(mapSelector) + .click(90, 250) + .click(150, 50) + .click(500, 50) + .click(500, 300) + .click(300, 350) + .click(90, 250); + + + cy.toolbarButton('cut') + .click(); + + // draw a polygon to cut + cy.get(mapSelector) + .click(200, 100) + .click(450, 100) + .click(150, 200) + .click(450, 200) + .click(200, 100); + + cy.toolbarButton('edit') + .click(); + + cy.hasVertexMarkers(5); + }) }); diff --git a/src/js/Draw/L.PM.Draw.Cut.js b/src/js/Draw/L.PM.Draw.Cut.js index c8f4f55f..53b4cafd 100644 --- a/src/js/Draw/L.PM.Draw.Cut.js +++ b/src/js/Draw/L.PM.Draw.Cut.js @@ -11,7 +11,8 @@ Draw.Cut = Draw.Polygon.extend({ this._editedLayers = []; // if self intersection is not allowed, do not finish the shape! if (!this.options.allowSelfIntersection) { - this._handleSelfIntersection(false); + // Check if polygon intersects when is completed and the line between the last and the first point is drawn + this._handleSelfIntersection(true, this._layer.getLatLngs()[0]); if (this._doesSelfIntersect) { return; From 2ec3e8071308c96fef1417149c22a1652468e54e Mon Sep 17 00:00:00 2001 From: Falke Design Date: Tue, 13 Oct 2020 17:15:09 +0200 Subject: [PATCH 10/30] Added markerdrag event (#663). Fixes #660, #662 (patch) * Add markerdrag event * Add markerdrag event to readme --- README.md | 1 + demo/events.js | 1 + src/js/Edit/L.PM.Edit.Circle.js | 9 +++++++++ src/js/Edit/L.PM.Edit.CircleMarker.js | 9 +++++++++ src/js/Edit/L.PM.Edit.LayerGroup.js | 3 ++- src/js/Edit/L.PM.Edit.Line.js | 6 ++++++ src/js/Edit/L.PM.Edit.Rectangle.js | 7 +++++++ 7 files changed, 35 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d77435ec..0fc4b9c3 100644 --- a/README.md +++ b/README.md @@ -341,6 +341,7 @@ The following events are available on a layer instance: | pm:vertexadded | `e` | Fired when a vertex is added | `layer`, `indexPath`, `latlng`, `marker`, `shape` | | pm:vertexremoved | `e` | Fired when a vertex is removed | `layer`, `indexPath`, `marker`, `shape` | | pm:markerdragstart | `e` | Fired when dragging of a marker which corresponds to a vertex starts | `layer`, `indexPath`, `markerEvent`, `shape` | +| pm:markerdrag | `e` | Fired when dragging a vertex-marker | `layer`, `indexPath`, `markerEvent`, `shape` | | pm:markerdragend | `e` | Fired when dragging of a vertex-marker ends | `layer`, `indexPath`, `markerEvent`, `shape` | | pm:snapdrag | `e` | Fired during a marker move/drag. Payload includes info about involved layers and snapping calculation| `shape`, `distance`, `layer` = `workingLayer`, `marker`, `layerInteractedWith`, `segment`, `snapLatLng` | | pm:snap | `e` | Fired when a vertex-marker is snapped to another vertex. Also fired on the marker itself. | `shape`, `distance`, `layer` = `workingLayer`, `marker`, `layerInteractedWith`, `segment`, `snapLatLng` | diff --git a/demo/events.js b/demo/events.js index 3b79846a..ea3792a5 100644 --- a/demo/events.js +++ b/demo/events.js @@ -42,6 +42,7 @@ map.on('pm:create',function (e) { layer.on('pm:vertexadded', logEvent); layer.on('pm:vertexremoved', logEvent); layer.on('pm:markerdragstart', logEvent); + layer.on('pm:markerdrag', logEvent); layer.on('pm:markerdragend', logEvent); layer.on('pm:snap', logEvent); layer.on('pm:snapdrag', logEvent); diff --git a/src/js/Edit/L.PM.Edit.Circle.js b/src/js/Edit/L.PM.Edit.Circle.js index cc680128..9a78f98d 100644 --- a/src/js/Edit/L.PM.Edit.Circle.js +++ b/src/js/Edit/L.PM.Edit.Circle.js @@ -155,6 +155,7 @@ Edit.Circle = Edit.extend({ marker._pmTempLayer = true; marker.on('dragstart', this._onMarkerDragStart, this); + marker.on('drag', this._onMarkerDrag, this); marker.on('dragend', this._onMarkerDragEnd, this); this._helperLayers.addLayer(marker); @@ -217,6 +218,14 @@ Edit.Circle = Edit.extend({ indexPath: undefined }); }, + _onMarkerDrag(e) { + this._layer.fire('pm:markerdrag', { + layer: this._layer, + markerEvent: e, + shape: this.getShape(), + indexPath: undefined + }); + }, _onMarkerDragEnd(e) { // fire edit event this._fireEdit(); diff --git a/src/js/Edit/L.PM.Edit.CircleMarker.js b/src/js/Edit/L.PM.Edit.CircleMarker.js index abda6377..91cf3b38 100644 --- a/src/js/Edit/L.PM.Edit.CircleMarker.js +++ b/src/js/Edit/L.PM.Edit.CircleMarker.js @@ -185,6 +185,7 @@ Edit.CircleMarker = Edit.extend({ marker._pmTempLayer = true; marker.on('dragstart', this._onMarkerDragStart, this); + marker.on('drag', this._onMarkerDrag, this); marker.on('dragend', this._onMarkerDragEnd, this); this._helperLayers.addLayer(marker); @@ -251,6 +252,14 @@ Edit.CircleMarker = Edit.extend({ indexPath: undefined }); }, + _onMarkerDrag(e) { + this._layer.fire('pm:markerdrag', { + layer: this._layer, + markerEvent: e, + shape: this.getShape(), + indexPath: undefined + }); + }, _onMarkerDragEnd(e) { this._layer.fire('pm:markerdragend', { layer: this._layer, diff --git a/src/js/Edit/L.PM.Edit.LayerGroup.js b/src/js/Edit/L.PM.Edit.LayerGroup.js index c7999e69..d1636d5a 100644 --- a/src/js/Edit/L.PM.Edit.LayerGroup.js +++ b/src/js/Edit/L.PM.Edit.LayerGroup.js @@ -76,8 +76,9 @@ Edit.LayerGroup = L.Class.extend({ 'pm:unsnap', 'pm:cut', 'pm:intersect', - 'pm:markerdragend', 'pm:markerdragstart', + 'pm:markerdrag', + 'pm:markerdragend', 'pm:vertexadded', 'pm:vertexremoved', 'pm:centerplaced', diff --git a/src/js/Edit/L.PM.Edit.Line.js b/src/js/Edit/L.PM.Edit.Line.js index 412d578e..366c5359 100644 --- a/src/js/Edit/L.PM.Edit.Line.js +++ b/src/js/Edit/L.PM.Edit.Line.js @@ -693,6 +693,12 @@ Edit.Line = Edit.extend({ if (!this.options.allowSelfIntersection) { this._handleLayerStyle(); } + this._layer.fire('pm:markerdrag', { + layer: this._layer, + markerEvent: e, + shape: this.getShape(), + indexPath + }); }, _onMarkerDragEnd(e) { const marker = e.target; diff --git a/src/js/Edit/L.PM.Edit.Rectangle.js b/src/js/Edit/L.PM.Edit.Rectangle.js index 8f1c7d81..99fffcb7 100644 --- a/src/js/Edit/L.PM.Edit.Rectangle.js +++ b/src/js/Edit/L.PM.Edit.Rectangle.js @@ -98,6 +98,13 @@ Edit.Rectangle = Edit.Polygon.extend({ if (!draggedMarker._snapped) { this._adjustRectangleForMarkerMove(draggedMarker); } + + this._layer.fire('pm:markerdrag', { + layer: this._layer, + markerEvent: e, + shape: this.getShape(), + indexPath: undefined + }); }, _onMarkerDragEnd(e) { From f74c1c3a346ae336599f032e8b77e3ed7b89926e Mon Sep 17 00:00:00 2001 From: Falke Design Date: Tue, 13 Oct 2020 17:29:01 +0200 Subject: [PATCH 11/30] Return layers of getGeomanLayers() and getGeomanDrawLayers() optionally as a FeatureGroup instead of an Array (#649) (minor) * Add toGeoJSON() to getGeomanLayers() and getGeomanDrawLayers() * Change the Funcs --- README.md | 24 ++++++++++++------------ src/js/L.PM.Map.js | 29 +++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 0fc4b9c3..130f272f 100644 --- a/README.md +++ b/README.md @@ -208,18 +208,18 @@ map.pm.Draw.getShapes(); The following methods are available on `map.pm`: -| Method | Returns | Description | -| :---------------------------- | :-------- | :----------------------------------------------------------------------- | -| enableDraw(`shape`,`options`) | - | Enable Drawing Mode with the passed shape. | -| disableDraw(`shape`) | - | Disable Drawing Mode. The passed shape is optional. | -| Draw.getShapes() | `Array` | Array of available shapes. | -| Draw.getActiveShape() | `String` | Returns the active shape. | -| globalDrawModeEnabled() | `Boolean` | Returns `true` if global draw mode is enabled. `false` when disabled. | -| setPathOptions(`options`) | - | Customize the style of the drawn layer. | -| setGlobalOptions(`options`) | - | Set drawing options. | -| getGlobalOptions() | `Object` | Returns the global options. | -| getGeomanLayers() | `Array` | Returns all Geoman layers on the map. | -| getGeomanDrawLayers() | `Array` | Returns all drawn Geoman layers on the map. | +| Method | Returns | Description | +| :---------------------------- | :-------- | :---------------------------------------------------------------------------------------------- | +| enableDraw(`shape`,`options`) | - | Enable Drawing Mode with the passed shape. | +| disableDraw(`shape`) | - | Disable Drawing Mode. The passed shape is optional. | +| Draw.getShapes() | `Array` | Array of available shapes. | +| Draw.getActiveShape() | `String` | Returns the active shape. | +| globalDrawModeEnabled() | `Boolean` | Returns `true` if global draw mode is enabled. `false` when disabled. | +| setPathOptions(`options`) | - | Customize the style of the drawn layer. | +| setGlobalOptions(`options`) | - | Set drawing options. | +| getGlobalOptions() | `Object` | Returns the global options. | +| getGeomanLayers(`Boolean`) | `Array` | Returns all Geoman layers on the map as array. Pass `true` to get a L.FeatureGroup. | +| getGeomanDrawLayers(`Boolean`)| `Array` | Returns all drawn Geoman layers on the map as array. Pass `true` to get a L.FeatureGroup. | See the available options in the table below. diff --git a/src/js/L.PM.Map.js b/src/js/L.PM.Map.js index 1933f61e..359127a9 100644 --- a/src/js/L.PM.Map.js +++ b/src/js/L.PM.Map.js @@ -126,13 +126,30 @@ const Map = L.Class.extend({ disableGlobalCutMode() { return this.Draw.Cut.disable(); }, - getGeomanLayers(){ - return findLayers(this.map); + getGeomanLayers(asGroup = false){ + const layers = findLayers(this.map); + if(!asGroup) { + return layers; + } + const group = L.featureGroup(); + group._pmTempLayer = true; + layers.forEach((layer) => { + group.addLayer(layer); + }); + return group; + }, + getGeomanDrawLayers(asGroup = false){ + const layers = findLayers(this.map).filter(l => l._drawnByGeoman === true); + if(!asGroup) { + return layers; + } + const group = L.featureGroup(); + group._pmTempLayer = true; + layers.forEach((layer) => { + group.addLayer(layer); + }); + return group; }, - getGeomanDrawLayers(){ - return findLayers(this.map).filter(l => l._drawnByGeoman === true); - } - }); export default Map; From ef3dc73036a0d4d863ce3e3b43854b351191257f Mon Sep 17 00:00:00 2001 From: Falke Design Date: Tue, 13 Oct 2020 18:37:03 +0200 Subject: [PATCH 12/30] Added L.ImageOverlay Support (#675). Fixes #673 (minor) --- README.md | 2 +- cypress/integration/imageoverlay.spec.js | 32 +++++++++++ src/js/Edit/L.PM.Edit.ImageOverlay.js | 73 ++++++++++++++++++++++++ src/js/L.PM.Utils.js | 3 +- src/js/L.PM.js | 16 ++++++ src/js/Mixins/Dragging.js | 42 +++++++++----- src/js/Mixins/Snapping.js | 5 +- 7 files changed, 156 insertions(+), 17 deletions(-) create mode 100644 cypress/integration/imageoverlay.spec.js create mode 100644 src/js/Edit/L.PM.Edit.ImageOverlay.js diff --git a/README.md b/README.md index 130f272f..361f38cf 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

Leaflet Plugin For Creating And Editing Geometry Layers
Draw, Edit, Drag, Cut, Snap and Pin Layers
- Supports Markers, CircleMarkers, Polylines, Polygons, Circles, Rectangles, LayerGroups, GeoJSON and MultiPolygons + Supports Markers, CircleMarkers, Polylines, Polygons, Circles, Rectangles, ImageOverlays, LayerGroups, GeoJSON and MultiPolygons

diff --git a/cypress/integration/imageoverlay.spec.js b/cypress/integration/imageoverlay.spec.js new file mode 100644 index 00000000..fdad2769 --- /dev/null +++ b/cypress/integration/imageoverlay.spec.js @@ -0,0 +1,32 @@ +describe('Opens Testing Environment', () => { + const mapSelector = '#map'; + + it('Snap to ImageOverlay', () => { + let eventcalled = ""; + cy.window().then(({ map, L }) => { + map.setView([18.74469, 72.1258],10); + const icon = 'https://camo.githubusercontent.com/33fa9a94048274f81a806631ca881a55c2aa8f0a/68747470733a2f2f66696c652d6a787a796a67717775742e6e6f772e73682f'; + L.imageOverlay(icon, [[18.74469, 72.1258], [18.94469, 72.3258]], {interactive: true}) + .addTo(map); + + map.on('pm:drawstart',(e)=>{ + const layer = e.workingLayer; + layer.on('pm:snap', (x)=>eventcalled = x.type); + }); + }); + + cy.toolbarButton('polygon') + .click(); + + cy.window().then(({map}) => { + const point = map.latLngToContainerPoint([18.74469, 72.1258]); + cy.get(mapSelector) + .click(point); + }); + + cy.window().then(({}) => { + expect(eventcalled).to.equal("pm:snap"); + }); + + }); +}); diff --git a/src/js/Edit/L.PM.Edit.ImageOverlay.js b/src/js/Edit/L.PM.Edit.ImageOverlay.js new file mode 100644 index 00000000..d3acb10d --- /dev/null +++ b/src/js/Edit/L.PM.Edit.ImageOverlay.js @@ -0,0 +1,73 @@ +import Edit from './L.PM.Edit'; + +Edit.ImageOverlay = Edit.extend({ + _shape: 'ImageOverlay', + initialize(layer) { + this._layer = layer; + this._enabled = false; + }, + toggleEdit(options) { + if (!this.enabled()) { + this.enable(options); + } else { + this.disable(); + } + }, + enabled() { + return this._enabled; + }, + enable(options = { draggable: true, snappable: true }) { + L.Util.setOptions(this, options); + this._map = this._layer._map; + // cancel when map isn't available, this happens when the polygon is removed before this fires + if (!this._map) { + return; + } + if (!this.enabled()) { + // if it was already enabled, disable first + // we don't block enabling again because new options might be passed + this.disable(); + } + // change state + this._enabled = true; + + this.enableLayerDrag(); + + + // create markers for four corners of ImageOverlay + this._otherSnapLayers = L.PM.Edit.Rectangle.prototype._findCorners.apply(this); + + this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); + }, + disable(layer = this._layer) { + // prevent disabling if layer is being dragged + if (layer.pm._dragging) { + return false; + } + + // Add map if it is not already set. This happens when disable() is called before enable() + if (!this._map) { + this._map = this._layer._map; + } + // disable dragging, as this could have been active even without being enabled + this.disableLayerDrag(); + + // only fire events if it was enabled before + if (!this.enabled()) { + if (this._layerEdited) { + layer.fire('pm:update', { layer, shape: this.getShape() }); + } + this._layerEdited = false; + layer.fire('pm:disable', { layer, shape: this.getShape() }); + } + + this._layer.off('contextmenu', this._removeMarker, this); + layer.pm._enabled = false; + return true; + }, + _fireEdit() { + // fire edit event + this._layer.fire('pm:edit', { layer: this._layer, shape: this.getShape() }); + this._layerEdited = true; + }, +}); diff --git a/src/js/L.PM.Utils.js b/src/js/L.PM.Utils.js index 33eac2b3..b43b945a 100644 --- a/src/js/L.PM.Utils.js +++ b/src/js/L.PM.Utils.js @@ -16,7 +16,8 @@ const Utils = { layer instanceof L.Polyline || layer instanceof L.Marker || layer instanceof L.Circle || - layer instanceof L.CircleMarker + layer instanceof L.CircleMarker || + layer instanceof L.ImageOverlay ) { layers.push(layer); } diff --git a/src/js/L.PM.js b/src/js/L.PM.js index 16b8bf7f..f94bb446 100644 --- a/src/js/L.PM.js +++ b/src/js/L.PM.js @@ -31,6 +31,7 @@ import './Edit/L.PM.Edit.Polygon'; import './Edit/L.PM.Edit.Rectangle'; import './Edit/L.PM.Edit.Circle'; import './Edit/L.PM.Edit.CircleMarker'; +import './Edit/L.PM.Edit.ImageOverlay'; import '../css/layers.css'; import '../css/controls.css'; @@ -164,6 +165,21 @@ L.PM = L.PM || { } L.Circle.addInitHook(initCircle); + + + function initImageOverlay() { + this.pm = undefined; + + if (L.PM.optIn) { + if (this.options.pmIgnore === false) { + this.pm = new L.PM.Edit.ImageOverlay(this); + } + } else if (!this.options.pmIgnore) { + this.pm = new L.PM.Edit.ImageOverlay(this); + } + } + + L.ImageOverlay.addInitHook(initImageOverlay); }, reInitLayer(layer){ if(layer instanceof L.LayerGroup){ diff --git a/src/js/Mixins/Dragging.js b/src/js/Mixins/Dragging.js index 366ddf02..b582acc7 100644 --- a/src/js/Mixins/Dragging.js +++ b/src/js/Mixins/Dragging.js @@ -22,6 +22,8 @@ const DragMixin = { this._layer.dragging.enable(); } return; + }else if(this._layer instanceof L.ImageOverlay){ + this._getDOMElem().ondragstart = ()=>false; } // temporary coord variable for delta calculation @@ -94,9 +96,7 @@ const DragMixin = { this._layer._map.on('mousemove', this._dragMixinOnMouseMove, this); }, _dragMixinOnMouseMove(e) { - const el = this._layer._path - ? this._layer._path - : this._layer._renderer._container; + const el = this._getDOMElem(); if (!this._dragging) { // set state @@ -118,9 +118,7 @@ const DragMixin = { this._onLayerDrag(e); }, _dragMixinOnMouseUp() { - const el = this._layer._path - ? this._layer._path - : this._layer._renderer._container; + const el = this._getDOMElem(); // re-enable map drag if (this._originalMapDragState) { @@ -193,6 +191,11 @@ const DragMixin = { const newCoords = moveCoords([this._layer.getLatLng()]); // set new coordinates and redraw this._layer.setLatLng(newCoords[0]); + } else if( this._layer instanceof L.ImageOverlay){ + // create the new coordinates array + const newCoords = moveCoords([this._layer.getBounds().getNorthWest(),this._layer.getBounds().getSouthEast()]); + // set new coordinates and redraw + this._layer.setBounds(newCoords); } else { // create the new coordinates array const newCoords = moveCoords(this._layer.getLatLngs()); @@ -224,16 +227,27 @@ const DragMixin = { }); }, addDraggingClass() { - const el = this._layer._path - ? this._layer._path - : this._layer._renderer._container; - L.DomUtil.addClass(el, 'leaflet-pm-draggable'); + const el = this._getDOMElem(); + if(el) { + L.DomUtil.addClass(el, 'leaflet-pm-draggable'); + } }, removeDraggingClass() { - const el = this._layer._path - ? this._layer._path - : this._layer._renderer._container; - L.DomUtil.removeClass(el, 'leaflet-pm-draggable'); + const el = this._getDOMElem(); + if(el) { + L.DomUtil.removeClass(el, 'leaflet-pm-draggable'); + } + }, + _getDOMElem(){ + let el = null; + if(this._layer._path){ + el = this._layer._path; + }else if(this._layer._renderer && this._layer._renderer._container){ + el = this._layer._renderer._container; + }else if(this._layer._image){ + el = this._layer._image; + } + return el; } }; diff --git a/src/js/Mixins/Snapping.js b/src/js/Mixins/Snapping.js index a9f1b47c..7577761d 100644 --- a/src/js/Mixins/Snapping.js +++ b/src/js/Mixins/Snapping.js @@ -164,13 +164,16 @@ const SnapMixin = { if ( (layer instanceof L.Polyline || layer instanceof L.Marker || - layer instanceof L.CircleMarker) && + layer instanceof L.CircleMarker || + layer instanceof L.ImageOverlay) && layer.options.snapIgnore !== true ) { // adds a hidden polygon which matches the border of the circle if ((layer instanceof L.Circle || layer instanceof L.CircleMarker) && layer.pm && layer.pm._hiddenPolyCircle) { layers.push(layer.pm._hiddenPolyCircle); + }else if(layer instanceof L.ImageOverlay){ + layer = L.rectangle(layer.getBounds()); } layers.push(layer); From c56f7644ca8977ed5051ed2da923c50e0df40825 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Tue, 13 Oct 2020 18:44:18 +0200 Subject: [PATCH 13/30] Add disabled state and click events to toolbar buttons (#646). Fixes #642, #503 (minor) * Add new events "pm:buttonclick" and "pm:actionclick" from #503 * Control button can now disabled * Add test for disable button --- README.md | 31 +++++++++---- cypress/integration/toolbar.spec.js | 32 +++++++++++++ demo/customcontrols.js | 6 +++ src/css/controls.css | 7 +++ src/js/Toolbar/L.Controls.js | 51 +++++++++++++++++---- src/js/Toolbar/L.PM.Toolbar.js | 71 ++++++++++++----------------- 6 files changed, 135 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 361f38cf..f13efdf8 100644 --- a/README.md +++ b/README.md @@ -715,14 +715,15 @@ map.pm.Toolbar.createCustomControl(options) | Option | Default | Description | | :------------ | :---------- | :----------------------------------------------------------------------------------------------- | -| name | Required | Name of the control | -| block | '' | block of the control. `draw`, `edit`, `options`⭐, `custom` | -| title | '' | Text showing when you hover the control | -| className | '' | CSS class with the Icon | -| onClick | - | Function fired when clicking the control | -| afterClick | - | Function fired after clicking the control | -| actions | [ ] | Action that appears as tooltip. Look under [actions](#actions) for more information | -| toggle | true | Control can be toggled | +| name | Required | Name of the control | +| block | '' | block of the control. `draw`, `edit`, `options`⭐, `custom` | +| title | '' | Text showing when you hover the control | +| className | '' | CSS class with the Icon | +| onClick | - | Function fired when clicking the control | +| afterClick | - | Function fired after clicking the control | +| actions | [ ] | Action that appears as tooltip. Look under [actions](#actions) for more information | +| toggle | true | Control can be toggled | +| disabled | false | Control is disabled | **Inherit from an Existing Control** @@ -778,10 +779,20 @@ The following methods are available on `map.pm.Toolbar`: | Method | Returns | Description | | :------------------------------------------ | :-------- | :------------------------------------------------------------------------------------------------------------ | | createCustomControl(`options`) | - | To add a custom Control to the Toolbar. | -| copyDrawControl(`instance`, `options`) | `Object` | Creates a copy of a draw Control. Returns the `drawInstance` and the `control`. | -| changeActionsOfControl(`name`, `actions`) | - | Change the actions of an existing button. | +| copyDrawControl(`instance`, `options`) | `Object` | Creates a copy of a draw Control. Returns the `drawInstance` and the `control`. | +| changeActionsOfControl(`name`, `actions`) | - | Change the actions of an existing button. | | changeControlOrder(`shapes`) | - | Change the order of the controls in the Toolbar. You can pass all shapes and `Edit`, `Drag`, `Removal`, `Cut` | | getControlOrder() | `Array` | Get the current order of the controls. | +| setButtonDisabled(`name`, `Boolean`) | - | Enable / disable a button. | + +The following events are available on a map instance: + +| Event | Params | Description | Output | +| :------------- | :----- | :---------------------------------------- | :--------------------------------------------------- | +| pm:buttonclick | `e` | Fired when a Toolbar button is clicked | `btnName`, `button` | +| pm:actionclick | `e` | Fired when a Toolbar action is clicked | `text`, `action`, `btnName`, `button` | + + ### Feature Requests diff --git a/cypress/integration/toolbar.spec.js b/cypress/integration/toolbar.spec.js index 866b242f..1f8b066c 100644 --- a/cypress/integration/toolbar.spec.js +++ b/cypress/integration/toolbar.spec.js @@ -351,4 +351,36 @@ describe('Testing the Toolbar', () => { }); }); }); + it('Listen on pm:buttonclick and pm:actionclick', () => { + let eventFired = ""; + cy.window().then(({map}) => { + map.on('pm:buttonclick', ({btnName})=>{eventFired = btnName}); + map.on('pm:actionclick', ({text})=>{eventFired = text}); + }); + + cy.toolbarButton('polygon').click(); + + cy.window().then(() => { + expect(eventFired).to.equal('drawPolygon'); + }); + + cy.get('.button-container.active .action-cancel').click(); + + cy.window().then(() => { + expect(eventFired).to.equal('Cancel'); + }); + }); + it('Disable button', () => { + let eventFired = ""; + cy.window().then(({map}) => { + map.on('pm:buttonclick', ({btnName})=>{eventFired = btnName}); + map.pm.Toolbar.setButtonDisabled('drawPolygon',true); + }); + + cy.toolbarButton('polygon').click(); + + cy.window().then(() => { + expect(eventFired).to.not.equal('drawPolygon'); + }); + }); }); diff --git a/demo/customcontrols.js b/demo/customcontrols.js index ca11b8c1..077cf282 100644 --- a/demo/customcontrols.js +++ b/demo/customcontrols.js @@ -30,3 +30,9 @@ map.pm.Draw.RectangleCopy.setPathOptions({color :'green'}); map.pm.Toolbar.changeControlOrder(["RectangleCopy"]) +map.on('pm:actionclick',function (e) { + console.log(e) +}) +map.on('pm:buttonclick',function (e) { + console.log(e) +}) diff --git a/src/css/controls.css b/src/css/controls.css index a4f2d5a8..9185bc59 100644 --- a/src/css/controls.css +++ b/src/css/controls.css @@ -144,3 +144,10 @@ z-index: 801; } +.leaflet-buttons-control-button.pm-disabled{ + background-color: #f4f4f4 !important; +} + +.control-icon.pm-disabled{ + filter: opacity(0.6); +} diff --git a/src/js/Toolbar/L.Controls.js b/src/js/Toolbar/L.Controls.js index 8fddebed..b63c7341 100644 --- a/src/js/Toolbar/L.Controls.js +++ b/src/js/Toolbar/L.Controls.js @@ -136,8 +136,23 @@ const PMButton = L.Control.extend({ actionNode.innerHTML = action.text; - if (action.onClick) { - L.DomEvent.addListener(actionNode, 'click', action.onClick, this); + if(!button.disabled) { + if (action.onClick) { + const actionClick = () => { + let btnName = ""; + const {buttons} = this._map.pm.Toolbar; + for (const btn in buttons) { + if (buttons[btn]._button === button) { + btnName = btn; + break; + } + } + this._map.fire('pm:actionclick', {text: action.text, action, btnName, button}); + }; + + L.DomEvent.addListener(actionNode, 'click', actionClick, this); + L.DomEvent.addListener(actionNode, 'click', action.onClick, this); + } } L.DomEvent.disableClickPropagation(actionNode); }); @@ -158,14 +173,30 @@ const PMButton = L.Control.extend({ if (button.className) { L.DomUtil.addClass(image, button.className); } - // before the actual click, trigger a click on currently toggled buttons to - // untoggle them and their functionality - L.DomEvent.addListener(newButton, 'click', () => { - if (this._button.disableOtherButtons) { - this._map.pm.Toolbar.triggerClickOnToggledButtons(this); - } - }); - L.DomEvent.addListener(newButton, 'click', this._triggerClick, this); + if(!button.disabled) { + // before the actual click, trigger a click on currently toggled buttons to + // untoggle them and their functionality + L.DomEvent.addListener(newButton, 'click', () => { + if (this._button.disableOtherButtons) { + this._map.pm.Toolbar.triggerClickOnToggledButtons(this); + } + let btnName = ""; + const {buttons} = this._map.pm.Toolbar; + for (const btn in buttons) { + if (buttons[btn]._button === button) { + btnName = btn; + break; + } + } + this._map.fire('pm:buttonclick', {btnName, button}); + }); + L.DomEvent.addListener(newButton, 'click', this._triggerClick, this); + } + + if(button.disabled){ + L.DomUtil.addClass(newButton, 'pm-disabled'); + L.DomUtil.addClass(image, 'pm-disabled'); + } L.DomEvent.disableClickPropagation(newButton); return buttonContainer; diff --git a/src/js/Toolbar/L.PM.Toolbar.js b/src/js/Toolbar/L.PM.Toolbar.js index fbc6db17..9ac09e99 100644 --- a/src/js/Toolbar/L.PM.Toolbar.js +++ b/src/js/Toolbar/L.PM.Toolbar.js @@ -429,21 +429,7 @@ const Toolbar = L.Class.extend({ options = { name: options }; } - const shapeMapping = { - "Marker": "drawMarker", - "Circle": "drawCircle", - "Polygon": "drawPolygon", - "Rectangle": "drawRectangle", - "Polyline": "drawPolyline", - "Line": "drawPolyline", - "CircleMarker": "drawCircleMarker", - "Edit": "editMode", - "Drag": "dragMode", - "Cut": "cutPolygon", - "Removal": "removalMode" - }; - - const instance = shapeMapping[copyInstance] ? shapeMapping[copyInstance] : copyInstance; + const instance = this._btnNameMapping(copyInstance); if (!options.name) { throw new TypeError( @@ -511,6 +497,7 @@ const Toolbar = L.Class.extend({ cssToggle: options.toggle, position: this.options.position, actions: options.actions || [], + disabled: !!options.disabled, }; if (this.options[options.name] !== false) { @@ -523,19 +510,7 @@ const Toolbar = L.Class.extend({ }, changeControlOrder(order = []) { - const shapeMapping = { - "Marker": "drawMarker", - "Circle": "drawCircle", - "Polygon": "drawPolygon", - "Rectangle": "drawRectangle", - "Polyline": "drawPolyline", - "Line": "drawPolyline", - "CircleMarker": "drawCircleMarker", - "Edit": "editMode", - "Drag": "dragMode", - "Cut": "cutPolygon", - "Removal": "removalMode" - }; + const shapeMapping = this._shapeMapping(); const _order = []; order.forEach((shape) => { @@ -599,21 +574,7 @@ const Toolbar = L.Class.extend({ return order; }, changeActionsOfControl(name, actions) { - const shapeMapping = { - "Marker": "drawMarker", - "Circle": "drawCircle", - "Polygon": "drawPolygon", - "Rectangle": "drawRectangle", - "Polyline": "drawPolyline", - "Line": "drawPolyline", - "CircleMarker": "drawCircleMarker", - "Edit": "editMode", - "Drag": "dragMode", - "Cut": "cutPolygon", - "Removal": "removalMode" - }; - - const btnName = shapeMapping[name] ? shapeMapping[name] : name; + const btnName = this._btnNameMapping(name); if (!btnName) { throw new TypeError( @@ -633,6 +594,30 @@ const Toolbar = L.Class.extend({ } this.buttons[btnName]._button.actions = actions; this.changeControlOrder(); + }, + setButtonDisabled(name,state){ + const btnName = this._btnNameMapping(name); + this.buttons[btnName]._button.disabled = !!state; + this._showHideButtons(); + }, + _shapeMapping(){ + return { + "Marker": "drawMarker", + "Circle": "drawCircle", + "Polygon": "drawPolygon", + "Rectangle": "drawRectangle", + "Polyline": "drawPolyline", + "Line": "drawPolyline", + "CircleMarker": "drawCircleMarker", + "Edit": "editMode", + "Drag": "dragMode", + "Cut": "cutPolygon", + "Removal": "removalMode" + } + }, + _btnNameMapping(name){ + const shapeMapping = this._shapeMapping(); + return shapeMapping[name] ? shapeMapping[name] : name; } }); From b7551d6d0475c1583f0b4866c91ffd54c488b566 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Tue, 27 Oct 2020 19:38:23 +0100 Subject: [PATCH 14/30] Snapping now works for layers added during draw and fix rare snap bug on rectangle (#689) (patch) * Fix throttle and wrong event name * Fix small bug in rectangle snapping --- src/js/Edit/L.PM.Edit.Line.js | 1 - src/js/Edit/L.PM.Edit.Rectangle.js | 26 +++++++++++++++++--------- src/js/Mixins/Modes/Mode.Edit.js | 2 +- src/js/Mixins/Snapping.js | 14 ++++++++++---- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/js/Edit/L.PM.Edit.Line.js b/src/js/Edit/L.PM.Edit.Line.js index 366c5359..349f52a4 100644 --- a/src/js/Edit/L.PM.Edit.Line.js +++ b/src/js/Edit/L.PM.Edit.Line.js @@ -92,7 +92,6 @@ Edit.Line = Edit.extend({ // remove onRemove listener this._layer.off('remove', this._onLayerRemove, this); - if (!this.options.allowSelfIntersection) { this._layer.off( 'pm:vertexremoved', diff --git a/src/js/Edit/L.PM.Edit.Rectangle.js b/src/js/Edit/L.PM.Edit.Rectangle.js index 99fffcb7..9c9820a1 100644 --- a/src/js/Edit/L.PM.Edit.Rectangle.js +++ b/src/js/Edit/L.PM.Edit.Rectangle.js @@ -28,10 +28,14 @@ Edit.Rectangle = Edit.Polygon.extend({ // convenience alias, for better readability [this._cornerMarkers] = this._markers; - + }, + applyOptions() { if (this.options.snappable) { this._initSnappableMarkers(); + } else { + this._disableSnapping(); } + this._addMarkerEvents(); }, // creates initial markers for coordinates @@ -45,18 +49,22 @@ Edit.Rectangle = Edit.Polygon.extend({ marker._index = index; marker._pmTempLayer = true; - marker.on('dragstart', this._onMarkerDragStart, this); - marker.on('drag', this._onMarkerDrag, this); - marker.on('dragend', this._onMarkerDragEnd, this); - marker.on('pm:snap', this._adjustRectangleForMarkerSnap, this); - if (!this.options.preventMarkerRemoval) { - marker.on('contextmenu', this._removeMarker, this); - } this._markerGroup.addLayer(marker); return marker; }, - + // Add marker events after adding the snapping events to the markers, beacause of the execution order + _addMarkerEvents(){ + this._markers[0].forEach((marker)=>{ + marker.on('dragstart', this._onMarkerDragStart, this); + marker.on('drag', this._onMarkerDrag, this); + marker.on('dragend', this._onMarkerDragEnd, this); + marker.on('pm:snap', this._adjustRectangleForMarkerSnap, this); + if (!this.options.preventMarkerRemoval) { + marker.on('contextmenu', this._removeMarker, this); + } + }); + }, // Empty callback for 'contextmenu' binding set in L.PM.Edit.Line.js's _createMarker method (AKA, right-click on marker event) // (A Rectangle is designed to always remain a "true" rectangle -- if you want it editable, use Polygon Tool instead!!!) _removeMarker() { diff --git a/src/js/Mixins/Modes/Mode.Edit.js b/src/js/Mixins/Modes/Mode.Edit.js index f98de979..01114af5 100644 --- a/src/js/Mixins/Modes/Mode.Edit.js +++ b/src/js/Mixins/Modes/Mode.Edit.js @@ -45,7 +45,7 @@ const GlobalEditMode = { }); // cleanup layer off event - this.map.off('layeroff', this.throttledReInitEdit, this); + this.map.off('layeradd', this.throttledReInitEdit, this); // Set toolbar button to currect status this.Toolbar.toggleButton('editMode', status); diff --git a/src/js/Mixins/Snapping.js b/src/js/Mixins/Snapping.js index 7577761d..ad04d22a 100644 --- a/src/js/Mixins/Snapping.js +++ b/src/js/Mixins/Snapping.js @@ -36,6 +36,11 @@ const SnapMixin = { // meanwhile, new layers could've been added to the map delete this._snapList; + if(this.throttledList) { + this._map.off('layeradd', this.throttledList, this); + this.throttledList = undefined; + } + // remove map event this._map.off('pm:remove', this._handleSnapLayerRemoval, this); @@ -46,8 +51,9 @@ const SnapMixin = { } }, _handleSnapping(e) { - function throttledList() { - return L.Util.throttle(this._createSnapList, 100, this); + + if(!this.throttledList) { + this.throttledList = L.Util.throttle(this._createSnapList, 100, this); } // if snapping is disabled via holding ALT during drag, stop right here @@ -62,8 +68,8 @@ const SnapMixin = { this._createSnapList(); // re-create the snaplist again when a layer is added during draw - this._map.off('layeradd', throttledList, this); - this._map.on('layeradd', throttledList, this); + this._map.off('layeradd', this.throttledList, this); + this._map.on('layeradd', this.throttledList, this); } // if there are no layers to snap to, stop here From 15522f4e2d2e7ac82ed10f1dcbaf49aa3132b8a3 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Tue, 27 Oct 2020 19:45:05 +0100 Subject: [PATCH 15/30] Fix state of enabled while edit mode for markers (#690) --- cypress/integration/marker.spec.js | 15 +++++++++++++-- src/js/Edit/L.PM.Edit.ImageOverlay.js | 4 ++-- src/js/Edit/L.PM.Edit.Marker.js | 3 ++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/cypress/integration/marker.spec.js b/cypress/integration/marker.spec.js index 65cd0687..f557e288 100644 --- a/cypress/integration/marker.spec.js +++ b/cypress/integration/marker.spec.js @@ -95,8 +95,6 @@ describe('Draw Marker', () => { }); }); - - it('calls pm:drag-events on Marker drag', () => { let dragstart = false; @@ -141,4 +139,17 @@ describe('Draw Marker', () => { }); }); + it('enabled of Marker is true in edit-mode', () => { + cy.toolbarButton('marker').click(); + cy.get(mapSelector) + .click(150, 250); + cy.toolbarButton('edit').click(); + + cy.window().then(({ map }) => { + const marker = map.pm.getGeomanDrawLayers()[0]; + const enabled = marker.pm.enabled(); + expect(enabled).to.equal(true); + }); + }); + }); diff --git a/src/js/Edit/L.PM.Edit.ImageOverlay.js b/src/js/Edit/L.PM.Edit.ImageOverlay.js index d3acb10d..333ab9db 100644 --- a/src/js/Edit/L.PM.Edit.ImageOverlay.js +++ b/src/js/Edit/L.PM.Edit.ImageOverlay.js @@ -28,11 +28,11 @@ Edit.ImageOverlay = Edit.extend({ // we don't block enabling again because new options might be passed this.disable(); } - // change state - this._enabled = true; this.enableLayerDrag(); + // change state + this._enabled = true; // create markers for four corners of ImageOverlay this._otherSnapLayers = L.PM.Edit.Rectangle.prototype._findCorners.apply(this); diff --git a/src/js/Edit/L.PM.Edit.Marker.js b/src/js/Edit/L.PM.Edit.Marker.js index 17c779e6..0e2f9f7e 100644 --- a/src/js/Edit/L.PM.Edit.Marker.js +++ b/src/js/Edit/L.PM.Edit.Marker.js @@ -18,9 +18,10 @@ Edit.Marker = Edit.extend({ if (this.enabled()) { return; } + this.applyOptions(); + this._enabled = true; - this.applyOptions(); this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); }, disable() { From a483bbee8c4c9c797ab47385a6717b5dbdc3cc4c Mon Sep 17 00:00:00 2001 From: beig Date: Tue, 27 Oct 2020 19:54:22 +0100 Subject: [PATCH 16/30] use map's crs to calculate distance (#553) (patch) --- src/js/Draw/L.PM.Draw.Circle.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/js/Draw/L.PM.Draw.Circle.js b/src/js/Draw/L.PM.Draw.Circle.js index 4d283101..215af541 100644 --- a/src/js/Draw/L.PM.Draw.Circle.js +++ b/src/js/Draw/L.PM.Draw.Circle.js @@ -144,7 +144,13 @@ Draw.Circle = Draw.extend({ const A = this._centerMarker.getLatLng(); const B = this._hintMarker.getLatLng(); - const distance = A.distanceTo(B); + let distance; + + if (this._map.options.crs === L.CRS.Simple) { + distance = this._map.distance(A, B); + } else { + distance = A.distanceTo(B); + } this._layer.setRadius(distance); }, @@ -207,7 +213,15 @@ Draw.Circle = Draw.extend({ // calc the radius const center = this._centerMarker.getLatLng(); const latlng = this._hintMarker.getLatLng(); - const radius = center.distanceTo(latlng); + + let radius; + + if (this._map.options.crs === L.CRS.Simple) { + radius = this._map.distance(center, latlng); + } else { + radius = center.distanceTo(latlng); + } + const options = Object.assign({}, this.options.pathOptions, { radius }); // create the final circle layer From 633b20df15e2f8b06ce791347e51bc1a83f82861 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Fri, 6 Nov 2020 12:55:20 +0100 Subject: [PATCH 17/30] Layers can now directly added to layergroups (#628) (minor) --- README.md | 10 ++- cypress/integration/layergroup.spec.js | 97 +++++++++++++++++++++++++- src/js/Draw/L.PM.Draw.Circle.js | 2 +- src/js/Draw/L.PM.Draw.CircleMarker.js | 4 +- src/js/Draw/L.PM.Draw.Cut.js | 4 +- src/js/Draw/L.PM.Draw.Line.js | 2 +- src/js/Draw/L.PM.Draw.Marker.js | 2 +- src/js/Draw/L.PM.Draw.Polygon.js | 2 +- src/js/Draw/L.PM.Draw.Rectangle.js | 2 +- src/js/L.PM.Map.js | 6 ++ src/js/Mixins/Modes/Mode.Removal.js | 1 + 11 files changed, 122 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f13efdf8..70d13332 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,15 @@ See the available options in the table below. | finishOn | `null` | leaflet layer event to finish the drawn shape, like `'dblclick'`. [Here's a list](http://leafletjs.com/reference-1.2.0.html#interactive-layer-click). | | markerStyle | `{ draggable: true }` | [leaflet marker options](https://leafletjs.com/reference-1.4.0.html#marker-icon) (only for drawing markers). | | editable | `false` | makes a `CircleMarker` editable like a `Circle` | -| hideMiddleMarkers | `false` | hide the middle Markers in edit mode from Polyline and Polygon. | +| hideMiddleMarkers | `false` | hide the middle Markers in edit mode from Polyline and Polygon. | + + + +This options are only available for the global options: + +| Option | Default | Description | +| :-------------------- | :------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------- | +| layerGroup | `map` | add the created layers to a layergroup instead to the map. | You can listen to map events to hook into the drawing procedure like this: diff --git a/cypress/integration/layergroup.spec.js b/cypress/integration/layergroup.spec.js index 081ba2bb..6706430c 100644 --- a/cypress/integration/layergroup.spec.js +++ b/cypress/integration/layergroup.spec.js @@ -1,5 +1,5 @@ describe('Edit LayerGroup', () => { - // const mapSelector = '#map'; + const mapSelector = '#map'; it('correctly enables geojson featureCollection', () => { cy.drawShape('FeatureCollectionWithCircles'); @@ -53,5 +53,100 @@ describe('Edit LayerGroup', () => { expect(featureGroup.pm._layers).to.have.lengthOf(0); }); + }); + + it('adds the created layers to a layergroup', () => { + let fg; + let fg2; + + // Add layer to group + cy.window().then(({ L, map }) => { + fg = L.featureGroup().addTo(map); + fg2 = L.featureGroup().addTo(map); + map.on('click',(e)=>console.log(map.latLngToContainerPoint(e.latlng))); + + map.pm.setGlobalOptions({layerGroup: fg}); + + cy.toolbarButton('rectangle') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + cy.get(mapSelector) + .click(200, 200) + .click(400, 350); + + cy.hasLayers(5); + }); + + // check if layer is on group and will be removed from map, when group is removed + cy.window().then(({ map }) => { + fg.removeFrom(map); + cy.hasLayers(3); + + const count = fg.getLayers().length; + expect(count).to.equal(1); + }); + // delete layer from group and map + cy.window().then(({ map }) => { + fg.addTo(map); + + cy.toolbarButton('delete') + .click(); + + cy.get(mapSelector) + .click(280, 280); + }); + cy.window().then(() => { + const count = fg.getLayers().length; + expect(count).to.equal(0); + }); + + // add layer and then change group + cy.window().then(() => { + cy.toolbarButton('rectangle') + .click(); + + cy.get(mapSelector) + .click(200, 200) + .click(400, 350); + }); + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({layerGroup: fg2}); + cy.hasLayers(5); + + cy.toolbarButton('circle') + .click(); + cy.get(mapSelector) + .click(200, 200) + .click(250, 250); + }); + // delete layer from another (first) group + cy.window().then(() => { + cy.toolbarButton('delete') + .click(); + + cy.get(mapSelector) + .click(280, 280); + }); + cy.window().then(() => { + const count = fg.getLayers().length; + expect(count).to.equal(1); + const count2 = fg2.getLayers().length; + expect(count2).to.equal(1); + cy.hasLayers(5); + }); + // delete circle from second group + cy.window().then(() => { + cy.get(mapSelector) + .click(180, 180); + cy.toolbarButton('delete') + .click(); + }); + cy.window().then(() => { + const count2 = fg2.getLayers().length; + expect(count2).to.equal(0); + cy.hasLayers(4); + }); }) }); diff --git a/src/js/Draw/L.PM.Draw.Circle.js b/src/js/Draw/L.PM.Draw.Circle.js index 215af541..be4cec6a 100644 --- a/src/js/Draw/L.PM.Draw.Circle.js +++ b/src/js/Draw/L.PM.Draw.Circle.js @@ -225,7 +225,7 @@ Draw.Circle = Draw.extend({ const options = Object.assign({}, this.options.pathOptions, { radius }); // create the final circle layer - const circleLayer = L.circle(center, options).addTo(this._map); + const circleLayer = L.circle(center, options).addTo(this._map.pm._getContainingLayer()); this._setShapeForFinishLayer(circleLayer); this._addDrawnLayerProp(circleLayer); diff --git a/src/js/Draw/L.PM.Draw.CircleMarker.js b/src/js/Draw/L.PM.Draw.CircleMarker.js index 59a3ca9b..a21c0aa3 100644 --- a/src/js/Draw/L.PM.Draw.CircleMarker.js +++ b/src/js/Draw/L.PM.Draw.CircleMarker.js @@ -252,7 +252,7 @@ Draw.CircleMarker = Draw.Marker.extend({ this._setShapeForFinishLayer(marker); this._addDrawnLayerProp(marker); // add marker to the map - marker.addTo(this._map); + marker.addTo(this._map.pm._getContainingLayer()); if(marker.pm) { // enable editing for the marker @@ -282,7 +282,7 @@ Draw.CircleMarker = Draw.Marker.extend({ const options = Object.assign({}, this.options.pathOptions, { radius }); // create the final circle layer - const circleLayer = L.circleMarker(center, options).addTo(this._map); + const circleLayer = L.circleMarker(center, options).addTo(this._map.pm._getContainingLayer()); this._setShapeForFinishLayer(circleLayer); this._addDrawnLayerProp(circleLayer); if(circleLayer.pm) { diff --git a/src/js/Draw/L.PM.Draw.Cut.js b/src/js/Draw/L.PM.Draw.Cut.js index 53b4cafd..b393da5d 100644 --- a/src/js/Draw/L.PM.Draw.Cut.js +++ b/src/js/Draw/L.PM.Draw.Cut.js @@ -87,7 +87,7 @@ Draw.Cut = Draw.Polygon.extend({ if (resultLayer.getLayers().length === 1) { [resultLayer] = resultLayer.getLayers(); // prevent that a unnecessary layergroup is created } - const resultingLayer = resultLayer.addTo(this._map); + const resultingLayer = resultLayer.addTo(this._map.pm._getContainingLayer()); // give the new layer the original options resultingLayer.pm.enable(this.options); @@ -99,7 +99,9 @@ Draw.Cut = Draw.Polygon.extend({ // remove old layer and cutting layer l.remove(); + l.removeFrom(this._map.pm._getContainingLayer()); layer.remove(); + layer.removeFrom(this._map.pm._getContainingLayer()); // Remove it only if it is a layergroup. It can be only not a layergroup if a layer exists if (resultingLayer.getLayers && resultingLayer.getLayers().length === 0) { diff --git a/src/js/Draw/L.PM.Draw.Line.js b/src/js/Draw/L.PM.Draw.Line.js index 92fa48a5..3ea54fc4 100644 --- a/src/js/Draw/L.PM.Draw.Line.js +++ b/src/js/Draw/L.PM.Draw.Line.js @@ -313,7 +313,7 @@ Draw.Line = Draw.extend({ // create the leaflet shape and add it to the map const polylineLayer = L.polyline(coords, this.options.pathOptions).addTo( - this._map + this._map.pm._getContainingLayer() ); this._setShapeForFinishLayer(polylineLayer); this._addDrawnLayerProp(polylineLayer); diff --git a/src/js/Draw/L.PM.Draw.Marker.js b/src/js/Draw/L.PM.Draw.Marker.js index 4ccd5f13..008bfe20 100644 --- a/src/js/Draw/L.PM.Draw.Marker.js +++ b/src/js/Draw/L.PM.Draw.Marker.js @@ -146,7 +146,7 @@ Draw.Marker = Draw.extend({ marker.options.draggable = false; } // add marker to the map - marker.addTo(this._map); + marker.addTo(this._map.pm._getContainingLayer()); if(marker.pm) { // enable editing for the marker diff --git a/src/js/Draw/L.PM.Draw.Polygon.js b/src/js/Draw/L.PM.Draw.Polygon.js index e601d227..100e7f22 100644 --- a/src/js/Draw/L.PM.Draw.Polygon.js +++ b/src/js/Draw/L.PM.Draw.Polygon.js @@ -72,7 +72,7 @@ Draw.Polygon = Draw.Line.extend({ } const polygonLayer = L.polygon(coords, this.options.pathOptions).addTo( - this._map + this._map.pm._getContainingLayer() ); this._setShapeForFinishLayer(polygonLayer); this._addDrawnLayerProp(polygonLayer); diff --git a/src/js/Draw/L.PM.Draw.Rectangle.js b/src/js/Draw/L.PM.Draw.Rectangle.js index 7076278a..9c28e95e 100644 --- a/src/js/Draw/L.PM.Draw.Rectangle.js +++ b/src/js/Draw/L.PM.Draw.Rectangle.js @@ -250,7 +250,7 @@ Draw.Rectangle = Draw.extend({ // create the final rectangle layer, based on opposite corners A & B const rectangleLayer = L.rectangle([A, B], this.options.pathOptions).addTo( - this._map + this._map.pm._getContainingLayer() ); this._setShapeForFinishLayer(rectangleLayer); this._addDrawnLayerProp(rectangleLayer); diff --git a/src/js/L.PM.Map.js b/src/js/L.PM.Map.js index 359127a9..10f7a8a2 100644 --- a/src/js/L.PM.Map.js +++ b/src/js/L.PM.Map.js @@ -20,6 +20,7 @@ const Map = L.Class.extend({ this.globalOptions = { snappable: true, + layerGroup: undefined, }; }, setLang(lang = 'en', t, fallback = 'en') { @@ -150,6 +151,11 @@ const Map = L.Class.extend({ }); return group; }, + // returns the map instance by default or a layergroup is set through global options + _getContainingLayer(){ + return this.globalOptions.layerGroup && this.globalOptions.layerGroup instanceof L.LayerGroup ? this.globalOptions.layerGroup : this.map; + } + }); export default Map; diff --git a/src/js/Mixins/Modes/Mode.Removal.js b/src/js/Mixins/Modes/Mode.Removal.js index 9dfe84fa..d3554a69 100644 --- a/src/js/Mixins/Modes/Mode.Removal.js +++ b/src/js/Mixins/Modes/Mode.Removal.js @@ -81,6 +81,7 @@ const GlobalRemovalMode = { !layer._pmTempLayer && (!layer.pm || !layer.pm.dragging()); if (removeable) { + layer.removeFrom(this.map.pm._getContainingLayer()); layer.remove(); if(layer instanceof L.LayerGroup){ layer.fire('pm:remove', { layer, shape: undefined }); From 4ca6913660a9c4c5d6222f7cff76c10eb2ebf48b Mon Sep 17 00:00:00 2001 From: Falke Design Date: Fri, 6 Nov 2020 12:56:56 +0100 Subject: [PATCH 18/30] Remove layers from Snaplist when pmIgnore is set (#682) (patch) --- src/js/Mixins/Snapping.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/js/Mixins/Snapping.js b/src/js/Mixins/Snapping.js index ad04d22a..884a4aee 100644 --- a/src/js/Mixins/Snapping.js +++ b/src/js/Mixins/Snapping.js @@ -172,9 +172,12 @@ const SnapMixin = { layer instanceof L.Marker || layer instanceof L.CircleMarker || layer instanceof L.ImageOverlay) && - layer.options.snapIgnore !== true + layer.options.snapIgnore !== true && + ( + (!L.PM.optIn && !layer.options.pmIgnore) || // if optIn is not set / true and pmIgnore is not set / true (default) + (L.PM.optIn && layer.options.pmIgnore === false) // if optIn is true and pmIgnore is false + ) ) { - // adds a hidden polygon which matches the border of the circle if ((layer instanceof L.Circle || layer instanceof L.CircleMarker) && layer.pm && layer.pm._hiddenPolyCircle) { layers.push(layer.pm._hiddenPolyCircle); From ff6535a7b3b100d980dfc4cd1866397bfc09b569 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Fri, 6 Nov 2020 16:01:44 +0100 Subject: [PATCH 19/30] Set the action-container always to direction ltr (#698) --- src/css/controls.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/css/controls.css b/src/css/controls.css index 9185bc59..af0ef3da 100644 --- a/src/css/controls.css +++ b/src/css/controls.css @@ -100,6 +100,7 @@ left: 100%; display: none; white-space: nowrap; + direction: ltr; } .leaflet-right From 12173cf67c30fa992a1b1caef64c6c16bc6b2ed2 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Fri, 6 Nov 2020 16:25:11 +0100 Subject: [PATCH 20/30] Enable layers which are added during Global Edit Mode (#696). Fixes #561 (patch) --- cypress/integration/globalmodes.spec.js | 16 +++++++++++ src/js/Mixins/Modes/Mode.Edit.js | 37 +++++++++++++++---------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/cypress/integration/globalmodes.spec.js b/cypress/integration/globalmodes.spec.js index d6e26ca2..e124cd3c 100644 --- a/cypress/integration/globalmodes.spec.js +++ b/cypress/integration/globalmodes.spec.js @@ -311,4 +311,20 @@ describe('Modes', () => { ).to.equal(0); }); }); + it('re-enable layers that added while in globaleditmode', () => { + + cy.window().then(({ map, L }) => { + map.pm.enableGlobalEditMode(); + + const json = JSON.parse("{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-74.058559,40.718564],[-74.058559,40.726045],[-74.03959,40.726045],[-74.03959,40.718564],[-74.058559,40.718564]]]}}"); + const json2 = JSON.parse("{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-74.035277,40.703719],[-74.035277,40.712633],[-74.017596,40.712633],[-74.017596,40.703719],[-74.035277,40.703719]]]}}"); + const p2 = L.geoJson(json).addTo(map); + L.geoJson(json2).addTo(map); + + map.fitBounds(p2.getBounds()); + map.setZoom(13); + }); + cy.hasVertexMarkers(8); + + }); }); diff --git a/src/js/Mixins/Modes/Mode.Edit.js b/src/js/Mixins/Modes/Mode.Edit.js index 01114af5..2b70857c 100644 --- a/src/js/Mixins/Modes/Mode.Edit.js +++ b/src/js/Mixins/Modes/Mode.Edit.js @@ -28,6 +28,9 @@ const GlobalEditMode = { this.throttledReInitEdit = L.Util.throttle(this.handleLayerAdditionInGlobalEditMode, 100, this) } + // save the added layers into the _addedLayers array, to read it later out + this._addedLayers = []; + this.map.on('layeradd', this._layerAdded, this); // handle layers that are added while in removal mode this.map.on('layeradd', this.throttledReInitEdit, this); @@ -74,20 +77,26 @@ const GlobalEditMode = { this.enableGlobalEditMode(options); } }, - handleLayerAdditionInGlobalEditMode({ layer }) { - // when global edit mode is enabled and a layer is added to the map, - // enable edit for that layer if it's relevant - - // do nothing if layer is not handled by leaflet so it doesn't fire unnecessarily - const isRelevant = !!layer.pm && !layer._pmTempLayer; - if (!isRelevant) { - return; - } - - if (this.globalEditModeEnabled()) { - // TODO: this._globalSnappingEnabled is a Pro Feature. Remove this option from OSS? - layer.pm.enable({ ...this.globalOptions, snappable: this._globalSnappingEnabled }); - } + handleLayerAdditionInGlobalEditMode() { + const layers = this._addedLayers; + this._addedLayers = []; + layers.forEach((layer)=> { + // when global edit mode is enabled and a layer is added to the map, + // enable edit for that layer if it's relevant + + // do nothing if layer is not handled by leaflet so it doesn't fire unnecessarily + const isRelevant = !!layer.pm && !layer._pmTempLayer; + if (!isRelevant) { + return; + } + + if (this.globalEditModeEnabled()) { + layer.pm.enable({...this.globalOptions}); + } + }); + }, + _layerAdded({layer}){ + this._addedLayers.push(layer); }, _fireEditModeEvent(enabled) { this.map.fire('pm:globaleditmodetoggled', { From 41cf8bdd6d211a01f898d2ef790805a6baa39699 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Sat, 7 Nov 2020 20:13:52 +0100 Subject: [PATCH 21/30] Added continueDrawing and markerEditable (#697). Fix #418 (minor) * Added continueDrawing and markerEditable also the geoman options now passed to a new layer * Update Readme and tests --- README.md | 2 + cypress/integration/circle.spec.js | 24 ++++++++++ cypress/integration/circlemarker.spec.js | 59 +++++++++++++++++++++++- cypress/integration/line.spec.js | 24 ++++++++++ cypress/integration/marker.spec.js | 54 ++++++++++++++++++++++ cypress/integration/polygon.spec.js | 26 ++++++++++- cypress/integration/rectangle.spec.js | 21 ++++++++- src/js/Draw/L.PM.Draw.Circle.js | 12 +++-- src/js/Draw/L.PM.Draw.CircleMarker.js | 30 ++++++++---- src/js/Draw/L.PM.Draw.Cut.js | 9 ++-- src/js/Draw/L.PM.Draw.Line.js | 12 +++-- src/js/Draw/L.PM.Draw.Marker.js | 29 ++++++++---- src/js/Draw/L.PM.Draw.Polygon.js | 12 +++-- src/js/Draw/L.PM.Draw.Rectangle.js | 12 +++-- src/js/Draw/L.PM.Draw.js | 19 ++++++-- src/js/Edit/L.PM.Edit.CircleMarker.js | 7 ++- 16 files changed, 301 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 70d13332..fdd3af1d 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,8 @@ See the available options in the table below. | markerStyle | `{ draggable: true }` | [leaflet marker options](https://leafletjs.com/reference-1.4.0.html#marker-icon) (only for drawing markers). | | editable | `false` | makes a `CircleMarker` editable like a `Circle` | | hideMiddleMarkers | `false` | hide the middle Markers in edit mode from Polyline and Polygon. | +| markerEditable | `true` | Markers and CircleMarkers are editable during the draw-session (you can drag them around immediately after drawing them) | +| continueDrawing | `false` / `true` | Draw-Mode stays enabled after finishing a layer to immediately draw the next layer. Defaults to `true` for Markers and CircleMarkers and `false` for all other layers. | | diff --git a/cypress/integration/circle.spec.js b/cypress/integration/circle.spec.js index b5548fef..ed7665c6 100644 --- a/cypress/integration/circle.spec.js +++ b/cypress/integration/circle.spec.js @@ -84,4 +84,28 @@ describe('Draw Circle', () => { }); }); }); + + it('enable continueDrawing', () => { + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({continueDrawing: true}); + }); + + cy.toolbarButton('circle') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + // draw first circle + cy.get(mapSelector) + .click(200, 200) + .click(250, 250); + + // draw with continueDrawing: ture the second circle + cy.get(mapSelector) + .click(300, 200) + .click(350, 250); + + cy.toolbarButton('edit').click(); + cy.hasVertexMarkers(4); + }); }); diff --git a/cypress/integration/circlemarker.spec.js b/cypress/integration/circlemarker.spec.js index 3e86b4d7..d9cda446 100644 --- a/cypress/integration/circlemarker.spec.js +++ b/cypress/integration/circlemarker.spec.js @@ -119,7 +119,7 @@ describe('Draw Circle Marker', () => { it('draw a CircleMarker like a Circle', () => { cy.window().then(({ map}) => { - map.pm.setGlobalOptions({editable: true}); + map.pm.setGlobalOptions({editable: true, continueDrawing: false}); }); cy.toolbarButton('circle-marker') @@ -162,4 +162,61 @@ describe('Draw Circle Marker', () => { cy.hasVertexMarkers(4); }); + + it('disable continueDrawing', () => { + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({continueDrawing: false}); + }); + + cy.toolbarButton('circle-marker').click(); + cy.get(mapSelector) + .click(191,216); + + cy.get(mapSelector) + .click(350, 350); + + cy.toolbarButton('circle-marker') + .closest('.button-container') + .should('have.not.class', 'active'); + + cy.toolbarButton('edit').click(); + cy.hasLayers(3); + }); + + it('disable markerEditable', () => { + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({markerEditable: false}); + }); + + cy.toolbarButton('circle-marker').click(); + cy.get(mapSelector) + .click(191,216); + + cy.window().then(({ map }) => { + const marker = map.pm.getGeomanDrawLayers()[0]; + const enabled = marker.pm.enabled(); + expect(enabled).to.equal(false); + }); + }); + + it('enable markerEditable but disable MarkerRemoval', () => { + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({markerEditable: true, preventMarkerRemoval: true}); + }); + + cy.toolbarButton('circle-marker').click(); + cy.get(mapSelector) + .click(191,216); + + cy.window().then(({ map }) => { + const marker = map.pm.getGeomanDrawLayers()[0]; + const enabled = marker.pm.enabled(); + expect(enabled).to.equal(true); + }); + + cy.get(mapSelector) + .rightclick(191,214); + + cy.hasLayers(5); + }); }); diff --git a/cypress/integration/line.spec.js b/cypress/integration/line.spec.js index 571ea327..21884f44 100644 --- a/cypress/integration/line.spec.js +++ b/cypress/integration/line.spec.js @@ -184,4 +184,28 @@ describe('Draw & Edit Line', () => { cy.hasMiddleMarkers(0); }); + it('enable continueDrawing', () => { + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({continueDrawing: true}); + }); + + cy.toolbarButton('polyline').click(); + + // draw a line + cy.get(mapSelector) + .click(150, 250) + .click(160, 50) + .click(250, 50) + .click(250, 50); + + cy.get(mapSelector) + .click(200, 200) + .click(250, 250) + .click(250, 250); + + + cy.toolbarButton('edit').click(); + cy.hasVertexMarkers(5); + }); + }); diff --git a/cypress/integration/marker.spec.js b/cypress/integration/marker.spec.js index f557e288..3b066a49 100644 --- a/cypress/integration/marker.spec.js +++ b/cypress/integration/marker.spec.js @@ -152,4 +152,58 @@ describe('Draw Marker', () => { }); }); + it('disable continueDrawing', () => { + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({continueDrawing: false}); + }); + + cy.toolbarButton('marker').click(); + cy.get(mapSelector) + .click(191,216); + + cy.get(mapSelector) + .click(350, 350); + + + cy.toolbarButton('edit').click(); + cy.hasLayers(2); + }); + + it('disable markerEditable', () => { + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({markerEditable: false}); + }); + + cy.toolbarButton('marker').click(); + cy.get(mapSelector) + .click(191,216); + + cy.window().then(({ map }) => { + const marker = map.pm.getGeomanDrawLayers()[0]; + const enabled = marker.pm.enabled(); + expect(enabled).to.equal(false); + }); + }); + + it('enable markerEditable but disable MarkerRemoval', () => { + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({markerEditable: true, preventMarkerRemoval: true}); + }); + + cy.toolbarButton('marker').click(); + cy.get(mapSelector) + .click(191,216); + + cy.window().then(({ map }) => { + const marker = map.pm.getGeomanDrawLayers()[0]; + const enabled = marker.pm.enabled(); + expect(enabled).to.equal(true); + }); + + cy.get(mapSelector) + .rightclick(191,214); + + cy.hasLayers(4); + }); + }); diff --git a/cypress/integration/polygon.spec.js b/cypress/integration/polygon.spec.js index 63247c34..989164fc 100644 --- a/cypress/integration/polygon.spec.js +++ b/cypress/integration/polygon.spec.js @@ -737,5 +737,29 @@ describe('Draw & Edit Poly', () => { .click(); cy.hasVertexMarkers(5); - }) + }); + + it('enable continueDrawing', () => { + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({continueDrawing: true}); + }); + + cy.toolbarButton('polygon').click(); + + // draw a line + cy.get(mapSelector) + .click(150, 250) + .click(160, 50) + .click(250, 50) + .click(150, 250); + + cy.get(mapSelector) + .click(230, 230) + .click(250, 250) + .click(250, 300) + .click(230, 230); + + cy.toolbarButton('edit').click(); + cy.hasVertexMarkers(6); + }); }); diff --git a/cypress/integration/rectangle.spec.js b/cypress/integration/rectangle.spec.js index 2a7f0e20..91ea3103 100644 --- a/cypress/integration/rectangle.spec.js +++ b/cypress/integration/rectangle.spec.js @@ -158,5 +158,24 @@ describe('Draw Rectangle', () => { const coords = geojson.geometry.coordinates; expect(coords.length).to.equal(1); }) - }) + }); + + it('enable continueDrawing', () => { + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({continueDrawing: true}); + }); + + cy.toolbarButton('rectangle').click(); + cy.get(mapSelector) + .click(191,216) + .click(608,323); + + cy.get(mapSelector) + .click(230, 230) + .click(350, 350); + + + cy.toolbarButton('edit').click(); + cy.hasVertexMarkers(8); + }); }); diff --git a/src/js/Draw/L.PM.Draw.Circle.js b/src/js/Draw/L.PM.Draw.Circle.js index be4cec6a..43d60bc6 100644 --- a/src/js/Draw/L.PM.Draw.Circle.js +++ b/src/js/Draw/L.PM.Draw.Circle.js @@ -226,21 +226,23 @@ Draw.Circle = Draw.extend({ // create the final circle layer const circleLayer = L.circle(center, options).addTo(this._map.pm._getContainingLayer()); - this._setShapeForFinishLayer(circleLayer); - this._addDrawnLayerProp(circleLayer); + this._finishLayer(circleLayer); if(circleLayer.pm) { // create polygon around the circle border circleLayer.pm._updateHiddenPolyCircle(); } - // disable drawing - this.disable(); - // fire the pm:create event and pass shape and layer this._map.fire('pm:create', { shape: this._shape, layer: circleLayer, }); + + // disable drawing + this.disable(); + if(this.options.continueDrawing){ + this.enable(); + } }, }); diff --git a/src/js/Draw/L.PM.Draw.CircleMarker.js b/src/js/Draw/L.PM.Draw.CircleMarker.js index a21c0aa3..1c5b06ef 100644 --- a/src/js/Draw/L.PM.Draw.CircleMarker.js +++ b/src/js/Draw/L.PM.Draw.CircleMarker.js @@ -7,6 +7,8 @@ Draw.CircleMarker = Draw.Marker.extend({ this._map = map; this._shape = 'CircleMarker'; this.toolbarButtonName = 'drawCircleMarker'; + // with _layerIsDragging we check if a circlemarker is currently dragged and disable marker creation + this._layerIsDragging = false; }, enable(options) { // TODO: Think about if these options could be passed globally for all @@ -102,7 +104,7 @@ Draw.CircleMarker = Draw.Marker.extend({ // sync hint marker with mouse cursor this._map.on('mousemove', this._syncHintMarker, this); - if (!this.options.editable) { + if (!this.options.editable && this.options.markerEditable) { // enable edit mode for existing markers this._map.eachLayer(layer => { if (this.isRelevantMarker(layer)) { @@ -111,6 +113,8 @@ Draw.CircleMarker = Draw.Marker.extend({ }); } + this._layer.bringToBack(); + // fire drawstart event this._map.fire('pm:drawstart', { shape: this._shape, @@ -234,7 +238,8 @@ Draw.CircleMarker = Draw.Marker.extend({ return layer instanceof L.CircleMarker && !(layer instanceof L.Circle) && layer.pm && !layer._pmTempLayer; }, _createMarker(e) { - if (!e.latlng) { + // with _layerIsDragging we check if a circlemarker is currently dragged + if (!e.latlng || this._layerIsDragging) { return; } @@ -249,12 +254,11 @@ Draw.CircleMarker = Draw.Marker.extend({ // create marker const marker = L.circleMarker(latlng, this.options.pathOptions); - this._setShapeForFinishLayer(marker); - this._addDrawnLayerProp(marker); + this._finishLayer(marker); // add marker to the map marker.addTo(this._map.pm._getContainingLayer()); - if(marker.pm) { + if(marker.pm && this.options.markerEditable) { // enable editing for the marker marker.pm.enable(); } @@ -267,6 +271,10 @@ Draw.CircleMarker = Draw.Marker.extend({ }); this._cleanupSnapping(); + + if(!this.options.continueDrawing){ + this.disable(); + } }, _finishShape(e) { // assign the coordinate of the click to the hintMarker, that's necessary for @@ -283,20 +291,22 @@ Draw.CircleMarker = Draw.Marker.extend({ // create the final circle layer const circleLayer = L.circleMarker(center, options).addTo(this._map.pm._getContainingLayer()); - this._setShapeForFinishLayer(circleLayer); - this._addDrawnLayerProp(circleLayer); + this._finishLayer(circleLayer); if(circleLayer.pm) { // create polygon around the circle border circleLayer.pm._updateHiddenPolyCircle(); } - // disable drawing - this.disable(); - // fire the pm:create event and pass shape and layer this._map.fire('pm:create', { shape: this._shape, layer: circleLayer, }); + + // disable drawing + this.disable(); + if(this.options.continueDrawing){ + this.enable(); + } }, }); diff --git a/src/js/Draw/L.PM.Draw.Cut.js b/src/js/Draw/L.PM.Draw.Cut.js index b393da5d..5cf8e150 100644 --- a/src/js/Draw/L.PM.Draw.Cut.js +++ b/src/js/Draw/L.PM.Draw.Cut.js @@ -23,9 +23,6 @@ Draw.Cut = Draw.Polygon.extend({ const polygonLayer = L.polygon(coords, this.options.pathOptions); this._cut(polygonLayer); - // disable drawing - this.disable(); - // clean up snapping states this._cleanupSnapping(); @@ -52,6 +49,12 @@ Draw.Cut = Draw.Polygon.extend({ originalLayer.fire('pm:edit', { layer: originalLayer, shape: originalLayer.pm.getShape()}); }); this._editedLayers = []; + + // disable drawing + this.disable(); + if(this.options.continueDrawing){ + this.enable(); + } }, _cut(layer) { const all = this._map._layers; diff --git a/src/js/Draw/L.PM.Draw.Line.js b/src/js/Draw/L.PM.Draw.Line.js index 3ea54fc4..b7f60f89 100644 --- a/src/js/Draw/L.PM.Draw.Line.js +++ b/src/js/Draw/L.PM.Draw.Line.js @@ -315,11 +315,7 @@ Draw.Line = Draw.extend({ const polylineLayer = L.polyline(coords, this.options.pathOptions).addTo( this._map.pm._getContainingLayer() ); - this._setShapeForFinishLayer(polylineLayer); - this._addDrawnLayerProp(polylineLayer); - - // disable drawing - this.disable(); + this._finishLayer(polylineLayer); // fire the pm:create event and pass shape and layer this._map.fire('pm:create', { @@ -330,6 +326,12 @@ Draw.Line = Draw.extend({ if (this.options.snappable) { this._cleanupSnapping(); } + + // disable drawing + this.disable(); + if(this.options.continueDrawing){ + this.enable(); + } }, _createMarker(latlng, first) { // create the new marker diff --git a/src/js/Draw/L.PM.Draw.Marker.js b/src/js/Draw/L.PM.Draw.Marker.js index 008bfe20..131656c1 100644 --- a/src/js/Draw/L.PM.Draw.Marker.js +++ b/src/js/Draw/L.PM.Draw.Marker.js @@ -48,11 +48,13 @@ Draw.Marker = Draw.extend({ // enable edit mode for existing markers - this._map.eachLayer(layer => { - if (this.isRelevantMarker(layer)) { - layer.pm.enable(); - } - }); + if(this.options.markerEditable) { + this._map.eachLayer(layer => { + if (this.isRelevantMarker(layer)) { + layer.pm.enable(); + } + }); + } // fire drawstart event this._map.fire('pm:drawstart', { @@ -139,18 +141,23 @@ Draw.Marker = Draw.extend({ // create marker const marker = new L.Marker(latlng, this.options.markerStyle); - this._setShapeForFinishLayer(marker); - this._addDrawnLayerProp(marker); + this._finishLayer(marker); - if(!marker.pm) { + if(!marker.pm){ marker.options.draggable = false; } + // add marker to the map marker.addTo(this._map.pm._getContainingLayer()); - if(marker.pm) { + + if(marker.pm && this.options.markerEditable) { // enable editing for the marker marker.pm.enable(); + }else{ + if(marker.dragging) { + marker.dragging.disable(); + } } // fire the pm:create event and pass shape and marker @@ -161,5 +168,9 @@ Draw.Marker = Draw.extend({ }); this._cleanupSnapping(); + + if(!this.options.continueDrawing){ + this.disable(); + } }, }); diff --git a/src/js/Draw/L.PM.Draw.Polygon.js b/src/js/Draw/L.PM.Draw.Polygon.js index 100e7f22..d76c2b2f 100644 --- a/src/js/Draw/L.PM.Draw.Polygon.js +++ b/src/js/Draw/L.PM.Draw.Polygon.js @@ -74,11 +74,7 @@ Draw.Polygon = Draw.Line.extend({ const polygonLayer = L.polygon(coords, this.options.pathOptions).addTo( this._map.pm._getContainingLayer() ); - this._setShapeForFinishLayer(polygonLayer); - this._addDrawnLayerProp(polygonLayer); - - // disable drawing - this.disable(); + this._finishLayer(polygonLayer); // fire the pm:create event and pass shape and layer this._map.fire('pm:create', { @@ -92,5 +88,11 @@ Draw.Polygon = Draw.Line.extend({ // remove the first vertex from "other snapping layers" this._otherSnapLayers.splice(this._tempSnapLayerIndex, 1); delete this._tempSnapLayerIndex; + + // disable drawing + this.disable(); + if(this.options.continueDrawing){ + this.enable(); + } }, }); diff --git a/src/js/Draw/L.PM.Draw.Rectangle.js b/src/js/Draw/L.PM.Draw.Rectangle.js index 9c28e95e..ba1f2912 100644 --- a/src/js/Draw/L.PM.Draw.Rectangle.js +++ b/src/js/Draw/L.PM.Draw.Rectangle.js @@ -252,16 +252,18 @@ Draw.Rectangle = Draw.extend({ const rectangleLayer = L.rectangle([A, B], this.options.pathOptions).addTo( this._map.pm._getContainingLayer() ); - this._setShapeForFinishLayer(rectangleLayer); - this._addDrawnLayerProp(rectangleLayer); - - // disable drawing - this.disable(); + this._finishLayer(rectangleLayer); // fire the pm:create event and pass shape and layer this._map.fire('pm:create', { shape: this._shape, layer: rectangleLayer, }); + + // disable drawing + this.disable(); + if(this.options.continueDrawing){ + this.enable(); + } }, }); diff --git a/src/js/Draw/L.PM.Draw.js b/src/js/Draw/L.PM.Draw.js index 1cab0def..124c3a3d 100644 --- a/src/js/Draw/L.PM.Draw.js +++ b/src/js/Draw/L.PM.Draw.js @@ -18,6 +18,8 @@ const Draw = L.Class.extend({ markerStyle: { draggable: true, }, + markerEditable: true, + continueDrawing: false, }, setOptions(options) { L.Util.setOptions(this, options); @@ -33,6 +35,9 @@ const Draw = L.Class.extend({ this.shapes.forEach(shape => { this[shape] = new L.PM.Draw[shape](this._map); }); + + this.Marker.setOptions({continueDrawing: true}); + this.CircleMarker.setOptions({continueDrawing: true}); }, setPathOptions(options) { this.options.pathOptions = options; @@ -142,14 +147,18 @@ const Draw = L.Class.extend({ } return this[name] ? this[name]._shape : name; }, - _addDrawnLayerProp(layer){ - layer._drawnByGeoman = true; - }, - _setShapeForFinishLayer(layer){ + _finishLayer(layer){ + // add the pm options from drawing to the new layer (edit) + layer.pm.setOptions(this.options); + // set the shape (can be a custom shape) if(layer.pm) { layer.pm._shape = this._shape; } - } + this._addDrawnLayerProp(layer); + }, + _addDrawnLayerProp(layer){ + layer._drawnByGeoman = true; + }, }); export default Draw; diff --git a/src/js/Edit/L.PM.Edit.CircleMarker.js b/src/js/Edit/L.PM.Edit.CircleMarker.js index 91cf3b38..38ddd805 100644 --- a/src/js/Edit/L.PM.Edit.CircleMarker.js +++ b/src/js/Edit/L.PM.Edit.CircleMarker.js @@ -19,7 +19,7 @@ Edit.CircleMarker = Edit.extend({ return; } - if (!this.enabled()) { + if (this.enabled()) { // if it was already enabled, disable first // we don't block enabling again because new options might be passed this.disable(); @@ -29,6 +29,7 @@ Edit.CircleMarker = Edit.extend({ // change state this._enabled = true; + this._layer.on('pm:dragstart', this._onDragStart, this); this._layer.on('pm:dragend', this._onMarkerDragEnd, this); // create polygon around the circle border @@ -244,6 +245,9 @@ Edit.CircleMarker = Edit.extend({ this._layer.fire('pm:remove', { layer: this._layer, shape: this.getShape() }); this._map.fire('pm:remove', { layer: this._layer, shape: this.getShape() }); }, + _onDragStart(){ + this._map.pm.Draw.CircleMarker._layerIsDragging = true; + }, _onMarkerDragStart(e) { this._layer.fire('pm:markerdragstart', { markerEvent: e, @@ -261,6 +265,7 @@ Edit.CircleMarker = Edit.extend({ }); }, _onMarkerDragEnd(e) { + this._map.pm.Draw.CircleMarker._layerIsDragging = false; this._layer.fire('pm:markerdragend', { layer: this._layer, markerEvent: e, From 6d4af6cc35b507aecb529eff350d0a4cee21b7a3 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Sat, 7 Nov 2020 20:26:09 +0100 Subject: [PATCH 22/30] Add cut for polylines & fix a bug in cutting (#691). Fixes #555 (minor) --- package.json | 4 +- src/js/Draw/L.PM.Draw.Cut.js | 78 ++++++++++++++++++++++++++++++++--- src/js/Draw/L.PM.Draw.Line.js | 6 +++ src/js/L.PM.Utils.js | 47 +++++++++++++++++++++ src/js/Mixins/Snapping.js | 1 + 5 files changed, 130 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index ccd2bbff..354660ff 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,9 @@ "@turf/kinks": "6.x", "@turf/line-intersect": "^6.0.2", "polygon-clipping": "^0.15.1", - "lodash": "^4.17.15" + "lodash": "^4.17.15", + "@turf/boolean-contains": "^6.0.1", + "@turf/line-split": "^5.1.5" }, "devDependencies": { "@babel/core": "^7.9.6", diff --git a/src/js/Draw/L.PM.Draw.Cut.js b/src/js/Draw/L.PM.Draw.Cut.js index 5cf8e150..509e7a55 100644 --- a/src/js/Draw/L.PM.Draw.Cut.js +++ b/src/js/Draw/L.PM.Draw.Cut.js @@ -1,3 +1,8 @@ +import lineIntersect from "@turf/line-intersect"; +import lineSplit from "@turf/line-split"; +import booleanContains from "@turf/boolean-contains"; +import get from "lodash/get"; +import Utils from "../L.PM.Utils"; import Draw from './L.PM.Draw'; import {difference, intersect} from "../helpers/turfHelper"; @@ -21,7 +26,9 @@ Draw.Cut = Draw.Polygon.extend({ const coords = this._layer.getLatLngs(); const polygonLayer = L.polygon(coords, this.options.pathOptions); - this._cut(polygonLayer); + // readout information about the latlngs like snapping points + polygonLayer._latlngInfos = this._layer._latlngInfo; + this.cut(polygonLayer); // clean up snapping states this._cleanupSnapping(); @@ -56,8 +63,10 @@ Draw.Cut = Draw.Polygon.extend({ this.enable(); } }, - _cut(layer) { + cut(layer) { const all = this._map._layers; + // contains information about snapping points + const _latlngInfos = layer._latlngInfos || []; // find all layers that intersect with `layer`, the just drawn cutting layer const layers = Object.keys(all) @@ -65,14 +74,21 @@ Draw.Cut = Draw.Polygon.extend({ .map(l => all[l]) // only layers handled by leaflet-geoman .filter(l => l.pm) - // only polygons - .filter(l => l instanceof L.Polygon) + // only polyline instances + .filter(l => l instanceof L.Polyline) // exclude the drawn one .filter(l => l !== layer) + .filter(l => !this._layerGroup.hasLayer(l)) // only layers with intersections .filter(l => { try { + const lineInter = !!lineIntersect(layer.toGeoJSON(15), l.toGeoJSON(15)).features.length > 0; + + if(lineInter){ + return lineInter; + } return !!intersect(layer.toGeoJSON(15), l.toGeoJSON(15)); + } catch (e) { /* eslint-disable-next-line no-console */ console.error('You cant cut polygons with self-intersections'); @@ -82,8 +98,35 @@ Draw.Cut = Draw.Polygon.extend({ // loop through all layers that intersect with the drawn (cutting) layer layers.forEach(l => { + let newLayer; + if(l instanceof L.Polygon) { // Also for L.Rectangle + // easiest way to clone the complete latlngs without reference + newLayer = L.polygon(l.getLatLngs()); + const coords = newLayer.getLatLngs(); + + // snapping points added to the layer, so borders are cutted correct + _latlngInfos.forEach((info)=>{ + if(info && info.snapInfo) { + const {latlng} = info; + // get closest layer ( == input layer) with the closest segment to the intersection point + const closest = this._calcClosestLayer(latlng, [newLayer]); + if (closest && closest.segment && closest.distance < this.options.snapDistance) { + const {segment} = closest; + if (segment && segment.length === 2) { + const {indexPath, parentPath, newIndex} = Utils._getIndexFromSegment(coords, segment); + // define the coordsRing that is edited + const coordsRing = indexPath.length > 1 ? get(coords, parentPath) : coords; + coordsRing.splice(newIndex, 0, latlng); + } + } + } + }); + }else{ // L.Polyline + newLayer = l; + } + // find layer difference - const diff = difference(l.toGeoJSON(15), layer.toGeoJSON(15)); + const diff = this._cutLayer(layer,newLayer); // the resulting layer after the cut let resultLayer = L.geoJSON(diff, l.options); @@ -117,6 +160,31 @@ Draw.Cut = Draw.Polygon.extend({ layer: resultingLayer, originalLayer: l }); + }); + + }, + _cutLayer(layer,l){ + const fg = L.geoJSON(); + let diff; + // cut + if (l instanceof L.Polygon) { + // find layer difference + diff = difference(l.toGeoJSON(15), layer.toGeoJSON(15)); + } else { + // get splitted line to look which line part is coverd by the cut polygon + const lineDiff = lineSplit(l.toGeoJSON(15), layer.toGeoJSON(15)); + if (!lineDiff) { + return null; + } + L.geoJSON(lineDiff).getLayers().forEach((lay) => { + // add only parts to the map, they are not covered by the cut polygon + if (!booleanContains(layer.toGeoJSON(15), lay.toGeoJSON(15))) { + lay.addTo(fg); + } + }); + diff = fg.toGeoJSON(15); + } + return diff; }, }); diff --git a/src/js/Draw/L.PM.Draw.Line.js b/src/js/Draw/L.PM.Draw.Line.js index b7f60f89..a17e8850 100644 --- a/src/js/Draw/L.PM.Draw.Line.js +++ b/src/js/Draw/L.PM.Draw.Line.js @@ -254,6 +254,12 @@ Draw.Line = Draw.extend({ // is this the first point? const first = this._layer.getLatLngs().length === 0; + this._layer._latlngInfo = this._layer._latlngInfo || []; + this._layer._latlngInfo.push({ + latlng, + snapInfo: this._hintMarker._snapInfo + }); + this._layer.addLatLng(latlng); const newMarker = this._createMarker(latlng, first); diff --git a/src/js/L.PM.Utils.js b/src/js/L.PM.Utils.js index b43b945a..2869e657 100644 --- a/src/js/L.PM.Utils.js +++ b/src/js/L.PM.Utils.js @@ -44,6 +44,53 @@ const Utils = { }, createGeodesicPolygon, getTranslation, + findDeepCoordIndex(arr, latlng) { + // find latlng in arr and return its location as path + // thanks for the function, Felix Heck + let result; + + const run = path => (v, i) => { + const iRes = path.concat(i); + + if (v.lat && v.lat === latlng.lat && v.lng === latlng.lng) { + result = iRes; + return true; + } + + return Array.isArray(v) && v.some(run(iRes)); + }; + arr.some(run([])); + + let returnVal = {}; + + if (result) { + returnVal = { + indexPath: result, + index: result[result.length - 1], + parentPath: result.slice(0, result.length - 1), + }; + } + + return returnVal; + }, + _getIndexFromSegment(coords, segment) { + if (segment && segment.length === 2) { + const indexA = this.findDeepCoordIndex(coords, segment[0]); + const indexB = this.findDeepCoordIndex(coords, segment[1]); + let newIndex = Math.max(indexA.index, indexB.index); + if ((indexA.index === 0 || indexB.index === 0) && newIndex !== 1) { + newIndex+=1; + } + return { + indexA, + indexB, + newIndex, + indexPath: indexA.indexPath, + parentPath: indexA.parentPath, + }; + } + return null; + }, }; export default Utils; diff --git a/src/js/Mixins/Snapping.js b/src/js/Mixins/Snapping.js index 884a4aee..4c526c2c 100644 --- a/src/js/Mixins/Snapping.js +++ b/src/js/Mixins/Snapping.js @@ -125,6 +125,7 @@ const SnapMixin = { marker.setLatLng(snapLatLng); marker._snapped = true; + marker._snapInfo = eventInfo; const triggerSnap = () => { this._snapLatLng = snapLatLng; From 4f29ecb20451e24ac3d7aa0ee6007b4faed25284 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Sat, 7 Nov 2020 20:26:57 +0100 Subject: [PATCH 23/30] Disable Popup opening while drawing (#694). Fixes #543 (patch) * Disable Popup opening while drawing * change _pmCopyPopup to _tempPopupCopy --- cypress/integration/rectangle.spec.js | 31 +++++++++++++++++++++++++++ src/js/Draw/L.PM.Draw.js | 12 +++++++++++ src/js/L.PM.Utils.js | 12 +++++++++++ 3 files changed, 55 insertions(+) diff --git a/cypress/integration/rectangle.spec.js b/cypress/integration/rectangle.spec.js index 91ea3103..d2e5d6ec 100644 --- a/cypress/integration/rectangle.spec.js +++ b/cypress/integration/rectangle.spec.js @@ -178,4 +178,35 @@ describe('Draw Rectangle', () => { cy.toolbarButton('edit').click(); cy.hasVertexMarkers(8); }); + + it('disable popup on layer while drawing', ()=>{ + let rect = null; + cy.window().then(({ map, L }) => { + map.on("pm:create",(e)=>{ + e.layer.bindPopup('Popup test'); + if(e.layer instanceof L.Rectangle){ + rect = e.layer; + } + }); + }); + + cy.toolbarButton('rectangle').click(); + cy.get(mapSelector) + .click(100,50) + .click(700,400); + + cy.toolbarButton('marker').click(); + cy.get(mapSelector) + .click(300,250); + + cy.toolbarButton('edit').click(); + + cy.window().then(({ map}) => { + const len = map.pm.getGeomanDrawLayers().length; + expect(len).to.equal(2); + + const text = rect.getPopup().getContent(); + expect(text).to.equal('Popup test'); + }) + }); }); diff --git a/src/js/Draw/L.PM.Draw.js b/src/js/Draw/L.PM.Draw.js index 124c3a3d..47df197b 100644 --- a/src/js/Draw/L.PM.Draw.js +++ b/src/js/Draw/L.PM.Draw.js @@ -1,4 +1,5 @@ import SnapMixin from '../Mixins/Snapping'; +import Utils from "../L.PM.Utils"; const Draw = L.Class.extend({ includes: [SnapMixin], @@ -99,6 +100,17 @@ const Draw = L.Class.extend({ map: this._map, }); } + + const layers = Utils.findLayers(this._map); + if (this._enabled) { + layers.forEach((layer) => { + Utils.disablePopup(layer); + }) + } else { + layers.forEach((layer) => { + Utils.enablePopup(layer); + }) + } }, createNewDrawInstance(name, jsClass) { diff --git a/src/js/L.PM.Utils.js b/src/js/L.PM.Utils.js index 2869e657..e1b0b7ab 100644 --- a/src/js/L.PM.Utils.js +++ b/src/js/L.PM.Utils.js @@ -42,6 +42,18 @@ const Utils = { } return L.polygon(polygon, circle.options); }, + disablePopup(layer){ + if(layer.getPopup()){ + layer._tempPopupCopy = layer.getPopup(); + layer.unbindPopup(); + } + }, + enablePopup(layer){ + if(layer._tempPopupCopy){ + layer.bindPopup(layer._tempPopupCopy); + delete layer._tempPopupCopy; + } + }, createGeodesicPolygon, getTranslation, findDeepCoordIndex(arr, latlng) { From 088b6e0b8a68f58cfb87c20c76c4dd72b78634c5 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Sat, 7 Nov 2020 20:31:11 +0100 Subject: [PATCH 24/30] Add option snappingOrder to prioritise layers for snapping (#686). Fixes #453 (minor) * Add snappingOrder * Add test * Minify prio sorting * Add comment. --- README.md | 27 ++++++------ cypress/integration/events.spec.js | 46 +++++++++++++++++++- src/js/L.PM.Map.js | 1 + src/js/Mixins/Snapping.js | 50 ++++++++++++++++------ src/js/helpers/index.js | 67 ++++++++++++++++++++++++++++++ 5 files changed, 164 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index fdd3af1d..02e6efb1 100644 --- a/README.md +++ b/README.md @@ -532,19 +532,20 @@ map.pm.setGlobalOptions({ pinning: true, limitMarkersToCount: 15, limitMarkersCo The following options are available globally and apply when going into global edit mode. -| Option | Default | Description | -| :------------------------ | :------ | :-------------------------------------------------------------------------------------------------------- | -| snappable | `true` | Enable snapping to other layers vertices for precision drawing. Can be disabled by holding the `ALT` key. | -| snapDistance | `20` | The distance to another vertex when a snap should happen. | -| pinning | `false` | Pin shared vertices/markers together during edit ⭐. [Details](#pinning) | -| allowSelfIntersection | `true` | Allow/Disallow self-intersections on polygons and polylines. | -| preventMarkerRemoval | `false` | Disable the removal of markers/vertexes via right click. | -| limitMarkersToCount | `-1` | Shows only `n` markers per layer closest to the cursor. Use `-1` for no limit | -| limitMarkersCountGlobally | `false` | Activates `limitMarkersToCount` across layers on the entire map, not just per layer ⭐ | -| limitMarkersToZoom | `-1` | Shows markers when under the given zoom level ⭐ | -| limitMarkersToViewport | `false` | Shows only markers in the viewport ⭐ | -| limitMarkersToClick | `false` | Shows markers only after the layer was clicked ⭐ | -| editable | `false` | Makes a `CircleMarker` editable like a `Circle` | +| Option | Default | Description | +| :------------------------ | :------ | :-------------------------------------------------------------------------------------------------------------------------- | +| snappable | `true` | Enable snapping to other layers vertices for precision drawing. Can be disabled by holding the `ALT` key. | +| snapDistance | `20` | The distance to another vertex when a snap should happen. | +| pinning | `false` | Pin shared vertices/markers together during edit ⭐. [Details](#pinning) | +| allowSelfIntersection | `true` | Allow/Disallow self-intersections on polygons and polylines. | +| preventMarkerRemoval | `false` | Disable the removal of markers/vertexes via right click. | +| limitMarkersToCount | `-1` | Shows only `n` markers per layer closest to the cursor. Use `-1` for no limit | +| limitMarkersCountGlobally | `false` | Activates `limitMarkersToCount` across layers on the entire map, not just per layer ⭐ | +| limitMarkersToZoom | `-1` | Shows markers when under the given zoom level ⭐ | +| limitMarkersToViewport | `false` | Shows only markers in the viewport ⭐ | +| limitMarkersToClick | `false` | Shows markers only after the layer was clicked ⭐ | +| editable | `false` | Makes a `CircleMarker` editable like a `Circle` | +| snappingOrder | `Array` | Prioritize the order of snapping. Default: `['Marker','CircleMarker','Circle','Line','Polygon','Rectangle']` | Some details about a few more powerful options: diff --git a/cypress/integration/events.spec.js b/cypress/integration/events.spec.js index 75d7afee..a008a4e3 100644 --- a/cypress/integration/events.spec.js +++ b/cypress/integration/events.spec.js @@ -523,7 +523,49 @@ describe('Events', () => { }); -/* - */ + it('snappingOrder', () => { + + let event = ""; + cy.window().then(({ map}) => { + map.on('pm:drawstart',(e)=>{ + e.workingLayer.on('pm:snap', (x)=>{event=x}); + }); + + map.pm.setGlobalOptions({snappingOrder: ['Marker']}); + }); + + cy.window().then(() => { + cy.toolbarButton('marker').click(); + cy.get(mapSelector) + .click(200, 250); + + cy.toolbarButton('circle-marker').click(); + cy.get(mapSelector) + .click(200, 250); + + cy.toolbarButton('marker').click(); + cy.get(mapSelector) + .trigger("mousemove", 200, 250, { which: 1 }) + }); + cy.window().then(() => { + const shape = event.layerInteractedWith.pm._shape; + expect(shape).to.eq("Marker"); + }); + + + cy.window().then(({map}) => { + map.pm.setGlobalOptions({snappingOrder: ['CircleMarker']}); + + map.pm.enableDraw('Marker'); + + cy.get(mapSelector) + .trigger("mousemove", 200, 150, { which: 1 }) + .trigger("mousemove", 200, 250, { which: 1 }) + }); + cy.window().then(() => { + const shape = event.layerInteractedWith.pm._shape; + expect(shape).to.eq("CircleMarker"); + }); + }); }); diff --git a/src/js/L.PM.Map.js b/src/js/L.PM.Map.js index 10f7a8a2..6f409b25 100644 --- a/src/js/L.PM.Map.js +++ b/src/js/L.PM.Map.js @@ -21,6 +21,7 @@ const Map = L.Class.extend({ this.globalOptions = { snappable: true, layerGroup: undefined, + snappingOrder: ['Marker','CircleMarker','Circle','Line','Polygon','Rectangle'] }; }, setLang(lang = 'en', t, fallback = 'en') { diff --git a/src/js/Mixins/Snapping.js b/src/js/Mixins/Snapping.js index 4c526c2c..e491eafd 100644 --- a/src/js/Mixins/Snapping.js +++ b/src/js/Mixins/Snapping.js @@ -1,5 +1,5 @@ import Utils from '../L.PM.Utils'; -import {isEmptyDeep} from "../helpers"; +import {isEmptyDeep, prioritiseSort} from "../helpers"; const SnapMixin = { _initSnappableMarkers() { @@ -36,7 +36,7 @@ const SnapMixin = { // meanwhile, new layers could've been added to the map delete this._snapList; - if(this.throttledList) { + if (this.throttledList) { this._map.off('layeradd', this.throttledList, this); this.throttledList = undefined; } @@ -52,8 +52,8 @@ const SnapMixin = { }, _handleSnapping(e) { - if(!this.throttledList) { - this.throttledList = L.Util.throttle(this._createSnapList, 100, this); + if (!this.throttledList) { + this.throttledList = L.Util.throttle(this._createSnapList, 100, this); } // if snapping is disabled via holding ALT during drag, stop right here @@ -86,7 +86,7 @@ const SnapMixin = { ); // if no layers found. Can happen when circle is the only visible layer on the map and the hidden snapping-border circle layer is also on the map - if(Object.keys(closestLayer).length === 0){ + if (Object.keys(closestLayer).length === 0) { return false; } @@ -173,7 +173,7 @@ const SnapMixin = { layer instanceof L.Marker || layer instanceof L.CircleMarker || layer instanceof L.ImageOverlay) && - layer.options.snapIgnore !== true && + layer.options.snapIgnore !== true && ( (!L.PM.optIn && !layer.options.pmIgnore) || // if optIn is not set / true and pmIgnore is not set / true (default) (L.PM.optIn && layer.options.pmIgnore === false) // if optIn is true and pmIgnore is false @@ -182,13 +182,13 @@ const SnapMixin = { // adds a hidden polygon which matches the border of the circle if ((layer instanceof L.Circle || layer instanceof L.CircleMarker) && layer.pm && layer.pm._hiddenPolyCircle) { layers.push(layer.pm._hiddenPolyCircle); - }else if(layer instanceof L.ImageOverlay){ + } else if (layer instanceof L.ImageOverlay) { layer = L.rectangle(layer.getBounds()); } layers.push(layer); // this is for debugging - const debugLine = L.polyline([], { color: 'red', pmIgnore: true }); + const debugLine = L.polyline([], {color: 'red', pmIgnore: true}); debugLine._pmTempLayer = true; debugIndicatorLines.push(debugLine); if (layer instanceof L.Circle || layer instanceof L.CircleMarker) { @@ -220,7 +220,7 @@ const SnapMixin = { this.debugIndicatorLines = debugIndicatorLines; }, - _handleSnapLayerRemoval({ layer }) { + _handleSnapLayerRemoval({layer}) { // find the layers index in snaplist const index = this._snapList.findIndex( e => e._leaflet_id === layer._leaflet_id @@ -230,6 +230,7 @@ const SnapMixin = { }, _calcClosestLayer(latlng, layers) { // the closest polygon to our dragged marker latlng + let closestLayers = []; let closestLayer = {}; // loop through the layers @@ -247,16 +248,20 @@ const SnapMixin = { // save the info if it doesn't exist or if the distance is smaller than the previous one if ( closestLayer.distance === undefined || - results.distance < closestLayer.distance + results.distance <= closestLayer.distance ) { + if (results.distance < closestLayer.distance) { + closestLayers = []; + } closestLayer = results; closestLayer.layer = layer; + closestLayers.push(closestLayer); } }); // return the closest layer and it's data - // if there is no closest layer, return undefined - return closestLayer; + // if there is no closest layer, return an empty object + return this._getClosestLayerByPriority(closestLayers); }, _calcLayerDistances(latlng, layer) { const map = this._map; @@ -339,6 +344,27 @@ const SnapMixin = { distance: shortestDistance, }; }, + _getClosestLayerByPriority(layers) { + // sort the layers by creation, so it is snapping to the oldest layer from the same shape + layers = layers.sort((a, b) => a._leaflet_id - b._leaflet_id); + + const shapes = ['Marker', 'CircleMarker', 'Circle', 'Line', 'Polygon', 'Rectangle']; + const order = this._map.pm.globalOptions.snappingOrder || []; + + let lastIndex = 0; + const prioOrder = {}; + // merge user-preferred priority with default priority + order.concat(shapes).forEach((shape) => { + if (!prioOrder[shape]) { + lastIndex += 1; + prioOrder[shape] = lastIndex; + } + }); + + // sort layers by priority + layers.sort(prioritiseSort('instanceofShape', prioOrder)); + return layers[0] || {}; + }, // we got the point we want to snap to (C), but we need to check if a coord of the polygon // receives priority over C as the snapping point. Let's check this here _checkPrioritiySnapping(closestLayer) { diff --git a/src/js/helpers/index.js b/src/js/helpers/index.js index f5e0abf7..f61bf673 100644 --- a/src/js/helpers/index.js +++ b/src/js/helpers/index.js @@ -99,3 +99,70 @@ export function createGeodesicPolygon(origin, radius, sides, rotation) { return points; } + +// this function is used with the .sort(prioritiseSort(key, sortingOrder)) function of arrays +export function prioritiseSort(key, _sortingOrder, order = 'asc') { + /* the sorting order has all possible keys (lowercase) with the index and then it is sorted by the key on the object */ + + if(!_sortingOrder || Object.keys(_sortingOrder).length === 0) { + return (a,b)=>a-b; // default sort method + } + + // change the keys to lowercase + const keys = Object.keys(_sortingOrder); + let objKey; + let n = keys.length; + const sortingOrder={}; + while (n--) { + objKey = keys[n]; + sortingOrder[objKey.toLowerCase()] = _sortingOrder[objKey]; + } + + function getShape(layer){ + if(layer instanceof L.Marker){ + return "Marker"; + }else if(layer instanceof L.Circle){ + return "Circle"; + }else if(layer instanceof L.CircleMarker){ + return "CircleMarker"; + }else if(layer instanceof L.Rectangle){ + return "Rectangle"; + }else if(layer instanceof L.Polygon){ + return "Polygon"; + }else if(layer instanceof L.Polyline){ + return "Line"; + }else{ + return undefined; + } + } + + return (a, b) => { + let keyA; + let keyB; + if(key === "instanceofShape"){ + keyA = getShape(a.layer).toLowerCase(); + keyB = getShape(b.layer).toLowerCase(); + if(!keyA || !keyB) return 0; + }else{ + /* eslint-disable-next-line no-prototype-builtins */ + if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) return 0; + keyA = a[key].toLowerCase(); + keyB = b[key].toLowerCase(); + } + + const first = + keyA in sortingOrder + ? sortingOrder[keyA] + : Number.MAX_SAFE_INTEGER; + + const second = + keyB in sortingOrder + ? sortingOrder[keyB] + : Number.MAX_SAFE_INTEGER; + + let result = 0; + if (first < second) result = -1; + else if (first > second) result = 1; + return order === 'desc' ? (result * -1) : result; + } +} From ed4a8328b494189669f412a5fce6cb24fec2397f Mon Sep 17 00:00:00 2001 From: Falke Design Date: Sat, 7 Nov 2020 21:11:14 +0100 Subject: [PATCH 25/30] Add min/max radius for Circle and CircleMarker (#635). Fixes #629 (minor) * Added min/max radius for Circle and CircleMarker (editable). Also fixed a bug in drag testing with the library cypress-wait-until * Fix circlemarker test * Changed the names of the circleMin/Max options * Fix test --- README.md | 6 +- cypress/integration/circle.spec.js | 129 ++++++++++ cypress/integration/circlemarker.spec.js | 132 ++++++++++ cypress/integration/marker.spec.js | 9 +- cypress/integration/polygon.spec.js | 40 +-- cypress/support/commands.js | 1 + package-lock.json | 298 +++++++++++++++++++++++ package.json | 1 + src/js/Draw/L.PM.Draw.Circle.js | 51 +++- src/js/Draw/L.PM.Draw.CircleMarker.js | 65 ++++- src/js/Edit/L.PM.Edit.Circle.js | 39 ++- src/js/Edit/L.PM.Edit.CircleMarker.js | 50 +++- src/js/L.PM.Map.js | 13 +- src/js/Mixins/Snapping.js | 1 + src/js/helpers/index.js | 38 ++- 15 files changed, 840 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 02e6efb1..b07f8780 100644 --- a/README.md +++ b/README.md @@ -235,8 +235,12 @@ See the available options in the table below. | cursorMarker | `true` | show a marker at the cursor | | finishOn | `null` | leaflet layer event to finish the drawn shape, like `'dblclick'`. [Here's a list](http://leafletjs.com/reference-1.2.0.html#interactive-layer-click). | | markerStyle | `{ draggable: true }` | [leaflet marker options](https://leafletjs.com/reference-1.4.0.html#marker-icon) (only for drawing markers). | -| editable | `false` | makes a `CircleMarker` editable like a `Circle` | | hideMiddleMarkers | `false` | hide the middle Markers in edit mode from Polyline and Polygon. | +| minRadiusCircle | `null` | set the min radius of a `Circle`. | +| maxRadiusCircle | `null` | set the max radius of a `Circle`. | +| minRadiusCircleMarker | `null` | set the min radius of a `CircleMarker` when editable is active. | +| maxRadiusCircleMarker | `null` | set the max radius of a `CircleMarker` when editable is active. | +| editable | `false` | makes a `CircleMarker` editable like a `Circle` | | markerEditable | `true` | Markers and CircleMarkers are editable during the draw-session (you can drag them around immediately after drawing them) | | continueDrawing | `false` / `true` | Draw-Mode stays enabled after finishing a layer to immediately draw the next layer. Defaults to `true` for Markers and CircleMarkers and `false` for all other layers. | | diff --git a/cypress/integration/circle.spec.js b/cypress/integration/circle.spec.js index ed7665c6..788d3b42 100644 --- a/cypress/integration/circle.spec.js +++ b/cypress/integration/circle.spec.js @@ -108,4 +108,133 @@ describe('Draw Circle', () => { cy.toolbarButton('edit').click(); cy.hasVertexMarkers(4); }); + + it('set max radius of circle', () => { + let handFinish = false; + + cy.toolbarButton('circle') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + cy.window().then(({ map, L }) => { + L.marker(map.getCenter()).addTo(map); + map.pm.setGlobalOptions({ + minRadiusCircle: 500, + maxRadiusCircle: 1500, + }); + cy.get(mapSelector) + .click(250,200) + .click(620,190) + .then(() => { + map.eachLayer(layer => { + if (layer instanceof L.Circle) { + expect(layer.getRadius()).to.equal(1500); + } + }); + }); + }); + + cy.toolbarButton('edit') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + cy.window().then(({ Hand, map, L }) => { + const handMarker = new Hand({ + timing: 'frame', + onStop: ()=>{ + map.eachLayer(layer => { + if (layer instanceof L.Circle) { + expect(true).to.equal(layer.getRadius() < 1500); + } + }); + const handMarker2 = new Hand({ + timing: 'frame', + onStop: () => { + handFinish = true; + map.eachLayer(layer => { + if (layer instanceof L.Circle) { + expect(true).to.equal(layer.getRadius() >= 1495 && layer.getRadius() < 1505); + } + }); + } + }); + const toucherMarker2 = handMarker2.growFinger('mouse'); + toucherMarker2.wait(100).moveTo(317,198, 100).down().wait(500).moveTo(500,198, 600).up().wait(100) + } + }); + const toucherMarker = handMarker.growFinger('mouse'); + toucherMarker.wait(100).moveTo(379,198, 100).down().wait(500).moveTo(317,198, 400).up().wait(100) + + // wait until hand is finished + cy.waitUntil(() => cy.window().then(() => handFinish)).then( ()=> { + expect(handFinish).to.equal(true); + }); + }); + + }); + it('set min radius of circle', () => { + let handFinish = false; + + cy.toolbarButton('circle') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + cy.window().then(({ map, L }) => { + L.marker(map.getCenter()).addTo(map); + map.pm.setGlobalOptions({ + minRadiusCircle: 1500, + maxRadiusCircle: 3000, + }); + cy.get(mapSelector) + .click(250,200) + .click(300,190) + .then(() => { + map.eachLayer(layer => { + if (layer instanceof L.Circle) { + expect(layer.getRadius()).to.equal(1500); + } + }); + }); + }); + + cy.toolbarButton('edit') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + cy.window().then(({ Hand, map, L }) => { + const handMarker = new Hand({ + timing: 'frame', + onStop: ()=>{ + map.eachLayer(layer => { + if (layer instanceof L.Circle) { + expect(true).to.equal(layer.getRadius() > 1500) + } + }); + const handMarker2 = new Hand({ + timing: 'frame', + onStop: () =>{ + handFinish = true; + map.eachLayer(layer => { + if (layer instanceof L.Circle) { + expect(true).to.equal(layer.getRadius() >= 1495 && layer.getRadius() < 1505); + } + }); + } + }); + const toucherMarker2 = handMarker2.growFinger('mouse'); + toucherMarker2.wait(200).moveTo(490,198, 100).down().wait(500).moveTo(317,198, 600).up().wait(100) + } + }); + const toucherMarker = handMarker.growFinger('mouse'); + toucherMarker.wait(100).moveTo(379,198, 100).down().wait(500).moveTo(500,198, 400).up().wait(100) + // wait until hand is finished + cy.waitUntil(() => cy.window().then(() => handFinish)).then( ()=> { + expect(handFinish).to.equal(true); + }); + }); + }); }); diff --git a/cypress/integration/circlemarker.spec.js b/cypress/integration/circlemarker.spec.js index d9cda446..7224ea32 100644 --- a/cypress/integration/circlemarker.spec.js +++ b/cypress/integration/circlemarker.spec.js @@ -219,4 +219,136 @@ describe('Draw Circle Marker', () => { cy.hasLayers(5); }); + + it('set max radius of circleMarker', () => { + let handFinish = false; + + cy.toolbarButton('circle-marker') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + cy.window().then(({ map, L }) => { + L.marker(map.getCenter()).addTo(map); + map.pm.setGlobalOptions({ + minRadiusCircleMarker: 50, + maxRadiusCircleMarker: 150, + editable: true + }); + cy.get(mapSelector) + .click(250,200) + .click(400,190) + .then(() => { + const layers = map.pm.getGeomanDrawLayers(); + layers.forEach(layer => { + if (layer instanceof L.CircleMarker) { + expect(layer.getRadius()).to.equal(150); + } + }); + }); + }); + + cy.toolbarButton('edit') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + cy.window().then(({ Hand, map, L }) => { + const handMarker = new Hand({ + timing: 'frame', + onStop: ()=>{ + map.eachLayer(layer => { + if (layer instanceof L.CircleMarker) { + expect(true).to.equal(layer.getRadius() < 150); + } + }); + const handMarker2 = new Hand({ + timing: 'frame', + onStop: () => { + handFinish = true; + map.eachLayer(layer => { + if (layer instanceof L.CircleMarker) { + expect(true).to.equal(layer.getRadius() >= 145 && layer.getRadius() < 155); + } + }); + } + }); + const toucherMarker2 = handMarker2.growFinger('mouse'); + toucherMarker2.wait(100).moveTo(317,198, 100).down().wait(500).moveTo(500,198, 600).up().wait(100) + } + }); + const toucherMarker = handMarker.growFinger('mouse'); + toucherMarker.wait(100).moveTo(400,198, 100).down().wait(500).moveTo(317,198, 400).up().wait(100); + + // wait until hand is finished + cy.waitUntil(() => cy.window().then(() => handFinish)).then( ()=> { + expect(handFinish).to.equal(true); + }); + }); + + }); + it('set min radius of circleMarker', () => { + let handFinish = false; + + cy.toolbarButton('circle-marker') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + cy.window().then(({ map, L }) => { + L.marker(map.getCenter()).addTo(map); + map.pm.setGlobalOptions({ + minRadiusCircleMarker: 150, + maxRadiusCircleMarker: 300, + editable: true + }); + cy.get(mapSelector) + .click(250,200) + .click(300,200) + .then(() => { + const layers = map.pm.getGeomanDrawLayers(); + layers.forEach(layer => { + if (layer instanceof L.CircleMarker) { + expect(layer.getRadius()).to.equal(150); + } + }); + }); + }); + cy.toolbarButton('edit') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + cy.window().then(({ Hand, map, L }) => { + const handMarker = new Hand({ + timing: 'frame', + onStop: ()=>{ + map.eachLayer(layer => { + if (layer instanceof L.CircleMarker) { + expect(true).to.equal(layer.getRadius() > 150) + } + }); + const handMarker2 = new Hand({ + timing: 'frame', + onStop: () =>{ + handFinish = true; + map.eachLayer(layer => { + if (layer instanceof L.CircleMarker) { + expect(true).to.equal(layer.getRadius() >= 145 && layer.getRadius() < 155); + } + }); + } + }); + const toucherMarker2 = handMarker2.growFinger('mouse'); + toucherMarker2.wait(200).moveTo(490,198, 100).down().wait(500).moveTo(317,198, 600).up().wait(100) + } + }); + const toucherMarker = handMarker.growFinger('mouse'); + toucherMarker.wait(100).moveTo(400,198, 100).down().wait(500).moveTo(500,198, 400).up().wait(100) + // wait until hand is finished + cy.waitUntil(() => cy.window().then(() => handFinish)).then( ()=> { + expect(handFinish).to.equal(true); + }); + }); + }); }); diff --git a/cypress/integration/marker.spec.js b/cypress/integration/marker.spec.js index 3b066a49..87151d1b 100644 --- a/cypress/integration/marker.spec.js +++ b/cypress/integration/marker.spec.js @@ -96,7 +96,7 @@ describe('Draw Marker', () => { }); it('calls pm:drag-events on Marker drag', () => { - + let handFinish = false; let dragstart = false; let drag = false; let dragend = false; @@ -131,14 +131,21 @@ describe('Draw Marker', () => { expect(dragstart).to.equal(true); expect(drag).to.equal(true); expect(dragend).to.equal(true); + handFinish = true; } }); const toucherMarker = handMarker.growFinger('mouse'); toucherMarker.wait(100).moveTo(150, 240, 100).down().wait(500).moveTo(170, 290, 400).up().wait(100) // Not allowed }); + + // wait until hand is finished + cy.waitUntil(() => cy.window().then(() => handFinish)).then( ()=> { + expect(handFinish).to.equal(true); + }); }); + it('enabled of Marker is true in edit-mode', () => { cy.toolbarButton('marker').click(); cy.get(mapSelector) diff --git a/cypress/integration/polygon.spec.js b/cypress/integration/polygon.spec.js index 989164fc..abac2720 100644 --- a/cypress/integration/polygon.spec.js +++ b/cypress/integration/polygon.spec.js @@ -640,26 +640,15 @@ describe('Draw & Edit Poly', () => { const layer = L.geoJSON(json).getLayers()[0].addTo(map); const bounds = layer.getBounds(); map.fitBounds(bounds); - console.log(map.getCenter()); return layer; }) .as('poly'); cy.get("@poly").then((poly)=>{ + let handFinish = false; expect(poly.pm.hasSelfIntersection()).to.equal(true); - const hand_selfIntersectionTrue = new Hand({ - timing: 'frame', - onStop () { - expect(poly.pm.hasSelfIntersection()).to.equal(true); - - const toucher_selfIntersectionFalse = hand_selfIntersectionFalse.growFinger('mouse'); - toucher_selfIntersectionFalse.wait(100).moveTo(504, 337, 100).down().wait(500).moveTo(780, 259, 400).up().wait(100) // allowed - // No intersection anymore - .moveTo(294, 114, 100).down().wait(500).moveTo(752, 327, 800).up().wait(500) // Not allowed - } - }); - var hand_selfIntersectionFalse = new Hand({ + const handSelfIntersectionFalse = new Hand({ timing: 'frame', onStop () { expect(poly.pm.hasSelfIntersection()).to.equal(false); @@ -668,7 +657,18 @@ describe('Draw & Edit Poly', () => { const center = map.getCenter(); expect(center.lat).to.equal(48.77492609799526); expect(center.lng).to.equal(4.847301999999988); + handFinish = true; + } + }); + const handSelfIntersectionTrue = new Hand({ + timing: 'frame', + onStop () { + expect(poly.pm.hasSelfIntersection()).to.equal(true); + const toucherSelfIntersectionFalse = handSelfIntersectionFalse.growFinger('mouse'); + toucherSelfIntersectionFalse.wait(100).moveTo(504, 337, 100).down().wait(500).moveTo(780, 259, 400).up().wait(100) // allowed + // No intersection anymore + .moveTo(294, 114, 100).down().wait(500).moveTo(752, 327, 800).up().wait(500) // Not allowed } }); @@ -676,12 +676,20 @@ describe('Draw & Edit Poly', () => { map.pm.enableGlobalEditMode({ allowSelfIntersection: false, allowSelfIntersectionEdit: true, }); - const toucher_selfIntersectionTrue = hand_selfIntersectionTrue.growFinger('mouse'); - toucher_selfIntersectionTrue.wait(100).moveTo(294, 114, 100).down().wait(500).moveTo(782, 127, 400).up().wait(100) // Not allowed + const toucherSelfIntersectionTrue = handSelfIntersectionTrue.growFinger('mouse'); + toucherSelfIntersectionTrue.wait(100).moveTo(294, 114, 100).down().wait(500).moveTo(782, 127, 400).up().wait(100) // Not allowed .moveTo(313, 345, 100).down().wait(500).moveTo(256, 311, 400).up().wait(100) // allowed .moveTo(317, 252, 100).down().wait(500).moveTo(782, 127, 400).up().wait(500); // allowed - }) + + // wait until hand is finished + cy.waitUntil(() => cy.window().then(() => handFinish),{ + timeout: 9000, + }).then( ()=> { + expect(handFinish).to.equal(true); + }); + + }); }); }); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index d6624bdd..8e465516 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -1,3 +1,4 @@ +import 'cypress-wait-until'; // *********************************************** // This example commands.js shows you how to // create various custom commands and overwrite diff --git a/package-lock.json b/package-lock.json index 6b86cb97..02a30fa4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1141,6 +1141,108 @@ "@turf/meta": "6.x" } }, + "@turf/bearing": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/bearing/-/bearing-5.1.5.tgz", + "integrity": "sha1-egt5ATbE70eX8CRjBdRcvi0ns/c=", + "requires": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + }, + "dependencies": { + "@turf/helpers": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz", + "integrity": "sha1-FTQFInq5M9AEpbuWQantmZ/L4M8=" + }, + "@turf/invariant": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-5.2.0.tgz", + "integrity": "sha1-8BUP9ykLOFd7c9CIt5MsHuCqkKc=", + "requires": { + "@turf/helpers": "^5.1.5" + } + } + } + }, + "@turf/boolean-contains": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@turf/boolean-contains/-/boolean-contains-6.0.1.tgz", + "integrity": "sha512-usAexEdWu7dV43paowGSFEM0PljexnlOuj09HF/VDZwO3FKelwUovF2ymetYatuG7KcIYcexeNEkQ5qQnGExlw==", + "requires": { + "@turf/bbox": "6.x", + "@turf/boolean-point-in-polygon": "6.x", + "@turf/boolean-point-on-line": "6.x", + "@turf/helpers": "6.x", + "@turf/invariant": "6.x" + } + }, + "@turf/boolean-point-in-polygon": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-6.0.1.tgz", + "integrity": "sha512-FKLOZ124vkJhjzNSDcqpwp2NvfnsbYoUOt5iAE7uskt4svix5hcjIEgX9sELFTJpbLGsD1mUbKdfns8tZxcMNg==", + "requires": { + "@turf/helpers": "6.x", + "@turf/invariant": "6.x" + } + }, + "@turf/boolean-point-on-line": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@turf/boolean-point-on-line/-/boolean-point-on-line-6.0.1.tgz", + "integrity": "sha512-Vl724Tzh4CF/13kgblOAQnMcHagromCP1EfyJ9G/8SxpSoTYeY2G6FmmcpbW51GqKxC7xgM9+Pck50dun7oYkg==", + "requires": { + "@turf/helpers": "6.x", + "@turf/invariant": "6.x" + } + }, + "@turf/destination": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/destination/-/destination-5.1.5.tgz", + "integrity": "sha1-7TU4G9zoO73cvQei4rzivd/7zCY=", + "requires": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + }, + "dependencies": { + "@turf/helpers": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz", + "integrity": "sha1-FTQFInq5M9AEpbuWQantmZ/L4M8=" + }, + "@turf/invariant": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-5.2.0.tgz", + "integrity": "sha1-8BUP9ykLOFd7c9CIt5MsHuCqkKc=", + "requires": { + "@turf/helpers": "^5.1.5" + } + } + } + }, + "@turf/distance": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-5.1.5.tgz", + "integrity": "sha1-Oc8YIEu/h1h9cH5gmmARiQkVZAk=", + "requires": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5" + }, + "dependencies": { + "@turf/helpers": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz", + "integrity": "sha1-FTQFInq5M9AEpbuWQantmZ/L4M8=" + }, + "@turf/invariant": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-5.2.0.tgz", + "integrity": "sha1-8BUP9ykLOFd7c9CIt5MsHuCqkKc=", + "requires": { + "@turf/helpers": "^5.1.5" + } + } + } + }, "@turf/helpers": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.1.4.tgz", @@ -1184,6 +1286,87 @@ "@turf/meta": "6.x" } }, + "@turf/line-split": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-split/-/line-split-5.1.5.tgz", + "integrity": "sha1-Wy30w3YZty73JbUWPPmSbVVArLc=", + "requires": { + "@turf/bbox": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/line-segment": "^5.1.5", + "@turf/meta": "^5.1.5", + "@turf/nearest-point-on-line": "^5.1.5", + "@turf/square": "^5.1.5", + "@turf/truncate": "^5.1.5", + "geojson-rbush": "2.1.0" + }, + "dependencies": { + "@turf/bbox": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-5.1.5.tgz", + "integrity": "sha1-MFHfUUrUxQ9KT5uKLRX9i2hA7aM=", + "requires": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "@turf/helpers": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz", + "integrity": "sha1-FTQFInq5M9AEpbuWQantmZ/L4M8=" + }, + "@turf/invariant": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-5.2.0.tgz", + "integrity": "sha1-8BUP9ykLOFd7c9CIt5MsHuCqkKc=", + "requires": { + "@turf/helpers": "^5.1.5" + } + }, + "@turf/line-intersect": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-5.1.5.tgz", + "integrity": "sha1-DikHGuQDKV5JFyO8SfXPrI0R3fM=", + "requires": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-segment": "^5.1.5", + "@turf/meta": "^5.1.5", + "geojson-rbush": "2.1.0" + } + }, + "@turf/line-segment": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-5.1.5.tgz", + "integrity": "sha1-Mgeq7lRqskw9jcPMY/kcdwuAE+U=", + "requires": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "@turf/meta": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-5.2.0.tgz", + "integrity": "sha1-OxrUhe4MOwsXdRMqMsOE1T5LpT0=", + "requires": { + "@turf/helpers": "^5.1.5" + } + }, + "geojson-rbush": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/geojson-rbush/-/geojson-rbush-2.1.0.tgz", + "integrity": "sha1-O9c745H8ELCuaT2bis6iquC4Oo0=", + "requires": { + "@turf/helpers": "*", + "@turf/meta": "*", + "rbush": "*" + } + } + } + }, "@turf/meta": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.0.2.tgz", @@ -1192,6 +1375,115 @@ "@turf/helpers": "6.x" } }, + "@turf/nearest-point-on-line": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/nearest-point-on-line/-/nearest-point-on-line-5.1.5.tgz", + "integrity": "sha1-VgauKX8VlHUkvqUaKp71HsG/nDY=", + "requires": { + "@turf/bearing": "^5.1.5", + "@turf/destination": "^5.1.5", + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-intersect": "^5.1.5", + "@turf/meta": "^5.1.5" + }, + "dependencies": { + "@turf/helpers": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz", + "integrity": "sha1-FTQFInq5M9AEpbuWQantmZ/L4M8=" + }, + "@turf/invariant": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-5.2.0.tgz", + "integrity": "sha1-8BUP9ykLOFd7c9CIt5MsHuCqkKc=", + "requires": { + "@turf/helpers": "^5.1.5" + } + }, + "@turf/line-intersect": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-intersect/-/line-intersect-5.1.5.tgz", + "integrity": "sha1-DikHGuQDKV5JFyO8SfXPrI0R3fM=", + "requires": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/line-segment": "^5.1.5", + "@turf/meta": "^5.1.5", + "geojson-rbush": "2.1.0" + } + }, + "@turf/line-segment": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/line-segment/-/line-segment-5.1.5.tgz", + "integrity": "sha1-Mgeq7lRqskw9jcPMY/kcdwuAE+U=", + "requires": { + "@turf/helpers": "^5.1.5", + "@turf/invariant": "^5.1.5", + "@turf/meta": "^5.1.5" + } + }, + "@turf/meta": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-5.2.0.tgz", + "integrity": "sha1-OxrUhe4MOwsXdRMqMsOE1T5LpT0=", + "requires": { + "@turf/helpers": "^5.1.5" + } + }, + "geojson-rbush": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/geojson-rbush/-/geojson-rbush-2.1.0.tgz", + "integrity": "sha1-O9c745H8ELCuaT2bis6iquC4Oo0=", + "requires": { + "@turf/helpers": "*", + "@turf/meta": "*", + "rbush": "*" + } + } + } + }, + "@turf/square": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/square/-/square-5.1.5.tgz", + "integrity": "sha1-qnsh5gM8ySUsOlvW89iNq9b+0YA=", + "requires": { + "@turf/distance": "^5.1.5", + "@turf/helpers": "^5.1.5" + }, + "dependencies": { + "@turf/helpers": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz", + "integrity": "sha1-FTQFInq5M9AEpbuWQantmZ/L4M8=" + } + } + }, + "@turf/truncate": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/truncate/-/truncate-5.1.5.tgz", + "integrity": "sha1-nu37Oxi6gfLJjT6tCUMcyhiErYk=", + "requires": { + "@turf/helpers": "^5.1.5", + "@turf/meta": "^5.1.5" + }, + "dependencies": { + "@turf/helpers": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz", + "integrity": "sha1-FTQFInq5M9AEpbuWQantmZ/L4M8=" + }, + "@turf/meta": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-5.2.0.tgz", + "integrity": "sha1-OxrUhe4MOwsXdRMqMsOE1T5LpT0=", + "requires": { + "@turf/helpers": "^5.1.5" + } + } + } + }, "@types/json-schema": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", @@ -3235,6 +3527,12 @@ } } }, + "cypress-wait-until": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/cypress-wait-until/-/cypress-wait-until-1.7.1.tgz", + "integrity": "sha512-8DL5IsBTbAxBjfYgCzdbohPq/bY+IKc63fxtso1C8RWhLnQkZbVESyaclNr76jyxfId6uyzX8+Xnt0ZwaXNtkA==", + "dev": true + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", diff --git a/package.json b/package.json index 354660ff..10da0ef9 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "copy-webpack-plugin": "^6.2.1", "css-loader": "^3.5.3", "cypress": "^3.8.3", + "cypress-wait-until": "^1.7.1", "eslint": "^4.19.1", "eslint-config-airbnb-base": "^12.1.0", "eslint-config-prettier": "^3.6.0", diff --git a/src/js/Draw/L.PM.Draw.Circle.js b/src/js/Draw/L.PM.Draw.Circle.js index 43d60bc6..621a21b8 100644 --- a/src/js/Draw/L.PM.Draw.Circle.js +++ b/src/js/Draw/L.PM.Draw.Circle.js @@ -1,6 +1,6 @@ import Draw from './L.PM.Draw'; -import { getTranslation } from '../helpers'; +import {destinationOnLine, getTranslation} from '../helpers'; Draw.Circle = Draw.extend({ initialize(map) { @@ -55,7 +55,6 @@ Draw.Circle = Draw.extend({ permanent: true, offset: L.point(0, 10), direction: 'bottom', - opacity: 0.8, }) .openTooltip(); @@ -136,9 +135,9 @@ Draw.Circle = Draw.extend({ }, _syncHintLine() { const latlng = this._centerMarker.getLatLng(); - + const secondLatLng = this._getNewDestinationOfHintMarker(); // set coords for hintline from marker to last vertex of drawin polyline - this._hintline.setLatLngs([latlng, this._hintMarker.getLatLng()]); + this._hintline.setLatLngs([latlng, secondLatLng]); }, _syncCircleRadius() { const A = this._centerMarker.getLatLng(); @@ -152,11 +151,19 @@ Draw.Circle = Draw.extend({ distance = A.distanceTo(B); } - this._layer.setRadius(distance); + if(this.options.minRadiusCircle && distance < this.options.minRadiusCircle) { + this._layer.setRadius(this.options.minRadiusCircle); + }else if(this.options.maxRadiusCircle && distance > this.options.maxRadiusCircle) { + this._layer.setRadius(this.options.maxRadiusCircle); + }else{ + this._layer.setRadius(distance); + } }, _syncHintMarker(e) { // move the cursor marker this._hintMarker.setLatLng(e.latlng); + // calculate the new latlng of marker if radius is out of min/max + this._hintMarker.setLatLng(this._getNewDestinationOfHintMarker()); // if snapping is enabled, do it if (this.options.snappable) { @@ -164,6 +171,8 @@ Draw.Circle = Draw.extend({ fakeDragEvent.target = this._hintMarker; this._handleSnapping(fakeDragEvent); } + + this._handleHintMarkerSnapping(); }, _placeCenterMarker(e) { // assign the coordinate of the click to the hintMarker, that's necessary for @@ -222,6 +231,12 @@ Draw.Circle = Draw.extend({ radius = center.distanceTo(latlng); } + if(this.options.minRadiusCircle && radius < this.options.minRadiusCircle){ + radius = this.options.minRadiusCircle; + }else if(this.options.maxRadiusCircle && radius > this.options.maxRadiusCircle){ + radius = this.options.maxRadiusCircle; + } + const options = Object.assign({}, this.options.pathOptions, { radius }); // create the final circle layer @@ -245,4 +260,30 @@ Draw.Circle = Draw.extend({ this.enable(); } }, + _getNewDestinationOfHintMarker(){ + const latlng = this._centerMarker.getLatLng(); + let secondLatLng = this._hintMarker.getLatLng(); + const distance = latlng.distanceTo(secondLatLng); + + if(this.options.minRadiusCircle && distance < this.options.minRadiusCircle) { + secondLatLng = destinationOnLine(this._map,latlng,secondLatLng,this.options.minRadiusCircle); + }else if(this.options.maxRadiusCircle && distance > this.options.maxRadiusCircle) { + secondLatLng = destinationOnLine(this._map,latlng,secondLatLng,this.options.maxRadiusCircle); + } + return secondLatLng; + }, + _handleHintMarkerSnapping(){ + if(this._hintMarker._snapped) { + const latlng = this._centerMarker.getLatLng(); + const secondLatLng = this._hintMarker.getLatLng(); + const distance = latlng.distanceTo(secondLatLng); + if(this.options.minRadiusCircle && distance < this.options.minRadiusCircle) { + this._hintMarker.setLatLng(this._hintMarker._orgLatLng); + } else if(this.options.maxRadiusCircle && distance > this.options.maxRadiusCircle) { + this._hintMarker.setLatLng(this._hintMarker._orgLatLng); + } + } + // calculate the new latlng of marker if the snapped latlng radius is out of min/max + this._hintMarker.setLatLng(this._getNewDestinationOfHintMarker()); + } }); diff --git a/src/js/Draw/L.PM.Draw.CircleMarker.js b/src/js/Draw/L.PM.Draw.CircleMarker.js index 1c5b06ef..226e1b74 100644 --- a/src/js/Draw/L.PM.Draw.CircleMarker.js +++ b/src/js/Draw/L.PM.Draw.CircleMarker.js @@ -1,6 +1,6 @@ import Draw from './L.PM.Draw'; -import { getTranslation } from '../helpers'; +import {destinationOnLine, getTranslation} from '../helpers'; Draw.CircleMarker = Draw.Marker.extend({ initialize(map) { @@ -211,9 +211,9 @@ Draw.CircleMarker = Draw.Marker.extend({ }, _syncHintLine() { const latlng = this._centerMarker.getLatLng(); - + const secondLatLng = this._getNewDestinationOfHintMarker(); // set coords for hintline from marker to last vertex of drawin polyline - this._hintline.setLatLngs([latlng, this._hintMarker.getLatLng()]); + this._hintline.setLatLngs([latlng, secondLatLng]); }, _syncCircleRadius() { const A = this._centerMarker.getLatLng(); @@ -221,11 +221,19 @@ Draw.CircleMarker = Draw.Marker.extend({ const distance = this._map.project(A).distanceTo(this._map.project(B)); - this._layer.setRadius(distance); + if(this.options.minRadiusCircleMarker && distance < this.options.minRadiusCircleMarker) { + this._layer.setRadius(this.options.minRadiusCircleMarker); + }else if(this.options.maxRadiusCircleMarker && distance > this.options.maxRadiusCircleMarker) { + this._layer.setRadius(this.options.maxRadiusCircleMarker); + }else{ + this._layer.setRadius(distance); + } }, _syncHintMarker(e) { // move the cursor marker this._hintMarker.setLatLng(e.latlng); + // calculate the new latlng of marker if radius is out of min/max + this._hintMarker.setLatLng(this._getNewDestinationOfHintMarker()); // if snapping is enabled, do it if (this.options.snappable) { @@ -233,6 +241,8 @@ Draw.CircleMarker = Draw.Marker.extend({ fakeDragEvent.target = this._hintMarker; this._handleSnapping(fakeDragEvent); } + + this._handleHintMarkerSnapping(); }, isRelevantMarker(layer) { return layer instanceof L.CircleMarker && !(layer instanceof L.Circle) && layer.pm && !layer._pmTempLayer; @@ -286,7 +296,17 @@ Draw.CircleMarker = Draw.Marker.extend({ // calc the radius const center = this._centerMarker.getLatLng(); const latlng = this._hintMarker.getLatLng(); - const radius = this._map.project(center).distanceTo(this._map.project(latlng)); + let radius = this._map.project(center).distanceTo(this._map.project(latlng)); + + + if(this.options.editable) { + if (this.options.minRadiusCircleMarker && radius < this.options.minRadiusCircleMarker) { + radius = this.options.minRadiusCircleMarker; + } else if (this.options.maxRadiusCircleMarker && radius > this.options.maxRadiusCircleMarker) { + radius = this.options.maxRadiusCircleMarker; + } + } + const options = Object.assign({}, this.options.pathOptions, { radius }); // create the final circle layer @@ -309,4 +329,39 @@ Draw.CircleMarker = Draw.Marker.extend({ this.enable(); } }, + _getNewDestinationOfHintMarker(){ + let secondLatLng = this._hintMarker.getLatLng(); + if(this.options.editable) { + const latlng = this._centerMarker.getLatLng(); + const distance = this._map.project(latlng).distanceTo(this._map.project(secondLatLng)); + if (this.options.minRadiusCircleMarker && distance < this.options.minRadiusCircleMarker) { + secondLatLng = destinationOnLine(this._map, latlng, secondLatLng, this._pxRadiusToMeter(this.options.minRadiusCircleMarker)); + } else if (this.options.maxRadiusCircleMarker && distance > this.options.maxRadiusCircleMarker) { + secondLatLng = destinationOnLine(this._map, latlng, secondLatLng, this._pxRadiusToMeter(this.options.maxRadiusCircleMarker)); + } + } + return secondLatLng; + }, + _handleHintMarkerSnapping(){ + if(this.options.editable) { + if (this._hintMarker._snapped) { + const latlng = this._centerMarker.getLatLng(); + const secondLatLng = this._hintMarker.getLatLng(); + const distance = this._map.project(latlng).distanceTo(this._map.project(secondLatLng)); + if (this.options.minRadiusCircleMarker && distance < this.options.minRadiusCircleMarker) { + this._hintMarker.setLatLng(this._hintMarker._orgLatLng); + } else if (this.options.maxRadiusCircleMarker && distance > this.options.maxRadiusCircleMarker) { + this._hintMarker.setLatLng(this._hintMarker._orgLatLng); + } + } + // calculate the new latlng of marker if the snapped latlng radius is out of min/max + this._hintMarker.setLatLng(this._getNewDestinationOfHintMarker()); + } + }, + _pxRadiusToMeter(radius){ + const center = this._centerMarker.getLatLng(); + const pointA = this._map.project(center); + const pointB = L.point(pointA.x + radius, pointA.y); + return this._map.unproject(pointB).distanceTo(center); + } }); diff --git a/src/js/Edit/L.PM.Edit.Circle.js b/src/js/Edit/L.PM.Edit.Circle.js index 9a78f98d..2ac686e3 100644 --- a/src/js/Edit/L.PM.Edit.Circle.js +++ b/src/js/Edit/L.PM.Edit.Circle.js @@ -1,5 +1,6 @@ import Edit from './L.PM.Edit'; import Utils from "../L.PM.Utils"; +import {destinationOnLine} from "../helpers"; Edit.Circle = Edit.extend({ _shape: 'Circle', @@ -51,6 +52,7 @@ Edit.Circle = Edit.extend({ this._centerMarker.off('dragstart', this._fireDragStart, this); this._centerMarker.off('drag', this._fireDrag, this); this._centerMarker.off('dragend', this._fireDragEnd, this); + this._outerMarker.off('drag',this._handleOuterMarkerSnapping, this); layer.pm._enabled = false; layer.pm._helperLayers.clearLayers(); @@ -108,6 +110,8 @@ Edit.Circle = Edit.extend({ applyOptions() { if (this.options.snappable) { this._initSnappableMarkers(); + // update marker latlng when snapped latlng radius is out of min/max + this._outerMarker.on('drag',this._handleOuterMarkerSnapping, this); // sync the hintline with hint marker this._outerMarker.on('move', this._syncHintLine, this); this._outerMarker.on('move', this._syncCircleRadius, this); @@ -163,6 +167,7 @@ Edit.Circle = Edit.extend({ return marker; }, _resizeCircle() { + this._outerMarker.setLatLng(this._getNewDestinationOfOuterMarker()); this._syncHintLine(); this._syncCircleRadius(); }, @@ -190,7 +195,14 @@ Edit.Circle = Edit.extend({ const distance = A.distanceTo(B); - this._layer.setRadius(distance); + if(this.options.minRadiusCircle && distance < this.options.minRadiusCircle) { + this._layer.setRadius(this.options.minRadiusCircle); + }else if(this.options.maxRadiusCircle && distance > this.options.maxRadiusCircle) { + this._layer.setRadius(this.options.maxRadiusCircle); + }else{ + this._layer.setRadius(distance); + } + this._updateHiddenPolyCircle(); }, _syncHintLine() { @@ -269,4 +281,29 @@ Edit.Circle = Edit.extend({ return this._map.unproject(pointB); }, + _getNewDestinationOfOuterMarker(){ + const latlng = this._centerMarker.getLatLng(); + let secondLatLng = this._outerMarker.getLatLng(); + const distance = latlng.distanceTo(secondLatLng); + if(this.options.minRadiusCircle && distance < this.options.minRadiusCircle) { + secondLatLng = destinationOnLine(this._map,latlng,secondLatLng,this.options.minRadiusCircle); + }else if(this.options.maxRadiusCircle && distance > this.options.maxRadiusCircle) { + secondLatLng = destinationOnLine(this._map,latlng,secondLatLng,this.options.maxRadiusCircle); + } + return secondLatLng; + }, + _handleOuterMarkerSnapping(){ + if(this._outerMarker._snapped) { + const latlng = this._centerMarker.getLatLng(); + const secondLatLng = this._outerMarker.getLatLng(); + const distance = latlng.distanceTo(secondLatLng); + if(this.options.minRadiusCircle && distance < this.options.minRadiusCircle) { + this._outerMarker.setLatLng(this._outerMarker._orgLatLng); + } else if(this.options.maxRadiusCircle && distance > this.options.maxRadiusCircle) { + this._outerMarker.setLatLng(this._outerMarker._orgLatLng); + } + } + // calculate the new latlng of marker if radius is out of min/max + this._outerMarker.setLatLng(this._getNewDestinationOfOuterMarker()); + } }); diff --git a/src/js/Edit/L.PM.Edit.CircleMarker.js b/src/js/Edit/L.PM.Edit.CircleMarker.js index 38ddd805..a78bc624 100644 --- a/src/js/Edit/L.PM.Edit.CircleMarker.js +++ b/src/js/Edit/L.PM.Edit.CircleMarker.js @@ -1,5 +1,6 @@ import Edit from './L.PM.Edit'; import Utils from "../L.PM.Utils"; +import {destinationOnLine} from "../helpers"; Edit.CircleMarker = Edit.extend({ _shape: 'CircleMarker', @@ -54,6 +55,10 @@ Edit.CircleMarker = Edit.extend({ if (this.options.editable) { this._map.off('move', this._syncMarkers, this); + if(this._outerMarker) { + // update marker latlng when snapped latlng radius is out of min/max + this._outerMarker.on('drag', this._handleOuterMarkerSnapping, this); + } } else { this._map.off('move', this._updateHiddenPolyCircle, this); } @@ -106,6 +111,10 @@ Edit.CircleMarker = Edit.extend({ if (this.options.snappable) { if (this.options.editable) { this._initSnappableMarkers(); + if(this.options.editable){ + // update marker latlng when snapped latlng radius is out of min/max + this._outerMarker.on('drag',this._handleOuterMarkerSnapping, this); + } // sync the hintline with hint marker this._outerMarker.on('move', this._syncHintLine, this); this._outerMarker.on('move', this._syncCircleRadius, this); @@ -218,6 +227,7 @@ Edit.CircleMarker = Edit.extend({ this._updateHiddenPolyCircle(); }, _resizeCircle() { + this._outerMarker.setLatLng(this._getNewDestinationOfOuterMarker()); this._syncHintLine(); this._syncCircleRadius(); }, @@ -226,14 +236,19 @@ Edit.CircleMarker = Edit.extend({ const B = this._outerMarker.getLatLng(); const distance = this._map.project(A).distanceTo(this._map.project(B)); + if(this.options.minRadiusCircleMarker && distance < this.options.minRadiusCircleMarker) { + this._layer.setRadius(this.options.minRadiusCircleMarker); + }else if(this.options.maxRadiusCircleMarker && distance > this.options.maxRadiusCircleMarker) { + this._layer.setRadius(this.options.maxRadiusCircleMarker); + }else{ + this._layer.setRadius(distance); + } - this._layer.setRadius(distance); this._updateHiddenPolyCircle(); }, _syncHintLine() { const A = this._centerMarker.getLatLng(); const B = this._outerMarker.getLatLng(); - // set coords for hintline from marker to last vertex of drawin polyline this._hintline.setLatLngs([A, B]); }, @@ -321,5 +336,36 @@ Edit.CircleMarker = Edit.extend({ this._hiddenPolyCircle._parentCopy = this._layer } } + }, + _getNewDestinationOfOuterMarker(){ + const latlng = this._centerMarker.getLatLng(); + let secondLatLng = this._outerMarker.getLatLng(); + const distance = this._map.project(latlng).distanceTo(this._map.project(secondLatLng)); + if(this.options.minRadiusCircleMarker && distance < this.options.minRadiusCircleMarker) { + secondLatLng = destinationOnLine(this._map,latlng,secondLatLng,this._pxRadiusToMeter(this.options.minRadiusCircleMarker)); + }else if(this.options.maxRadiusCircleMarker && distance > this.options.maxRadiusCircleMarker) { + secondLatLng = destinationOnLine(this._map,latlng,secondLatLng,this._pxRadiusToMeter(this.options.maxRadiusCircleMarker)); + } + return secondLatLng; + }, + _handleOuterMarkerSnapping(){ + if(this._outerMarker._snapped) { + const latlng = this._centerMarker.getLatLng(); + const secondLatLng = this._outerMarker.getLatLng(); + const distance = this._map.project(latlng).distanceTo(this._map.project(secondLatLng)); + if(this.options.minRadiusCircleMarker && distance < this.options.minRadiusCircleMarker) { + this._outerMarker.setLatLng(this._outerMarker._orgLatLng); + } else if(this.options.maxRadiusCircleMarker && distance > this.options.maxRadiusCircleMarker) { + this._outerMarker.setLatLng(this._outerMarker._orgLatLng); + } + } + // calculate the new latlng of marker if radius is out of min/max + this._outerMarker.setLatLng(this._getNewDestinationOfOuterMarker()); + }, + _pxRadiusToMeter(radius){ + const center = this._centerMarker.getLatLng(); + const pointA = this._map.project(center); + const pointB = L.point(pointA.x + radius, pointA.y); + return this._map.unproject(pointB).distanceTo(center); } }); diff --git a/src/js/L.PM.Map.js b/src/js/L.PM.Map.js index 6f409b25..9cc63298 100644 --- a/src/js/L.PM.Map.js +++ b/src/js/L.PM.Map.js @@ -88,10 +88,21 @@ const Map = L.Class.extend({ ...o }; + // check if switched the editable mode for CircleMarker while drawing + let reenableCircleMarker = false; + if(this.map.pm.Draw.CircleMarker.enabled() && this.map.pm.Draw.CircleMarker.options.editable !== options.editable){ + this.map.pm.Draw.CircleMarker.disable(); + reenableCircleMarker = true; + } + // enable options for Drawing Shapes this.map.pm.Draw.shapes.forEach(shape => { this.map.pm.Draw[shape].setOptions(options) - }) + }); + + if(reenableCircleMarker){ + this.map.pm.Draw.CircleMarker.enable(); + } // enable options for Editing const layers = findLayers(this.map); diff --git a/src/js/Mixins/Snapping.js b/src/js/Mixins/Snapping.js index e491eafd..79c9ed4e 100644 --- a/src/js/Mixins/Snapping.js +++ b/src/js/Mixins/Snapping.js @@ -122,6 +122,7 @@ const SnapMixin = { if (closestLayer.distance < minDistance) { // snap the marker + marker._orgLatLng = marker.getLatLng(); marker.setLatLng(snapLatLng); marker._snapped = true; diff --git a/src/js/helpers/index.js b/src/js/helpers/index.js index f61bf673..4ad24d3d 100644 --- a/src/js/helpers/index.js +++ b/src/js/helpers/index.js @@ -39,7 +39,7 @@ function destinationVincenty(lonlat, brng, dist) { // rewritten to work with lea f: 1 / 298.257223563 }; - const { a, b, f } = VincentyConstants; + const {a, b, f} = VincentyConstants; const lon1 = lonlat.lng; const lat1 = lonlat.lat; const s = dist; @@ -84,6 +84,7 @@ function destinationVincenty(lonlat, brng, dist) { // rewritten to work with lea return L.latLng(lamFunc, lat2a); } + export function createGeodesicPolygon(origin, radius, sides, rotation) { let angle; let newLonlat; @@ -100,6 +101,41 @@ export function createGeodesicPolygon(origin, radius, sides, rotation) { return points; } +/* Copied from L.GeometryUtil */ +function destination(latlng, heading, distance) { + heading = (heading + 360) % 360; + const rad = Math.PI / 180; + const radInv = 180 / Math.PI; + const R = 6378137; // approximation of Earth's radius + const lon1 = latlng.lng * rad; + const lat1 = latlng.lat * rad; + const rheading = heading * rad; + const sinLat1 = Math.sin(lat1); + const cosLat1 = Math.cos(lat1); + const cosDistR = Math.cos(distance / R); + const sinDistR = Math.sin(distance / R); + const lat2 = Math.asin(sinLat1 * cosDistR + cosLat1 * sinDistR * Math.cos(rheading)); + let lon2 = lon1 + Math.atan2(Math.sin(rheading) * sinDistR * cosLat1, cosDistR - sinLat1 * Math.sin(lat2)); + lon2 *= radInv; + lon2 = lon2 > 180 ? lon2 - 360 : lon2 < -180 ? lon2 + 360 : lon2; + return L.latLng([lat2 * radInv, lon2]); +} +/* Copied from L.GeometryUtil */ +function angle(map, latlngA, latlngB) { + const pointA = map.latLngToContainerPoint(latlngA); + const pointB = map.latLngToContainerPoint(latlngB); + let angleDeg = Math.atan2(pointB.y - pointA.y, pointB.x - pointA.x) * 180 / Math.PI + 90; + angleDeg += angleDeg < 0 ? 360 : 0; + return angleDeg; + +} + +export function destinationOnLine(map, latlngA, latlngB, distance) { + const angleDeg = angle(map, latlngA, latlngB); + return destination(latlngA, angleDeg, distance); +} + + // this function is used with the .sort(prioritiseSort(key, sortingOrder)) function of arrays export function prioritiseSort(key, _sortingOrder, order = 'asc') { /* the sorting order has all possible keys (lowercase) with the index and then it is sorted by the key on the object */ From baa29a2c63f67d496cc929b677aea9964f25ecf4 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Sat, 7 Nov 2020 21:35:28 +0100 Subject: [PATCH 26/30] Add event support for nested LayerGroups (#695). Fixes #549 (minor) * WIP * Layergroup refactoring -> events now called directly instead over a listener on the child layer * Fix double import --- .../fixtures/FeatureCollectionEventFire.json | 29 ++++ cypress/integration/layergroup.spec.js | 142 +++++++++++++++++- cypress/support/commands.js | 13 ++ src/js/Draw/L.PM.Draw.Circle.js | 9 +- src/js/Draw/L.PM.Draw.CircleMarker.js | 13 +- src/js/Draw/L.PM.Draw.Cut.js | 6 +- src/js/Draw/L.PM.Draw.Line.js | 9 +- src/js/Draw/L.PM.Draw.Marker.js | 8 +- src/js/Draw/L.PM.Draw.Polygon.js | 3 +- src/js/Draw/L.PM.Draw.Rectangle.js | 7 +- src/js/Draw/L.PM.Draw.js | 4 +- src/js/Edit/L.PM.Edit.Circle.js | 22 +-- src/js/Edit/L.PM.Edit.CircleMarker.js | 20 +-- src/js/Edit/L.PM.Edit.ImageOverlay.js | 9 +- src/js/Edit/L.PM.Edit.LayerGroup.js | 142 ++++++++++-------- src/js/Edit/L.PM.Edit.Line.js | 20 +-- src/js/Edit/L.PM.Edit.Marker.js | 14 +- src/js/Edit/L.PM.Edit.Rectangle.js | 7 +- src/js/Edit/L.PM.Edit.js | 2 +- src/js/L.PM.Map.js | 2 +- src/js/L.PM.Utils.js | 44 ++++++ src/js/Mixins/Dragging.js | 8 +- src/js/Mixins/Modes/Mode.Drag.js | 2 +- src/js/Mixins/Modes/Mode.Edit.js | 4 +- src/js/Mixins/Modes/Mode.Removal.js | 12 +- src/js/Mixins/Snapping.js | 12 +- src/js/Toolbar/L.Controls.js | 5 +- 27 files changed, 406 insertions(+), 162 deletions(-) create mode 100644 cypress/fixtures/FeatureCollectionEventFire.json diff --git a/cypress/fixtures/FeatureCollectionEventFire.json b/cypress/fixtures/FeatureCollectionEventFire.json new file mode 100644 index 00000000..bdf678de --- /dev/null +++ b/cypress/fixtures/FeatureCollectionEventFire.json @@ -0,0 +1,29 @@ +{ + "type":"FeatureCollection", + "features":[ + { + "type":"Feature", + "geometry":{ + "type":"MultiPoint", + "coordinates":[ + [ + 32.83604819, + 40.0169319 + ] + ] + } + }, + { + "type":"Feature", + "geometry":{ + "type":"MultiPoint", + "coordinates":[ + [ + 32.8298979, + 40.02567418 + ] + ] + } + } + ] +} diff --git a/cypress/integration/layergroup.spec.js b/cypress/integration/layergroup.spec.js index 6706430c..f49d908f 100644 --- a/cypress/integration/layergroup.spec.js +++ b/cypress/integration/layergroup.spec.js @@ -1,5 +1,5 @@ describe('Edit LayerGroup', () => { - const mapSelector = '#map'; + const mapSelector = '#map'; it('correctly enables geojson featureCollection', () => { cy.drawShape('FeatureCollectionWithCircles'); @@ -43,8 +43,7 @@ describe('Edit LayerGroup', () => { }); it('supports clearLayers', () => { - - cy.window().then(({ L, map }) => { + cy.window().then(({L, map}) => { const featureGroup = new L.FeatureGroup(); featureGroup.addTo(map) featureGroup.addLayer(new L.Marker([19.04469, 72.9258])); @@ -60,10 +59,10 @@ describe('Edit LayerGroup', () => { let fg2; // Add layer to group - cy.window().then(({ L, map }) => { + cy.window().then(({L, map}) => { fg = L.featureGroup().addTo(map); fg2 = L.featureGroup().addTo(map); - map.on('click',(e)=>console.log(map.latLngToContainerPoint(e.latlng))); + map.on('click', (e) => console.log(map.latLngToContainerPoint(e.latlng))); map.pm.setGlobalOptions({layerGroup: fg}); @@ -80,7 +79,7 @@ describe('Edit LayerGroup', () => { }); // check if layer is on group and will be removed from map, when group is removed - cy.window().then(({ map }) => { + cy.window().then(({map}) => { fg.removeFrom(map); cy.hasLayers(3); @@ -88,7 +87,7 @@ describe('Edit LayerGroup', () => { expect(count).to.equal(1); }); // delete layer from group and map - cy.window().then(({ map }) => { + cy.window().then(({map}) => { fg.addTo(map); cy.toolbarButton('delete') @@ -111,7 +110,7 @@ describe('Edit LayerGroup', () => { .click(200, 200) .click(400, 350); }); - cy.window().then(({ map }) => { + cy.window().then(({map}) => { map.pm.setGlobalOptions({layerGroup: fg2}); cy.hasLayers(5); @@ -149,4 +148,131 @@ describe('Edit LayerGroup', () => { cy.hasLayers(4); }); }) + + + it('pass the fired event of the layer to group', () => { + let fg; + let firedEvent = ""; + + cy.window().then(({map, L}) => { + fg = L.featureGroup(); + fg.on("pm:cut", (e) => { + firedEvent = e.type; + }); + + map.on('pm:create', (e) => { + e.layer.addTo(fg); + }) + }); + // activate polygon drawing + cy.toolbarButton('polygon') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + // draw a polygon + cy.get(mapSelector) + .click(90, 250) + .click(150, 50) + .click(500, 50) + .click(500, 300) + .click(300, 350) + .click(90, 250); + + // activate cutting drawing + cy.toolbarButton('cut') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + // draw a polygon to cut + cy.get(mapSelector) + .click(450, 100) + .click(450, 150) + .click(400, 150) + .click(390, 140) + .click(390, 100) + .click(450, 100); + + cy.window().then(() => { + expect(firedEvent).to.equal('pm:cut'); + }); + }); + + it('event is fired only once if group has multiple sub-groups with the same layer', () => { + + let firedEventCount = 0; + cy.window().then(({map, L}) => { + const group = L.featureGroup().addTo(map); + const layers = L.featureGroup().addTo(group); + const markers = L.featureGroup().addTo(group); + const markersChild = L.featureGroup().addTo(markers); + L.marker(map.getCenter()).addTo(layers).addTo(markers).addTo(markersChild); + + group.on('pm:enable', () => { + firedEventCount += 1 + }); + }); + + cy.wait(100); + + cy.toolbarButton('edit').click(); + + cy.window().then(() => { + expect(firedEventCount).to.equal(1); + }); + }); + + it('event is fired on every parent group of a layer (once)', () => { + + let firedEventCount = 0; + cy.window().then(({map, L}) => { + const group = L.featureGroup().addTo(map); + const layers = L.featureGroup().addTo(group); + const markers = L.featureGroup().addTo(group); + const markersChild = L.featureGroup().addTo(markers); + L.marker(map.getCenter()).addTo(layers).addTo(markers).addTo(markersChild); + + group.on('pm:enable', () => { + firedEventCount += 1 + }); + layers.on('pm:enable', () => { + firedEventCount += 1 + }); + markers.on('pm:enable', () => { + firedEventCount += 1 + }); + markersChild.on('pm:enable', () => { + firedEventCount += 1 + }); + }); + + cy.wait(100); + + cy.toolbarButton('edit').click(); + + cy.window().then(() => { + expect(firedEventCount).to.equal(4); + }); + }); + + it('event is fired on L.geoJson when it has FeatureCollections', () => { + cy.drawShape('FeatureCollectionEventFire'); + + let firedEventCount = 0; + cy.get('@feature').then(feature => { + feature.on('pm:enable', () => { + firedEventCount += 1 + }); + }); + + cy.wait(100); + + cy.toolbarButton('edit').click(); + + cy.window().then(() => { + expect(firedEventCount).to.equal(2); + }); + }); + }); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 8e465516..1eb047e3 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -173,6 +173,19 @@ Cypress.Commands.add('drawShape', (shape, ignore) => { }); } + if (shape === 'FeatureCollectionEventFire') { + cy.fixture(shape) + .then(json => { + // + const layer = L.geoJSON(json).addTo(map); + const bounds = layer.getBounds(); + map.fitBounds(bounds); + + return layer; + }) + .as('feature'); + } + if (shape === 'FeatureCollectionWithCircles') { cy.fixture(shape, ignore) .then(json => { diff --git a/src/js/Draw/L.PM.Draw.Circle.js b/src/js/Draw/L.PM.Draw.Circle.js index 621a21b8..61f13f79 100644 --- a/src/js/Draw/L.PM.Draw.Circle.js +++ b/src/js/Draw/L.PM.Draw.Circle.js @@ -1,6 +1,7 @@ import Draw from './L.PM.Draw'; import {destinationOnLine, getTranslation} from '../helpers'; +import Utils from "../L.PM.Utils"; Draw.Circle = Draw.extend({ initialize(map) { @@ -83,7 +84,7 @@ Draw.Circle = Draw.extend({ this._otherSnapLayers = []; // fire drawstart event - this._map.fire('pm:drawstart', { + Utils._fireEvent(this._map,'pm:drawstart', { shape: this._shape, workingLayer: this._layer, }); @@ -120,7 +121,7 @@ Draw.Circle = Draw.extend({ } // fire drawend event - this._map.fire('pm:drawend', { shape: this._shape }); + Utils._fireEvent(this._map,'pm:drawend', { shape: this._shape }); this._setGlobalDrawMode(); }, enabled() { @@ -205,7 +206,7 @@ Draw.Circle = Draw.extend({ getTranslation('tooltips.finishCircle') ); - this._layer.fire('pm:centerplaced', { + Utils._fireEvent(this._layer,'pm:centerplaced', { workingLayer: this._layer, latlng, shape: this._shape @@ -249,7 +250,7 @@ Draw.Circle = Draw.extend({ } // fire the pm:create event and pass shape and layer - this._map.fire('pm:create', { + Utils._fireEvent(this._map,'pm:create', { shape: this._shape, layer: circleLayer, }); diff --git a/src/js/Draw/L.PM.Draw.CircleMarker.js b/src/js/Draw/L.PM.Draw.CircleMarker.js index 226e1b74..98f3fc3f 100644 --- a/src/js/Draw/L.PM.Draw.CircleMarker.js +++ b/src/js/Draw/L.PM.Draw.CircleMarker.js @@ -1,6 +1,7 @@ import Draw from './L.PM.Draw'; -import {destinationOnLine, getTranslation} from '../helpers'; +import {destinationOnLine, getTranslation } from '../helpers'; +import Utils from "../L.PM.Utils"; Draw.CircleMarker = Draw.Marker.extend({ initialize(map) { @@ -116,7 +117,7 @@ Draw.CircleMarker = Draw.Marker.extend({ this._layer.bringToBack(); // fire drawstart event - this._map.fire('pm:drawstart', { + Utils._fireEvent(this._map,'pm:drawstart', { shape: this._shape, workingLayer: this._layer, }); @@ -168,7 +169,7 @@ Draw.CircleMarker = Draw.Marker.extend({ } // fire drawend event - this._map.fire('pm:drawend', { shape: this._shape }); + Utils._fireEvent(this._map,'pm:drawend', { shape: this._shape }); this._setGlobalDrawMode(); }, _placeCenterMarker(e) { @@ -202,7 +203,7 @@ Draw.CircleMarker = Draw.Marker.extend({ getTranslation('tooltips.finishCircle') ); - this._layer.fire('pm:centerplaced', { + Utils._fireEvent(this._layer,'pm:centerplaced', { shape: this._shape, workingLayer: this._layer, latlng, @@ -274,7 +275,7 @@ Draw.CircleMarker = Draw.Marker.extend({ } // fire the pm:create event and pass shape and marker - this._map.fire('pm:create', { + Utils._fireEvent(this._map,'pm:create', { shape: this._shape, marker, // DEPRECATED layer: marker, @@ -318,7 +319,7 @@ Draw.CircleMarker = Draw.Marker.extend({ } // fire the pm:create event and pass shape and layer - this._map.fire('pm:create', { + Utils._fireEvent(this._map,'pm:create', { shape: this._shape, layer: circleLayer, }); diff --git a/src/js/Draw/L.PM.Draw.Cut.js b/src/js/Draw/L.PM.Draw.Cut.js index 509e7a55..5225cf9f 100644 --- a/src/js/Draw/L.PM.Draw.Cut.js +++ b/src/js/Draw/L.PM.Draw.Cut.js @@ -39,21 +39,21 @@ Draw.Cut = Draw.Polygon.extend({ this._editedLayers.forEach(({layer, originalLayer}) =>{ // fire pm:cut on the cutted layer - originalLayer.fire('pm:cut', { + Utils._fireEvent(originalLayer,'pm:cut', { shape: this._shape, layer, originalLayer, }); // fire pm:cut on the map - this._map.fire('pm:cut', { + Utils._fireEvent(this._map,'pm:cut', { shape: this._shape, layer, originalLayer, }); // fire edit event after cut - originalLayer.fire('pm:edit', { layer: originalLayer, shape: originalLayer.pm.getShape()}); + Utils._fireEvent(originalLayer,'pm:edit', { layer: originalLayer, shape: originalLayer.pm.getShape()}); }); this._editedLayers = []; diff --git a/src/js/Draw/L.PM.Draw.Line.js b/src/js/Draw/L.PM.Draw.Line.js index a17e8850..2f6445d3 100644 --- a/src/js/Draw/L.PM.Draw.Line.js +++ b/src/js/Draw/L.PM.Draw.Line.js @@ -1,5 +1,6 @@ import kinks from '@turf/kinks'; import Draw from './L.PM.Draw'; +import Utils from "../L.PM.Utils"; import { getTranslation } from '../helpers'; @@ -98,7 +99,7 @@ Draw.Line = Draw.extend({ // fire drawstart event - this._map.fire('pm:drawstart', { + Utils._fireEvent(this._map,'pm:drawstart', { shape: this._shape, workingLayer: this._layer, }); @@ -140,7 +141,7 @@ Draw.Line = Draw.extend({ } // fire drawend event - this._map.fire('pm:drawend', { shape: this._shape }); + Utils._fireEvent(this._map,'pm:drawend', { shape: this._shape }); this._setGlobalDrawMode(); }, @@ -265,7 +266,7 @@ Draw.Line = Draw.extend({ this._hintline.setLatLngs([latlng, latlng]); - this._layer.fire('pm:vertexadded', { + Utils._fireEvent(this._layer,'pm:vertexadded', { shape: this._shape, workingLayer: this._layer, marker: newMarker, @@ -324,7 +325,7 @@ Draw.Line = Draw.extend({ this._finishLayer(polylineLayer); // fire the pm:create event and pass shape and layer - this._map.fire('pm:create', { + Utils._fireEvent(this._map,'pm:create', { shape: this._shape, layer: polylineLayer, }); diff --git a/src/js/Draw/L.PM.Draw.Marker.js b/src/js/Draw/L.PM.Draw.Marker.js index 131656c1..d718e01d 100644 --- a/src/js/Draw/L.PM.Draw.Marker.js +++ b/src/js/Draw/L.PM.Draw.Marker.js @@ -1,6 +1,6 @@ import Draw from './L.PM.Draw'; - import { getTranslation } from '../helpers'; +import Utils from "../L.PM.Utils"; Draw.Marker = Draw.extend({ initialize(map) { @@ -57,7 +57,7 @@ Draw.Marker = Draw.extend({ } // fire drawstart event - this._map.fire('pm:drawstart', { + Utils._fireEvent(this._map,'pm:drawstart', { shape: this._shape, workingLayer: this._layer, }); @@ -98,7 +98,7 @@ Draw.Marker = Draw.extend({ } // fire drawend event - this._map.fire('pm:drawend', { shape: this._shape }); + Utils._fireEvent(this._map,'pm:drawend', { shape: this._shape }); this._setGlobalDrawMode(); }, enabled() { @@ -161,7 +161,7 @@ Draw.Marker = Draw.extend({ } // fire the pm:create event and pass shape and marker - this._map.fire('pm:create', { + Utils._fireEvent(this._map,'pm:create', { shape: this._shape, marker, // DEPRECATED layer: marker, diff --git a/src/js/Draw/L.PM.Draw.Polygon.js b/src/js/Draw/L.PM.Draw.Polygon.js index d76c2b2f..6cb40490 100644 --- a/src/js/Draw/L.PM.Draw.Polygon.js +++ b/src/js/Draw/L.PM.Draw.Polygon.js @@ -1,4 +1,5 @@ import Draw from './L.PM.Draw'; +import Utils from "../L.PM.Utils"; import { getTranslation } from '../helpers'; @@ -77,7 +78,7 @@ Draw.Polygon = Draw.Line.extend({ this._finishLayer(polygonLayer); // fire the pm:create event and pass shape and layer - this._map.fire('pm:create', { + Utils._fireEvent(this._map,'pm:create', { shape: this._shape, layer: polygonLayer, }); diff --git a/src/js/Draw/L.PM.Draw.Rectangle.js b/src/js/Draw/L.PM.Draw.Rectangle.js index ba1f2912..e8e52696 100644 --- a/src/js/Draw/L.PM.Draw.Rectangle.js +++ b/src/js/Draw/L.PM.Draw.Rectangle.js @@ -1,4 +1,5 @@ import Draw from './L.PM.Draw'; +import Utils from "../L.PM.Utils"; import { getTranslation } from '../helpers'; @@ -94,7 +95,7 @@ Draw.Rectangle = Draw.extend({ this._otherSnapLayers = []; // fire drawstart event - this._map.fire('pm:drawstart', { + Utils._fireEvent(this._map,'pm:drawstart', { shape: this._shape, workingLayer: this._layer, }); @@ -130,7 +131,7 @@ Draw.Rectangle = Draw.extend({ this._cleanupSnapping(); } // fire drawend event - this._map.fire('pm:drawend', { shape: this._shape }); + Utils._fireEvent(this._map,'pm:drawend', { shape: this._shape }); this._setGlobalDrawMode(); }, @@ -255,7 +256,7 @@ Draw.Rectangle = Draw.extend({ this._finishLayer(rectangleLayer); // fire the pm:create event and pass shape and layer - this._map.fire('pm:create', { + Utils._fireEvent(this._map,'pm:create', { shape: this._shape, layer: rectangleLayer, }); diff --git a/src/js/Draw/L.PM.Draw.js b/src/js/Draw/L.PM.Draw.js index 47df197b..5769c0a4 100644 --- a/src/js/Draw/L.PM.Draw.js +++ b/src/js/Draw/L.PM.Draw.js @@ -89,12 +89,12 @@ const Draw = L.Class.extend({ _setGlobalDrawMode() { // extended to all PM.Draw shapes if (this._shape === "Cut") { - this._map.fire('pm:globalcutmodetoggled', { + Utils._fireEvent(this._map,'pm:globalcutmodetoggled', { enabled: !!this._enabled, map: this._map, }); } else { - this._map.fire('pm:globaldrawmodetoggled', { + Utils._fireEvent(this._map,'pm:globaldrawmodetoggled', { enabled: this._enabled, shape: this._shape, map: this._map, diff --git a/src/js/Edit/L.PM.Edit.Circle.js b/src/js/Edit/L.PM.Edit.Circle.js index 2ac686e3..1038617d 100644 --- a/src/js/Edit/L.PM.Edit.Circle.js +++ b/src/js/Edit/L.PM.Edit.Circle.js @@ -36,7 +36,7 @@ Edit.Circle = Edit.extend({ // create polygon around the circle border this._updateHiddenPolyCircle(); - this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:enable', { layer: this._layer, shape: this.getShape() }); }, disable(layer = this._layer) { // if it's not enabled, it doesn't need to be disabled @@ -66,11 +66,11 @@ Edit.Circle = Edit.extend({ if (this._layerEdited) { - this._layer.fire('pm:update', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:update', { layer: this._layer, shape: this.getShape() }); } this._layerEdited = false; - this._layer.fire('pm:disable', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:disable', { layer: this._layer, shape: this.getShape() }); return true; }, enabled() { @@ -183,7 +183,7 @@ Edit.Circle = Edit.extend({ this._updateHiddenPolyCircle(); - this._layer.fire('pm:centerplaced', { + Utils._fireEvent(this._layer,'pm:centerplaced', { layer: this._layer, latlng: center, shape: this.getShape() @@ -223,7 +223,7 @@ Edit.Circle = Edit.extend({ this._layer.off('pm:dragstart', this._unsnap, this); }, _onMarkerDragStart(e) { - this._layer.fire('pm:markerdragstart', { + Utils._fireEvent(this._layer,'pm:markerdragstart', { layer: this._layer, markerEvent: e, shape: this.getShape(), @@ -231,7 +231,7 @@ Edit.Circle = Edit.extend({ }); }, _onMarkerDrag(e) { - this._layer.fire('pm:markerdrag', { + Utils._fireEvent(this._layer,'pm:markerdrag', { layer: this._layer, markerEvent: e, shape: this.getShape(), @@ -243,7 +243,7 @@ Edit.Circle = Edit.extend({ this._fireEdit(); // fire markerdragend event - this._layer.fire('pm:markerdragend', { + Utils._fireEvent(this._layer,'pm:markerdragend', { layer: this._layer, markerEvent: e, shape: this.getShape(), @@ -252,17 +252,17 @@ Edit.Circle = Edit.extend({ }, _fireEdit() { // fire edit event - this._layer.fire('pm:edit', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:edit', { layer: this._layer, shape: this.getShape() }); this._layerEdited = true; }, _fireDragStart() { - this._layer.fire('pm:dragstart', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:dragstart', { layer: this._layer, shape: this.getShape() }); }, _fireDrag(e) { - this._layer.fire('pm:drag', Object.assign({},e, {shape:this.getShape()})); + Utils._fireEvent(this._layer,'pm:drag', Object.assign({},e, {shape:this.getShape()})); }, _fireDragEnd() { - this._layer.fire('pm:dragend', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:dragend', { layer: this._layer, shape: this.getShape() }); }, _updateHiddenPolyCircle() { if (this._hiddenPolyCircle) { diff --git a/src/js/Edit/L.PM.Edit.CircleMarker.js b/src/js/Edit/L.PM.Edit.CircleMarker.js index a78bc624..5936345c 100644 --- a/src/js/Edit/L.PM.Edit.CircleMarker.js +++ b/src/js/Edit/L.PM.Edit.CircleMarker.js @@ -36,7 +36,7 @@ Edit.CircleMarker = Edit.extend({ // create polygon around the circle border this._updateHiddenPolyCircle(); - this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:enable', { layer: this._layer, shape: this.getShape() }); }, disable(layer = this._layer) { // prevent disabling if layer is being dragged @@ -70,10 +70,10 @@ Edit.CircleMarker = Edit.extend({ // only fire events if it was enabled before if (!this.enabled()) { if (this._layerEdited) { - this._layer.fire('pm:update', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:update', { layer: this._layer, shape: this.getShape() }); } this._layerEdited = false; - this._layer.fire('pm:disable', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:disable', { layer: this._layer, shape: this.getShape() }); } layer.pm._enabled = false; @@ -211,7 +211,7 @@ Edit.CircleMarker = Edit.extend({ this._outerMarker.setLatLng(outer); this._syncHintLine(); - this._layer.fire('pm:centerplaced', { + Utils._fireEvent(this._layer,'pm:centerplaced', { layer: this._layer, latlng: center, shape: this.getShape() @@ -257,14 +257,14 @@ Edit.CircleMarker = Edit.extend({ this.disable(); } this._layer.remove(); - this._layer.fire('pm:remove', { layer: this._layer, shape: this.getShape() }); - this._map.fire('pm:remove', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:remove', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._map,'pm:remove', { layer: this._layer, shape: this.getShape() }); }, _onDragStart(){ this._map.pm.Draw.CircleMarker._layerIsDragging = true; }, _onMarkerDragStart(e) { - this._layer.fire('pm:markerdragstart', { + Utils._fireEvent(this._layer,'pm:markerdragstart', { markerEvent: e, layer: this._layer, shape: this.getShape(), @@ -272,7 +272,7 @@ Edit.CircleMarker = Edit.extend({ }); }, _onMarkerDrag(e) { - this._layer.fire('pm:markerdrag', { + Utils._fireEvent(this._layer,'pm:markerdrag', { layer: this._layer, markerEvent: e, shape: this.getShape(), @@ -281,7 +281,7 @@ Edit.CircleMarker = Edit.extend({ }, _onMarkerDragEnd(e) { this._map.pm.Draw.CircleMarker._layerIsDragging = false; - this._layer.fire('pm:markerdragend', { + Utils._fireEvent(this._layer,'pm:markerdragend', { layer: this._layer, markerEvent: e, shape: this.getShape(), @@ -290,7 +290,7 @@ Edit.CircleMarker = Edit.extend({ }, _fireEdit() { // fire edit event - this._layer.fire('pm:edit', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:edit', { layer: this._layer, shape: this.getShape() }); this._layerEdited = true; }, // _initSnappableMarkers when option editable is not true diff --git a/src/js/Edit/L.PM.Edit.ImageOverlay.js b/src/js/Edit/L.PM.Edit.ImageOverlay.js index 333ab9db..9de25642 100644 --- a/src/js/Edit/L.PM.Edit.ImageOverlay.js +++ b/src/js/Edit/L.PM.Edit.ImageOverlay.js @@ -1,4 +1,5 @@ import Edit from './L.PM.Edit'; +import Utils from "../L.PM.Utils"; Edit.ImageOverlay = Edit.extend({ _shape: 'ImageOverlay', @@ -37,7 +38,7 @@ Edit.ImageOverlay = Edit.extend({ // create markers for four corners of ImageOverlay this._otherSnapLayers = L.PM.Edit.Rectangle.prototype._findCorners.apply(this); - this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:enable', { layer: this._layer, shape: this.getShape() }); }, disable(layer = this._layer) { // prevent disabling if layer is being dragged @@ -55,10 +56,10 @@ Edit.ImageOverlay = Edit.extend({ // only fire events if it was enabled before if (!this.enabled()) { if (this._layerEdited) { - layer.fire('pm:update', { layer, shape: this.getShape() }); + Utils._fireEvent(layer,'pm:update', { layer, shape: this.getShape() }); } this._layerEdited = false; - layer.fire('pm:disable', { layer, shape: this.getShape() }); + Utils._fireEvent(layer,'pm:disable', { layer, shape: this.getShape() }); } this._layer.off('contextmenu', this._removeMarker, this); @@ -67,7 +68,7 @@ Edit.ImageOverlay = Edit.extend({ }, _fireEdit() { // fire edit event - this._layer.fire('pm:edit', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:edit', { layer: this._layer, shape: this.getShape() }); this._layerEdited = true; }, }); diff --git a/src/js/Edit/L.PM.Edit.LayerGroup.js b/src/js/Edit/L.PM.Edit.LayerGroup.js index d1636d5a..f162f1c3 100644 --- a/src/js/Edit/L.PM.Edit.LayerGroup.js +++ b/src/js/Edit/L.PM.Edit.LayerGroup.js @@ -14,106 +14,126 @@ Edit.LayerGroup = L.Class.extend({ // if a new layer is added to the group, reinitialize // This only works for FeatureGroups, not LayerGroups // https://github.com/Leaflet/Leaflet/issues/4861 - this._layerGroup.on('layeradd', e => { + + const addThrottle = (e) => { if (e.target._pmTempLayer) { return; } - this._layers = this.findLayers(); - // init the newly added layer - if (e.layer.pm) { - this._initLayer(e.layer); - } + const _initLayers = this._layers.filter((layer) => !layer.pm._parentLayerGroup || !(this._layerGroup._leaflet_id in layer.pm._parentLayerGroup)); + // init the newly added layers (can be multiple because of the throttle) + _initLayers.forEach((layer) => { + this._initLayer(layer); + }); // if editing was already enabled for this group, enable it again // so the new layers are enabled - if (e.target.pm.enabled()) { - this.enable(this.getOptions()); + if (_initLayers.length > 0) { + if (this.enabled()) { + this.enable(this.getOptions()); + } } - }); + }; + this._layerGroup.on('layeradd', L.Util.throttle(addThrottle, 100, this), this); + + // Remove the layergroup from the layer + this._layerGroup.on('layerremove', (e) => { + this._removeLayerFromGroup(e.target); + }, this); - // if a layer is removed from the group, calc the layers list again - this._layerGroup.on('layerremove', e => { + const removeThrottle = (e) => { if (e.target._pmTempLayer) { return; } - this._layers = this.findLayers(); - }) + }; + // if a layer is removed from the group, calc the layers list again. + // we run this as throttle because the findLayers() is a larger function + this._layerGroup.on('layerremove', L.Util.throttle(removeThrottle, 100, this), this); }, - enable(options) { + enable(options, _layerIds = []) { this._options = options; this._layers.forEach(layer => { - layer.pm.enable(options); + if (layer instanceof L.LayerGroup) { + if (_layerIds.indexOf(layer._leaflet_id) === -1) { + _layerIds.push(layer._leaflet_id); + layer.pm.enable(options, _layerIds); + } + } else { + layer.pm.enable(options); + } }); }, - disable() { + disable(_layerIds = []) { this._layers.forEach(layer => { - layer.pm.disable(); + if (layer instanceof L.LayerGroup) { + if (_layerIds.indexOf(layer._leaflet_id) === -1) { + _layerIds.push(layer._leaflet_id); + layer.pm.disable(_layerIds); + } + } else { + layer.pm.disable(); + } }); }, - enabled() { - const enabled = this._layers.find(layer => layer.pm.enabled()); + enabled(_layerIds = []) { + + const enabled = this._layers.find((layer) => { + if (layer instanceof L.LayerGroup) { + if (_layerIds.indexOf(layer._leaflet_id) === -1) { + _layerIds.push(layer._leaflet_id); + return layer.pm.enabled(_layerIds); + } + return false; // enabled is already returned because this is not the first time, so we can return always false + } else { + return layer.pm.enabled(); + } + }); + return !!enabled; }, - toggleEdit(options) { + toggleEdit(options, _layerIds = []) { this._options = options; this._layers.forEach(layer => { - layer.pm.toggleEdit(options); + if (layer instanceof L.LayerGroup) { + if (_layerIds.indexOf(layer._leaflet_id) === -1) { + _layerIds.push(layer._leaflet_id); + layer.pm.toggleEdit(options, _layerIds); + } + } else { + layer.pm.toggleEdit(options); + } }); }, _initLayer(layer) { - // available events - const availableEvents = [ - 'pm:edit', - 'pm:update', - 'pm:enable', - 'pm:disable', - 'pm:remove', - 'pm:dragstart', - 'pm:drag', - 'pm:dragend', - 'pm:snap', - 'pm:unsnap', - 'pm:cut', - 'pm:intersect', - 'pm:markerdragstart', - 'pm:markerdrag', - 'pm:markerdragend', - 'pm:vertexadded', - 'pm:vertexremoved', - 'pm:centerplaced', - ]; - - // listen to the events of the layers in this group - availableEvents.forEach(event => { - layer.on(event, this._fireEvent, this); - }); - - // add reference for the group to each layer inside said group - layer.pm._layerGroup = this._layerGroup; + // add reference for the group to each layer inside said group by id, a layer can have multiple groups + const id = L.Util.stamp(this._layerGroup); + if (!layer.pm._parentLayerGroup) { + layer.pm._parentLayerGroup = {}; + } + layer.pm._parentLayerGroup[id] = this._layerGroup; + }, + _removeLayerFromGroup(layer) { + if(layer.pm && layer.pm._layerGroup) { + const id = L.Util.stamp(this._layerGroup); + delete layer.pm._layerGroup[id]; + } }, findLayers() { // get all layers of the layer group let layers = this._layerGroup.getLayers(); - - // filter out layers that are no layerGroup - layers = layers.filter(layer => !(layer instanceof L.LayerGroup)); - // filter out layers that don't have leaflet-geoman layers = layers.filter(layer => !!layer.pm); - // filter out everything that's leaflet-geoman specific temporary stuff layers = layers.filter(layer => !layer._pmTempLayer); - // return them return layers; }, - _fireEvent(e) { - this._layerGroup.fireEvent(e.type, e); - }, dragging() { - const dragging = this._layers.find(layer => layer.pm.dragging()); - return !!dragging; + if (this._layers) { + const dragging = this._layers.find(layer => layer.pm.dragging()); + return !!dragging; + } + return false; }, getOptions() { return this._options; diff --git a/src/js/Edit/L.PM.Edit.Line.js b/src/js/Edit/L.PM.Edit.Line.js index 349f52a4..551dd04e 100644 --- a/src/js/Edit/L.PM.Edit.Line.js +++ b/src/js/Edit/L.PM.Edit.Line.js @@ -69,7 +69,7 @@ Edit.Line = Edit.extend({ }else{ this.cachedColor = undefined; } - this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:enable', { layer: this._layer, shape: this.getShape() }); }, disable(poly = this._layer) { // if it's not enabled, it doesn't need to be disabled @@ -110,10 +110,10 @@ Edit.Line = Edit.extend({ } if (this._layerEdited) { - this._layer.fire('pm:update', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:update', { layer: this._layer, shape: this.getShape() }); } this._layerEdited = false; - this._layer.fire('pm:disable', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:disable', { layer: this._layer, shape: this.getShape() }); return true; }, enabled() { @@ -297,7 +297,7 @@ Edit.Line = Edit.extend({ // fire edit event this._fireEdit(); - this._layer.fire('pm:vertexadded', { + Utils._fireEvent(this._layer,'pm:vertexadded', { layer: this._layer, marker: newM, indexPath: this.findDeepMarkerIndex(this._markers, newM).indexPath, @@ -357,7 +357,7 @@ Edit.Line = Edit.extend({ } // fire intersect event - this._layer.fire('pm:intersect', { + Utils._fireEvent(this._layer,'pm:intersect', { layer: this._layer, intersection: kinks(this._layer.toGeoJSON(15)), shape: this.getShape() @@ -500,7 +500,7 @@ Edit.Line = Edit.extend({ this._fireEdit(); // fire vertex removal event - this._layer.fire('pm:vertexremoved', { + Utils._fireEvent(this._layer,'pm:vertexremoved', { layer: this._layer, marker, indexPath, @@ -604,7 +604,7 @@ Edit.Line = Edit.extend({ const marker = e.target; const { indexPath } = this.findDeepMarkerIndex(this._markers, marker); - this._layer.fire('pm:markerdragstart', { + Utils._fireEvent(this._layer,'pm:markerdragstart', { layer: this._layer, markerEvent: e, indexPath, @@ -692,7 +692,7 @@ Edit.Line = Edit.extend({ if (!this.options.allowSelfIntersection) { this._handleLayerStyle(); } - this._layer.fire('pm:markerdrag', { + Utils._fireEvent(this._layer,'pm:markerdrag', { layer: this._layer, markerEvent: e, shape: this.getShape(), @@ -703,7 +703,7 @@ Edit.Line = Edit.extend({ const marker = e.target; const { indexPath } = this.findDeepMarkerIndex(this._markers, marker); - this._layer.fire('pm:markerdragend', { + Utils._fireEvent(this._layer,'pm:markerdragend', { layer: this._layer, markerEvent: e, indexPath, @@ -738,6 +738,6 @@ Edit.Line = Edit.extend({ _fireEdit() { // fire edit event this._layerEdited = true; - this._layer.fire('pm:edit', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:edit', { layer: this._layer, shape: this.getShape() }); }, }); diff --git a/src/js/Edit/L.PM.Edit.Marker.js b/src/js/Edit/L.PM.Edit.Marker.js index 0e2f9f7e..e35a5c70 100644 --- a/src/js/Edit/L.PM.Edit.Marker.js +++ b/src/js/Edit/L.PM.Edit.Marker.js @@ -1,4 +1,5 @@ import Edit from './L.PM.Edit'; +import Utils from "../L.PM.Utils"; Edit.Marker = Edit.extend({ _shape: 'Marker', @@ -19,10 +20,9 @@ Edit.Marker = Edit.extend({ return; } this.applyOptions(); - this._enabled = true; - this._layer.fire('pm:enable', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:enable', { layer: this._layer, shape: this.getShape() }); }, disable() { this._enabled = false; @@ -32,10 +32,10 @@ Edit.Marker = Edit.extend({ this._layer.off('contextmenu', this._removeMarker, this); - this._layer.fire('pm:disable', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:disable', { layer: this._layer, shape: this.getShape() }); if (this._layerEdited) { - this._layer.fire('pm:update', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(this._layer,'pm:update', { layer: this._layer, shape: this.getShape() }); } this._layerEdited = false; }, @@ -70,14 +70,14 @@ Edit.Marker = Edit.extend({ const marker = e.target; marker.remove(); // TODO: find out why this is fired manually, shouldn't it be catched by L.PM.Map 'layerremove'? - marker.fire('pm:remove', { layer: marker, shape: this.getShape() }); - this._map.fire('pm:remove', { layer: marker, shape: this.getShape() }); + Utils._fireEvent(marker,'pm:remove', { layer: marker, shape: this.getShape() }); + Utils._fireEvent(this._map,'pm:remove', { layer: marker, shape: this.getShape() }); }, _onDragEnd(e) { const marker = e.target; // fire the pm:edit event and pass shape and marker - marker.fire('pm:edit', { layer: this._layer, shape: this.getShape() }); + Utils._fireEvent(marker,'pm:edit', { layer: this._layer, shape: this.getShape() }); this._layerEdited = true; }, // overwrite initSnappableMarkers from Snapping.js Mixin diff --git a/src/js/Edit/L.PM.Edit.Rectangle.js b/src/js/Edit/L.PM.Edit.Rectangle.js index 9c9820a1..dbd984e1 100644 --- a/src/js/Edit/L.PM.Edit.Rectangle.js +++ b/src/js/Edit/L.PM.Edit.Rectangle.js @@ -2,6 +2,7 @@ // https://github.com/Leaflet/Leaflet.draw/blob/master/src/edit/handler/Edit.Rectangle.js import Edit from './L.PM.Edit'; +import Utils from "../L.PM.Utils"; Edit.Rectangle = Edit.Polygon.extend({ _shape: 'Rectangle', @@ -85,7 +86,7 @@ Edit.Rectangle = Edit.Polygon.extend({ // (Without this, it's occasionally possible for a marker to get stuck as 'snapped,' which prevents Rectangle resizing) draggedMarker._snapped = false; - this._layer.fire('pm:markerdragstart', { + Utils._fireEvent(this._layer,'pm:markerdragstart', { layer: this._layer, markerEvent: e, shape: this.getShape(), @@ -107,7 +108,7 @@ Edit.Rectangle = Edit.Polygon.extend({ this._adjustRectangleForMarkerMove(draggedMarker); } - this._layer.fire('pm:markerdrag', { + Utils._fireEvent(this._layer,'pm:markerdrag', { layer: this._layer, markerEvent: e, shape: this.getShape(), @@ -129,7 +130,7 @@ Edit.Rectangle = Edit.Polygon.extend({ // Update bounding box this._layer.setLatLngs(corners); - this._layer.fire('pm:markerdragend', { + Utils._fireEvent(this._layer,'pm:markerdragend', { layer: this._layer, markerEvent: e, shape: this.getShape(), diff --git a/src/js/Edit/L.PM.Edit.js b/src/js/Edit/L.PM.Edit.js index 90869ea3..98fd3dd1 100644 --- a/src/js/Edit/L.PM.Edit.js +++ b/src/js/Edit/L.PM.Edit.js @@ -23,7 +23,7 @@ const Edit = L.Class.extend({ }, getShape(){ return this._shape; - } + }, }); export default Edit; diff --git a/src/js/L.PM.Map.js b/src/js/L.PM.Map.js index 9cc63298..8acdec19 100644 --- a/src/js/L.PM.Map.js +++ b/src/js/L.PM.Map.js @@ -32,7 +32,7 @@ const Map = L.Class.extend({ L.PM.activeLang = lang; this.map.pm.Toolbar.reinit(); - this.map.fire("pm:langchange", { + Utils._fireEvent(this.map,"pm:langchange", { oldLang, activeLang: lang, fallback, diff --git a/src/js/L.PM.Utils.js b/src/js/L.PM.Utils.js index e1b0b7ab..663dda01 100644 --- a/src/js/L.PM.Utils.js +++ b/src/js/L.PM.Utils.js @@ -54,6 +54,50 @@ const Utils = { delete layer._tempPopupCopy; } }, + _fireEvent(layer,type,data,propagate = false) { + layer.fire(type, data, propagate); + + // fire event to all parent layers + const {groups} = this.getAllParentGroups(layer); + groups.forEach((group) => { + group.fire(type, data, propagate); + }); + }, + getAllParentGroups(layer){ + const groupIds = []; + const groups = []; + + // get every group layer once + const loopThroughParents = (_layer) => { + for (const _id in _layer._eventParents) { + if(groupIds.indexOf(_id) === -1){ + groupIds.push(_id); + const group = _layer._eventParents[_id]; + groups.push(group); + loopThroughParents(group) + } + } + }; + + // check if the last group fetch is under 1 sec, then we use the groups from before + if(!layer._pmLastGroupFetch || !layer._pmLastGroupFetch.time || (new Date().getTime() - layer._pmLastGroupFetch.time)> 1000){ + loopThroughParents(layer); + layer._pmLastGroupFetch = { + time: new Date().getTime(), + groups, + groupIds + } ; + return { + groupIds, + groups + } + }else{ + return { + groups: layer._pmLastGroupFetch.groups, + groupIds: layer._pmLastGroupFetch.groupIds + } + } + }, createGeodesicPolygon, getTranslation, findDeepCoordIndex(arr, latlng) { diff --git a/src/js/Mixins/Dragging.js b/src/js/Mixins/Dragging.js index b582acc7..9063bd01 100644 --- a/src/js/Mixins/Dragging.js +++ b/src/js/Mixins/Dragging.js @@ -1,3 +1,5 @@ +import Utils from "../L.PM.Utils"; + const DragMixin = { enableLayerDrag() { // before enabling layer drag, disable layer editing @@ -212,16 +214,16 @@ const DragMixin = { this._fireDrag(e); }, _fireDragStart() { - this._layer.fire('pm:dragstart', { + Utils._fireEvent(this._layer,'pm:dragstart', { layer: this._layer, shape: this.getShape() }); }, _fireDrag(e) { - this._layer.fire('pm:drag', Object.assign({},e, {shape:this.getShape()})); + Utils._fireEvent(this._layer,'pm:drag', Object.assign({},e, {shape:this.getShape()})); }, _fireDragEnd() { - this._layer.fire('pm:dragend', { + Utils._fireEvent(this._layer,'pm:dragend', { layer: this._layer, shape: this.getShape() }); diff --git a/src/js/Mixins/Modes/Mode.Drag.js b/src/js/Mixins/Modes/Mode.Drag.js index 97561872..b7dbb27b 100644 --- a/src/js/Mixins/Modes/Mode.Drag.js +++ b/src/js/Mixins/Modes/Mode.Drag.js @@ -65,7 +65,7 @@ const GlobalDragMode = { } }, _fireDragModeEvent(enabled) { - this.map.fire('pm:globaldragmodetoggled', { + Utils._fireEvent(this.map,'pm:globaldragmodetoggled', { enabled, map: this.map, }); diff --git a/src/js/Mixins/Modes/Mode.Edit.js b/src/js/Mixins/Modes/Mode.Edit.js index 2b70857c..44214bf6 100644 --- a/src/js/Mixins/Modes/Mode.Edit.js +++ b/src/js/Mixins/Modes/Mode.Edit.js @@ -9,7 +9,7 @@ const GlobalEditMode = { const options = { snappable: this._globalSnappingEnabled, ...o - } + }; const status = true; @@ -99,7 +99,7 @@ const GlobalEditMode = { this._addedLayers.push(layer); }, _fireEditModeEvent(enabled) { - this.map.fire('pm:globaleditmodetoggled', { + Utils._fireEvent(this.map,'pm:globaleditmodetoggled', { enabled, map: this.map, }); diff --git a/src/js/Mixins/Modes/Mode.Removal.js b/src/js/Mixins/Modes/Mode.Removal.js index d3554a69..448e2bd1 100644 --- a/src/js/Mixins/Modes/Mode.Removal.js +++ b/src/js/Mixins/Modes/Mode.Removal.js @@ -1,3 +1,5 @@ +import Utils from "../../L.PM.Utils"; + const GlobalRemovalMode = { enableGlobalRemovalMode() { const isRelevant = layer => @@ -68,7 +70,7 @@ const GlobalRemovalMode = { } }, _fireRemovalModeEvent(enabled) { - this.map.fire('pm:globalremovalmodetoggled', { + Utils._fireEvent(this.map,'pm:globalremovalmodetoggled', { enabled, map: this.map, }); @@ -84,11 +86,11 @@ const GlobalRemovalMode = { layer.removeFrom(this.map.pm._getContainingLayer()); layer.remove(); if(layer instanceof L.LayerGroup){ - layer.fire('pm:remove', { layer, shape: undefined }); - this.map.fire('pm:remove', { layer, shape: undefined }); + Utils._fireEvent(layer,'pm:remove', { layer, shape: undefined }); + Utils._fireEvent(this.map,'pm:remove', { layer, shape: undefined }); }else{ - layer.fire('pm:remove', { layer, shape: layer.pm.getShape() }); - this.map.fire('pm:remove', { layer, shape: layer.pm.getShape() }); + Utils._fireEvent(layer,'pm:remove', { layer, shape: layer.pm.getShape() }); + Utils._fireEvent(this.map,'pm:remove', { layer, shape: layer.pm.getShape() }); } } diff --git a/src/js/Mixins/Snapping.js b/src/js/Mixins/Snapping.js index 79c9ed4e..0fa0b010 100644 --- a/src/js/Mixins/Snapping.js +++ b/src/js/Mixins/Snapping.js @@ -117,8 +117,8 @@ const SnapMixin = { distance: closestLayer.distance, }; - eventInfo.marker.fire('pm:snapdrag', eventInfo); - this._layer.fire('pm:snapdrag', eventInfo); + Utils._fireEvent(eventInfo.marker,'pm:snapdrag', eventInfo); + Utils._fireEvent(this._layer,'pm:snapdrag', eventInfo); if (closestLayer.distance < minDistance) { // snap the marker @@ -130,8 +130,8 @@ const SnapMixin = { const triggerSnap = () => { this._snapLatLng = snapLatLng; - marker.fire('pm:snap', eventInfo); - this._layer.fire('pm:snap', eventInfo); + Utils._fireEvent(marker,'pm:snap', eventInfo); + Utils._fireEvent(this._layer,'pm:snap', eventInfo); }; // check if the snapping position differs from the last snap @@ -152,8 +152,8 @@ const SnapMixin = { marker._snapped = false; // and fire unsnap event - eventInfo.marker.fire('pm:unsnap', eventInfo); - this._layer.fire('pm:unsnap', eventInfo); + Utils._fireEvent(eventInfo.marker,'pm:unsnap', eventInfo); + Utils._fireEvent(this._layer,'pm:unsnap', eventInfo); } return true; diff --git a/src/js/Toolbar/L.Controls.js b/src/js/Toolbar/L.Controls.js index b63c7341..fe9356a7 100644 --- a/src/js/Toolbar/L.Controls.js +++ b/src/js/Toolbar/L.Controls.js @@ -1,4 +1,5 @@ import { getTranslation } from '../helpers'; +import Utils from "../L.PM.Utils"; const PMButton = L.Control.extend({ options: { @@ -147,7 +148,7 @@ const PMButton = L.Control.extend({ break; } } - this._map.fire('pm:actionclick', {text: action.text, action, btnName, button}); + Utils._fireEvent(this._map,'pm:actionclick', {text: action.text, action, btnName, button}); }; L.DomEvent.addListener(actionNode, 'click', actionClick, this); @@ -188,7 +189,7 @@ const PMButton = L.Control.extend({ break; } } - this._map.fire('pm:buttonclick', {btnName, button}); + Utils._fireEvent(this._map,'pm:buttonclick', {btnName, button}); }); L.DomEvent.addListener(newButton, 'click', this._triggerClick, this); } From 82355514786a7153943aa1382445fcfc9c4d7b32 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Wed, 18 Nov 2020 20:20:25 +0100 Subject: [PATCH 27/30] Added zh_tw translation and added missing texts (#707). Fixes #706 (minor) --- README.md | 2 +- src/assets/translations/de.json | 4 +++- src/assets/translations/en.json | 2 +- src/assets/translations/es.json | 4 +++- src/assets/translations/fr.json | 8 ++++++-- src/assets/translations/id.json | 8 ++++++-- src/assets/translations/index.js | 3 +++ src/assets/translations/it.json | 4 +++- src/assets/translations/nl.json | 4 +++- src/assets/translations/pl.json | 4 +++- src/assets/translations/pt_br.json | 9 ++++++--- src/assets/translations/ro.json | 4 +++- src/assets/translations/sv.json | 4 +++- src/assets/translations/zh.json | 8 ++++++-- src/assets/translations/zh_tw.json | 32 ++++++++++++++++++++++++++++++ 15 files changed, 82 insertions(+), 18 deletions(-) create mode 100644 src/assets/translations/zh_tw.json diff --git a/README.md b/README.md index b07f8780..06c8d5f4 100644 --- a/README.md +++ b/README.md @@ -579,7 +579,7 @@ Change the language of user-facing copy in leaflet-geoman map.pm.setLang('de'); ``` -Currently available languages are `en`, `de`, `it`, `ru`, `ro`, `es`, `fr`, `pt_br`, `id`, `zh`, `nl`, `el`, `pl`, `sv`, `da` and `hu`. +Currently available languages are `en`, `de`, `it`, `ru`, `ro`, `es`, `fr`, `pt_br`, `id`, `zh`, `zh_tw`, `nl`, `el`, `pl`, `sv`, `da` and `hu`. To add translations to the plugin, you can add [a translation file](src/assets/translations) via Pull Request. You can also provide your own custom translations. diff --git a/src/assets/translations/de.json b/src/assets/translations/de.json index f84b179d..5a99ec32 100644 --- a/src/assets/translations/de.json +++ b/src/assets/translations/de.json @@ -25,6 +25,8 @@ "dragButton": "Layer bewegen", "cutButton": "Layer schneiden", "deleteButton": "Layer löschen", - "drawCircleMarkerButton": "Kreismarker zeichnen" + "drawCircleMarkerButton": "Kreismarker zeichnen", + "snappingButton": "Bewegter Layer an andere Layer oder Vertexe einhacken", + "pinningButton": "Vertexe an der gleichen Position verknüpfen" } } diff --git a/src/assets/translations/en.json b/src/assets/translations/en.json index 57b37843..b5b052fa 100644 --- a/src/assets/translations/en.json +++ b/src/assets/translations/en.json @@ -29,4 +29,4 @@ "snappingButton": "Snap dragged marker to other layers and vertices", "pinningButton": "Pin shared vertices together" } -} \ No newline at end of file +} diff --git a/src/assets/translations/es.json b/src/assets/translations/es.json index e975bfce..e6d04e4b 100644 --- a/src/assets/translations/es.json +++ b/src/assets/translations/es.json @@ -25,6 +25,8 @@ "dragButton": "Arrastrar Capas", "cutButton": "Cortar Capas", "deleteButton": "Remover Capas", - "drawCircleMarkerButton": "Dibujar Marcador de Circulo" + "drawCircleMarkerButton": "Dibujar Marcador de Circulo", + "snappingButton": "El marcador de Snap arrastrado a otras capas y vértices", + "pinningButton": "Fijar juntos los vértices compartidos" } } diff --git a/src/assets/translations/fr.json b/src/assets/translations/fr.json index 942c5a8b..ff647492 100644 --- a/src/assets/translations/fr.json +++ b/src/assets/translations/fr.json @@ -7,7 +7,8 @@ "finishPoly": "Cliquez sur le premier marqueur pour terminer", "finishRect": "Cliquez pour terminer", "startCircle": "Cliquez pour placer le centre du cercle", - "finishCircle": "Cliquez pour finir le cercle" + "finishCircle": "Cliquez pour finir le cercle", + "placeCircleMarker": "Cliquez pour placer le marqueur circulaire" }, "actions": { "finish": "Terminer", @@ -23,6 +24,9 @@ "editButton": "Éditer des calques", "dragButton": "Déplacer des calques", "cutButton": "Couper des calques", - "deleteButton": "Supprimer des calques" + "deleteButton": "Supprimer des calques", + "drawCircleMarkerButton": "Dessiner un marqueur circulaire", + "snappingButton": "Glisser le marqueur vers d'autres couches et sommets", + "pinningButton": "Épingler ensemble les sommets partagés" } } diff --git a/src/assets/translations/id.json b/src/assets/translations/id.json index ae3b369c..129f05c4 100644 --- a/src/assets/translations/id.json +++ b/src/assets/translations/id.json @@ -7,7 +7,8 @@ "finishPoly": "Klik marker pertama untuk mengakhiri", "finishRect": "Klik untuk mengakhiri", "startCircle": "Klik untuk menempatkan titik pusat lingkaran", - "finishCircle": "Klik untuk mengakhiri lingkaran" + "finishCircle": "Klik untuk mengakhiri lingkaran", + "placeCircleMarker": "Klik untuk menempatkan penanda lingkarann" }, "actions": { "finish": "Selesai", @@ -23,6 +24,9 @@ "editButton": "Edit Layer", "dragButton": "Geser Layer", "cutButton": "Potong Layer", - "deleteButton": "Hilangkan Layer" + "deleteButton": "Hilangkan Layer", + "drawCircleMarkerButton": "Digitasi Penanda Lingkaran", + "snappingButton": "Jepretkan penanda yang ditarik ke lapisan dan simpul lain", + "pinningButton": "Sematkan simpul bersama bersama" } } diff --git a/src/assets/translations/index.js b/src/assets/translations/index.js index 93f8dd42..bfa54d76 100644 --- a/src/assets/translations/index.js +++ b/src/assets/translations/index.js @@ -9,6 +9,8 @@ import nl from './nl.json'; import fr from './fr.json'; import zh from './zh.json'; // eslint-disable-next-line camelcase +import zh_tw from './zh_tw.json'; +// eslint-disable-next-line camelcase import pt_br from './pt_br.json'; import pl from './pl.json'; import sv from './sv.json'; @@ -28,6 +30,7 @@ export default { fr, pt_br, zh, + zh_tw, pl, sv, el, diff --git a/src/assets/translations/it.json b/src/assets/translations/it.json index 850cad9f..21f7e08e 100644 --- a/src/assets/translations/it.json +++ b/src/assets/translations/it.json @@ -25,6 +25,8 @@ "dragButton": "Sposta Livelli", "cutButton": "Ritaglia Livelli", "deleteButton": "Elimina Livelli", - "drawCircleMarkerButton": "Disegna Marker del Cherchio" + "drawCircleMarkerButton": "Disegna Marker del Cerchio", + "snappingButton": "Snap ha trascinato il pennarello su altri strati e vertici", + "pinningButton": "Pin condiviso vertici insieme" } } diff --git a/src/assets/translations/nl.json b/src/assets/translations/nl.json index 645f0810..92acb090 100644 --- a/src/assets/translations/nl.json +++ b/src/assets/translations/nl.json @@ -25,6 +25,8 @@ "dragButton": "Verplaats", "cutButton": "Knip", "deleteButton": "Verwijder", - "drawCircleMarkerButton": "Plaats Marker" + "drawCircleMarkerButton": "Plaats Marker", + "snappingButton": "Snap gesleepte marker naar andere lagen en hoekpunten", + "pinningButton": "Speld gedeelde hoekpunten samen" } } diff --git a/src/assets/translations/pl.json b/src/assets/translations/pl.json index c043a036..1c4c2938 100644 --- a/src/assets/translations/pl.json +++ b/src/assets/translations/pl.json @@ -25,6 +25,8 @@ "dragButton": "Przesuń", "cutButton": "Wytnij", "deleteButton": "Usuń", - "drawCircleMarkerButton": "Narysuj okrągły znacznik" + "drawCircleMarkerButton": "Narysuj okrągły znacznik", + "snappingButton": "Snap przeciągnięty marker na inne warstwy i wierzchołki", + "pinningButton": "Sworzeń wspólne wierzchołki razem" } } diff --git a/src/assets/translations/pt_br.json b/src/assets/translations/pt_br.json index b45753df..5a12666f 100644 --- a/src/assets/translations/pt_br.json +++ b/src/assets/translations/pt_br.json @@ -7,7 +7,8 @@ "finishPoly": "Clique no primeiro ponto para fechar o polígono", "finishRect": "Clique para finalizar", "startCircle": "Clique para posicionar o centro do círculo", - "finishCircle": "Clique para fechar o círculo" + "finishCircle": "Clique para fechar o círculo", + "placeCircleMarker": "Clique para posicionar o marcador circular" }, "actions": { "finish": "Finalizar", @@ -23,7 +24,9 @@ "editButton": "Editar camada(s)", "dragButton": "Mover camada(s)", "cutButton": "Recortar camada(s)", - "deleteButton": "Remover camada(s)" + "deleteButton": "Remover camada(s)", + "drawCircleMarkerButton": "Marcador de círculos de desenho", + "snappingButton": "Marcador arrastado para outras camadas e vértices", + "pinningButton": "Vértices compartilhados de pinos juntos" } } - \ No newline at end of file diff --git a/src/assets/translations/ro.json b/src/assets/translations/ro.json index a80a990d..01c3d5d6 100644 --- a/src/assets/translations/ro.json +++ b/src/assets/translations/ro.json @@ -25,6 +25,8 @@ "dragButton": "Mută straturile", "cutButton": "Taie straturile", "deleteButton": "Șterge straturile", - "placeCircleMarker": "Adaugă o bulină" + "drawCircleMarkerButton": "Desenează marcatorul cercului", + "snappingButton": "Fixați marcatorul glisat pe alte straturi și vârfuri", + "pinningButton": "Fixați vârfurile partajate împreună" } } diff --git a/src/assets/translations/sv.json b/src/assets/translations/sv.json index 78ae7f57..f5c96404 100644 --- a/src/assets/translations/sv.json +++ b/src/assets/translations/sv.json @@ -25,6 +25,8 @@ "dragButton": "Dra Lager", "cutButton": "Klipp i Lager", "deleteButton": "Ta bort Lager", - "drawCircleMarkerButton": "Rita Cirkelmarkör" + "drawCircleMarkerButton": "Rita Cirkelmarkör", + "snappingButton": "Snäpp dra markören till andra lager och hörn", + "pinningButton": "Fäst delade hörn tillsammans" } } diff --git a/src/assets/translations/zh.json b/src/assets/translations/zh.json index 76f97df2..e835fac0 100644 --- a/src/assets/translations/zh.json +++ b/src/assets/translations/zh.json @@ -7,7 +7,8 @@ "finishPoly": "单击第一个标记以完成", "finishRect": "单击完成", "startCircle": "单击放置圆心", - "finishCircle": "单击完成圆形" + "finishCircle": "单击完成圆形", + "placeCircleMarker": "点击放置圆形标记" }, "actions": { "finish": "完成", @@ -23,6 +24,9 @@ "editButton": "编辑图层", "dragButton": "拖拽图层", "cutButton": "剪切图层", - "deleteButton": "删除图层" + "deleteButton": "删除图层", + "drawCircleMarkerButton": "画圆圈标记", + "snappingButton": "将拖动的标记捕捉到其他图层和顶点", + "pinningButton": "将共享顶点固定在一起" } } diff --git a/src/assets/translations/zh_tw.json b/src/assets/translations/zh_tw.json new file mode 100644 index 00000000..9517c92d --- /dev/null +++ b/src/assets/translations/zh_tw.json @@ -0,0 +1,32 @@ +{ + "tooltips": { + "placeMarker": "單擊放置標記", + "firstVertex": "單擊放置第一個頂點", + "continueLine": "單擊繼續繪製", + "finishLine": "單擊任何存在的標記以完成", + "finishPoly": "單擊第一個標記以完成", + "finishRect": "單擊完成", + "startCircle": "單擊放置圓心", + "finishCircle": "單擊完成圓形", + "placeCircleMarker": "點擊放置圓形標記" + }, + "actions": { + "finish": "完成", + "cancel": "取消", + "removeLastVertex": "移除最後一個頂點" + }, + "buttonTitles": { + "drawMarkerButton": "放置標記", + "drawPolyButton": "繪製多邊形", + "drawLineButton": "繪製線段", + "drawCircleButton": "繪製圓形", + "drawRectButton": "繪製方形", + "editButton": "編輯圖形", + "dragButton": "移動圖形", + "cutButton": "裁切圖形", + "deleteButton": "刪除圖形", + "drawCircleMarkerButton": "畫圓圈標記", + "snappingButton": "將拖動的標記對齊到其他圖層和頂點", + "pinningButton": "將共享頂點固定在一起" + } +} From 92e673aa3ce48348356871be092695038824ad20 Mon Sep 17 00:00:00 2001 From: Falke Design Date: Wed, 18 Nov 2020 20:29:38 +0100 Subject: [PATCH 28/30] Fix Tooltip Position for Default Marker in Leaflet 1.7.1 (#708) (patch) --- src/js/Draw/L.PM.Draw.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/js/Draw/L.PM.Draw.js b/src/js/Draw/L.PM.Draw.js index 5769c0a4..0966f6c0 100644 --- a/src/js/Draw/L.PM.Draw.js +++ b/src/js/Draw/L.PM.Draw.js @@ -18,6 +18,7 @@ const Draw = L.Class.extend({ }, markerStyle: { draggable: true, + icon: L.icon() }, markerEditable: true, continueDrawing: false, @@ -26,6 +27,12 @@ const Draw = L.Class.extend({ L.Util.setOptions(this, options); }, initialize(map) { + // Overwriting the default tooltipAnchor of the default Marker Icon, because the tooltip functionality was updated but not the anchor in the Icon + // Issue https://github.com/Leaflet/Leaflet/issues/7302 - Leaflet v1.7.1 + const defaultIcon = new L.Icon.Default(); + defaultIcon.options.tooltipAnchor = [0,0]; + this.options.markerStyle.icon = defaultIcon; + // save the map this._map = map; From f83b669d1fa5155bed970e62501b42a55c20c9de Mon Sep 17 00:00:00 2001 From: Sumit Kumar Date: Wed, 18 Nov 2020 20:33:20 +0100 Subject: [PATCH 29/30] 2.8.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 02a30fa4..4567e737 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@geoman-io/leaflet-geoman-free", - "version": "2.7.0", + "version": "2.8.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 10da0ef9..8d96aac1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@geoman-io/leaflet-geoman-free", - "version": "2.7.0", + "version": "2.8.0", "description": "A Leaflet Plugin For Editing Geometry Layers in Leaflet 1.0", "keywords": [ "leaflet", From 2b22f19eb8972970ae95adb0e3b7fbde75a9ea33 Mon Sep 17 00:00:00 2001 From: Sumit Kumar Date: Wed, 18 Nov 2020 20:55:58 +0100 Subject: [PATCH 30/30] removed flaky test in circlemarker (drag) --- cypress/integration/circlemarker.spec.js | 169 +++++++++-------------- 1 file changed, 69 insertions(+), 100 deletions(-) diff --git a/cypress/integration/circlemarker.spec.js b/cypress/integration/circlemarker.spec.js index 7224ea32..cd681901 100644 --- a/cypress/integration/circlemarker.spec.js +++ b/cypress/integration/circlemarker.spec.js @@ -44,55 +44,53 @@ describe('Draw Circle Marker', () => { createMarkers(); }); - it('handles 6k circle markers in under 1 sec', () => { + cy.toolbarButton('circle-marker').click(); - cy.toolbarButton('circle-marker') - .click() - - cy.get(mapSelector) - .click(150, 250) + cy.get(mapSelector).click(150, 250); cy.testLayerAdditionPerformance(); }); - - it('correctly disables drag', () => { - createMarkers(); cy.window().then(({ map, L }) => { - map.eachLayer((layer) => { + map.eachLayer(layer => { if (layer instanceof L.CircleMarker) { - assert.isFalse(L.DomUtil.hasClass(layer._path, 'leaflet-pm-draggable'), 'not draggable') + assert.isFalse( + L.DomUtil.hasClass(layer._path, 'leaflet-pm-draggable'), + 'not draggable' + ); } - }) + }); }); - cy.toolbarButton('edit') - .click() + cy.toolbarButton('edit').click(); cy.window().then(({ map, L }) => { - map.eachLayer((layer) => { + map.eachLayer(layer => { if (layer instanceof L.CircleMarker) { - assert.isTrue(L.DomUtil.hasClass(layer._path, 'leaflet-pm-draggable'), 'draggable') + assert.isTrue( + L.DomUtil.hasClass(layer._path, 'leaflet-pm-draggable'), + 'draggable' + ); } - }) + }); }); - cy.toolbarButton('edit') - .click() + cy.toolbarButton('edit').click(); cy.window().then(({ map, L }) => { - map.eachLayer((layer) => { + map.eachLayer(layer => { if (layer instanceof L.CircleMarker) { - assert.isFalse(L.DomUtil.hasClass(layer._path, 'leaflet-pm-draggable'), 'not draggable') + assert.isFalse( + L.DomUtil.hasClass(layer._path, 'leaflet-pm-draggable'), + 'not draggable' + ); } - }) + }); }); - - }); it('deletes all circle markers', () => { @@ -116,10 +114,9 @@ describe('Draw Circle Marker', () => { cy.hasCircleLayers(0); }); - it('draw a CircleMarker like a Circle', () => { - cy.window().then(({ map}) => { - map.pm.setGlobalOptions({editable: true, continueDrawing: false}); + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({ editable: true, continueDrawing: false }); }); cy.toolbarButton('circle-marker') @@ -133,7 +130,6 @@ describe('Draw Circle Marker', () => { cy.hasCircleLayers(1); - cy.toolbarButton('edit') .click() .closest('.button-container') @@ -142,8 +138,8 @@ describe('Draw Circle Marker', () => { cy.hasVertexMarkers(2); }); it('snapping to CircleMarker with pmIgnore:true', () => { - cy.window().then(({ map, L}) => { - L.circleMarker(map.getCenter(),{pmIgnore: true}).addTo(map); + cy.window().then(({ map, L }) => { + L.circleMarker(map.getCenter(), { pmIgnore: true }).addTo(map); }); cy.toolbarButton('rectangle') @@ -165,15 +161,13 @@ describe('Draw Circle Marker', () => { it('disable continueDrawing', () => { cy.window().then(({ map }) => { - map.pm.setGlobalOptions({continueDrawing: false}); + map.pm.setGlobalOptions({ continueDrawing: false }); }); cy.toolbarButton('circle-marker').click(); - cy.get(mapSelector) - .click(191,216); + cy.get(mapSelector).click(191, 216); - cy.get(mapSelector) - .click(350, 350); + cy.get(mapSelector).click(350, 350); cy.toolbarButton('circle-marker') .closest('.button-container') @@ -185,12 +179,11 @@ describe('Draw Circle Marker', () => { it('disable markerEditable', () => { cy.window().then(({ map }) => { - map.pm.setGlobalOptions({markerEditable: false}); + map.pm.setGlobalOptions({ markerEditable: false }); }); cy.toolbarButton('circle-marker').click(); - cy.get(mapSelector) - .click(191,216); + cy.get(mapSelector).click(191, 216); cy.window().then(({ map }) => { const marker = map.pm.getGeomanDrawLayers()[0]; @@ -201,12 +194,14 @@ describe('Draw Circle Marker', () => { it('enable markerEditable but disable MarkerRemoval', () => { cy.window().then(({ map }) => { - map.pm.setGlobalOptions({markerEditable: true, preventMarkerRemoval: true}); + map.pm.setGlobalOptions({ + markerEditable: true, + preventMarkerRemoval: true, + }); }); cy.toolbarButton('circle-marker').click(); - cy.get(mapSelector) - .click(191,216); + cy.get(mapSelector).click(191, 216); cy.window().then(({ map }) => { const marker = map.pm.getGeomanDrawLayers()[0]; @@ -214,15 +209,12 @@ describe('Draw Circle Marker', () => { expect(enabled).to.equal(true); }); - cy.get(mapSelector) - .rightclick(191,214); + cy.get(mapSelector).rightclick(191, 214); cy.hasLayers(5); }); it('set max radius of circleMarker', () => { - let handFinish = false; - cy.toolbarButton('circle-marker') .click() .closest('.button-container') @@ -233,11 +225,11 @@ describe('Draw Circle Marker', () => { map.pm.setGlobalOptions({ minRadiusCircleMarker: 50, maxRadiusCircleMarker: 150, - editable: true + editable: true, }); cy.get(mapSelector) - .click(250,200) - .click(400,190) + .click(250, 200) + .click(400, 190) .then(() => { const layers = map.pm.getGeomanDrawLayers(); layers.forEach(layer => { @@ -247,45 +239,6 @@ describe('Draw Circle Marker', () => { }); }); }); - - cy.toolbarButton('edit') - .click() - .closest('.button-container') - .should('have.class', 'active'); - - cy.window().then(({ Hand, map, L }) => { - const handMarker = new Hand({ - timing: 'frame', - onStop: ()=>{ - map.eachLayer(layer => { - if (layer instanceof L.CircleMarker) { - expect(true).to.equal(layer.getRadius() < 150); - } - }); - const handMarker2 = new Hand({ - timing: 'frame', - onStop: () => { - handFinish = true; - map.eachLayer(layer => { - if (layer instanceof L.CircleMarker) { - expect(true).to.equal(layer.getRadius() >= 145 && layer.getRadius() < 155); - } - }); - } - }); - const toucherMarker2 = handMarker2.growFinger('mouse'); - toucherMarker2.wait(100).moveTo(317,198, 100).down().wait(500).moveTo(500,198, 600).up().wait(100) - } - }); - const toucherMarker = handMarker.growFinger('mouse'); - toucherMarker.wait(100).moveTo(400,198, 100).down().wait(500).moveTo(317,198, 400).up().wait(100); - - // wait until hand is finished - cy.waitUntil(() => cy.window().then(() => handFinish)).then( ()=> { - expect(handFinish).to.equal(true); - }); - }); - }); it('set min radius of circleMarker', () => { let handFinish = false; @@ -300,11 +253,11 @@ describe('Draw Circle Marker', () => { map.pm.setGlobalOptions({ minRadiusCircleMarker: 150, maxRadiusCircleMarker: 300, - editable: true + editable: true, }); cy.get(mapSelector) - .click(250,200) - .click(300,200) + .click(250, 200) + .click(300, 200) .then(() => { const layers = map.pm.getGeomanDrawLayers(); layers.forEach(layer => { @@ -319,34 +272,50 @@ describe('Draw Circle Marker', () => { .closest('.button-container') .should('have.class', 'active'); - cy.window().then(({ Hand, map, L }) => { + cy.window().then(({ Hand, map, L }) => { const handMarker = new Hand({ timing: 'frame', - onStop: ()=>{ + onStop: () => { map.eachLayer(layer => { if (layer instanceof L.CircleMarker) { - expect(true).to.equal(layer.getRadius() > 150) + expect(true).to.equal(layer.getRadius() > 150); } }); const handMarker2 = new Hand({ timing: 'frame', - onStop: () =>{ + onStop: () => { handFinish = true; map.eachLayer(layer => { if (layer instanceof L.CircleMarker) { - expect(true).to.equal(layer.getRadius() >= 145 && layer.getRadius() < 155); + expect(true).to.equal( + layer.getRadius() >= 145 && layer.getRadius() < 155 + ); } }); - } + }, }); const toucherMarker2 = handMarker2.growFinger('mouse'); - toucherMarker2.wait(200).moveTo(490,198, 100).down().wait(500).moveTo(317,198, 600).up().wait(100) - } + toucherMarker2 + .wait(200) + .moveTo(490, 198, 100) + .down() + .wait(500) + .moveTo(317, 198, 600) + .up() + .wait(100); + }, }); const toucherMarker = handMarker.growFinger('mouse'); - toucherMarker.wait(100).moveTo(400,198, 100).down().wait(500).moveTo(500,198, 400).up().wait(100) + toucherMarker + .wait(100) + .moveTo(400, 198, 100) + .down() + .wait(500) + .moveTo(500, 198, 400) + .up() + .wait(100); // wait until hand is finished - cy.waitUntil(() => cy.window().then(() => handFinish)).then( ()=> { + cy.waitUntil(() => cy.window().then(() => handFinish)).then(() => { expect(handFinish).to.equal(true); }); });