From 0dbfcb9820b981fa8590353e57b1b15df57f3dfe Mon Sep 17 00:00:00 2001 From: muerwre Date: Fri, 1 Mar 2019 11:01:14 +0700 Subject: [PATCH 01/27] moved arrows to separate module --- src/modules/Arrows.ts | 65 +++ src/modules/Poly.ts | 77 +--- src/styles/dialogs.less | 1 - src/utils/EditablePolyline.js | 782 ---------------------------------- 4 files changed, 71 insertions(+), 854 deletions(-) create mode 100644 src/modules/Arrows.ts delete mode 100644 src/utils/EditablePolyline.js diff --git a/src/modules/Arrows.ts b/src/modules/Arrows.ts new file mode 100644 index 0000000..107fc35 --- /dev/null +++ b/src/modules/Arrows.ts @@ -0,0 +1,65 @@ +import { LatLngLiteral, LayerGroup, Map } from "leaflet"; +import { arrowClusterIcon, createArrow } from "$utils/arrow"; +import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js'; +import { angleBetweenPoints, dist2, middleCoord } from "$utils/geom"; + +class Component extends LayerGroup { + constructor(props){ + super(props); + } + + setLatLngs = (latlngs: LatLngLiteral[]): void => { + if (!this.map) return; + + 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)) + )); + }; + + map: Map; + arrowLayer: MarkerClusterGroup = new MarkerClusterGroup({ + spiderfyOnMaxZoom: false, + showCoverageOnHover: false, + zoomToBoundsOnClick: false, + animate: false, + maxClusterRadius: 120, + iconCreateFunction: arrowClusterIcon, + }); +} + + +Component.addInitHook(function () { + this.once('add', (event) => { + if (event.target instanceof Arrows) { + this.map = event.target._map; + this.arrowLayer.addTo(this.map); + } + }); + + this.once('remove', (event) => { + if (event.target instanceof Arrows) { + this.arrowLayer.removeFrom(this.map); + } + }); +}); + +export const Arrows = Component; diff --git a/src/modules/Poly.ts b/src/modules/Poly.ts index e1a3fe0..1937067 100644 --- a/src/modules/Poly.ts +++ b/src/modules/Poly.ts @@ -1,14 +1,13 @@ -import { Map, LayerGroup, LatLng, LatLngLiteral, LeafletEventHandlerFn, marker, divIcon, Marker } from 'leaflet'; -import { EditablePolyline } from '$utils/EditablePolyline'; +import { Map, LatLng } from 'leaflet'; 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"; +import { Arrows } from "$modules/Arrows"; interface Props { map: Map; @@ -28,7 +27,6 @@ export class Poly { weight: 6, maxMarkers: 100, smoothFactor: 3, - // bubblingMouseEvents: false, }) .on('distancechange', this.onDistanceUpdate) .on('allvertexhide', this.onVertexHide) @@ -45,7 +43,7 @@ export class Poly { this.triggerOnChange = triggerOnChange; this.lockMapClicks = lockMapClicks; - this.arrowLayer.addTo(map); + this.arrows = new Arrows({}).addTo(map); } onDistanceUpdate = (event) => { @@ -60,62 +58,9 @@ export class Poly { this.editor.setChanged(true); 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)) - )); + this.arrows.setLatLngs(latlngs); }; - // setModeOnDrawing = (): void => { - // if (this.editor.getMode() !== MODES.POLY) this.editor.setMode(MODES.POLY); - // }; - // - // drawArrows = () => { - // // todo: fix this - // this.arrows.clearLayers(); - // const { latlngs } = this; - // - // if (!latlngs || latlngs.length <= 1) return; - // - // latlngs.forEach((latlng, i) => { - // if (i === 0) return; - // - // const mid = middleCoord(latlngs[i], latlngs[i - 1]); - // const dist = findDistance(latlngs[i - 1].lat, latlngs[i - 1].lng, latlngs[i].lat, latlngs[i].lng); - // - // if (dist <= 1) return; - // - // const slide = new Polyline( - // [ - // latlngs[i - 1], - // [mid.lat, mid.lng] - // ], - // { color: 'none', weight: CLIENT.STROKE_WIDTH } - // ).addTo(this.arrows) as any; - // - // // todo: uncomment and fix this: - // slide._path.setAttribute('marker-end', 'url(#long-arrow)'); - // }); - // }; - continue = (): void => { this.poly.editor.continue(); }; @@ -148,8 +93,6 @@ export class Poly { this.poly.setPoints([]); }; - // clearArrows = (): LayerGroup => this.arrows.clearLayers(); - dumpData = (): Array => this.latlngs; get latlngs(): Array { @@ -162,16 +105,8 @@ export class Poly { return (!this.latlngs || Object.values(this.latlngs).length <= 0); } - poly: EditablePolyline; - arrowLayer: MarkerClusterGroup = new MarkerClusterGroup({ - spiderfyOnMaxZoom: false, - showCoverageOnHover: false, - zoomToBoundsOnClick: false, - animate: false, - maxClusterRadius: 120, - // disableClusteringAtZoom: 13, - iconCreateFunction: arrowClusterIcon, - }); + arrows; + poly; editor: Props['editor']; map: Props['map']; diff --git a/src/styles/dialogs.less b/src/styles/dialogs.less index cf26949..eef5048 100644 --- a/src/styles/dialogs.less +++ b/src/styles/dialogs.less @@ -252,7 +252,6 @@ transform-origin: 0 0; padding: 0 5px; box-sizing: border-box; - // display: flex; align-items: center; fill: white; diff --git a/src/utils/EditablePolyline.js b/src/utils/EditablePolyline.js deleted file mode 100644 index 416c3f9..0000000 --- a/src/utils/EditablePolyline.js +++ /dev/null @@ -1,782 +0,0 @@ -import L from 'leaflet'; - -const EditablePolyline = L.Polyline.polylineEditor = L.Polyline.extend({ - _prepareMapIfNeeded() { - const that = this; - that._changed = false; - - if (this._map._editablePolylines != null) { - return; - } - - // Container for all editable polylines on this map: - this._map._editablePolylines = []; - this._map._editablePolylinesEnabled = true; - - // Click anywhere on map to add a new point-polyline: - if (this._options && this._options.newPolylines) { - that._map.on('dblclick', (event) => { - // console.log(`click, target=${event.target == that._map} type=${event.type}`); - if (that._map.isEditablePolylinesBusy()) { return; } - - const latLng = event.latlng; - // if (that._options.newPolylineConfirmMessage) { - // if (!confirm(that._options.newPolylineConfirmMessage)) { return; } - // } - const contexts = [{ originalPolylineNo: null, originalPointNo: null }]; - L.Polyline.PolylineEditor([latLng], that._options, contexts).addTo(that._map); - - that._showBoundMarkers(); - that._changed = true; - }); - } - - this.constrLineStyle = { - dashArray: '2,10', - weight: 4, - color: 'red', - opacity: 0.5, - }; - - /** - * Check if there is *any* busy editable polyline on this map. - */ - this._map.isEditablePolylinesBusy = () => this._editablePolylines.some(el => el._isBusy()); - // { - // for (let i = 0; i < this._editablePolylines.length; i += 1) { - // if (this._editablePolylines[i]._isBusy()) { return true; } - // } - // - // return false; - // }; - - /** - * Enable/disable editing. - */ - this._map.setEditablePolylinesEnabled = enabled => { - // const map = this; - this._editablePolylinesEnabled = enabled; - - for (let i = 0; i < this._map._editablePolylines.length; i += 1) { - const polyline = this._map._editablePolylines[i]; - - if (enabled) { - polyline._showBoundMarkers(); - } else { - // polyline._hideAll(); - this._hideAllMarkers(); - } - } - }; - - this.editor = { - enable: () => { - this._map.setEditablePolylinesEnabled(true); - }, - - disable: () => { - this._map.setEditablePolylinesEnabled(false); - }, - - continueForward: () => { - if (this.getLatLngs().length === 0) { - return this._map.on('click', this._addFirstPoint); - } - - if (!this._editablePolylinesEnabled) { - this._map.setEditablePolylinesEnabled(true); - } - - this._prepareForNewPoint( - this._markers[this._markers.length - 1], - this._markers.length, - ); - - return; - }, - - stopDrawing: () => { - this._clearDragLines(); - - this._map.off('click', this._addPointForward); - this._map.off('click', this._addFirstPoint); - - if (this._markers.length <= 1) this.editor.clear(); - }, - - reset: () => { - const latlngs = this.getLatLngs(); - - this._markers = []; - - for (let i = 0; i < latlngs.length; i += 1) { - this._addMarkers(i, latlngs[i]); - } - - this._reloadPolyline(); - }, - - clear: () => { - this.setPoints([]); - } - }; - - /* - * Utility method added to this map to retreive editable - * polylines. - */ - this._map.getEditablePolylines = () => this._editablePolylines; - - this._map.fixAroundEditablePoint = marker => { - for (let i = 0; i < this._editablePolylines.length; i += 1) { - const polyline = this._editablePolylines[i]; - polyline._reloadPolyline(marker); - } - }; - }, - - constr: {}, - /** - * Will add all needed methods to this polyline. - */ - _addMethods() { - const that = this; - - this._init = (options, contexts) => { - this._prepareMapIfNeeded(); - - /** - * Since all point editing is done by marker events, markers - * will be the main holder of the polyline points locations. - * Every marker contains a reference to the newPointMarker - * *before* him (=> the first marker has newPointMarker=null). - */ - this._parseOptions(options); - - this._markers = []; - const points = this.getLatLngs(); - - for (let i = 0; i < points.length; i += 1) { - const marker = this._addMarkers(i, points[i]); - - if (!('context' in marker)) { - marker.context = {}; - if (that._contexts != null) { - marker.context = contexts[i]; - } - } - - if (marker.context && !('originalPointNo' in marker.context)) { marker.context.originalPointNo = i; } - if (marker.context && !('originalPolylineNo' in marker.context)) { marker.context.originalPolylineNo = that._map._editablePolylines.length; } - } - - // Map move => show different editable markers: - this._map.on('zoomend', this._showBoundMarkers); - this._map.on('moveend', this._showBoundMarkers); - - if (this._desiredPolylineNo && this._desiredPolylineNo != null) { - this._map._editablePolylines.splice(this._desiredPolylineNo, 0, this); - } else { - this._map._editablePolylines.push(this); - } - }; - - // this.setLatLngs = latlngs => { - // - // }; - /** - * Check if is busy adding/moving new nodes. Note, there may be - * *other* editable polylines on the same map which *are* busy. - */ - this._isBusy = function () { - return that._busy; - }; - - this._setBusy = function (busy) { - that._busy = busy; - }; - - /** - * Get markers for this polyline. - */ - this.getPoints = () => this._markers; - - this.isChanged = () => this._changed; - - this._parseOptions = (original = {}) => { - const options = { ...original }; - // Do not show edit markers if more than maxMarkers would be shown: - if (!('maxMarkers' in original)) { options.maxMarkers = 100; } - if (!('newPolylines' in original)) { options.newPolylines = false; } - if (!('newPolylineConfirmMessage' in options)) { options.newPolylineConfirmMessage = ''; } - if (!('addFirstLastPointEvent' in options)) { options.addFirstLastPointEvent = 'click'; } - if (!('customPointListeners' in options)) { options.customPointListeners = {}; } - if (!('customNewPointListeners' in options)) { options.customNewPointListeners = {}; } - - this._options = options; - - // Icons: - if (!options.pointIcon) { - this._options.pointIcon = L.divIcon({ - className: 'leaflet-vertex-icon', - iconSize: [11, 11], - iconAnchor: [6, 6] - }); - } - // this._options.pointIcon = L.icon({ - // iconUrl: '../static/img/editmarker.png', iconSize: [11, 11], iconAnchor: [6, 6] - // }); - if (!options.newPointIcon) { - this._options.newPointIcon = L.divIcon({ - className: 'leaflet-middle-icon', - iconSize: [11, 11], - iconAnchor: [6, 6] - }); - } - // this._options.newPointIcon = L.icon({ - // iconUrl: '../static/img/editmarker2.png', iconSize: [11, 11], iconAnchor: [6, 6] - // }); - }; - - /** - * Show only markers in current map bounds *is* there are only a certain - * number of markers. This method is called on eventy that change map - * bounds. - */ - this._showBoundMarkers = () => { - if (!this._map) return; - - this._setBusy(false); - - if (!this._editablePolylinesEnabled) return; - - const bounds = this._map.getBounds(); - let found = 0; - - // todo: optimise this FUCK THIS - for (let polylineNo in this._map._editablePolylines) { - const polyline = this._map._editablePolylines[polylineNo]; - - for (let markerNo in polyline._markers) { - const marker = polyline._markers[markerNo]; - if (bounds.contains(marker.getLatLng())) { found += 1; } - } - } - - if (found < that._options.maxMarkers) { - if (this._options.onMarkersShow) this._options.onMarkersShow(); - } else { - if (this._options.onMarkersHide) this._options.onMarkersHide(); - } - - // todo: optimise this - for (const polylineNo in that._map._editablePolylines) { - const polyline = that._map._editablePolylines[polylineNo]; - - for (const markerNo in polyline._markers) { - const marker = polyline._markers[markerNo]; - - if (found < that._options.maxMarkers) { - that._setMarkerVisible(marker, bounds.contains(marker.getLatLng())); - that._setMarkerVisible(marker.newPointMarker, markerNo > 0 && bounds.contains(marker.getLatLng())); - } else { - that._setMarkerVisible(marker, false); - that._setMarkerVisible(marker.newPointMarker, false); - } - } - } - }; - - this._hideAllMarkers = () => { - this._markers.map(marker => { - if (marker.newPointMarker) { - this._map.removeLayer(marker.newPointMarker); - marker.newPointMarker._visible = false; - } - marker._visible = false; - this._map.removeLayer(marker); - }); - }; - - /** - * Used when adding/moving points in order to disable the user to mess - * with other markers (+ easier to decide where to put the point - * without too many markers). - */ - this._hideAll = (except) => { - this._setBusy(true); - for (const polylineNo in that._map._editablePolylines) { - const polyline = that._map._editablePolylines[polylineNo]; - - for (const markerNo in polyline._markers) { - const marker = polyline._markers[markerNo]; - if (except == null || except !== marker) { - polyline._setMarkerVisible(marker, false); - } - if (except == null || except !== marker.newPointMarker) { - polyline._setMarkerVisible(marker.newPointMarker, false); - } - } - } - }; - - /** - * Show/hide marker. - */ - this._setMarkerVisible = (marker, show) => { - if (!marker) { return; } - - const map = this._map; - if (show) { - // if (!marker._visible) { - if (!marker._map) { // First show for this marker: - marker.addTo(map); - } else { // Marker was already shown and hidden: - map.addLayer(marker); - } - marker._map = map; - // } - marker._visible = true; - } else { - // if (marker._visible) { - map.removeLayer(marker); - // } - marker._visible = false; - } - }; - - this.setPoints = latlngs => { - this.setLatLngs(latlngs); - - this._hideAllMarkers(); - this._markers = []; - - for (let i = 0; i < latlngs.length; i += 1) { - this._addMarkers(i, latlngs[i]); - } - - this._reloadPolyline(); - - if (this._options.onPointsSet) this._options.onPointsSet(latlngs); - }; - - /** - * Reload polyline. If it is busy, then the bound markers will not be - * shown. - */ - this._reloadPolyline = fixAroundPointNo => { - this.setLatLngs(this._getMarkerLatLngs()); - - if (fixAroundPointNo != null) this._fixAround(fixAroundPointNo); - - if (this._editablePolylinesEnabled) { - this._showBoundMarkers(); - } else { - this._hideAllMarkers(); - } - - this._changed = true; - }; - - this._onMarkerDrag = (event) => { - // if (this.constr.is_drawing) { - // this._map.off('click', this._addFirstPoint); - // this._map.off('click', this._addPointForward); - // } - - if (this._options.onMarkerDragStart) this._options.onMarkerDragStart(event); - - if (this.constr.line1) that._map.removeLayer(this.constr.line1); - if (this.constr.line2) that._map.removeLayer(this.constr.line2); - - const marker = event.target; - const point = that._getPointNo(event.target); - const previousPoint = point && point > 0 ? that._markers[point - 1].getLatLng() : null; - const nextPoint = point < that._markers.length - 1 ? that._markers[point + 1].getLatLng() : null; - - this._setupDragLines(marker, previousPoint, nextPoint); - this._hideAll(marker); - }; - - this._onMarkerDrop = event => { - const point = that._getPointNo(event.target); - // setTimeout(() => { - this._reloadPolyline(point); - - if (this._options.onMarkerDragEnd) this._options.onMarkerDragEnd(event); - // - // if (this.constr.is_drawing) { - // setTimeout(this._prepareForNewPoint.bind(this), 25); - // } - // }, 25); - }; - /** - * Add two markers (a point marker and his newPointMarker) for a - * single point. - * - * Markers are not added on the map here, the marker.addTo(map) is called - * only later when needed first time because of performance issues. - */ - this._addMarkers = (pointNo, latLng, fixNeighbourPositions) => { - const points = this.getLatLngs(); - const marker = L.marker(latLng, { draggable: true, icon: this._options.pointIcon }); - - marker.newPointMarker = null; - - marker.on('dragstart', this._onMarkerDrag); - marker.on('dragend', this._onMarkerDrop); - - marker.on('contextmenu', (event) => { - const _marker = event.target; - const _pointNo = this._getPointNo(event.target); - - if (!this._markers || this._markers.length <= 2) return; - if (this.constr.is_drawing) return; - - if (_marker.newPointMarker) { - this._map.removeLayer(_marker.newPointMarker); - } - this._map.removeLayer(_marker); - this._markers.splice(_pointNo, 1); - this._reloadPolyline(_pointNo); - - if (this._options.onPointDropped) this._options.onPointDropped(event, 'dropped'); - }); - - // marker.on(that._options.addFirstLastPointEvent, (event) => { - // console.log('click on marker'); - // const point = that._getPointNo(event.target); - // console.log(`pointNo=${point} that._markers.length=${that._markers.length}`); - // - // if (pointNo === 0 || pointNo === that._markers.length - 1) { - // console.log('first or last'); - // that._prepareForNewPoint(event.target, point === 0 ? 0 : point + 1); - // } else { - // console.log('not first or last'); - // } - // }); - - // User-defined custom event listeners: - if (that._options.customPointListeners) { - for (let eventName in that._options.customPointListeners) { - marker.on(eventName, that._options.customPointListeners[eventName]); - } - } - if (that._options.customNewPointListeners) { - for (let eventName in that._options.customNewPointListeners) { - newPointMarker.on(eventName, that._options.customNewPointListeners[eventName]); - } - } - - // exit if its first marker - if (!this._markers || this._markers.length === 0 || points.length === 0) { - this._markers.push(marker); - return marker; - } - - const previousPoint = points[pointNo === 0 ? pointNo : pointNo - 1]; - const newPointMarker = L.marker( - [ - (latLng.lat + previousPoint.lat) / 2.0, - (latLng.lng + previousPoint.lng) / 2.0 - ], - { - draggable: true, - icon: this._options.newPointIcon - } - ); - - marker.newPointMarker = newPointMarker; - - newPointMarker.on('dragstart', (event) => { - this._clearDragLines(); - const point = that._getPointNo(event.target); - const previous = that._markers[point - 1].getLatLng(); - const next = that._markers[point].getLatLng(); - this._setupDragLines(marker.newPointMarker, previous, next); - - this._hideAll(marker.newPointMarker); - }); - - newPointMarker.on('dragend', (event) => { - const marker = event.target; - const pointNo = that._getPointNo(event.target); - this._addMarkers(pointNo, marker.getLatLng(), true); - - // setTimeout(() => { - this._reloadPolyline(); - if (this._options.onPointAdded) this._options.onPointAdded(event, 'added'); - // }, 25); - }); - - // newPointMarker.on('contextmenu', (event) => { - // // 1. Remove this polyline from map - // var marker = event.target; - // const pointNo = that._getPointNo(marker); - // const markers = that.getPoints(); - // that._hideAll(); - // - // const secondPartMarkers = that._markers.slice(pointNo, pointNo.length); - // that._markers.splice(pointNo, that._markers.length - pointNo); - // - // that._reloadPolyline(); - // - // const points = []; - // const contexts = []; - // for (let i = 0; i < secondPartMarkers.length; i += 1) { - // const item = secondPartMarkers[i]; - // points.push(item.getLatLng()); - // contexts.push(item.context); - // } - // - // // Need to know the current polyline order numbers, because - // // the splitted one need to be inserted immediately after: - // const originalPolylineNo = that._map._editablePolylines.indexOf(that); - // - // L.Polyline.PolylineEditor(points, that._options, contexts, originalPolylineNo + 1) - // .addTo(that._map); - // - // that._showBoundMarkers(); - // }); - - this._markers.splice(pointNo, 0, marker); - - if (fixNeighbourPositions) { - this._fixAround(pointNo); - } - - return marker; - }; - // - // this._addNewPoint = pointNo => event => { - // // if (that._markers.length === 1) { - // // pointNo += 1; - // // } - // // - // that._addMarkers(pointNo, event.latlng, true); - // that._reloadPolyline(); - // - // if (pointNo === 0) { - // this._prepareForNewPoint(this._markers[0], 0); - // } else { - // this._prepareForNewPoint(this._markers[this._markers.length - 1], (this._markers.length)); - // } - // }; - - this._addFirstPoint = event => { - this._addMarkers(0, event.latlng, true); - this._map.setEditablePolylinesEnabled(true); - this._reloadPolyline(); - - this._map.off('click', this._addFirstPoint); - this._prepareForNewPoint(this._markers[0], 0); - }; - - this._addPointForward = event => { - if (event.sourceTarget && typeof event.sourceTarget._moved === 'boolean' && event.sourceTarget._moved) { - // prevent from adding markers after drag - this._map.off('click', this._addPointForward); - this._clearDragLines(); - - setTimeout(this._prepareForNewPoint.bind(this), 25); - return; - } - - const pointNo = (this._markers && this._markers.length) || 0; - - this._addMarkers(pointNo, event.latlng, true); - this._reloadPolyline(); - - this._map.off('click', this._addPointForward); - - if (this._options.onPointAdded) this._options.onPointAdded(event); - - setTimeout(this._prepareForNewPoint.bind(this), 25); - }; - - /** - * Event handlers for first and last point. - */ - this._prepareForNewPoint = target => { - if (this._options.onContinueDrawing) this._options.onContinueDrawing(); - - const marker = target || this._markers[this._markers.length - 1]; - this._setupDragLines(marker, marker.getLatLng()); - - this._map.on('click', this._addPointForward); - }; - - /** - * Fix nearby new point markers when the new point is created. - */ - this._fixAround = pointNoOrMarker => { - const pointNo = (typeof pointNoOrMarker) === 'number' - ? pointNoOrMarker - : this._markers.indexOf(pointNoOrMarker); - - if (pointNo < 0) { return; } - - const previousMarker = pointNo === 0 ? null : this._markers[pointNo - 1]; - const marker = this._markers[pointNo]; - const nextMarker = pointNo < that._markers.length - 1 ? this._markers[pointNo + 1] : null; - - if (marker && previousMarker) { - marker.newPointMarker.setLatLng([ - (previousMarker.getLatLng().lat + marker.getLatLng().lat) / 2.0, - (previousMarker.getLatLng().lng + marker.getLatLng().lng) / 2.0 - ]); - } - if (marker && nextMarker) { - nextMarker.newPointMarker.setLatLng([ - (marker.getLatLng().lat + nextMarker.getLatLng().lat) / 2.0, - (marker.getLatLng().lng + nextMarker.getLatLng().lng) / 2.0 - ]); - } - }; - - /** - * Find the order number of the marker. - */ - this._getPointNo = (marker) => { - for (let i = 0; i < this._markers.length; i += 1) { - if (marker === this._markers[i] || marker === this._markers[i].newPointMarker) { - return i; - } - } - return -1; - }; - - /** - * Get polyline latLngs based on marker positions. - */ - this._getMarkerLatLngs = () => this._markers.map(marker => marker.getLatLng()); - - this._moveDragLines = event => { - if (this.constr.line1) { this.constr.line1.setLatLngs([event.latlng, this.constr.point1]); } - if (this.constr.line2) { this.constr.line2.setLatLngs([event.latlng, this.constr.point2]); } - }; - - this._clearDragLines = event => { - if (that._map && this.constr.is_drawing) { - if (this.constr.line1) that._map.removeLayer(this.constr.line1); - if (this.constr.line2) that._map.removeLayer(this.constr.line2); - that._map.off('mousemove', this._moveDragLines); - that._map.off('click', this._clearDragLines); - this.constr.marker.off('click', this._clearDragLines); - this.constr.marker.off('dragend', this._clearDragLines); - - this.constr.is_drawing = false; - - if (event && event.target !== that._map) { - that._map.fire('click', event); - } - } - }; - - this._setupDragLines = (marker, point1, point2) => { - this.constr.line1 = null; - this.constr.line2 = null; - this.constr.point1 = point1; - this.constr.point2 = point2; - this.constr.marker = marker; - this.constr.is_drawing = true; - - if (point1) { - this.constr.line1 = L.polyline([marker.getLatLng(), this.constr.point1], this.constrLineStyle) - .addTo(that._map); - } - - if (point2) { - this.constr.line2 = L.polyline([marker.getLatLng(), this.constr.point2], this.constrLineStyle) - .addTo(that._map); - } - - that._map.on('mousemove', this._moveDragLines); - that._map.on('click', this._clearDragLines); - this.constr.marker.on('dragend', this._clearDragLines); - this.constr.marker.on('click', this._clearDragLines); - if (this.constr.line1) this.constr.line1.on('click', this._clearDragLines); - if (this.constr.line2) this.constr.line2.on('click', this._clearDragLines); - }; - } -}); - -L.Polyline.polylineEditor.addInitHook(function () { - this.on('add', function (event) { - this._map = event.target._map; - this._addMethods(); - - /** - * When addint a new point we must disable the user to mess with other - * markers. One way is to check everywhere if the user is busy. The - * other is to just remove other markers when the user is doing - * somethinng. - * - * TODO: Decide the right way to do this and then leave only _busy or - * _hideAll(). - */ - this._busy = false; - this._initialized = false; - - this._init(this._options, this._contexts); - - this._initialized = true; - - return this; - }); - - this.on('remove', (event) => { - const polyline = event.target; - const map = polyline._map; - const polylines = map.getEditablePolylines(); - const index = polylines.indexOf(polyline); - if (index > -1) { - polylines[index]._markers.forEach((marker) => { - map.removeLayer(marker); - if (marker.newPointMarker) { map.removeLayer(marker.newPointMarker); } - }); - polylines.splice(index, 1); - } - }); -}); - -/** - * Construct a new editable polyline. - * - * latlngs ... a list of points (or two-element tuples with coordinates) - * options ... polyline options - * contexts ... custom contexts for every point in the polyline. Must have the - * same number of elements as latlngs and this data will be - * preserved when new points are added or polylines splitted. - * polylineNo ... insert this polyline in a specific order (used when splitting). - * - * More about contexts: - * This is an array of objects that will be kept as "context" for every - * point. Marker will keep this value as marker.context. New markers will - * have context set to null. - * - * Contexts must be the same size as the polyline size! - * - * By default, even without calling this method -- every marker will have - * context with one value: marker.context.originalPointNo with the - * original order number of this point. The order may change if some - * markers before this one are delted or new added. - */ -L.Polyline.PolylineEditor = (latlngs, options, contexts, polylineNo) => { - // Since the app code may not be able to explicitly call the - // initialization of all editable polylines (if the user created a new - // one by splitting an existing), with this method you can control the - // options for new polylines: - if (options.prepareOptions) { - options.prepareOptions(options); - } - - const result = new L.Polyline.polylineEditor(latlngs, options); - result._options = options; - result._contexts = contexts; - result._desiredPolylineNo = polylineNo; - - return result; -}; - -export { EditablePolyline }; From c853d656b874390e3736254dd38dd07e6a460fac Mon Sep 17 00:00:00 2001 From: muerwre Date: Fri, 1 Mar 2019 11:02:01 +0700 Subject: [PATCH 02/27] moved arrows to separate module --- src/modules/Poly.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/modules/Poly.ts b/src/modules/Poly.ts index 1937067..0c84203 100644 --- a/src/modules/Poly.ts +++ b/src/modules/Poly.ts @@ -4,9 +4,6 @@ import { CLIENT } from '$config/frontend'; import { editor, Editor } from "$modules/Editor"; import { ILatLng } from "$modules/Stickers"; import { InteractivePoly } from "$modules/InteractivePoly"; -import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js'; -import { angleBetweenPoints, dist2, distToSegment, middleCoord } from "$utils/geom"; -import { arrowClusterIcon, createArrow } from "$utils/arrow"; import { Arrows } from "$modules/Arrows"; interface Props { From d4c839a42216b8035bc82da816acc80ff9e34d2e Mon Sep 17 00:00:00 2001 From: muerwre Date: Fri, 1 Mar 2019 12:28:37 +0700 Subject: [PATCH 03/27] km marks initial --- src/components/panels/DistanceBar.tsx | 2 +- src/modules/InteractivePoly.ts | 62 ++++++------- src/modules/KmMarks.ts | 127 ++++++++++++++++++++++++++ src/modules/Poly.ts | 8 +- src/styles/map.less | 21 +++++ 5 files changed, 186 insertions(+), 34 deletions(-) create mode 100644 src/modules/KmMarks.ts diff --git a/src/components/panels/DistanceBar.tsx b/src/components/panels/DistanceBar.tsx index 5ea8699..dd4df1e 100644 --- a/src/components/panels/DistanceBar.tsx +++ b/src/components/panels/DistanceBar.tsx @@ -57,7 +57,7 @@ class Component extends React.PureComponent { - {toHours(estimated)} +
{toHours(estimated)}
{ dialogOpened && diff --git a/src/modules/InteractivePoly.ts b/src/modules/InteractivePoly.ts index 4ac24cc..cb2f819 100644 --- a/src/modules/InteractivePoly.ts +++ b/src/modules/InteractivePoly.ts @@ -33,8 +33,8 @@ export class Component extends Polyline { this.constraintsStyle = { ...this.constraintsStyle, ...options.constraintsStyle }; this.maxMarkers = options.maxMarkers || this.maxMarkers; - this.kmMarksEnabled = options.kmMarksEnabled || this.kmMarksEnabled; - this.kmMarksStep = options.kmMarksStep || this.kmMarksStep; + // this.kmMarksEnabled = options.kmMarksEnabled || this.kmMarksEnabled; + // this.kmMarksStep = options.kmMarksStep || this.kmMarksStep; this.constrLine = new Polyline([], this.constraintsStyle); @@ -49,7 +49,7 @@ export class Component extends Polyline { this.setLatLngs(latlngs); this.recreateMarkers(); this.recalcDistance(); - this.recalcKmMarks(); + // this.recalcKmMarks(); this.touchHinter.setLatLngs(latlngs); this.fire('latlngschange', { latlngs }); }; @@ -483,35 +483,35 @@ export class Component extends Polyline { this.fire('distancechange', { distance: this.distance }); }; + // + // recalcKmMarks = () => { + // if (!this.kmMarksEnabled) return; + // + // const latlngs = this.getLatLngs() as LatLngLiteral[]; + // + // this.kmMarks = { }; + // + // let last_km_mark = 0; + // + // latlngs.reduce((dist, latlng, index) => { + // if (index >= latlngs.length - 1) return; + // + // const next = latlngs[index + 1]; + // const sum = dist + distKm(latlng, next); + // const rounded = Math.floor(dist / this.kmMarksStep) * this.kmMarksStep; + // + // if (rounded > last_km_mark) { + // last_km_mark = rounded; + // this.kmMarks[rounded] = latlng; + // } + // + // return sum; + // }, 0); + // }; - recalcKmMarks = () => { - if (!this.kmMarksEnabled) return; - - const latlngs = this.getLatLngs() as LatLngLiteral[]; - - this.kmMarks = { }; - - let last_km_mark = 0; - - latlngs.reduce((dist, latlng, index) => { - if (index >= latlngs.length - 1) return; - - const next = latlngs[index + 1]; - const sum = dist + distKm(latlng, next); - const rounded = Math.floor(dist / this.kmMarksStep) * this.kmMarksStep; - - if (rounded > last_km_mark) { - last_km_mark = rounded; - this.kmMarks[rounded] = latlng; - } - - return sum; - }, 0); - }; - - kmMarksEnabled?: InteractivePolylineOptions['kmMarksEnabled'] = true; - kmMarksStep?: InteractivePolylineOptions['kmMarksStep'] = 5; - kmMarks?: { [x: number]: LatLngLiteral }; + // kmMarksEnabled?: InteractivePolylineOptions['kmMarksEnabled'] = true; + // kmMarksStep?: InteractivePolylineOptions['kmMarksStep'] = 5; + // kmMarks?: { [x: number]: LatLngLiteral }; // kmMarksLayer?: LayerGroup = new LayerGroup(); markers: Marker[] = []; diff --git a/src/modules/KmMarks.ts b/src/modules/KmMarks.ts new file mode 100644 index 0000000..554087c --- /dev/null +++ b/src/modules/KmMarks.ts @@ -0,0 +1,127 @@ +import { divIcon, LatLngLiteral, LayerGroup, Map, marker, Marker } from "leaflet"; +import { arrowClusterIcon, createArrow } from "$utils/arrow"; +import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js'; +import { angleBetweenPoints, dist2, distKm, middleCoord } from "$utils/geom"; + +interface KmMarksOptions { + showMiddleMarkers: boolean, + showEndMarker: boolean, + kmMarksStep: number, +} + +class Component extends LayerGroup { + constructor(latlngs?: LatLngLiteral[], options?: KmMarksOptions){ + super(); + + this.options = { + showMiddleMarkers: true, + showEndMarker: true, + kmMarksStep: 5, + ...(options || {}), + } as KmMarksOptions; + } + + setLatLngs = (latlngs: LatLngLiteral[]): void => { + if (!this.map) return; + this.marksLayer.clearLayers(); + + if (latlngs.length === 0) return; + + if (this.options.showMiddleMarkers) this.drawMiddleMarkers(latlngs); + if (this.options.showEndMarker) this.drawEndMarker(latlngs); + }; + + drawMiddleMarkers = (latlngs: LatLngLiteral[]) => { + const kmMarks = {}; + let last_km_mark = 0; + + latlngs.reduce((dist, current, index) => { + if (index >= latlngs.length - 1) return; + + const next = latlngs[index + 1]; + const diff = distKm(current, next); + const sum = dist + diff; + const rounded = Math.floor(sum / this.options.kmMarksStep) * this.options.kmMarksStep; + const count = Math.floor((rounded - last_km_mark) / this.options.kmMarksStep); + + if (rounded > last_km_mark) { + const angle = angleBetweenPoints( + this.map.latLngToContainerPoint(current), + this.map.latLngToContainerPoint(next), + ); + + console.log(`[${count}] found at ${index}`, dist, sum, rounded); + + for (let i = 1; i <= count; i += 1) { + const step = last_km_mark + (i * this.options.kmMarksStep); + const shift = (step - dist) / diff; + + const coords = { + lat: current.lat - ((current.lat - next.lat) * shift), + lng: current.lng - ((current.lng - next.lng) * shift), + }; + + kmMarks[step] = { ...coords, angle }; + this.marksLayer.addLayer(this.createMiddleMarker(coords, angle, step)); + + console.log('-->', step, shift); + } + + last_km_mark = rounded; + } + + return sum; + }, 0); + + console.log(kmMarks); + }; + + createMiddleMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, { + draggable: false, + interactive: false, + icon: divIcon({ + html: ` +
+ ${distance} +
+ `, + className: 'leaflet-km-marker', + iconSize: [11, 11], + iconAnchor: [6, 6] + }) + }); + + + drawEndMarker = (latlngs: LatLngLiteral[]): void => { + + }; + + options: KmMarksOptions; + map: Map; + marksLayer: MarkerClusterGroup = new MarkerClusterGroup({ + spiderfyOnMaxZoom: false, + showCoverageOnHover: false, + zoomToBoundsOnClick: false, + animate: false, + maxClusterRadius: 10, + // iconCreateFunction: arrowClusterIcon, + }); +} + + +Component.addInitHook(function () { + this.once('add', (event) => { + if (event.target instanceof KmMarks) { + this.map = event.target._map; + this.marksLayer.addTo(this.map); + } + }); + + this.once('remove', (event) => { + if (event.target instanceof KmMarks) { + this.marksLayer.removeFrom(this.map); + } + }); +}); + +export const KmMarks = Component; diff --git a/src/modules/Poly.ts b/src/modules/Poly.ts index 0c84203..b69e378 100644 --- a/src/modules/Poly.ts +++ b/src/modules/Poly.ts @@ -5,6 +5,7 @@ import { editor, Editor } from "$modules/Editor"; import { ILatLng } from "$modules/Stickers"; import { InteractivePoly } from "$modules/InteractivePoly"; import { Arrows } from "$modules/Arrows"; +import { KmMarks } from "$modules/KmMarks"; interface Props { map: Map; @@ -28,7 +29,7 @@ export class Poly { .on('distancechange', this.onDistanceUpdate) .on('allvertexhide', this.onVertexHide) .on('allvertexshow', this.onVertexShow) - .on('latlngschange', this.updateArrows); + .on('latlngschange', this.updateMarks) this.poly.addTo(map); this.editor = editor; @@ -41,6 +42,7 @@ export class Poly { this.lockMapClicks = lockMapClicks; this.arrows = new Arrows({}).addTo(map); + this.kmMarks = new KmMarks().addTo(map); } onDistanceUpdate = (event) => { @@ -51,11 +53,12 @@ export class Poly { onVertexHide = (): void => this.editor.setMarkersShown(false); onVertexShow = (): void => this.editor.setMarkersShown(true); - updateArrows = event => { + updateMarks = event => { this.editor.setChanged(true); const { latlngs } = event; this.arrows.setLatLngs(latlngs); + this.kmMarks.setLatLngs(latlngs); }; continue = (): void => { @@ -104,6 +107,7 @@ export class Poly { arrows; poly; + kmMarks; editor: Props['editor']; map: Props['map']; diff --git a/src/styles/map.less b/src/styles/map.less index 23cfc4a..97be0a2 100644 --- a/src/styles/map.less +++ b/src/styles/map.less @@ -99,6 +99,27 @@ height: 48px; } +.leaflet-km-marker { + position: relative; + background: green; + + .leaflet-km-dist { + background: blue; + color: white; + border-radius: 11px; + font-size: 9px; + text-align: center; + min-width: 20px; + height: 12px; + display: flex; + align-items: center; + justify-content: center; + position: relative; + top: -5px; + left: -5px; + } +} + .touch-hinter-poly { stroke: rgba(255, 50, 0, 0.1); cursor: grab; From 7aed6bea01e69709a468faf852d59b758e1bdfa2 Mon Sep 17 00:00:00 2001 From: muerwre Date: Mon, 4 Mar 2019 12:46:30 +0700 Subject: [PATCH 04/27] Clustered middle markers --- src/modules/KmMarks.ts | 17 +++++------------ src/styles/map.less | 17 ++++++++++++----- src/utils/geom.ts | 18 ++++++++++++++++-- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/modules/KmMarks.ts b/src/modules/KmMarks.ts index 554087c..779b7ec 100644 --- a/src/modules/KmMarks.ts +++ b/src/modules/KmMarks.ts @@ -1,7 +1,7 @@ import { divIcon, LatLngLiteral, LayerGroup, Map, marker, Marker } from "leaflet"; import { arrowClusterIcon, createArrow } from "$utils/arrow"; import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js'; -import { angleBetweenPoints, dist2, distKm, middleCoord } from "$utils/geom"; +import { angleBetweenPoints, dist2, distKm, middleCoord, pointOnDistance } from "$utils/geom"; interface KmMarksOptions { showMiddleMarkers: boolean, @@ -16,7 +16,7 @@ class Component extends LayerGroup { this.options = { showMiddleMarkers: true, showEndMarker: true, - kmMarksStep: 5, + kmMarksStep: 10, ...(options || {}), } as KmMarksOptions; } @@ -50,8 +50,6 @@ class Component extends LayerGroup { this.map.latLngToContainerPoint(next), ); - console.log(`[${count}] found at ${index}`, dist, sum, rounded); - for (let i = 1; i <= count; i += 1) { const step = last_km_mark + (i * this.options.kmMarksStep); const shift = (step - dist) / diff; @@ -63,8 +61,6 @@ class Component extends LayerGroup { kmMarks[step] = { ...coords, angle }; this.marksLayer.addLayer(this.createMiddleMarker(coords, angle, step)); - - console.log('-->', step, shift); } last_km_mark = rounded; @@ -72,13 +68,11 @@ class Component extends LayerGroup { return sum; }, 0); - - console.log(kmMarks); }; createMiddleMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, { draggable: false, - interactive: false, + interactive: true, icon: divIcon({ html: `
@@ -91,7 +85,6 @@ class Component extends LayerGroup { }) }); - drawEndMarker = (latlngs: LatLngLiteral[]): void => { }; @@ -103,8 +96,8 @@ class Component extends LayerGroup { showCoverageOnHover: false, zoomToBoundsOnClick: false, animate: false, - maxClusterRadius: 10, - // iconCreateFunction: arrowClusterIcon, + maxClusterRadius: 120, + iconCreateFunction: arrowClusterIcon, }); } diff --git a/src/styles/map.less b/src/styles/map.less index 97be0a2..1051222 100644 --- a/src/styles/map.less +++ b/src/styles/map.less @@ -99,14 +99,14 @@ height: 48px; } -.leaflet-km-marker { - position: relative; +.leaflet-km-marker, .leaflet-km-marker-2 { background: green; + position: absolute; .leaflet-km-dist { - background: blue; + background: @red_secondary; color: white; - border-radius: 11px; + border-radius: 2px; font-size: 9px; text-align: center; min-width: 20px; @@ -115,8 +115,15 @@ align-items: center; justify-content: center; position: relative; - top: -5px; + top: 0; left: -5px; + + } +} + +.leaflet-km-marker-2 { + .leaflet-km-dist { + background: green; } } diff --git a/src/utils/geom.ts b/src/utils/geom.ts index 103ea21..84ab861 100644 --- a/src/utils/geom.ts +++ b/src/utils/geom.ts @@ -1,4 +1,4 @@ -import { LatLng, LatLngLiteral, Point } from "leaflet"; +import { LatLng, LatLngLiteral, Point, PointExpression } from "leaflet"; interface ILatLng { lat: number, @@ -84,4 +84,18 @@ export const distToSegment = (A: LatLng, B: LatLng, C: LatLng): number => Math.s // 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)); +export const angleBetweenPoints = (A: Point, B: Point): number => parseFloat(((Math.atan2(B.y - A.y, B.x - A.x))* 180 / Math.PI).toFixed()); +export const angleBetweenPointsRad = (A: Point, B: Point): number => ((Math.atan2(B.x - A.x, B.y - A.y))); + +export const pointOnDistance = (A: Point, B: Point, shift: number): Point => { + const c = Math.sqrt((((B.x - A.x) ** 2) + ((B.y - A.y) ** 2))); + const angle = angleBetweenPointsRad(A, B); + + // console.log({ angle, c, shift }); + const x = Math.floor(B.x - c * Math.sin(angle) * shift); + const y = Math.floor(B.y - c * Math.cos(angle) * shift); + + // console.log({ x, y }); + + return new Point(x, y); +}; From 653dd8ace19aebaf5bf433f73ca900490e01930d Mon Sep 17 00:00:00 2001 From: muerwre Date: Mon, 4 Mar 2019 12:59:00 +0700 Subject: [PATCH 05/27] better km marks --- src/modules/KmMarks.ts | 14 ++++++++------ src/styles/map.less | 9 +++++---- src/utils/geom.ts | 25 ++++++++++++++++--------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/modules/KmMarks.ts b/src/modules/KmMarks.ts index 779b7ec..bdcdbb2 100644 --- a/src/modules/KmMarks.ts +++ b/src/modules/KmMarks.ts @@ -1,7 +1,7 @@ import { divIcon, LatLngLiteral, LayerGroup, Map, marker, Marker } from "leaflet"; import { arrowClusterIcon, createArrow } from "$utils/arrow"; import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js'; -import { angleBetweenPoints, dist2, distKm, middleCoord, pointOnDistance } from "$utils/geom"; +import { allwaysPositiveAngleDeg, angleBetweenPoints, distKm } from "$utils/geom"; interface KmMarksOptions { showMiddleMarkers: boolean, @@ -68,17 +68,19 @@ class Component extends LayerGroup { return sum; }, 0); + + console.log(kmMarks); }; createMiddleMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, { draggable: false, - interactive: true, + interactive: false, icon: divIcon({ html: ` -
- ${distance} -
- `, +
+ ${distance} +
+ `, className: 'leaflet-km-marker', iconSize: [11, 11], iconAnchor: [6, 6] diff --git a/src/styles/map.less b/src/styles/map.less index 1051222..2d57d36 100644 --- a/src/styles/map.less +++ b/src/styles/map.less @@ -106,17 +106,18 @@ .leaflet-km-dist { background: @red_secondary; color: white; - border-radius: 2px; - font-size: 9px; + border-radius: 4px; + font-size: 12px; text-align: center; min-width: 20px; - height: 12px; + height: 14px; display: flex; align-items: center; justify-content: center; position: relative; top: 0; - left: -5px; + left: -4px; + font-weight: bold; } } diff --git a/src/utils/geom.ts b/src/utils/geom.ts index 84ab861..85b2045 100644 --- a/src/utils/geom.ts +++ b/src/utils/geom.ts @@ -87,15 +87,22 @@ export const pointBetweenPoints = (A: LatLng, B: LatLng, C: LatLng): boolean => export const angleBetweenPoints = (A: Point, B: Point): number => parseFloat(((Math.atan2(B.y - A.y, B.x - A.x))* 180 / Math.PI).toFixed()); export const angleBetweenPointsRad = (A: Point, B: Point): number => ((Math.atan2(B.x - A.x, B.y - A.y))); -export const pointOnDistance = (A: Point, B: Point, shift: number): Point => { - const c = Math.sqrt((((B.x - A.x) ** 2) + ((B.y - A.y) ** 2))); - const angle = angleBetweenPointsRad(A, B); +// export const pointOnDistance = (A: Point, B: Point, shift: number): Point => { +// const c = Math.sqrt((((B.x - A.x) ** 2) + ((B.y - A.y) ** 2))); +// const angle = angleBetweenPointsRad(A, B); +// +// // console.log({ angle, c, shift }); +// const x = Math.floor(B.x - c * Math.sin(angle) * shift); +// const y = Math.floor(B.y - c * Math.cos(angle) * shift); +// +// // console.log({ x, y }); +// +// return new Point(x, y); +// }; - // console.log({ angle, c, shift }); - const x = Math.floor(B.x - c * Math.sin(angle) * shift); - const y = Math.floor(B.y - c * Math.cos(angle) * shift); +export const allwaysPositiveAngleDeg = (angle: number): number => { + const res = (angle >= -90 && angle <= 90) ? angle : (180 + angle) + console.log(`${angle} ==> ${res}`) - // console.log({ x, y }); - - return new Point(x, y); + return res; }; From e44780d90e61e75e2d55a6f3fb85626919cfe221 Mon Sep 17 00:00:00 2001 From: muerwre Date: Mon, 4 Mar 2019 13:03:12 +0700 Subject: [PATCH 06/27] removed console.logs --- src/modules/KmMarks.ts | 1 - src/utils/geom.ts | 9 +++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/modules/KmMarks.ts b/src/modules/KmMarks.ts index bdcdbb2..5e52c87 100644 --- a/src/modules/KmMarks.ts +++ b/src/modules/KmMarks.ts @@ -69,7 +69,6 @@ class Component extends LayerGroup { return sum; }, 0); - console.log(kmMarks); }; createMiddleMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, { diff --git a/src/utils/geom.ts b/src/utils/geom.ts index 85b2045..11db158 100644 --- a/src/utils/geom.ts +++ b/src/utils/geom.ts @@ -100,9 +100,6 @@ export const angleBetweenPointsRad = (A: Point, B: Point): number => ((Math.atan // return new Point(x, y); // }; -export const allwaysPositiveAngleDeg = (angle: number): number => { - const res = (angle >= -90 && angle <= 90) ? angle : (180 + angle) - console.log(`${angle} ==> ${res}`) - - return res; -}; +export const allwaysPositiveAngleDeg = (angle: number): number => ( + (angle >= -90 && angle <= 90) ? angle : (180 + angle) +); From b968839095d69ed9b1e0cd4cd3610acc542faf3b Mon Sep 17 00:00:00 2001 From: muerwre Date: Mon, 4 Mar 2019 13:04:41 +0700 Subject: [PATCH 07/27] changed vertexes a little bit --- src/styles/map.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/map.less b/src/styles/map.less index 2d57d36..cc71e33 100644 --- a/src/styles/map.less +++ b/src/styles/map.less @@ -80,7 +80,7 @@ width:8px; height:8px; background: white; - box-shadow: @red_secondary 0 0 0 3px; + //box-shadow: @red_secondary 0 0 0 3px; border-radius: 8px; transform:scale(1); transition:transform 150ms; From 37a7a48131b69929fce5f3074df482b724cacade Mon Sep 17 00:00:00 2001 From: muerwre Date: Mon, 4 Mar 2019 15:06:03 +0700 Subject: [PATCH 08/27] end markers --- src/constants/app_info.ts | 2 ++ src/index.tsx | 10 +++--- src/modules/InteractivePoly.ts | 1 + src/modules/KmMarks.ts | 45 +++++++++++++++++++++++--- src/styles/map.less | 59 ++++++++++++++++++++++++---------- 5 files changed, 90 insertions(+), 27 deletions(-) diff --git a/src/constants/app_info.ts b/src/constants/app_info.ts index 647d2d0..e39478c 100644 --- a/src/constants/app_info.ts +++ b/src/constants/app_info.ts @@ -13,6 +13,8 @@ export const APP_INFO = { 'Приложение для vk', // [11.12.18] 'Фильтр в диалоге поиска карт', // [13.12.18] 'Экспорт GPX', // [18.02.19] + 'Улучшенный редактор ломанных', // [23.02.19] + 'Отметки расстояний и стрелки', // [04.03.19] ], [ 'Первый коммит', // [15.08.18] diff --git a/src/index.tsx b/src/index.tsx index 07ec1a0..e554cef 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,12 +2,10 @@ ## BUGS - todo fix arrows (can't reproduce now :-( ) - done adding route, applying it and adding again and deleting it makes ghost points on the map - + todo moving out the screen makes stickers editable again ## FEATURES - todo make arrows and distance points + done make arrows and distance points todo selecting logo on crop done public maps @@ -20,10 +18,12 @@ todo check canvas support at startup todo check osrm is up - todo maybe: map preview on save + todo maybe: map preview on save (nope) done maybe: stickers clusterization? ## DONE + done fix arrows (can't reproduce now :-( ) + done adding route, applying it and adding again and deleting it makes ghost points on the map done adding/removing points doesn't change distance done cancelling editing someone's else map return back to it's original address /razminochnyj/ diff --git a/src/modules/InteractivePoly.ts b/src/modules/InteractivePoly.ts index cb2f819..2ee587f 100644 --- a/src/modules/InteractivePoly.ts +++ b/src/modules/InteractivePoly.ts @@ -405,6 +405,7 @@ export class Component extends Polyline { this.setLatLngs(latlngs); this.fire('latlngschange', { latlngs }); + this.showVisibleMarkers(); this.startDrawing(); }; diff --git a/src/modules/KmMarks.ts b/src/modules/KmMarks.ts index 5e52c87..b39958d 100644 --- a/src/modules/KmMarks.ts +++ b/src/modules/KmMarks.ts @@ -1,7 +1,8 @@ -import { divIcon, LatLngLiteral, LayerGroup, Map, marker, Marker } from "leaflet"; +import { divIcon, LatLngLiteral, Layer, LayerGroup, Map, marker, Marker } from "leaflet"; import { arrowClusterIcon, createArrow } from "$utils/arrow"; import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js'; import { allwaysPositiveAngleDeg, angleBetweenPoints, distKm } from "$utils/geom"; +import classNames from 'classnames'; interface KmMarksOptions { showMiddleMarkers: boolean, @@ -24,8 +25,11 @@ class Component extends LayerGroup { setLatLngs = (latlngs: LatLngLiteral[]): void => { if (!this.map) return; this.marksLayer.clearLayers(); + this.endMarker.clearLayers(); - if (latlngs.length === 0) return; + this.distance = 0; + + if (latlngs.length <= 1) return; if (this.options.showMiddleMarkers) this.drawMiddleMarkers(latlngs); if (this.options.showEndMarker) this.drawEndMarker(latlngs); @@ -35,8 +39,8 @@ class Component extends LayerGroup { const kmMarks = {}; let last_km_mark = 0; - latlngs.reduce((dist, current, index) => { - if (index >= latlngs.length - 1) return; + this.distance = latlngs.reduce((dist, current, index) => { + if (index >= latlngs.length - 1) return dist; const next = latlngs[index + 1]; const diff = distKm(current, next); @@ -86,8 +90,35 @@ class Component extends LayerGroup { }) }); - drawEndMarker = (latlngs: LatLngLiteral[]): void => { + createEndMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, { + draggable: false, + interactive: true, + icon: divIcon({ + html: ` +
+ ${parseFloat(distance.toFixed(1))} +
+ `, + className: classNames('leaflet-km-marker end-marker', { right: (angle > -90 && angle < 90) }), + iconSize: [11, 11], + iconAnchor: [6, 6] + }), + zIndexOffset: -100, + }); + drawEndMarker = (latlngs: LatLngLiteral[]): void => { + this.endMarker.clearLayers(); + + const current = latlngs[latlngs.length - 2]; + const next = latlngs[latlngs.length - 1 + ]; + + const angle = angleBetweenPoints( + this.map.latLngToContainerPoint(current), + this.map.latLngToContainerPoint(next), + ); + + this.endMarker.addLayer(this.createEndMarker(next, angle, this.distance)); }; options: KmMarksOptions; @@ -100,6 +131,8 @@ class Component extends LayerGroup { maxClusterRadius: 120, iconCreateFunction: arrowClusterIcon, }); + endMarker: LayerGroup = new LayerGroup(); + distance: number = 0; } @@ -108,12 +141,14 @@ Component.addInitHook(function () { if (event.target instanceof KmMarks) { this.map = event.target._map; this.marksLayer.addTo(this.map); + this.endMarker.addTo(this.map); } }); this.once('remove', (event) => { if (event.target instanceof KmMarks) { this.marksLayer.removeFrom(this.map); + this.endMarker.removeFrom(this.map); } }); }); diff --git a/src/styles/map.less b/src/styles/map.less index cc71e33..e39374f 100644 --- a/src/styles/map.less +++ b/src/styles/map.less @@ -40,13 +40,31 @@ } } -.leaflet-vertex-icon, .leaflet-middle-icon { +.vertex-icon-mixin(@left, @right) { + &::after { + content: ' '; + position:absolute; + top:4px; + left: @left; + right: @right; + width:8px; + height:8px; + background: white; + border-radius: 8px; + transform:scale(1); + transition:transform 150ms; + } +} + +.leaflet-vertex-icon { outline: none !important; border-radius: 10px; opacity: 1; border: none; width: 16px !important; - height: 16px !important;margin-left:-8px !important;margin-top:-8px !important; + height: 16px !important; + margin-left:-8px !important; + margin-top:-8px !important; background: transparent; position: absolute; cursor: grab; @@ -72,19 +90,7 @@ } } - &::after { - content: ' '; - position:absolute; - top:4px; - left:4px; - width:8px; - height:8px; - background: white; - //box-shadow: @red_secondary 0 0 0 3px; - border-radius: 8px; - transform:scale(1); - transition:transform 150ms; - } + .vertex-icon-mixin(4px, auto); &:hover { opacity: 1; @@ -100,8 +106,8 @@ } .leaflet-km-marker, .leaflet-km-marker-2 { - background: green; position: absolute; + z-index: 0 !important; .leaflet-km-dist { background: @red_secondary; @@ -111,14 +117,33 @@ text-align: center; min-width: 20px; height: 14px; - display: flex; + display: inline-flex; align-items: center; justify-content: center; position: relative; top: 0; left: -4px; font-weight: bold; + padding: 0 2px; + } +} +.end-marker { + .leaflet-km-dist { + left: auto; + right: -3px; + top: -3px; + position: absolute; + z-index: -10; + padding: 2px 16px 2px 4px; + } + + &.right { + .leaflet-km-dist { + padding: 2px 4px 2px 16px; + left: -3px; + right: auto; + } } } From 8225bc274aaadae41f75c97b4b6640e4e7a88ca5 Mon Sep 17 00:00:00 2001 From: muerwre Date: Mon, 4 Mar 2019 16:31:00 +0700 Subject: [PATCH 09/27] added debug info --- src/modules/KmMarks.ts | 4 ++-- src/modules/Sticker.tsx | 2 ++ src/styles/map.less | 4 ++-- webpack.config.js | 1 + 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/modules/KmMarks.ts b/src/modules/KmMarks.ts index b39958d..8c64fdd 100644 --- a/src/modules/KmMarks.ts +++ b/src/modules/KmMarks.ts @@ -77,10 +77,10 @@ class Component extends LayerGroup { createMiddleMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, { draggable: false, - interactive: false, + interactive: true, icon: divIcon({ html: ` -
+
${distance}
`, diff --git a/src/modules/Sticker.tsx b/src/modules/Sticker.tsx index fc69eb0..2ffff77 100644 --- a/src/modules/Sticker.tsx +++ b/src/modules/Sticker.tsx @@ -55,6 +55,8 @@ export class Sticker { this.deleteSticker = deleteSticker; this.lockMapClicks = lockMapClicks; + console.log({ set, sticker }); + ReactDOM.render(
{ devServer: { historyApiFallback: true, port: 8000, + host: '192.168.88.40', contentBase: 'dist', publicPath: '/', hot: true, From 72f6a63f473cb57df83d59f28fd215f12c380dbc Mon Sep 17 00:00:00 2001 From: muerwre Date: Mon, 4 Mar 2019 16:34:23 +0700 Subject: [PATCH 10/27] added debug info x2 --- src/components/dialogs/MapListDialog.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/dialogs/MapListDialog.tsx b/src/components/dialogs/MapListDialog.tsx index cb0a2bf..68681db 100644 --- a/src/components/dialogs/MapListDialog.tsx +++ b/src/components/dialogs/MapListDialog.tsx @@ -48,6 +48,8 @@ class Component extends React.Component { openRoute = (_id: string): void => { if (isMobile()) this.props.setDialogActive(false); + console.log('HERE!', { isMobile: isMobile() }); + pushPath(`/${_id}/${this.props.editing ? 'edit' : ''}`); }; From 5dde6d88a92fdc81473c95a42c00ccd51dc5e5b6 Mon Sep 17 00:00:00 2001 From: muerwre Date: Mon, 4 Mar 2019 16:36:27 +0700 Subject: [PATCH 11/27] fixed undefined sticker issue --- src/modules/Stickers.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/Stickers.ts b/src/modules/Stickers.ts index 5525b60..537934a 100644 --- a/src/modules/Stickers.ts +++ b/src/modules/Stickers.ts @@ -3,6 +3,7 @@ import { IStickerDump, Sticker } from '$modules/Sticker'; import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js'; import { clusterIcon } from '$utils/clusterIcon'; import { editor, Editor } from "$modules/Editor"; +import { STICKERS } from "$constants/stickers"; export interface ILatLng { lat: number, @@ -36,6 +37,9 @@ export class Stickers { createSticker = ({ latlng, sticker, angle = 2.2, text = '', set }: IStickerDump): void => { + + if (!STICKERS[set] || !STICKERS[set].layers || !STICKERS[set].layers[sticker + 111]) return; + const marker = new Sticker({ latlng, angle, From 6aecbd3d26917149ab641dfa5534991dfeaecce9 Mon Sep 17 00:00:00 2001 From: muerwre Date: Mon, 4 Mar 2019 16:38:09 +0700 Subject: [PATCH 12/27] fixed undefined sticker issue x2 --- src/modules/Stickers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/Stickers.ts b/src/modules/Stickers.ts index 537934a..e1844b5 100644 --- a/src/modules/Stickers.ts +++ b/src/modules/Stickers.ts @@ -38,7 +38,7 @@ export class Stickers { latlng, sticker, angle = 2.2, text = '', set }: IStickerDump): void => { - if (!STICKERS[set] || !STICKERS[set].layers || !STICKERS[set].layers[sticker + 111]) return; + if (!STICKERS[set] || !STICKERS[set].layers || !STICKERS[set].layers[sticker]) return; const marker = new Sticker({ latlng, From 31e042e945bfe0ad281ab64d19298f6772d9ec2a Mon Sep 17 00:00:00 2001 From: muerwre Date: Mon, 4 Mar 2019 16:42:06 +0700 Subject: [PATCH 13/27] fixed isMobile() --- src/components/dialogs/MapListDialog.tsx | 2 -- src/modules/Sticker.tsx | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/components/dialogs/MapListDialog.tsx b/src/components/dialogs/MapListDialog.tsx index 68681db..cb0a2bf 100644 --- a/src/components/dialogs/MapListDialog.tsx +++ b/src/components/dialogs/MapListDialog.tsx @@ -48,8 +48,6 @@ class Component extends React.Component { openRoute = (_id: string): void => { if (isMobile()) this.props.setDialogActive(false); - console.log('HERE!', { isMobile: isMobile() }); - pushPath(`/${_id}/${this.props.editing ? 'edit' : ''}`); }; diff --git a/src/modules/Sticker.tsx b/src/modules/Sticker.tsx index 2ffff77..fc69eb0 100644 --- a/src/modules/Sticker.tsx +++ b/src/modules/Sticker.tsx @@ -55,8 +55,6 @@ export class Sticker { this.deleteSticker = deleteSticker; this.lockMapClicks = lockMapClicks; - console.log({ set, sticker }); - ReactDOM.render(
Date: Mon, 4 Mar 2019 16:46:25 +0700 Subject: [PATCH 14/27] Now we display lesser points on mobile --- src/modules/Poly.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/Poly.ts b/src/modules/Poly.ts index b69e378..523f6e0 100644 --- a/src/modules/Poly.ts +++ b/src/modules/Poly.ts @@ -6,6 +6,7 @@ import { ILatLng } from "$modules/Stickers"; import { InteractivePoly } from "$modules/InteractivePoly"; import { Arrows } from "$modules/Arrows"; import { KmMarks } from "$modules/KmMarks"; +import { isMobile } from "$utils/window"; interface Props { map: Map; @@ -23,7 +24,7 @@ export class Poly { this.poly = new InteractivePoly([], { color: 'url(#activePathGradient)', weight: 6, - maxMarkers: 100, + maxMarkers: isMobile() ? 20 : 100, smoothFactor: 3, }) .on('distancechange', this.onDistanceUpdate) From 413a89a0bb0a19bb5fb3297f0bb277c3ec7c812a Mon Sep 17 00:00:00 2001 From: muerwre Date: Mon, 4 Mar 2019 16:49:18 +0700 Subject: [PATCH 15/27] changed todo --- src/index.tsx | 2 ++ webpack.config.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/index.tsx b/src/index.tsx index e554cef..44b6fa0 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,6 +3,8 @@ ## BUGS todo moving out the screen makes stickers editable again + todo make poly editable only on click/manual edit dialog open + ## FEATURES done make arrows and distance points diff --git a/webpack.config.js b/webpack.config.js index 4b085ce..deea4f7 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -143,7 +143,7 @@ module.exports = () => { devServer: { historyApiFallback: true, port: 8000, - host: '192.168.88.40', + // host: '192.168.88.40', contentBase: 'dist', publicPath: '/', hot: true, From 143f3107e104153198385cdc3a86847b9c403d78 Mon Sep 17 00:00:00 2001 From: muerwre Date: Mon, 4 Mar 2019 17:45:41 +0700 Subject: [PATCH 16/27] sped up the router --- src/modules/Router.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/modules/Router.ts b/src/modules/Router.ts index 545a6a9..ec69bed 100644 --- a/src/modules/Router.ts +++ b/src/modules/Router.ts @@ -40,6 +40,7 @@ export class Router { serviceUrl: CLIENT.OSRM_URL, profile: 'bike', fitSelectedRoutes: false, + showAlternatives: false, routeLine, altLineOptions: { styles: [{ color: '#4597d0', opacity: 1, weight: 3 }] @@ -50,9 +51,13 @@ export class Router { draggable: true, icon: this.createWaypointMarker(), }), - routeWhileDragging: true, + routeWhileDragging: false, }), - routeWhileDragging: true + routeWhileDragging: false, + routingOptions: { + geometryOnly: false, + }, + useHints: false, }).on('waypointschanged', this.updateWaypointsCount); this.router.addTo(map); @@ -87,7 +92,7 @@ export class Router { } window.removeEventListener('mouseup', this.unlockPropagations); - setTimeout(() => this.lockMapClicks(false), 300); + setTimeout(() => this.lockMapClicks(false), 0); }; startFrom = (latlngs: ILatLng): void => { From 9744b0dfd5fa792d1f5db7867ff3852d621857e1 Mon Sep 17 00:00:00 2001 From: muerwre Date: Tue, 5 Mar 2019 16:48:30 +0700 Subject: [PATCH 17/27] osrm service checking --- src/components/dialogs/MapListDialog.tsx | 3 +- src/components/panels/EditorPanel.tsx | 37 ++++++++++++------------ src/index.tsx | 6 +++- src/redux/user/actions.ts | 2 ++ src/redux/user/constants.ts | 2 ++ src/redux/user/reducer.ts | 17 +++++++++++ src/redux/user/sagas.ts | 30 +++++++++++++------ src/utils/api.ts | 6 ++++ 8 files changed, 74 insertions(+), 29 deletions(-) diff --git a/src/components/dialogs/MapListDialog.tsx b/src/components/dialogs/MapListDialog.tsx index cb0a2bf..900673a 100644 --- a/src/components/dialogs/MapListDialog.tsx +++ b/src/components/dialogs/MapListDialog.tsx @@ -48,7 +48,8 @@ class Component extends React.Component { openRoute = (_id: string): void => { if (isMobile()) this.props.setDialogActive(false); - pushPath(`/${_id}/${this.props.editing ? 'edit' : ''}`); + // pushPath(`/${_id}/${this.props.editing ? 'edit' : ''}`); + pushPath(`/${_id}`); }; onScroll = (e: { target: { scrollHeight: number, scrollTop: number, clientHeight: number }}): void => { diff --git a/src/components/panels/EditorPanel.tsx b/src/components/panels/EditorPanel.tsx index 49a70fd..d2babca 100644 --- a/src/components/panels/EditorPanel.tsx +++ b/src/components/panels/EditorPanel.tsx @@ -11,6 +11,7 @@ import { IRootState } from "$redux/user/reducer"; import { Tooltip } from "$components/panels/Tooltip"; interface Props extends IRootState { + routing: IRootState['features']['routing'], setMode: typeof setMode, startEditing: typeof startEditing, stopEditing: typeof stopEditing, @@ -46,20 +47,25 @@ class Component extends React.PureComponent { render() { const { - mode, changed, editing, + mode, changed, editing, routing, } = this.props; return (
{ this.panel = el; }}>
- + { + routing && + + } + + +