diff --git a/src/components/Fills.tsx b/src/components/Fills.tsx index d025755..70b511f 100644 --- a/src/components/Fills.tsx +++ b/src/components/Fills.tsx @@ -38,6 +38,10 @@ export const Fills = () => ( > + + + + diff --git a/src/modules/InteractivePoly.ts b/src/modules/InteractivePoly.ts index 04dbc7c..539dd74 100644 --- a/src/modules/InteractivePoly.ts +++ b/src/modules/InteractivePoly.ts @@ -595,4 +595,6 @@ export const InteractivePoly = Component; editorenable distancechange + + latlngschange */ diff --git a/src/modules/Poly.ts b/src/modules/Poly.ts index 4008be4..f7046f4 100644 --- a/src/modules/Poly.ts +++ b/src/modules/Poly.ts @@ -1,11 +1,14 @@ -import { Map, LayerGroup, LatLng } from 'leaflet'; +import { Map, LayerGroup, LatLng, LatLngLiteral, LeafletEventHandlerFn, marker, divIcon, Marker } from 'leaflet'; import { EditablePolyline } from '$utils/EditablePolyline'; import { simplify } from '$utils/simplify'; import { CLIENT } from '$config/frontend'; import { editor, Editor } from "$modules/Editor"; import { ILatLng } from "$modules/Stickers"; import { InteractivePoly } from "$modules/InteractivePoly"; - +import { clusterIcon } from "$utils/clusterIcon"; +import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js'; +import { angleBetweenPoints, dist2, distToSegment, middleCoord } from "$utils/geom"; +import { arrowClusterIcon, createArrow } from "$utils/arrow"; interface Props { map: Map; @@ -29,7 +32,8 @@ export class Poly { }) .on('distancechange', this.onDistanceUpdate) .on('allvertexhide', this.onVertexHide) - .on('allvertexshow', this.onVertexShow); + .on('allvertexshow', this.onVertexShow) + .on('latlngschange', this.updateArrows); this.poly.addTo(map); this.editor = editor; @@ -41,7 +45,7 @@ export class Poly { this.triggerOnChange = triggerOnChange; this.lockMapClicks = lockMapClicks; - this.arrows.addTo(map); + this.arrowLayer.addTo(map); } onDistanceUpdate = (event) => { @@ -52,6 +56,32 @@ export class Poly { onVertexHide = (): void => this.editor.setMarkersShown(false); onVertexShow = (): void => this.editor.setMarkersShown(true); + updateArrows = event => { + const { latlngs } = event; + this.arrowLayer.clearLayers(); + + if (latlngs.length === 0) return; + + const midpoints = latlngs.reduce((res, latlng, i) => ( + latlngs[i + 1] && dist2(latlngs[i], latlngs[i + 1]) > 0.00005 + ? [ + ...res, + { + latlng: middleCoord(latlngs[i], latlngs[i + 1]), + angle: angleBetweenPoints( + this.map.latLngToContainerPoint(latlngs[i]), + this.map.latLngToContainerPoint(latlngs[i + 1]) + ), + } + ] + : res + ), []); + + midpoints.forEach(({ latlng, angle }) => ( + this.arrowLayer.addLayer(createArrow(latlng, angle)) + )); + }; + // setModeOnDrawing = (): void => { // if (this.editor.getMode() !== MODES.POLY) this.editor.setMode(MODES.POLY); // }; @@ -133,7 +163,15 @@ export class Poly { } poly: EditablePolyline; - arrows: LayerGroup = new LayerGroup(); + arrowLayer: MarkerClusterGroup = new MarkerClusterGroup({ + spiderfyOnMaxZoom: false, + showCoverageOnHover: false, + zoomToBoundsOnClick: false, + animate: false, + maxClusterRadius: 100, + // disableClusteringAtZoom: 13, + iconCreateFunction: arrowClusterIcon, + }); editor: Props['editor']; map: Props['map']; diff --git a/src/styles/map.less b/src/styles/map.less index ea2f153..b59f739 100644 --- a/src/styles/map.less +++ b/src/styles/map.less @@ -91,6 +91,14 @@ } } +.leaflet-arrow { + position: absolute; + left: -14px; + top: -14px; + width: 40px; + height: 40px; +} + .touch-hinter-poly { stroke: rgba(255, 50, 0, 0.1); cursor: grab; diff --git a/src/utils/arrow.ts b/src/utils/arrow.ts new file mode 100644 index 0000000..9b20a14 --- /dev/null +++ b/src/utils/arrow.ts @@ -0,0 +1,22 @@ +import { divIcon, LatLngLiteral, Marker, marker, DivIcon } from "leaflet"; + +export const createArrow = (latlng: LatLngLiteral, angle: number): Marker => marker(latlng, { + draggable: false, + interactive: false, + icon: divIcon({ + html: ` +
+ + + +
+ `, + className: 'leaflet-arrow-icon', + iconSize: [11, 11], + iconAnchor: [6, 6] + }) +}); + +export const arrowClusterIcon = (): DivIcon => divIcon({ + html: `
` +}); diff --git a/src/utils/geom.ts b/src/utils/geom.ts index 7fb814e..103ea21 100644 --- a/src/utils/geom.ts +++ b/src/utils/geom.ts @@ -1,4 +1,4 @@ -import { LatLng, LatLngLiteral } from "leaflet"; +import { LatLng, LatLngLiteral, Point } from "leaflet"; interface ILatLng { lat: number, @@ -58,7 +58,7 @@ export const pointInArea = (A: LatLng, B: LatLng, C: LatLng, radius: number = 0. ); -const dist2 = (A: LatLngLiteral, B: LatLngLiteral): number => (((A.lat - B.lat) ** 2) + ((A.lng - B.lng) ** 2)); +export const dist2 = (A: LatLngLiteral, B: LatLngLiteral): number => (((A.lat - B.lat) ** 2) + ((A.lng - B.lng) ** 2)); const distToSegmentSquared = (A: LatLng, B: LatLng, C: LatLng): number => { const l2 = dist2(A, B); @@ -83,3 +83,5 @@ const distToSegmentSquared = (A: LatLng, B: LatLng, C: LatLng): number => { export const distToSegment = (A: LatLng, B: LatLng, C: LatLng): number => Math.sqrt(distToSegmentSquared(A, B, C)); // if C between A and B export const pointBetweenPoints = (A: LatLng, B: LatLng, C: LatLng): boolean => (distToSegment(A, B, C) < 0.01); + +export const angleBetweenPoints = (A: Point, B: Point): number => parseFloat(((Math.atan2(B.y - A.y, B.x - A.x))* 180 / Math.PI).toFixed(6));