From 47a8492239b82a318bfcca43172d8a7b62881b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Pinc=CC=A7on?= Date: Fri, 5 Nov 2021 15:18:08 +0100 Subject: [PATCH 1/4] added beizer curve drawing/edition --- cypress/integration/curve.spec.js | 213 + cypress/integration/polygon.spec.js | 4 +- cypress/integration/rectangle.spec.js | 6 +- cypress/integration/toolbar.spec.js | 4 +- cypress/support/commands.js | 6 + demo/demo.js | 334 +- demo/index.html | 3 +- index.html | 1 + leaflet-geoman.d.ts | 1 + package-lock.json | 8091 ++++++++++++++++++++++++- src/assets/icons/Curve.svg | 4 + src/assets/translations/cz.json | 2 + src/assets/translations/da.json | 2 + src/assets/translations/de.json | 2 + src/assets/translations/el.json | 2 + src/assets/translations/en.json | 2 + src/assets/translations/es.json | 2 + src/assets/translations/fa.json | 2 + src/assets/translations/fr.json | 2 + src/assets/translations/hu.json | 2 + src/assets/translations/id.json | 2 + src/assets/translations/it.json | 2 + src/assets/translations/nl.json | 2 + src/assets/translations/no.json | 2 + src/assets/translations/pl.json | 2 + src/assets/translations/pt_br.json | 2 + src/assets/translations/ro.json | 2 + src/assets/translations/ru.json | 2 + src/assets/translations/sv.json | 2 + src/assets/translations/tr.json | 2 + src/assets/translations/ua.json | 2 + src/assets/translations/zh.json | 2 + src/assets/translations/zh_tw.json | 2 + src/css/controls.css | 5 +- src/css/layers.css | 6 + src/js/Draw/L.PM.Draw.Curve.js | 453 ++ src/js/Draw/L.PM.Draw.Line.js | 6 +- src/js/Draw/L.PM.Draw.js | 3 +- src/js/Edit/L.PM.Edit.Curve.js | 392 ++ src/js/L.PM.Utils.js | 9 +- src/js/L.PM.js | 18 + src/js/Mixins/CurveCommon.js | 66 + src/js/Mixins/Dragging.js | 8 +- src/js/Mixins/Modes/Mode.Rotate.js | 4 +- src/js/Mixins/Rotating.js | 29 +- src/js/Mixins/Snapping.js | 17 +- src/js/Toolbar/L.PM.Toolbar.js | 20 +- src/js/helpers/index.js | 37 + 48 files changed, 9573 insertions(+), 211 deletions(-) create mode 100644 cypress/integration/curve.spec.js create mode 100644 src/assets/icons/Curve.svg create mode 100644 src/js/Draw/L.PM.Draw.Curve.js create mode 100644 src/js/Edit/L.PM.Edit.Curve.js create mode 100644 src/js/Mixins/CurveCommon.js diff --git a/cypress/integration/curve.spec.js b/cypress/integration/curve.spec.js new file mode 100644 index 00000000..7cbd251a --- /dev/null +++ b/cypress/integration/curve.spec.js @@ -0,0 +1,213 @@ +describe('Draw & Edit Curve', () => { + // map and leaflet object + + const mapSelector = '#map'; + + it('doesnt finish single point curves', () => { + cy.toolbarButton('curve').click(); + + cy.get(mapSelector).click(90, 250).click(200, 250); + + cy.toolbarButton('edit').click(); + + cy.hasVertexMarkers(0); + }); + + it('removes last vertex', () => { + cy.toolbarButton('curve').click(); + + cy.get(mapSelector) + .click(190, 250) + .trigger('mousedown', 500, 250) + .trigger('mousemove', 500, 300) + .trigger('mousemove', 400, 350) + .trigger('mouseup') + + cy.hasVertexMarkers(3); // 2 + mouse + + cy.get('.button-container.active .action-removeLastVertex').click(); + + cy.hasVertexMarkers(2); // first + mouse + + cy.get('.button-container.active .action-removeLastVertex').click(); + + cy.hasVertexMarkers(0); // remove all + }); + + it('respects custom style', () => { + cy.window().then(({ map }) => { + map.on('pm:create', (e) => { + e.layer.pm.enable({ + allowSelfIntersection: false, + snappable: false, + snapDistance: 20, + }); + + e.layer.setStyle({ color: 'black' }); + }); + + map.pm.enableDraw('Curve', { + snappable: false, + snapDistance: 20, + allowSelfIntersection: true, + finishOn: 'dblclick', + templineStyle: { + color: 'orange', + dashArray: [10, 10], + weight: 5, + }, + hintlineStyle: { + color: 'orange', + dashArray: [10, 10], + weight: 1, + }, + pathOptions: { + fill: true, + color: 'orange', + fillColor: 'yellow', + dashArray: [10, 10], + weight: 5, + fillOpacity: 1, + opacity: 1, + }, + }); + }); + + cy.get(mapSelector) + .click(120, 150) + .click(120, 100) + .click(300, 100) + .click(300, 200) + .click(120, 150); + + cy.toolbarButton('curve').click(); + + cy.get(mapSelector) + .click(320, 150) + .click(320, 100) + .click(400, 100) + .click(400, 200) + .click(320, 150); + + cy.toolbarButton('edit').click(); + + cy.window().then(({ map, L }) => { + map.eachLayer((l) => { + if (l instanceof L.Curve) expect(l.options.color).to.equal('black'); + }); + }); + }); + + it('draws and edits a curve', () => { + cy.hasLayers(1); + + // activate curve drawing + cy.toolbarButton('curve') + .click() + .closest('.button-container') + .should('have.class', 'active'); + + // draw a curve + cy.get(mapSelector) + .click(190, 250) + .trigger('mousedown', 500, 250) + .trigger('mousemove', 500, 300) + .trigger('mousemove', 400, 350) + .trigger('mouseup') + .click(190, 250) + + // button should be disabled after successful draw + cy.toolbarButton('curve') + .closest('.button-container') + .should('have.not.class', 'active'); + + cy.hasLayers(3); + + // enable global edit mode + cy.toolbarButton('edit').click(); + + cy.hasVertexMarkers(3); + + // click the second marker (cubic edit) + cy.get('.marker-icon').eq(1).click(); + + // now there should be 2 editing handles + cy.hasHandleMarkers(2); + + // rightclick on a vertex-marker to delete it + cy.get('.marker-icon:not(.marker-edit-handle)') + .eq(1) + .trigger('contextmenu'); + + cy.hasVertexMarkers(2); + // handle markers should disappear + cy.hasHandleMarkers(0); + // disable global edit mode + cy.toolbarButton('edit').click(); + + // there should be no markers anymore + cy.hasVertexMarkers(0); + }); + + it('enable continueDrawing', () => { + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({ continueDrawing: true }); + }); + + cy.toolbarButton('curve').click(); + + // draw a line with the curve tool + + cy.get(mapSelector) + .click(150, 250) + .trigger('mousedown', 160, 50) + .trigger('mousemove', 250, 300) + .trigger('mousemove', 250, 300) + .trigger('mouseup') + .click(150, 250) + + cy.get(mapSelector) + .click(300, 250) + .trigger('mousedown', 250, 50) + .trigger('mousemove', 300, 300) + .trigger('mousemove', 300, 300) + .trigger('mouseup') + .click(300, 250) + + cy.toolbarButton('edit').click(); + cy.hasVertexMarkers(6); + }); + + + it('requireSnapToFinish', () => { + cy.window().then(({ map }) => { + map.pm.setGlobalOptions({ + requireSnapToFinish: true, + snapSegment: false, + }); + }); + + cy.toolbarButton('polygon').click(); + cy.get(mapSelector) + .click(150, 250) + .click(160, 50) + .click(250, 50) + .click(150, 250); + + cy.toolbarButton('curve').click(); + cy.get(mapSelector).click(350, 250).click(300, 160).click(190, 60); + + cy.window().then(({ map }) => { + map.pm.Draw.Curve._finishShape(); + expect(1).to.eq(map.pm.getGeomanDrawLayers().length); + }); + + cy.get(mapSelector).click(250, 50); + + cy.window().then(({ map }) => { + map.pm.Draw.Curve._finishShape(); + expect(2).to.eq(map.pm.getGeomanDrawLayers().length); + }); + }); + +}); diff --git a/cypress/integration/polygon.spec.js b/cypress/integration/polygon.spec.js index 8c428662..97cf6e5b 100644 --- a/cypress/integration/polygon.spec.js +++ b/cypress/integration/polygon.spec.js @@ -825,10 +825,10 @@ describe('Draw & Edit Poly', () => { .click(150, 250); cy.get(mapSelector) - .click(230, 230) + .click(230, 250) .click(250, 250) .click(250, 300) - .click(230, 230); + .click(230, 250); cy.toolbarButton('edit').click(); cy.hasVertexMarkers(6); diff --git a/cypress/integration/rectangle.spec.js b/cypress/integration/rectangle.spec.js index d75911cc..681d461d 100644 --- a/cypress/integration/rectangle.spec.js +++ b/cypress/integration/rectangle.spec.js @@ -66,10 +66,10 @@ describe('Draw Rectangle', () => { cy.toolbarButton('cut').click(); cy.get(mapSelector) - .click(226, 389) + .click(226, 350) .click(230, 105) - .click(270, 396) - .click(226, 389); + .click(270, 350) + .click(226, 350); cy.toolbarButton('cut').click(); cy.get(mapSelector) diff --git a/cypress/integration/toolbar.spec.js b/cypress/integration/toolbar.spec.js index a1c9e87c..8b7fb331 100644 --- a/cypress/integration/toolbar.spec.js +++ b/cypress/integration/toolbar.spec.js @@ -326,7 +326,7 @@ describe('Testing the Toolbar', () => { oneBlock: true, }); cy.get('.leaflet-pm-toolbar.leaflet-pm-topleft').then((container) => { - expect(container[0].children.length).to.equal(11); + expect(container[0].children.length).to.equal(12); }); }); }); @@ -366,7 +366,7 @@ describe('Testing the Toolbar', () => { title: 'Display text on hover button', }); cy.get('.leaflet-pm-toolbar.leaflet-pm-topright').then((container) => { - expect(container[0].children.length).to.equal(6); + expect(container[0].children.length).to.equal(7); }); cy.get('.leaflet-pm-toolbar.leaflet-pm-topleft').then((container) => { expect(container[0].children.length).to.equal(6); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index d4fe5dbe..be62dae0 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -105,6 +105,12 @@ Cypress.Commands.add('hasVertexMarkers', (count) => { }); }); +Cypress.Commands.add('hasHandleMarkers', (count) => { + cy.get('.marker-edit-handle').should(($p) => { + expect($p).to.have.length(count); + }); +}); + Cypress.Commands.add('hasTotalVertexMarkers', (count) => { cy.get('.marker-icon').should(($p) => { expect($p).to.have.length(count); diff --git a/demo/demo.js b/demo/demo.js index 37854a9c..317968a9 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -25,9 +25,12 @@ const tiles3 = L.tileLayer( } ); -const map2 = L.map('example2').setView([51.505, -0.09], 13).addLayer(tiles1); -const map3 = L.map('example3').setView([51.505, -0.09], 13).addLayer(tiles2); -const map4 = L.map('example4').setView([51.505, -0.09], 13).addLayer(tiles3); +const map2 = L.map('example2', {preferCanvas: false}).setView([51.505, -0.09], 13).addLayer(tiles1); +map2.on('touchstart', () => { + console.log('alo') +}) +// const map3 = L.map('example3').setView([51.505, -0.09], 13).addLayer(tiles2); +// const map4 = L.map('example4').setView([51.505, -0.09], 13).addLayer(tiles3); // map2.dragging.disable(); // map2.on('pm:create', function(e) { @@ -58,16 +61,16 @@ const m1 = L.circleMarker([51.50313, -0.091223], { radius: 10 }); const m2 = L.marker([51.50614, -0.0989]); const m3 = L.marker([51.50915, -0.096112], { pmIgnore: true }); -const mGroup = L.layerGroup([m1, m2, m3]).addTo(map2); +// const mGroup = L.layerGroup([m1, m2, m3]).addTo(map2); // mGroup.pm.enable(); -map2.pm.addControls({ - drawMarker: false, - drawPolygon: true, - editMode: false, - drawPolyline: false, - removalMode: true, -}); +// map2.pm.addControls({ +// drawMarker: false, +// drawPolygon: true, +// editMode: false, +// drawPolyline: false, +// removalMode: true, +// }); // map2.pm.addControls({ // drawMarker: false, // drawPolygon: true, @@ -88,6 +91,7 @@ map2.pm.addControls({ editMode: true, drawPolyline: true, removalMode: true, + // oneBlock: true, }); // map2.pm.disableDraw('Polygon'); @@ -100,7 +104,7 @@ map2.pm.addControls({ // map2.pm.enableDraw('Polygon', { allowSelfIntersection: false }); map2.on('pm:globaleditmodetoggled', function (e) { - // console.log(e); + console.log(e); }); // GEOSJON EXAMPLE @@ -191,33 +195,32 @@ const geoJsonData = { ], }; -const theCollection = L.geoJson(geoJsonData, { - pointToLayer: (feature, latlng) => { - if (feature.properties.customGeometry) { - return new L.Circle(latlng, feature.properties.customGeometry.radius); - } else { - return new L.Marker(latlng); - } - }, - // onEachFeature: (feature, layer) => { - // layer.addTo(map2); - // }, -}); +// const theCollection = L.geoJson(geoJsonData, { +// pointToLayer: (feature, latlng) => { +// if (feature.properties.customGeometry) { +// return new L.Circle(latlng, feature.properties.customGeometry.radius); +// } else { +// return new L.Marker(latlng); +// } +// }, +// // onEachFeature: (feature, layer) => { +// // layer.addTo(map2); +// // }, +// }); -theCollection.addTo(map2); +// theCollection.addTo(map2); -const b = theCollection.getBounds(); -map2.fitBounds(b); +// const b = theCollection.getBounds(); +// map2.fitBounds(b); -console.log(theCollection); -theCollection.on('pm:edit', function (e) { - console.log(e); -}); +// theCollection.on('pm:edit', function (e) { +// console.log(e); +// }); -theCollection.on('pm:dragstart', function (e) { - console.log(e); -}); +// theCollection.on('pm:dragstart', function (e) { +// console.log(e); +// }); // const geoJsonButton = document.getElementById('test-geojson'); // const geoJsonLayer = L.geoJson(null, { pmIgnore: false }); @@ -229,58 +232,58 @@ theCollection.on('pm:dragstart', function (e) { // snappable: true, // }); -map3.pm.addControls({ - drawMarker: true, - drawPolygon: true, - editMode: true, - removalMode: true, - drawPolyline: true, -}); - -const markerStyle = { - opacity: 0.5, - draggable: false, -}; - -map3.pm.enableDraw('Polygon', { - snappable: true, - templineStyle: { - color: 'blue', - }, - hintlineStyle: { - color: 'blue', - dashArray: [5, 5], - }, - pathOptions: { - color: 'red', - fillColor: 'orange', - fillOpacity: 0.7, - }, - markerStyle: markerStyle, - cursorMarker: false, - // finishOn: 'contextmenu', - finishOnDoubleClick: true, -}); - -var scotland = L.polygon([ - [ - [60, -13], - [60, 0], - [50, 4], - [50, -13], - ], - [ - [55.7, -4.5], - [56, -4.5], - [56, -4], - [55.7, -4], - ], -]); -scotland.addTo(map3); +// map3.pm.addControls({ +// drawMarker: true, +// drawPolygon: true, +// editMode: true, +// removalMode: true, +// drawPolyline: true, +// }); -const bounds = scotland.getBounds(); +// const markerStyle = { +// opacity: 0.5, +// draggable: false, +// }; + +// map3.pm.enableDraw('Polygon', { +// snappable: true, +// templineStyle: { +// color: 'blue', +// }, +// hintlineStyle: { +// color: 'blue', +// dashArray: [5, 5], +// }, +// pathOptions: { +// color: 'red', +// fillColor: 'orange', +// fillOpacity: 0.7, +// }, +// markerStyle: markerStyle, +// cursorMarker: false, +// // finishOn: 'contextmenu', +// finishOnDoubleClick: true, +// }); -map3.fitBounds(bounds); +// var scotland = L.polygon([ +// [ +// [60, -13], +// [60, 0], +// [50, 4], +// [50, -13], +// ], +// [ +// [55.7, -4.5], +// [56, -4.5], +// [56, -4], +// [55.7, -4], +// ], +// ]); +// scotland.addTo(map3); + +// const bounds = scotland.getBounds(); + +// map3.fitBounds(bounds); // geoJsonLayer.addEventListener('click', function(e) { // geoJsonLayer.pm.toggleEdit(); @@ -289,21 +292,48 @@ map3.fitBounds(bounds); // geoJsonLayer.on('pm:drag', function(e) { // console.log(e); // }); +// const curve = L.curve([ "M", [ 51.518382078677675, -0.08222579956054689 ], "L", [ 51.50171532651141, -0.08977890014648438 ], "C", [ 51.50171532651141, -0.08977890014648438 ], [ 51.51645930305757, -0.06351470947265626 ], [ 51.502677035642726, -0.06265640258789064 ], "C", [ 51.48889476822788, -0.061798095703125014 ], [ 51.49359341785831, -0.06986618041992189 ], [ 51.49359341785831, -0.06986618041992189 ] ]) +// .addTo(map2); +// map2.on('pm:drawstart', function (e) { +// console.log('drawstart', e); +// }); +// map2.on('pm:drawend', function (e) { +// console.log('drawend', e); +// }); +// map2.on('pm:create', function (e) { +// console.log('create', e); +// }); +// curve.on('pm:vertexadded', function (e) { +// console.log('vertexadded', e); +// }); + +// map2.on('touchstart', () => { +// console.log('touchstart'); +// }); +// map2.on('mousedown', () => { +// console.log('mousedown'); +// }); +// map2.on('click', () => { +// console.log('click'); +// }); +// curve.on('pm:snapdrag', function (e) { +// console.log('snapdrag', e); +// }); +// curve.on('pm:snap', function (e) { +// console.log('snap', e); +// }); +// curve.on('pm:unsnap', function (e) { +// console.log('unsnap', e); +// }); +// map2.on('layeradd', function (e) { +// console.log('add', e); +// }); +// map2.on('layerremove', function (e) { +// console.log('remove', e); +// }); + + -map2.on('pm:drawstart', function (e) { - var layer = e.workingLayer; - // console.log(layer); - layer.on('pm:centerplaced', function (e) { - // console.log(e); - }); -}); -map2.on('pm:create', function (e) { - var layer = e.layer; - // console.log(layer); - layer.on('pm:centerplaced', function (e) { - // console.log(e); - }); -}); // Polygon Example @@ -312,8 +342,8 @@ const polygonLayer = L.polygon([ [51.503, -0.06], [51.51, -0.047], ]) - .addTo(map3) - .addTo(map2); + // .addTo(map3) + // .addTo(map2); // polygonLayer.pm.toggleEdit({ // allowSelfIntersection: false, @@ -321,11 +351,11 @@ const polygonLayer = L.polygon([ // preventMarkerRemoval: false, // }); -polygonLayer.on('pm:update', function (e) { +map2.on('pm:update', function (e) { console.log(e); }); -polygonLayer.on('pm:intersect', function (e) { +map2.on('pm:intersect', function (e) { console.log(e); }); @@ -336,10 +366,12 @@ polygonLayer.on('pm:intersect', function (e) { // }); // map2.pm.disableGlobalEditMode(); -map2.pm.enableDraw('Polygon', { allowSelfIntersection: false }); -map2.pm.disableDraw('Polygon'); -map2.pm.enableDraw('Line', { allowSelfIntersection: false }); -map2.pm.disableDraw('Line'); +// map2.pm.enableDraw('Polygon', { allowSelfIntersection: false }); +// map2.pm.disableDraw('Polygon'); +// map2.pm.enableDraw('Line', { allowSelfIntersection: false }); +// map2.pm.disableDraw('Line'); +// map2.pm.enableDraw('Curve', { allowSelfIntersection: true }); +// map2.pm.disableDraw('Curve'); map2.on('pm:create', function (e) { // e.layer.pm.enable({ allowSelfIntersection: false }); @@ -417,40 +449,40 @@ const feature = { }, }; -const layerGroup = L.featureGroup([layerGroupItem1]).addTo(map4); -layerGroup.pm.toggleEdit({ - draggable: true, - snappable: true, - snapDistance: 30, -}); -const someLayer = L.geoJSON(feature); +// const layerGroup = L.featureGroup([layerGroupItem1]).addTo(map4); +// layerGroup.pm.toggleEdit({ +// draggable: true, +// snappable: true, +// snapDistance: 30, +// }); +// const someLayer = L.geoJSON(feature); -layerGroup.addLayer(someLayer); +// layerGroup.addLayer(someLayer); -someLayer.addData(feature); +// someLayer.addData(feature); -layerGroup.on('pm:snap', function (e) { - console.log('snap'); - console.log(e); -}); -layerGroup.on('pm:unsnap', function (e) { - console.log('unsnap'); - console.log(e); -}); +// layerGroup.on('pm:snap', function (e) { +// console.log('snap'); +// console.log(e); +// }); +// layerGroup.on('pm:unsnap', function (e) { +// console.log('unsnap'); +// console.log(e); +// }); -map4.pm.addControls({ - position: 'topright', -}); +// map4.pm.addControls({ +// position: 'topright', +// }); -map4.pm.enableDraw('Polygon', { - finishOn: 'mouseout', -}); -map4.pm.disableDraw('Polygon'); +// map4.pm.enableDraw('Polygon', { +// finishOn: 'mouseout', +// }); +// map4.pm.disableDraw('Polygon'); -map4.pm.enableDraw('Marker', { - snappable: false, -}); -map4.pm.disableDraw('Marker'); +// map4.pm.enableDraw('Marker', { +// snappable: false, +// }); +// map4.pm.disableDraw('Marker'); // map4.pm.setPathOptions({ // color: 'orange', @@ -458,26 +490,26 @@ map4.pm.disableDraw('Marker'); // fillOpacity: 0.4, // }); -layerGroup.addLayer(layerGroupItem2); -layerGroup.addLayer(layerGroupItem3); +// layerGroup.addLayer(layerGroupItem2); +// layerGroup.addLayer(layerGroupItem3); // layerGroup.addLayer(layerGroupItem4); // layerGroup.addLayer(layerGroupItem5); -layerGroup.on('pm:dragstart', function (e) { - console.log(e); -}); -layerGroup.on('pm:drag', function (e) { - console.log(e); -}); -layerGroup.on('pm:dragend', function (e) { - console.log(e); -}); -layerGroup.on('pm:markerdragstart', function (e) { - console.log(e); -}); -layerGroup.on('pm:markerdragend', function (e) { - console.log(e); -}); +// layerGroup.on('pm:dragstart', function (e) { +// console.log(e); +// }); +// layerGroup.on('pm:drag', function (e) { +// console.log(e); +// }); +// layerGroup.on('pm:dragend', function (e) { +// console.log(e); +// }); +// layerGroup.on('pm:markerdragstart', function (e) { +// console.log(e); +// }); +// layerGroup.on('pm:markerdragend', function (e) { +// console.log(e); +// }); // test with markercluster // var markers = L.markerClusterGroup(); diff --git a/demo/index.html b/demo/index.html index 0c3d0a5d..cffb4f82 100644 --- a/demo/index.html +++ b/demo/index.html @@ -40,7 +40,8 @@

Editing A Layer Group

- + + diff --git a/index.html b/index.html index b557433e..3aec4dca 100644 --- a/index.html +++ b/index.html @@ -9,6 +9,7 @@ /> +