From bd8207053864af68699ba9da4a9778b731c02fea Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Sun, 20 Jul 2025 01:43:46 -0400 Subject: [PATCH 1/3] PoC shift crop --- app/static/app/js/components/CropButton.jsx | 61 +++++++++++++++++++-- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/app/static/app/js/components/CropButton.jsx b/app/static/app/js/components/CropButton.jsx index 7164af05..1cc26a1c 100644 --- a/app/static/app/js/components/CropButton.jsx +++ b/app/static/app/js/components/CropButton.jsx @@ -35,7 +35,8 @@ class CropButton extends React.Component { super(props); this.state = { - cropping: false + cropping: false, + shiftPressed: false } this.map = props.map; @@ -64,6 +65,9 @@ class CropButton extends React.Component { this.captureMarker = null; } + document.removeEventListener('keydown', this.handleKeyDown); + document.removeEventListener('keyup', this.handleKeyUp); + if (this.acceptMarker) { this.group.removeLayer(this.acceptMarker); this.acceptMarker = null; @@ -97,6 +101,9 @@ class CropButton extends React.Component { this.map.on('resize', this.onMapResize); } + document.addEventListener('keydown', this.handleKeyDown); + document.addEventListener('keyup', this.handleKeyUp); + this.deletePolygon(); // Reset latlngs @@ -109,7 +116,10 @@ class CropButton extends React.Component { handleMarkerClick = e => { L.DomEvent.stop(e); - const latlng = this.map.mouseEventToLatLng(e.originalEvent); + let latlng = this.map.mouseEventToLatLng(e.originalEvent); + if (this.state.shiftPressed && this.latlngs.length > 0) { + latlng = this.snapToAngle(this.latlngs[this.latlngs.length - 1], latlng); + } this.uniqueLatLonPush(latlng); if (this.latlngs.length >= 1) { @@ -217,14 +227,22 @@ class CropButton extends React.Component { handleMarkerDblClick = e => { if (this.latlngs.length >= 2){ - const latlng = this.map.mouseEventToLatLng(e.originalEvent); + let latlng = this.map.mouseEventToLatLng(e.originalEvent); + if (this.state.shiftPressed && this.latlngs.length > 0) { + latlng = this.snapToAngle(this.latlngs[this.latlngs.length - 1], latlng); + } + this.uniqueLatLonPush(latlng); this.confirmPolygon(); } } handleMarkerMove = e => { - const latlng = this.map.mouseEventToLatLng(e.originalEvent); + let latlng = this.map.mouseEventToLatLng(e.originalEvent); + if (this.state.shiftPressed && this.latlngs.length > 0) { + latlng = this.snapToAngle(this.latlngs[this.latlngs.length - 1], latlng); + } + let lls = this.latlngs.concat(latlng); lls.push(lls[0]); if (this.measureBoundary) { @@ -235,9 +253,40 @@ class CropButton extends React.Component { } } + // Snap the current point to the nearest 30deg angle + snapToAngle = (lastPoint, currentPoint) => { + const dx = currentPoint.lng - lastPoint.lng; + const dy = currentPoint.lat - lastPoint.lat; + + const angle = Math.atan2(dy, dx); + const snapAngle = Math.round(angle / (Math.PI / 6)) * (Math.PI / 6); + const distance = Math.sqrt(dx * dx + dy * dy); + + const snappedLng = lastPoint.lng + Math.cos(snapAngle) * distance; + const snappedLat = lastPoint.lat + Math.sin(snapAngle) * distance; + + return L.latLng(snappedLat, snappedLng); + } + + handleKeyDown = e => { + if (e.key === 'Shift') { + this.setState({ shiftPressed: true }); + } + } + + handleKeyUp = e => { + if (e.key === 'Shift') { + this.setState({ shiftPressed: false }); + } + } + handleMarkerContextMenu = e => { if (this.latlngs.length >= 2){ - const latlng = this.map.mouseEventToLatLng(e.originalEvent); + let latlng = this.map.mouseEventToLatLng(e.originalEvent); + if (this.state.shiftPressed && this.latlngs.length > 0) { + latlng = this.snapToAngle(this.latlngs[this.latlngs.length - 1], latlng); + } + this.uniqueLatLonPush(latlng); this.confirmPolygon(); }else if (this.state.cropping){ @@ -252,7 +301,7 @@ class CropButton extends React.Component { }; onMapResize = () => { - if (this.captureMarker) this.captureMarker.setIcon(L.divIcon({ + if (this.captureMarker && this._map) this.captureMarker.setIcon(L.divIcon({ iconSize: this._map.getSize().multiplyBy(2) })); } From 1db64499f1416de220b3c73efb26bb384fa98588 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Sun, 20 Jul 2025 01:52:51 -0400 Subject: [PATCH 2/3] Fixes --- app/static/app/js/components/CropButton.jsx | 35 +++++++++------------ 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/app/static/app/js/components/CropButton.jsx b/app/static/app/js/components/CropButton.jsx index 1cc26a1c..358b6f71 100644 --- a/app/static/app/js/components/CropButton.jsx +++ b/app/static/app/js/components/CropButton.jsx @@ -110,16 +110,22 @@ class CropButton extends React.Component { this.latlngs = []; } - this.setState({cropping: !cropping}); + this.setState({cropping: !cropping, shiftPressed: false}); + } + + // Helper function to get mouse position with optional angle snapping + getMouseLatLng = (e) => { + let latlng = this.map.mouseEventToLatLng(e.originalEvent); + if (this.state.shiftPressed && this.latlngs.length > 0) { + latlng = this.snapToAngle(this.latlngs[this.latlngs.length - 1], latlng); + } + return latlng; } handleMarkerClick = e => { L.DomEvent.stop(e); - let latlng = this.map.mouseEventToLatLng(e.originalEvent); - if (this.state.shiftPressed && this.latlngs.length > 0) { - latlng = this.snapToAngle(this.latlngs[this.latlngs.length - 1], latlng); - } + const latlng = this.getMouseLatLng(e); this.uniqueLatLonPush(latlng); if (this.latlngs.length >= 1) { @@ -221,27 +227,20 @@ class CropButton extends React.Component { if (this.latlngs.length === 0) this.latlngs.push(latlng); else{ const last = this.latlngs[this.latlngs.length - 1]; - if (last.lat !== latlng.lat && last.lng !== latlng.lng) this.latlngs.push(latlng); + if (last.lat !== latlng.lat || last.lng !== latlng.lng) this.latlngs.push(latlng); } }; handleMarkerDblClick = e => { if (this.latlngs.length >= 2){ - let latlng = this.map.mouseEventToLatLng(e.originalEvent); - if (this.state.shiftPressed && this.latlngs.length > 0) { - latlng = this.snapToAngle(this.latlngs[this.latlngs.length - 1], latlng); - } - + const latlng = this.getMouseLatLng(e); this.uniqueLatLonPush(latlng); this.confirmPolygon(); } } handleMarkerMove = e => { - let latlng = this.map.mouseEventToLatLng(e.originalEvent); - if (this.state.shiftPressed && this.latlngs.length > 0) { - latlng = this.snapToAngle(this.latlngs[this.latlngs.length - 1], latlng); - } + const latlng = this.getMouseLatLng(e); let lls = this.latlngs.concat(latlng); lls.push(lls[0]); @@ -282,11 +281,7 @@ class CropButton extends React.Component { handleMarkerContextMenu = e => { if (this.latlngs.length >= 2){ - let latlng = this.map.mouseEventToLatLng(e.originalEvent); - if (this.state.shiftPressed && this.latlngs.length > 0) { - latlng = this.snapToAngle(this.latlngs[this.latlngs.length - 1], latlng); - } - + const latlng = this.getMouseLatLng(e); this.uniqueLatLonPush(latlng); this.confirmPolygon(); }else if (this.state.cropping){ From 39fa5dad8148be9f3c9c9421ac7b114920e86450 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Sun, 20 Jul 2025 02:09:37 -0400 Subject: [PATCH 3/3] Remove comment --- app/static/app/js/components/CropButton.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/app/static/app/js/components/CropButton.jsx b/app/static/app/js/components/CropButton.jsx index 358b6f71..9bf24e9f 100644 --- a/app/static/app/js/components/CropButton.jsx +++ b/app/static/app/js/components/CropButton.jsx @@ -113,7 +113,6 @@ class CropButton extends React.Component { this.setState({cropping: !cropping, shiftPressed: false}); } - // Helper function to get mouse position with optional angle snapping getMouseLatLng = (e) => { let latlng = this.map.mouseEventToLatLng(e.originalEvent); if (this.state.shiftPressed && this.latlngs.length > 0) {