refactored rerendering speed

This commit is contained in:
Fedor Katurov 2020-01-16 11:49:24 +07:00
parent b6bf317649
commit 69d1d749cf
32 changed files with 144 additions and 2045 deletions

38
package-lock.json generated
View file

@ -778,17 +778,20 @@
"@types/classnames": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.7.tgz",
"integrity": "sha512-rzOhiQ55WzAiFgXRtitP/ZUT8iVNyllEpylJ5zHzR4vArUvMB39GTk+Zon/uAM0JxEFAWnwsxC2gH8s+tZ3Myg=="
"integrity": "sha512-rzOhiQ55WzAiFgXRtitP/ZUT8iVNyllEpylJ5zHzR4vArUvMB39GTk+Zon/uAM0JxEFAWnwsxC2gH8s+tZ3Myg==",
"dev": true
},
"@types/geojson": {
"version": "7946.0.6",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.6.tgz",
"integrity": "sha512-f6qai3iR62QuMPPdgyH+LyiXTL2n9Rf62UniJjV7KHrbiwzLTZUKsdq0mFSTxAHbO7JvwxwC4tH0m1UnweuLrA=="
"integrity": "sha512-f6qai3iR62QuMPPdgyH+LyiXTL2n9Rf62UniJjV7KHrbiwzLTZUKsdq0mFSTxAHbO7JvwxwC4tH0m1UnweuLrA==",
"dev": true
},
"@types/leaflet": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.4.3.tgz",
"integrity": "sha512-jFRBSsPHi1EwQSwrN0cOJLdPhwOZsRl4IMxvm/2ShLh0YM5GfCtQXCzsrv8RE7DWL+AykXdYSAd9bFLWbZT4CQ==",
"dev": true,
"requires": {
"@types/geojson": "7946.0.6"
}
@ -796,22 +799,34 @@
"@types/node": {
"version": "11.9.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.0.tgz",
"integrity": "sha512-ry4DOrC+xenhQbzk1iIPzCZGhhPGEFv7ia7Iu6XXSLVluiJIe9FfG7Iu3mObH9mpxEXCWLCMU4JWbCCR9Oy1Zg=="
"integrity": "sha512-ry4DOrC+xenhQbzk1iIPzCZGhhPGEFv7ia7Iu6XXSLVluiJIe9FfG7Iu3mObH9mpxEXCWLCMU4JWbCCR9Oy1Zg==",
"dev": true
},
"@types/prop-types": {
"version": "15.5.8",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.8.tgz",
"integrity": "sha512-3AQoUxQcQtLHsK25wtTWIoIpgYjH3vSDroZOUr7PpCHw/jLY1RB9z9E8dBT/OSmwStVgkRNvdh+ZHNiomRieaw=="
"integrity": "sha512-3AQoUxQcQtLHsK25wtTWIoIpgYjH3vSDroZOUr7PpCHw/jLY1RB9z9E8dBT/OSmwStVgkRNvdh+ZHNiomRieaw==",
"dev": true
},
"@types/q": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.1.tgz",
"integrity": "sha512-eqz8c/0kwNi/OEHQfvIuJVLTst3in0e7uTKeuY+WL/zfKn0xVujOTp42bS/vUUokhK5P2BppLd9JXMOMHcgbjA=="
},
"@types/ramda": {
"version": "0.26.39",
"resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.26.39.tgz",
"integrity": "sha512-3bu32X02VpjJhsYPUWkdOQGoBXjb/UveZgGg4IYMm+SPAXio96BOYrRhVELfM4AoP00sxoi/f2tqrXdwtR4jjg==",
"dev": true,
"requires": {
"ts-toolbelt": "4.14.6"
}
},
"@types/react": {
"version": "16.8.1",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.1.tgz",
"integrity": "sha512-tD1ETKJcuhANOejRc/p7OgQ16DKnbGi0M3LccelKlPnUCDp2a5koVxZFoRN9HN+A+m84HB5VGN7I+r3nNhS3PA==",
"dev": true,
"requires": {
"@types/prop-types": "15.5.8",
"csstype": "2.6.2"
@ -4464,7 +4479,8 @@
"csstype": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.2.tgz",
"integrity": "sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow=="
"integrity": "sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow==",
"dev": true
},
"currently-unhandled": {
"version": "0.4.1",
@ -13015,6 +13031,12 @@
"yn": "3.0.0"
}
},
"ts-toolbelt": {
"version": "4.14.6",
"resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-4.14.6.tgz",
"integrity": "sha512-SONcnRd93+LuYGfn/CZg5A5qhCODohZslAVZKHHu5bnwUxoXLqd2k2VIdwRUXYfKnY+UCeNbI2pTPz+Dno6Mpg==",
"dev": true
},
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
@ -13085,9 +13107,9 @@
"integrity": "sha512-NLpRc/FY+jPfWL0aUXQzjxPyF0Xug2om6akaoRLQ18KGwP2yYNBJu9vkv2q1q+Cx/+edy2Qf6O8xXnYY/xwz1A=="
},
"typescript": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3.tgz",
"integrity": "sha512-Y21Xqe54TBVp+VDSNbuDYdGw0BpoR/Q6wo/+35M8PAU0vipahnyduJWirxxdxjsAkS7hue53x2zp8gz7F05u0A==",
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz",
"integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==",
"dev": true
},
"uglifyjs-webpack-plugin": {

View file

@ -15,6 +15,11 @@
"devDependencies": {
"@babel/cli": "^7.0.0-rc.1",
"@babel/preset-env": "^7.0.0-rc.1",
"@types/ramda": "^0.26.39",
"@types/classnames": "^2.2.7",
"@types/leaflet": "^1.4.3",
"@types/node": "^11.9.0",
"@types/react": "16.8.1",
"awesome-typescript-loader": "^5.2.1",
"babel-core": "^6.26.0",
"babel-eslint": "^8.2.3",
@ -43,7 +48,7 @@
"style-loader": "^0.21.0",
"sw-precache-webpack-plugin": "^0.11.5",
"ts-node": "^8.0.1",
"typescript": "^3.2.4",
"typescript": "^3.7.4",
"uglifyjs-webpack-plugin": "^1.3.0",
"webpack": "^4.6.0",
"webpack-cli": "^3.2.3",
@ -52,10 +57,6 @@
"webpack-pwa-manifest": "^4.0.0"
},
"dependencies": {
"@types/classnames": "^2.2.7",
"@types/leaflet": "^1.4.3",
"@types/node": "^11.9.0",
"@types/react": "16.8.1",
"axios": "^0.18.0",
"babel-runtime": "^6.26.0",
"body-parser": "^1.18.3",

View file

@ -1,462 +0,0 @@
import { Map } from '~/modules/Map';
import { Poly } from '~/modules/Poly';
import { MODES } from '~/constants/modes';
import { ILatLng, Stickers } from '~/modules/Stickers';
import { Router } from '~/modules/Router';
import { DEFAULT_LOGO, ILogos, LOGOS } from '~/constants/logos';
import { getUrlData } from '~/utils/history';
import { store } from '~/redux/store';
import {
resetSaveDialog,
setActiveSticker,
setChanged,
setDistance,
setIsEmpty, setIsRouting,
setMarkersShown,
setMode,
setRouterPoints, setStarred,
} from '~/redux/user/actions';
import {
mapSetAddress,
mapSetDescription,
mapSetLogo,
mapSetPublic,
mapSetTitle,
mapSetProvider,
} from '~/redux/map/actions';
import { DEFAULT_PROVIDER, IProvider, PROVIDERS } from '~/constants/providers';
import { STICKERS } from '~/constants/stickers';
import { IRootState } from "~/redux/user";
import { DEFAULT_USER, IUser } from "~/constants/auth";
interface IEditor {
map: Map;
poly: Poly;
stickers: Stickers;
router: Router;
logo: keyof ILogos;
owner: number;
initialData: {
version: number,
title: IRootState['title'],
owner: number,
address: IRootState['address'],
path: any,
route: any,
stickers: any,
provider: IRootState['provider'],
is_public: IRootState['is_public'],
is_published: IRootState['is_published'],
description: IRootState['description'],
logo: IRootState['logo'],
};
activeSticker: IRootState['activeSticker'];
mode: IRootState['mode'];
provider: IProvider;
switches: {
[x: string]: {
start?: () => any,
stop?: () => any,
toggle?: () => any,
}
};
clickHandlers: {
[x: string]: (event) => void
};
user: IUser;
}
export class Editor {
constructor() {
this.logo = DEFAULT_LOGO;
this.owner = null;
this.map = new Map({ container: 'map' });
this.activeSticker = {};
this.mode = MODES.NONE;
this.provider = PROVIDERS[DEFAULT_PROVIDER];
const {
triggerOnChange, lockMapClicks, routerMoveStart, pushPolyPoints,
map: { map }
} = this;
this.poly = new Poly({
map, routerMoveStart, lockMapClicks, setDistance: this.setDistance, triggerOnChange, editor: this,
});
this.stickers = new Stickers({
map,
lockMapClicks,
triggerOnChange,
editor: this
});
this.router = new Router({
map,
lockMapClicks,
pushPolyPoints,
setRouterPoints: this.setRouterPoints,
setIsRouting: this.setIsRouting,
});
this.switches = {
[MODES.POLY]: {
start: this.startPoly,
stop: this.poly.stop,
toggle: this.clearMode,
},
[MODES.ROUTER]: {
toggle: this.clearMode,
start: this.routerSetStart,
},
[MODES.STICKERS]: {
toggle: this.clearSticker,
},
[MODES.STICKERS_SELECT]: {
toggle: this.clearSticker,
},
[MODES.TRASH]: {
toggle: this.clearMode,
},
[MODES.CONFIRM_CANCEL]: {
toggle: this.cancelEditing,
},
[MODES.LOGO]: {
toggle: this.clearMode,
},
[MODES.SAVE]: {
toggle: this.clearMode,
stop: this.resetSaveDialog,
},
[MODES.PROVIDER]: {
toggle: this.clearMode,
}
};
this.clickHandlers = {
[MODES.STICKERS]: this.createStickerOnClick,
[MODES.ROUTER]: this.router.pushWaypointOnClick,
};
map.addEventListener('mouseup', this.onClick);
// map.addEventListener('touchend', this.onClick);
map.addEventListener('dragstart', () => lockMapClicks(true));
map.addEventListener('dragstop', () => lockMapClicks(false));
}
map: IEditor['map'];
poly: IEditor['poly'];
stickers: IEditor['stickers'];
router: IEditor['router'];
logo: IEditor['logo'] = DEFAULT_LOGO;
owner: IEditor['owner'] = null;
initialData: IEditor['initialData'] = {
version: null,
title: null,
owner: null,
address: null,
path: null,
route: null,
stickers: null,
provider: null,
is_public: false,
is_published: false,
description: '',
logo: null,
};
activeSticker: IEditor['activeSticker'];
mode: IEditor['mode'];
provider: IEditor['provider'];
switches: IEditor['switches'];
clickHandlers: IEditor['clickHandlers'];
user: IEditor['user'] = DEFAULT_USER;
getState = (): IRootState => <any>store.getState().user;
getUser = () => this.getState().user;
getMode = () => this.getState().mode;
getProvider = () => this.getState().provider;
getTitle = () => this.getState().title;
getEditing = () => this.getState().editing;
getChanged = () => this.getState().changed;
getRouterPoints = () => this.getState().routerPoints;
getDistance = () => this.getState().distance;
getIsEmpty = () => this.getState().is_empty;
mapSetLogo: typeof mapSetLogo = logo => store.dispatch(mapSetLogo(logo));
setMode: typeof setMode = value => store.dispatch(setMode(value));
setRouterPoints: typeof setRouterPoints = value => store.dispatch(setRouterPoints(value));
setActiveSticker: typeof setActiveSticker = value => store.dispatch(setActiveSticker(value));
mapSetTitle: typeof mapSetTitle = value => store.dispatch(mapSetTitle(value));
mapSetDescription: typeof mapSetDescription = value => store.dispatch(mapSetDescription(value));
mapSetAddress: typeof mapSetAddress = value => store.dispatch(mapSetAddress(value));
mapSetPublic: typeof mapSetPublic = value => store.dispatch(mapSetPublic(value));
setStarred: typeof setStarred = value => store.dispatch(setStarred(value));
setIsEmpty: typeof setIsEmpty = value => store.dispatch(setIsEmpty(value));
setIsRouting: typeof setIsRouting = value => store.dispatch(setIsRouting(value));
setMarkersShown = (value: boolean): void => {
if (this.getState().markers_shown !== value) store.dispatch(setMarkersShown(value));
};
resetSaveDialog = (): void => { store.dispatch(resetSaveDialog()); };
setDistance = (value: number): void => {
if (this.getDistance() !== value) store.dispatch(setDistance(value));
};
setChanged = (value: boolean): void => {
if (this.getChanged() !== value) store.dispatch(setChanged(value));
};
clearMode = (): void => { this.setMode(MODES.NONE); };
clearChanged = (): void => { store.dispatch(setChanged(false)); };
startPoly = (): void => {
if (this.getRouterPoints()) this.router.clearAll();
this.poly.continue();
};
triggerOnChange = (): void => {
if (this.isEmpty !== this.getIsEmpty()) this.setIsEmpty(this.isEmpty);
if (this.getEditing() && !this.getChanged()) this.setChanged(true);
};
createStickerOnClick = (e): void => {
if (!e || !e.latlng || !this.activeSticker) return;
const { latlng }: { latlng: ILatLng } = e;
this.stickers.createSticker({ latlng, sticker: this.activeSticker.sticker, set: this.activeSticker.set });
this.setActiveSticker(null);
this.setChanged(true);
// this.setMode(MODES.STICKERS_SELECT);
};
changeMode = (mode: IRootState['mode']): void => {
if (this.mode === mode) {
if (this.switches[mode] && this.switches[mode].toggle) {
// if we have special function on mode when it clicked again
this.switches[mode].toggle();
} else {
this.disableMode(mode);
// this.setMode(MODES.NONE);
this.mode = MODES.NONE;
}
} else {
this.disableMode(this.mode);
// this.setMode(mode);
this.mode = mode;
this.enableMode(mode);
}
};
enableMode = (mode: IRootState['mode']): void => {
if (this.switches[mode] && this.switches[mode].start) this.switches[mode].start();
};
disableMode = (mode: IRootState['mode']): void => {
if (this.switches[mode] && this.switches[mode].stop) this.switches[mode].stop();
};
onClick = (e): void => {
if (e.originalEvent.which === 3) return; // skip right / middle click
if (this.clickHandlers[this.mode]) this.clickHandlers[this.mode](e);
};
lockMapClicks = (lock: boolean): void => {
if (lock) {
this.map.map.removeEventListener('mouseup', this.onClick);
this.map.map.addEventListener('mouseup', this.unlockMapClicks);
} else {
this.map.map.removeEventListener('mouseup', this.unlockMapClicks);
this.map.map.addEventListener('mouseup', this.onClick);
}
};
unlockMapClicks = (): void => {
this.lockMapClicks(false);
};
routerSetStart = (): void => {
const { latlngs } = this.poly;
if (!latlngs || !latlngs.length) return;
this.router.startFrom(latlngs[latlngs.length - 1]);
};
routerMoveStart = (): void => {
const { _latlngs } = this.poly.poly;
if (_latlngs) this.router.moveStart(_latlngs[_latlngs.length - 1]);
};
pushPolyPoints = (latlngs: Array<ILatLng>): void => {
this.poly.pushPoints(latlngs);
};
clearSticker = (): void => {
if (this.activeSticker) {
this.setActiveSticker(null);
} else {
this.setMode(MODES.NONE);
}
};
clearAll = (): void => {
this.poly.clearAll();
this.router.clearAll();
this.stickers.clearAll();
this.setIsEmpty(true);
};
setData = ({
route = [],
stickers = [],
owner,
title,
address,
provider = DEFAULT_PROVIDER,
logo = DEFAULT_LOGO,
is_public,
is_published,
description,
}: Partial<IEditor['initialData']>): void => {
this.mapSetTitle(title || '');
const { id } = this.getUser();
if (address && id && owner && id === owner) {
this.mapSetAddress(address);
}
if (route) this.poly.setPoints(route);
this.stickers.clearAll();
if (stickers) {
stickers.map(sticker =>
sticker.set && STICKERS[sticker.set].url &&
this.stickers.createSticker({
latlng: sticker.latlng,
angle: sticker.angle,
sticker: sticker.sticker,
set: sticker.set,
text: sticker.text,
})
);
}
this.mapSetPublic(is_public);
this.setStarred(is_published);
this.mapSetDescription(description);
this.mapSetLogo((logo && LOGOS[DEFAULT_LOGO] && logo) || DEFAULT_LOGO);
this.setProvider((provider && PROVIDERS[provider] && provider) || DEFAULT_PROVIDER);
if (owner) this.owner = owner;
};
fitDrawing = (): void => {
if (!this.poly.isEmpty) {
const poly_bounds = this.poly.poly.getBounds();
if (poly_bounds && Object.values(poly_bounds).length) {
this.map.map.fitBounds(poly_bounds);
return;
}
}
if (!this.stickers.isEmpty) {
const stickers_bounds = this.stickers.layer.getBounds();
if (stickers_bounds && Object.values(stickers_bounds).length) {
this.map.map.fitBounds(stickers_bounds);
return;
}
}
// no bounds to fit. better do something later
};
setInitialData = (): void => {
const { path } = getUrlData();
const { id } = this.getUser();
const { is_public, logo, is_published , description} = this.getState();
const { route, stickers, provider } = this.dumpData();
this.initialData = {
version: 2,
title: this.getTitle(),
owner: this.owner,
address: (this.owner === id ? path : null),
path,
route,
stickers,
provider,
is_public,
logo,
is_published,
description,
};
};
startEditing = (): void => {
const { id } = this.getUser();
this.setInitialData();
this.owner = id;
this.poly.enableEditor();
this.stickers.startEditing();
};
stopEditing = (): void => {
this.poly.poly.editor.disable();
this.stickers.stopEditing();
this.router.clearAll();
};
cancelEditing = (): void => {
if (this.hasEmptyHistory) {
this.clearAll();
this.startEditing();
this.clearChanged();
return;
} else {
this.setData(this.initialData);
}
this.stopEditing();
this.clearChanged();
};
dumpData = () => ({
route: this.poly.dumpData(),
stickers: this.stickers.dumpData(),
provider: this.getProvider(),
});
setProvider: typeof mapSetProvider = provider => store.dispatch(mapSetProvider(provider));
get isEmpty(): boolean {
const { route, stickers } = this.dumpData();
return (!route || route.length <= 1) && (!stickers || stickers.length <= 0);
}
get hasEmptyHistory(): boolean {
const { route, stickers } = this.initialData;
return (!route || route.length < 1) && (!stickers || stickers.length <= 0);
};
}
export const editor = new Editor();
// for debug purposes
declare let window: any;
window.editor = editor;

View file

@ -1,606 +0,0 @@
/*
done IMPORTANT: select closest point on drag instead of first
done add touch hint poly
done approx radius for dragFindNearest
*/
import {
LatLngExpression,
Marker,
Polyline,
PolylineOptions,
marker,
divIcon,
LayerGroup,
LatLng,
LeafletMouseEvent,
latLng,
LatLngLiteral
} from "leaflet";
import { distKmHaversine, distToSegment, getPolyLength, pointInArea } from "~/utils/geom";
interface InteractivePolylineOptions extends PolylineOptions {
maxMarkers?: number;
constraintsStyle?: PolylineOptions;
kmMarksEnabled?: boolean;
kmMarksStep?: number;
}
export class InteractivePoly extends Polyline {
constructor(
latlngs: LatLngExpression[] | LatLngExpression[][],
options?: InteractivePolylineOptions
) {
super(latlngs, options);
this.constraintsStyle = {
...this.constraintsStyle,
...options.constraintsStyle
};
this.maxMarkers = options.maxMarkers || this.maxMarkers;
this.constrLine = new Polyline([], this.constraintsStyle);
this.startDragHinting();
}
updateTouchHinter = ({ latlngs }: { latlngs: LatLngLiteral[] }): void => {
this.touchHinter.setLatLngs(latlngs);
};
setPoints = (latlngs: LatLng[], emitEvent = false) => {
this.setLatLngs(latlngs);
this.recreateMarkers();
this.recalcDistance();
this.touchHinter.setLatLngs(latlngs);
if (emitEvent) {
this.fire("latlngschange", { latlngs });
}
};
createHintMarker = (latlng: LatLng): Marker =>
marker(latlng, {
draggable: false,
icon: divIcon({
className: "leaflet-vertex-drag-helper",
iconSize: [11, 11],
iconAnchor: [6, 6]
})
});
createMarker = (latlng: LatLng): Marker =>
marker(latlng, {
draggable: true,
icon: divIcon({
className: "leaflet-vertex-icon",
iconSize: [11, 11],
iconAnchor: [6, 6]
})
})
.on("contextmenu", this.dropMarker)
.on("drag", this.onMarkerDrag)
.on("dragstart", this.onMarkerDragStart)
.on("dragend", this.onMarkerDragEnd)
.addTo(this.markerLayer);
recreateMarkers = () => {
this.clearAllMarkers();
const latlngs = this.getLatLngs();
if (!latlngs || latlngs.length === 0) return;
latlngs.forEach(latlng => this.markers.push(this.createMarker(latlng)));
this.updateMarkers();
};
clearAllMarkers = (): void => {
this.markerLayer.clearLayers();
this.markers = [];
};
updateMarkers = (): void => {
this.showVisibleMarkers();
};
showAllMarkers = (): void => {
if (!this.is_editing) return;
if (this._map.hasLayer(this.markerLayer)) return;
this._map.addLayer(this.markerLayer);
this.fire("allvertexshow");
};
hideAllMarkers = (): void => {
if (!this._map.hasLayer(this.markerLayer)) return;
this._map.removeLayer(this.markerLayer);
this.fire("allvertexhide");
};
showVisibleMarkers = (): void => {
if (!this.is_editing) return;
const northEast = this._map.getBounds().getNorthEast();
const southWest = this._map.getBounds().getSouthWest();
const { visible, hidden } = this.markers.reduce(
(obj, marker) => {
const { lat, lng } = marker.getLatLng();
const is_hidden =
lat > northEast.lat ||
lng > northEast.lng ||
lat < southWest.lat ||
lng < southWest.lng;
return is_hidden
? { ...obj, hidden: [...obj.hidden, marker] }
: { ...obj, visible: [...obj.visible, marker] };
},
{ visible: [], hidden: [] }
);
if (visible.length > this.maxMarkers) return this.hideAllMarkers();
this.showAllMarkers();
visible.forEach(marker => {
if (!this.markerLayer.hasLayer(marker)) this.markerLayer.addLayer(marker);
});
hidden.forEach(marker => {
if (this.markerLayer.hasLayer(marker))
this.markerLayer.removeLayer(marker);
});
};
editor = {
disable: () => {
this.hideAllMarkers();
this.is_editing = false;
this.stopDragHinting();
this.stopDrawing();
this.touchHinter.removeFrom(this._map);
this.fire("editordisable");
},
enable: () => {
this.is_editing = true;
this.showVisibleMarkers();
this.startDragHinting();
this.touchHinter.addTo(this._map);
this.fire("editorenable");
},
continue: () => {
this.is_drawing = true;
this.drawing_direction = "forward";
this.startDrawing();
},
prepend: () => {
this.is_drawing = true;
this.drawing_direction = "backward";
this.startDrawing();
}
};
moveDragHint = ({ latlng }: LeafletMouseEvent): void => {
this.hintMarker.setLatLng(latlng);
};
hideDragHint = (): void => {
if (this._map.hasLayer(this.hintMarker))
this._map.removeLayer(this.hintMarker);
};
showDragHint = (): void => {
this._map.addLayer(this.hintMarker);
};
startDragHinting = (): void => {
this.touchHinter.on("mousemove", this.moveDragHint);
this.touchHinter.on("mousedown", this.startDragHintMove);
this.touchHinter.on("mouseover", this.showDragHint);
this.touchHinter.on("mouseout", this.hideDragHint);
};
stopDragHinting = (): void => {
this.touchHinter.off("mousemove", this.moveDragHint);
this.touchHinter.off("mousedown", this.startDragHintMove);
this.touchHinter.off("mouseover", this.showDragHint);
this.touchHinter.off("mouseout", this.hideDragHint);
};
startDragHintMove = (event: LeafletMouseEvent): void => {
event.originalEvent.stopPropagation();
event.originalEvent.preventDefault();
if (this.is_drawing) {
this.stopDrawing();
this.is_drawing = true;
}
const prev = this.dragHintFindNearest(event.latlng);
if (prev < 0) return;
this.hint_prev_marker = prev;
this.constrLine.setLatLngs([]).addTo(this._map);
this._map.dragging.disable();
this.is_dragging = true;
this._map.on("mousemove", this.dragHintMove);
this._map.on("mouseup", this.dragHintAddMarker);
this._map.on("mouseout", this.stopDragHintMove);
};
stopDragHintMove = (): void => {
this._map.dragging.enable();
this.constrLine.removeFrom(this._map);
this._map.off("mousemove", this.dragHintMove);
this._map.off("mouseup", this.dragHintAddMarker);
this._map.off("mouseout", this.stopDragHintMove);
if (this.is_drawing) this.startDrawing();
setTimeout(() => {
this.is_dragging = false;
}, 0);
};
dragHintAddMarker = ({ latlng }: LeafletMouseEvent): void => {
this.dragHintChangeDistance(this.hint_prev_marker, latlng);
this.markers.splice(
this.hint_prev_marker + 1,
0,
this.createMarker(latlng)
);
this.insertLatLng(latlng, this.hint_prev_marker + 1);
this.hideDragHint();
this.stopDragHintMove();
};
dragHintChangeDistance = (index: number, current: LatLngLiteral): void => {
const prev = this.markers[index];
const next = this.markers[index + 1];
const initial_distance = distKmHaversine(prev.getLatLng(), next.getLatLng());
const current_distance =
((prev && distKmHaversine(prev.getLatLng(), current)) || 0) +
((next && distKmHaversine(next.getLatLng(), current)) || 0);
this.distance += current_distance - initial_distance;
this.fire("distancechange", { distance: this.distance });
};
dragHintFindNearest = (latlng: LatLng): any => {
const latlngs = this.getLatLngs() as LatLng[];
const neighbours = latlngs
.filter((current, index) => {
const next = latlngs[index + 1] as LatLng;
return next && pointInArea(current, next, latlng);
})
.map(el => latlngs.indexOf(el))
.sort(
(a, b) =>
distToSegment(latlngs[a], latlngs[a + 1], latlng) -
distToSegment(latlngs[b], latlngs[b + 1], latlng)
);
return neighbours.length > 0 ? neighbours[0] : -1;
};
dragHintMove = (event: LeafletMouseEvent): void => {
event.originalEvent.stopPropagation();
event.originalEvent.preventDefault();
this.setConstraints([
this.markers[this.hint_prev_marker].getLatLng(),
event.latlng,
this.markers[this.hint_prev_marker + 1].getLatLng()
]);
};
onMarkerDrag = ({ target }: { target: Marker }) => {
const coords = new Array(0)
.concat(
(this.vertex_index > 0 &&
this.markers[this.vertex_index - 1].getLatLng()) ||
[]
)
.concat(target.getLatLng())
.concat(
(this.vertex_index < this.markers.length - 1 &&
this.markers[this.vertex_index + 1].getLatLng()) ||
[]
);
this.setConstraints(coords);
this.fire("vertexdrag", { index: this.vertex_index, vertex: target });
};
onMarkerDragStart = ({ target }: { target: Marker }) => {
if (this.is_drawing) {
this.stopDrawing();
this.is_drawing = true;
}
if (this.is_dragging) this.stopDragHintMove();
this.vertex_index = this.markers.indexOf(target);
this.is_dragging = true;
this.constrLine.addTo(this._map);
this.fire("vertexdragstart", { index: this.vertex_index, vertex: target });
};
onMarkerDragEnd = ({ target }: { target: Marker }): void => {
const latlngs = this.getLatLngs() as LatLngLiteral[];
this.markerDragChangeDistance(
this.vertex_index,
latlngs[this.vertex_index],
target.getLatLng()
);
this.replaceLatlng(target.getLatLng(), this.vertex_index);
this.is_dragging = false;
this.constrLine.removeFrom(this._map);
this.vertex_index = null;
if (this.is_drawing) this.startDrawing();
this.fire("vertexdragend", { index: this.vertex_index, vertex: target });
};
markerDragChangeDistance = (
index: number,
initial: LatLngLiteral,
current: LatLngLiteral
): void => {
const prev = index > 0 ? this.markers[index - 1] : null;
const next =
index <= this.markers.length + 1 ? this.markers[index + 1] : null;
const initial_distance =
((prev && distKmHaversine(prev.getLatLng(), initial)) || 0) +
((next && distKmHaversine(next.getLatLng(), initial)) || 0);
const current_distance =
((prev && distKmHaversine(prev.getLatLng(), current)) || 0) +
((next && distKmHaversine(next.getLatLng(), current)) || 0);
this.distance += current_distance - initial_distance;
this.fire("distancechange", { distance: this.distance });
};
startDrawing = (): void => {
this.is_drawing = true;
this.setConstraints([]);
this.constrLine.addTo(this._map);
this._map.on("mousemove", this.onDrawingMove);
this._map.on("click", this.onDrawingClick);
};
stopDrawing = (): void => {
this.constrLine.removeFrom(this._map);
this._map.off("mousemove", this.onDrawingMove);
this._map.off("click", this.onDrawingClick);
this.is_drawing = false;
};
onDrawingMove = ({ latlng }: LeafletMouseEvent): void => {
if (this.markers.length === 0) {
this.setConstraints([]);
return;
}
if (!this._map.hasLayer(this.constrLine))
this._map.addLayer(this.constrLine);
const marker =
this.drawing_direction === "forward"
? this.markers[this.markers.length - 1]
: this.markers[0];
this.setConstraints([latlng, marker.getLatLng()]);
};
onDrawingClick = (event: LeafletMouseEvent): void => {
if (this.is_dragging) return;
const { latlng } = event;
this.stopDrawing();
const latlngs = this.getLatLngs() as any[];
this.drawingChangeDistance(latlng);
if (this.drawing_direction === "forward") {
latlngs.push(latlng);
this.markers.push(this.createMarker(latlng));
} else {
latlngs.unshift(latlng);
this.markers.unshift(this.createMarker(latlng));
}
this.setLatLngs(latlngs);
this.fire("latlngschange", { latlngs });
this.showVisibleMarkers();
this.startDrawing();
};
drawingChangeDistance = (latlng: LatLngLiteral): void => {
const latlngs = this.getLatLngs() as LatLngLiteral[];
if (latlngs.length < 1) {
this.distance = 0;
this.fire("distancechange", { distance: this.distance });
return;
}
const point =
this.drawing_direction === "forward"
? latlngs[latlngs.length - 1]
: latlngs[0];
this.distance += distKmHaversine(point, latlng);
this.fire("distancechange", { distance: this.distance });
};
replaceLatlng = (latlng: LatLng, index: number): void => {
const latlngs = this.getLatLngs() as LatLngLiteral[];
latlngs.splice(index, 1, latlng);
this.setLatLngs(latlngs);
this.fire("latlngschange", { latlngs });
};
insertLatLng = (latlng, index): void => {
const latlngs = this.getLatLngs();
latlngs.splice(index, 0, latlng);
this.setLatLngs(latlngs);
this.fire("latlngschange", { latlngs });
};
setConstraints = (coords: LatLng[]) => {
this.constrLine.setLatLngs(coords);
};
dropMarker = ({ target }: LeafletMouseEvent): void => {
const index = this.markers.indexOf(target);
const latlngs = this.getLatLngs();
if (typeof index === "undefined" || latlngs.length <= 2) return;
this.dropMarkerDistanceChange(index);
this._map.removeLayer(this.markers[index]);
this.markers.splice(index, 1);
latlngs.splice(index, 1);
this.setLatLngs(latlngs);
this.fire("latlngschange", { latlngs });
};
dropMarkerDistanceChange = (index: number): void => {
const latlngs = this.getLatLngs() as LatLngLiteral[];
const prev = index > 0 ? latlngs[index - 1] : null;
const current = latlngs[index];
const next = index <= latlngs.length + 1 ? latlngs[index + 1] : null;
const initial_distance =
((prev && distKmHaversine(prev, current)) || 0) +
((next && distKmHaversine(next, current)) || 0);
const current_distance = (prev && next && distKmHaversine(prev, next)) || 0;
this.distance += current_distance - initial_distance;
this.fire("distancechange", { distance: this.distance });
};
recalcDistance = () => {
const latlngs = this.getLatLngs() as LatLngLiteral[];
this.distance = getPolyLength(latlngs);
this.fire("distancechange", { distance: this.distance });
};
markers: Marker[] = [];
maxMarkers: InteractivePolylineOptions["maxMarkers"] = 2;
markerLayer: LayerGroup = new LayerGroup();
constraintsStyle: InteractivePolylineOptions["constraintsStyle"] = {
weight: 6,
color: "red",
dashArray: "10, 12",
opacity: 0.5,
interactive: false
};
touchHinter: Polyline = new Polyline([], {
weight: 24,
smoothFactor: 3,
className: "touch-hinter-poly"
});
hintMarker: Marker = this.createHintMarker(latLng({ lat: 0, lng: 0 }));
constrLine: Polyline;
is_editing: boolean = true;
is_dragging: boolean = false;
is_drawing: boolean = false;
drawing_direction: "forward" | "backward" = "forward";
vertex_index?: number = null;
hint_prev_marker: number = null;
distance: number = 0;
}
InteractivePoly.addInitHook(function() {
this.once("add", event => {
if (event.target instanceof InteractivePoly) {
this.map = event.target._map;
this.markerLayer.addTo(event.target._map);
this.hintMarker.addTo(event.target._map);
this.constrLine.addTo(event.target._map);
this.touchHinter.addTo(event.target._map);
this.map.on("moveend", this.updateMarkers);
this.on("latlngschange", this.updateTouchHinter);
if (window.innerWidth < 768) {
this.touchHinter.setStyle({ weight: 32 });
}
}
});
this.once("remove", event => {
if (event.target instanceof InteractivePoly) {
this.markerLayer.removeFrom(this._map);
this.hintMarker.removeFrom(this._map);
this.constrLine.removeFrom(this._map);
this.touchHinter.removeFrom(this._map);
this.map.off("moveend", this.updateMarkers);
}
});
});
// export const InteractivePoly = Component;
/*
events:
vertexdragstart,
vertexdragend,
vertexdrag,
allvertexhide
allvertexshow
editordisable
editorenable
distancechange
latlngschange
*/

View file

@ -1,156 +0,0 @@
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, distKmHaversine } from "~/utils/geom";
import classNames from 'classnames';
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: 10,
...(options || {}),
} as KmMarksOptions;
}
setLatLngs = (latlngs: LatLngLiteral[]): void => {
if (!this.map) return;
this.marksLayer.clearLayers();
this.endMarker.clearLayers();
this.distance = 0;
if (latlngs.length <= 1) 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;
this.distance = latlngs.reduce((dist, current, index) => {
if (index >= latlngs.length - 1) return dist;
const next = latlngs[index + 1];
const diff = distKmHaversine(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),
);
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));
}
last_km_mark = rounded;
}
return sum;
}, 0);
};
createMiddleMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, {
draggable: false,
interactive: false,
icon: divIcon({
html: `
<div class="leaflet-km-dist" style="transform: translate(-50%, -50%) rotate(${allwaysPositiveAngleDeg(angle)}deg);">
${distance}
</div>
`,
className: 'leaflet-km-marker',
iconSize: [11, 11],
iconAnchor: [6, 6]
})
});
createEndMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, {
draggable: false,
interactive: false,
icon: divIcon({
html: `
<div class="leaflet-km-dist">
${parseFloat(distance.toFixed(1))}
</div>
`,
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;
map: Map;
marksLayer: MarkerClusterGroup = new MarkerClusterGroup({
spiderfyOnMaxZoom: false,
showCoverageOnHover: false,
zoomToBoundsOnClick: false,
animate: false,
maxClusterRadius: 120,
iconCreateFunction: arrowClusterIcon,
});
endMarker: LayerGroup = new LayerGroup();
distance: number = 0;
}
Component.addInitHook(function () {
this.once('add', (event) => {
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);
}
});
});
export const KmMarks = Component;

View file

@ -1,36 +0,0 @@
import {
Map as MapInterface,
map,
tileLayer,
TileLayer,
} from 'leaflet';
import 'leaflet/dist/leaflet.css';
import { PROVIDER } from '~/config/frontend';
import { DEFAULT_PROVIDER, PROVIDERS } from '~/constants/providers';
interface Props {
container: string
}
export class Map {
constructor({ container }: Props) {
this.map = map(container).setView([55.0153275, 82.9071235], 13);
// todo: change coords?
this.tileLayer.addTo(this.map);
}
map: MapInterface;
tileLayer: TileLayer = tileLayer(PROVIDER.url, {
attribution: 'Независимое Велосообщество',
maxNativeZoom: 18,
maxZoom: 18,
});
setProvider = (provider: string): void => {
const { url } = (provider && PROVIDERS[provider] && PROVIDERS[provider]) || PROVIDERS[DEFAULT_PROVIDER];
this.tileLayer.setUrl(url);
}
}

View file

@ -1,119 +0,0 @@
import { Map, LatLng } from 'leaflet';
import { simplify } from '~/utils/simplify';
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";
import { isMobile } from "~/utils/window";
interface Props {
map: Map;
editor: Editor;
routerMoveStart: typeof editor.routerMoveStart,
lockMapClicks: typeof editor.lockMapClicks,
setDistance: typeof editor.setDistance,
triggerOnChange: typeof editor.triggerOnChange,
}
export class Poly {
constructor({
map, routerMoveStart, lockMapClicks, setDistance, triggerOnChange, editor,
}: Props) {
this.poly = new InteractivePoly([ ], {
color: 'url(#activePathGradient)',
weight: 6,
maxMarkers: isMobile() ? 20 : 100,
smoothFactor: 3,
})
.on('distancechange', this.onDistanceUpdate)
.on('allvertexhide', this.onVertexHide)
.on('allvertexshow', this.onVertexShow)
.on('latlngschange', this.updateMarks)
this.poly.addTo(map);
this.editor = editor;
this.map = map;
this.routerMoveStart = routerMoveStart;
this.setDistance = setDistance;
this.triggerOnChange = triggerOnChange;
this.lockMapClicks = lockMapClicks;
this.arrows = new Arrows({}).addTo(map);
this.kmMarks = new KmMarks().addTo(map);
}
onDistanceUpdate = (event) => {
const { distance } = event as { distance: number };
this.setDistance(parseFloat(distance.toFixed(2)));
};
onVertexHide = (): void => this.editor.setMarkersShown(false);
onVertexShow = (): void => this.editor.setMarkersShown(true);
updateMarks = event => {
// this.editor.setChanged(true);
this.editor.triggerOnChange();
const { latlngs } = event;
this.arrows.setLatLngs(latlngs);
this.kmMarks.setLatLngs(latlngs);
};
continue = (): void => {
this.poly.editor.continue();
};
stop = (): void => {
this.poly.stopDrawing();
};
enableEditor = (): void => {
this.poly.editor.enable();
};
setPoints = (latlngs: Array<ILatLng>): void => {
if (!latlngs || latlngs.length <= 1) return;
this.poly.setPoints(latlngs);
};
pushPoints = (latlngs: Array<ILatLng>): void => {
const { map } = this;
const simplified = simplify({ map, latlngs });
const summary = [
...this.poly.getLatLngs(),
...simplified,
];
this.poly.setPoints(summary);
};
clearAll = (): void => {
this.poly.setPoints([]);
};
dumpData = (): Array<LatLng> => this.latlngs;
get latlngs(): Array<LatLng> {
return (
this.poly && this.poly.getLatLngs().length
&& this.poly.getLatLngs().map(el => ({ ...el }))) || [];
}
get isEmpty(): boolean {
return (!this.latlngs || Object.values(this.latlngs).length <= 0);
}
arrows;
poly;
kmMarks;
editor: Props['editor'];
map: Props['map'];
routerMoveStart: Props['routerMoveStart'];
setDistance: Props['setDistance'];
triggerOnChange: Props['triggerOnChange'];
lockMapClicks: Props['lockMapClicks'];
}

View file

@ -1,174 +0,0 @@
import { Map, Marker } from 'leaflet';
import * as Routing from 'leaflet-routing-machine/src/index';
import { CLIENT } from '~/config/frontend';
import { DomMarker } from '~/utils/DomMarker';
import { editor } from "~/modules/Editor";
interface ILatLng {
lat: number, lng: number
}
interface IWaypoint {
latLng: ILatLng
}
interface Props {
setIsRouting: typeof editor.setIsRouting,
map: Map,
setRouterPoints: typeof editor.setRouterPoints,
pushPolyPoints: typeof editor.pushPolyPoints,
lockMapClicks: typeof editor.lockMapClicks;
}
export class Router {
constructor({
map, lockMapClicks, setRouterPoints, pushPolyPoints, setIsRouting,
}: Props) {
this.waypoints = [];
this.lockMapClicks = lockMapClicks;
this.setRouterPoints = setRouterPoints;
this.pushPolyPoints = pushPolyPoints;
this.setIsRouting = setIsRouting;
const routeLine = r => Routing.line(r, {
styles: [
{ color: 'white', opacity: 0.8, weight: 6 },
{ color: '#4597d0', opacity: 1, weight: 4, dashArray: '15,10' }
],
addWaypoints: true,
}).on('linetouched', this.lockPropagations);
this.router = Routing.control({
serviceUrl: CLIENT.OSRM_URL,
profile: CLIENT.OSRM_PROFILE,
fitSelectedRoutes: false,
showAlternatives: false,
routeLine,
altLineOptions: {
styles: [{ color: '#4597d0', opacity: 1, weight: 3 }]
},
show: false,
plan: Routing.plan([], {
createMarker: (i, wp) => new Marker(wp.latLng, {
draggable: true,
icon: this.createWaypointMarker(),
}),
routeWhileDragging: false,
}),
routeWhileDragging: false,
routingOptions: {
geometryOnly: false,
},
useHints: false,
})
.on('routingstart', this.showSpinner)
.on('routesfound routingerror routeselected routingzoomend', this.hideSpinner)
.on('waypointschanged', this.updateWaypointsCount);
this.router.addTo(map);
}
showSpinner = () => {
this.setIsRouting(true);
};
hideSpinner = () => {
this.setIsRouting(false);
};
pushWaypointOnClick = ({ latlng: { lat, lng } }: { latlng: ILatLng }): void => {
const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng);
this.router.setWaypoints([...waypoints, { lat, lng }]);
};
createWaypointMarker = (): DomMarker => {
const element = document.createElement('div');
element.addEventListener('mousedown', this.lockPropagations);
element.addEventListener('mouseup', this.unlockPropagations);
return new DomMarker({
element,
className: 'router-waypoint',
});
};
lockPropagations = (): void => {
window.addEventListener('mouseup', this.unlockPropagations);
this.lockMapClicks(true);
};
unlockPropagations = (e): void => {
if (e && e.preventPropagations) {
e.preventDefault();
e.preventPropagations();
}
window.removeEventListener('mouseup', this.unlockPropagations);
setTimeout(() => this.lockMapClicks(false), 0);
};
startFrom = (latlngs: ILatLng): void => {
const waypoints = this.router.getWaypoints();
if (waypoints && waypoints.length) {
waypoints[0] = { ...latlngs };
this.router.setWaypoints(waypoints);
return;
}
this.router.setWaypoints([{ ...latlngs }]);
};
moveStart = (latlng: ILatLng): void => {
const waypoints = this.router.getWaypoints();
const { latLng }: { latLng: ILatLng } = (waypoints[0] || {});
if (!latLng || !latlng) return;
if (
latlng.lat.toFixed(5) === latLng.lat.toFixed(5) &&
latlng.lng.toFixed(5) === latLng.lng.toFixed(5)
) {
return;
}
waypoints[0] = { ...latlng };
this.router.setWaypoints(waypoints);
};
updateWaypointsCount = (): void => {
const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng);
this.setRouterPoints(waypoints.length);
};
cancelDrawing = (): void => {
this.router.setWaypoints([]);
};
submitDrawing = (): void => {
const [route] = this.router._routes;
if (!route) return;
const { coordinates } = route;
this.pushPolyPoints(coordinates);
this.router.setWaypoints([]);
// UNCOMMENT THIS TO CONTINUE DRAWING
// const waypoints = this.router.getWaypoints().filter(({ latLng }) => !!latLng);
// this.router.setWaypoints(waypoints[waypoints.length - 1]);
};
clearAll = (): void => {
this.router.setWaypoints([]);
};
waypoints: Array<IWaypoint> = [];
setIsRouting: Props['setIsRouting'];
lockMapClicks: Props['lockMapClicks'];
setRouterPoints: Props['setRouterPoints'];
pushPolyPoints: Props['pushPolyPoints'];
router: Routing;
}

View file

@ -1,236 +0,0 @@
import { Map, Marker, marker } from 'leaflet';
import React from 'react';
import { DomMarker } from '~/utils/DomMarker';
import { STICKERS } from '~/constants/stickers';
import ReactDOM from 'react-dom';
import { StickerDesc } from '~/components/StickerDesc';
import classnames from 'classnames';
import { getLabelDirection } from '~/utils/geom';
import { ILatLng } from "~/modules/Stickers";
import { IRootState } from "~/redux/user";
import { Editor, editor } from "~/modules/Editor";
const getX = e => (
e.touches && e.touches.length > 0
? { pageX: e.touches[0].pageX, pageY: e.touches[0].pageY }
: { pageX: e.pageX, pageY: e.pageY }
);
export interface IStickerDump {
latlng: ILatLng,
set: IRootState['activeSticker']['set'],
sticker: IRootState['activeSticker']['sticker'],
angle?: number,
text?: string,
}
interface Props {
latlng: ILatLng;
map: Map;
sticker: IRootState['activeSticker']['sticker'];
set: IRootState['activeSticker']['set'];
angle?: number;
text?: string;
editor: Editor,
deleteSticker: (sticker: this) => void;
triggerOnChange: typeof editor.triggerOnChange;
lockMapClicks: typeof editor.lockMapClicks;
}
export class Sticker {
constructor({
latlng, deleteSticker, map, lockMapClicks, sticker, set, triggerOnChange, angle = 2.2, text = '', editor,
}: Props) {
this.text = text;
this.latlng = latlng;
this.angle = parseFloat(((angle && (angle % Math.PI)) || 2.2).toFixed(2));
this.map = map;
this.sticker = sticker;
this.set = set;
this.triggerOnChange = triggerOnChange;
this.direction = getLabelDirection(this.angle);
this.deleteSticker = deleteSticker;
this.lockMapClicks = lockMapClicks;
this.editor = editor;
this.element = document.createElement('div');
ReactDOM.render(
<React.Fragment>
<div
className="sticker-arrow"
ref={el => { this.stickerArrow = el; }}
/>
<div
className={classnames(`sticker-label ${this.direction}`, {})}
ref={el => { this.stickerImage = el; }}
>
<StickerDesc value={this.text} onChange={this.setText} />
<div
className="sticker-image"
style={{
backgroundImage: `url('${STICKERS[set].url}`,
backgroundPosition: `${-STICKERS[set].layers[sticker].off * 72} 50%`,
}}
onMouseDown={this.onDragStart}
onMouseUp={this.onDragStop}
onTouchStart={this.onDragStart}
onTouchEnd={this.onDragStop}
/>
<div
className="sticker-delete"
onMouseDown={this.onDelete}
onTouchStart={this.onDelete}
/>
</div>
</React.Fragment>,
this.element
);
const mark = new DomMarker({
element: this.element,
className: 'sticker-container',
});
this.marker = marker(latlng, { icon: mark, draggable: true });
this.marker.on('add', this.updateModeOnAdd);
this.element.addEventListener('mouseup', this.onDragStop);
this.element.addEventListener('mouseup', this.preventPropagations);
this.element.addEventListener('touchend', this.onDragStop);
this.element.addEventListener('touchend', this.preventPropagations);
this.marker.on('dragend', this.triggerOnChange);
this.setAngle(this.angle);
}
updateModeOnAdd = () => {
if (this.editor.getEditing()) {
this.startEditing();
} else {
this.stopEditing();
}
};
setText = (text: Props['text']): void => {
this.text = text;
};
onDelete = (): void => {
if (!this.isDragging) this.deleteSticker(this);
};
onDragStart = (e): void => {
this.preventPropagations(e);
this.marker.dragging.disable();
this.isDragging = true;
this.lockMapClicks(true);
window.addEventListener('mousemove', this.onDrag);
window.addEventListener('touchmove', this.onDrag);
window.addEventListener('mouseup', this.onDragStop);
window.addEventListener('touchend', this.onDragStop);
};
preventPropagations = (e): void => {
if (!e || !e.stopPropagation) return;
e.stopPropagation();
e.preventDefault();
};
onDragStop = (e): void => {
this.preventPropagations(e);
this.marker.dragging.enable();
this.triggerOnChange();
this.isDragging = false;
window.removeEventListener('mousemove', this.onDrag);
window.removeEventListener('touchmove', this.onDrag);
window.removeEventListener('mouseup', this.onDragStop);
window.removeEventListener('touchend', this.onDragStop);
this.lockMapClicks(false);
};
onDrag = (e: DragEvent): void => {
this.preventPropagations(e);
this.estimateAngle(e);
};
estimateAngle = (e): void => {
const { x, y } = this.element.getBoundingClientRect() as DOMRect;
const { pageX, pageY } = getX(e);
this.angle = parseFloat(Math.atan2((y - pageY), (x - pageX)).toFixed(2));
this.setAngle(this.angle);
};
setAngle = (angle: Props['angle']): void => {
if (!this.stickerImage) return;
const direction = getLabelDirection(angle);
if (direction !== this.direction) {
this.direction = direction;
this.stickerImage.className = `sticker-label ${direction}`;
}
const rad = 56;
const x = ((Math.cos(angle + Math.PI) * rad) - 30);
const y = ((Math.sin(angle + Math.PI) * rad) - 30);
this.stickerImage.style.left = String(6 + x);
this.stickerImage.style.top = String(6 + y);
this.stickerArrow.style.transform = `rotate(${angle + Math.PI}rad)`;
};
dumpData = (): IStickerDump => ({
angle: this.angle,
latlng: { ...this.marker.getLatLng() },
sticker: this.sticker,
set: this.set,
text: this.text,
});
stopEditing = (): void => {
this.element.className = 'sticker-container inactive';
};
startEditing = (): void => {
this.element.className = 'sticker-container';
};
element: HTMLDivElement = document.createElement('div');
stickerImage: HTMLDivElement;
stickerArrow: HTMLDivElement;
marker: Marker;
isDragging: boolean = false;
direction: string;
editor: Editor;
text: Props['text'];
latlng: Props['latlng'];
angle: Props['angle'];
map: Props['map'];
sticker: Props['sticker'];
set: Props['set'];
triggerOnChange: Props['triggerOnChange'];
deleteSticker: Props['deleteSticker'];
lockMapClicks: Props['lockMapClicks'];
}

View file

@ -1,117 +0,0 @@
import {FeatureGroup, LayerGroup, layerGroup, Map} from 'leaflet';
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,
lng: number,
}
interface Props {
editor: Editor;
map: Map;
triggerOnChange: typeof editor.triggerOnChange;
lockMapClicks: typeof editor.lockMapClicks;
}
export class Stickers {
constructor({ map, lockMapClicks, triggerOnChange, editor }: Props) {
this.map = map;
this.triggerOnChange = triggerOnChange;
this.editor = editor;
// this.clusterLayer.addTo(map);
// this.clusterLayer.on('animationend', this.onSpiderify);
this.lockMapClicks = lockMapClicks;
this.stickers = [];
this.layer.addTo(this.map);
}
createSticker = ({
latlng, sticker, angle = 2.2, text = '', set
}: IStickerDump): void => {
if (!STICKERS[set] || !STICKERS[set].layers || !STICKERS[set].layers[sticker]) return;
const marker = new Sticker({
latlng,
angle,
deleteSticker: this.deleteStickerByReference,
map: this.map,
lockMapClicks: this.lockMapClicks,
sticker,
set,
triggerOnChange: this.triggerOnChange,
text,
editor: this.editor,
});
this.stickers.push(marker);
marker.marker.addTo(this.layer);
this.triggerOnChange();
};
deleteStickerByReference = (ref: Sticker): void => {
const index = this.stickers.indexOf(ref);
if (index < 0) return;
// this.clusterLayer.removeLayer(ref.marker);
this.layer.removeLayer(ref.marker);
this.stickers.splice(index, 1);
this.triggerOnChange();
};
clearAll = (): void => {
const target = [...this.stickers];
target.map(sticker => {
this.deleteStickerByReference(sticker);
return true;
});
};
dumpData = (): Array<IStickerDump> => this.stickers.map(sticker => sticker.dumpData());
startEditing = (): void => {
this.stickers.map(sticker => sticker.startEditing());
};
stopEditing = (): void => {
this.stickers.map(sticker => sticker.stopEditing());
};
get isEmpty(): boolean {
return !this.stickers || this.stickers.length === 0
};
// clusterLayer: LayerGroup = new LayerGroup();
// uncomment to enable clustering
// clusterLayer: MarkerClusterGroup = new MarkerClusterGroup({
// spiderfyOnMaxZoom: false,
// showCoverageOnHover: false,
// zoomToBoundsOnClick: true,
// animate: false,
// maxClusterRadius: 8,
// // disableClusteringAtZoom: 13,
// iconCreateFunction: clusterIcon,
// });
editor: Props['editor'];
map: Props['map'];
stickers: Array<Sticker> = [];
layer: FeatureGroup = new FeatureGroup();
triggerOnChange: Props['triggerOnChange'];
lockMapClicks: Props['lockMapClicks'];
}

View file

@ -32,20 +32,11 @@ const mapStateToProps = ({
user: { role },
},
}: IState) => {
if (routes.filter.max >= 9999) {
return {
routes,
editing,
ready: false,
role,
};
}
return {
role,
routes,
editing,
ready: true,
ready: routes.filter.max < 9999,
};
};

View file

@ -89,18 +89,17 @@ const mapDispatchToProps = {
editorRouterSubmit: EDITOR_ACTIONS.editorRouterSubmit,
};
type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & { };
type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
const RouterDialogUnconnected: FC<Props> = ({
editor: {
router: { waypoints },
is_routing,
},
editorRouterCancel,
editorRouterSubmit,
}) => (
<div className="control-dialog bottom right">
<div className={classnames('save-loader', { active: is_routing })} />
<div className={classnames('save-loader')} />
{!waypoints.length && noPoints({ editorRouterCancel })}
{waypoints.length === 1 && firstPoint({ editorRouterCancel })}

View file

@ -1,29 +1,25 @@
// @flow
import React from 'react';
import { Icon } from '~/components/panels/Icon';
import { MapListDialog } from "~/components/dialogs/MapListDialog";
import { Tooltip } from "~/components/panels/Tooltip";
import { ReactElement } from "react";
import React, { FC, memo } from 'react';
import { MapListDialog } from '~/components/dialogs/MapListDialog';
import { ReactElement } from 'react';
interface Props {
address: string,
stopEditing: typeof MapListDialog.stopEditing,
dropRoute: typeof MapListDialog.dropRoute,
address: string;
stopEditing: typeof MapListDialog.stopEditing;
dropRoute: typeof MapListDialog.dropRoute;
}
export const RouteRowDrop = ({
address, stopEditing, dropRoute,
}: Props): ReactElement<Props, null> => (
<div
className="route-row-drop"
>
<div
className="route-row"
>
export const RouteRowDrop: FC<Props> = memo(({ address, stopEditing, dropRoute }) => (
<div className="route-row-drop">
<div className="route-row">
<div className="button-group">
<div className="button" onClick={dropRoute.bind(null, address)}>Удалить</div>
<div className="button primary" onClick={stopEditing}>Отмена</div>
<div className="button" onClick={dropRoute.bind(null, address)}>
Удалить
</div>
<div className="button primary" onClick={stopEditing}>
Отмена
</div>
</div>
</div>
</div>
);
));

View file

@ -4,27 +4,24 @@ import { Icon } from '~/components/panels/Icon';
import { connect } from 'react-redux';
import Slider from 'rc-slider/lib/Slider';
import { editorSetSpeed } from '~/redux/editor/actions';
import { Tooltip } from "~/components/panels/Tooltip";
import { isMobile } from "~/utils/window";
import { Tooltip } from '~/components/panels/Tooltip';
import { isMobile } from '~/utils/window';
import { IState } from '~/redux/store';
import pick from 'ramda/es/pick';
import { selectEditor } from '~/redux/editor/selectors';
function mapStateToProps(state) {
const {
editor: { distance, estimated, speed },
}: IState = state;
return { distance, estimated, speed };
}
const mapStateToProps = (state: IState) =>
pick(['distance', 'estimated', 'speed'], selectEditor(state));
const mapDispatchToProps = { editorSetSpeed };
type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
interface State {
dialogOpened: boolean,
dialogOpened: boolean;
}
class Component extends React.PureComponent<Props, State> {
class DistanceBarUnconnected extends React.PureComponent<Props, State> {
constructor(props) {
super(props);
this.state = {
@ -36,10 +33,15 @@ class Component extends React.PureComponent<Props, State> {
min: number = 5;
max: number = 30;
marks: { [x: number]: string } = [...Array((Math.floor(this.max - this.min) / this.step) + 1)].reduce((obj, el, index) => ({
...obj,
[this.min + (index * this.step)]: String(this.min + (index * this.step)),
}), { });
marks: { [x: number]: string } = [
...Array(Math.floor(this.max - this.min) / this.step + 1),
].reduce(
(obj, el, index) => ({
...obj,
[this.min + index * this.step]: String(this.min + index * this.step),
}),
{}
);
toggleDialog = () => {
if (isMobile()) return;
@ -51,10 +53,12 @@ class Component extends React.PureComponent<Props, State> {
const {
props: { distance, estimated, speed },
state: { dialogOpened },
min, max, step, marks,
min,
max,
step,
marks,
} = this;
return (
<React.Fragment>
<div className="status-bar padded pointer tooltip-container" onClick={this.toggleDialog}>
@ -65,8 +69,7 @@ class Component extends React.PureComponent<Props, State> {
</span>
<div className="desktop-only">{toHours(estimated)}</div>
</div>
{
dialogOpened &&
{dialogOpened && (
<div className="control-dialog top left" style={{ left: 0, top: 42 }}>
<div className="helper speed-helper">
<Slider
@ -80,13 +83,10 @@ class Component extends React.PureComponent<Props, State> {
/>
</div>
</div>
}
)}
</React.Fragment>
);
}
}
export const DistanceBar = connect(
mapStateToProps,
mapDispatchToProps
)(Component);
export const DistanceBar = connect(mapStateToProps, mapDispatchToProps)(DistanceBarUnconnected);

View file

@ -4,7 +4,6 @@ import classnames from 'classnames';
import { Icon } from '~/components/panels/Icon';
import { EditorDialog } from '~/components/panels/EditorDialog';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import {
editorSetMode,
@ -16,10 +15,10 @@ import {
import { Tooltip } from '~/components/panels/Tooltip';
import { IState } from '~/redux/store';
import { selectEditor } from '~/redux/editor/selectors';
import pick from 'ramda/es/pick';
const mapStateToProps = (state: IState) => ({
editor: selectEditor(state),
});
const mapStateToProps = (state: IState) =>
pick(['mode', 'changed', 'editing', 'features'], selectEditor(state));
const mapDispatchToProps = {
editorSetMode,
@ -54,18 +53,15 @@ class EditorPanelUnconnected extends PureComponent<Props, void> {
startRouterMode = () => this.props.editorSetMode(MODES.ROUTER);
startTrashMode = () => this.props.editorSetMode(MODES.TRASH);
startSaveMode = () => {
// if (!this.props.changed) return;
this.props.editorSetMode(MODES.SAVE);
};
render() {
const {
editor: {
mode,
changed,
editing,
features: { routing },
},
mode,
changed,
editing,
features: { routing },
} = this.props;
return (

View file

@ -1,6 +1,6 @@
import React from 'react';
import React, { memo } from 'react';
export const Icon = ({ icon, size = 32 }: { icon: string, size?: number }) => (
export const Icon = memo(({ icon, size = 32 }: { icon: string; size?: number }) => (
<svg width={size} height={size} viewBox="0 0 32 32">
<defs>
<mask id={`icon-mask-${icon}`}>
@ -9,5 +9,4 @@ export const Icon = ({ icon, size = 32 }: { icon: string, size?: number }) => (
</defs>
<rect x="0" y="0" width="32" height="32" stroke="none" mask={`url(#icon-mask-${icon})`} />
</svg>
);
));

View file

@ -1,10 +1,10 @@
import React from 'react';
import React, { memo } from 'react';
import { UserLocation } from '~/components/UserLocation';
import { DistanceBar } from '~/components/panels/DistanceBar';
export const TopLeftPanel = () => (
export const TopLeftPanel = memo(() => (
<div className="status-panel top left">
<UserLocation />
<DistanceBar />
</div>
);
));

View file

@ -9,11 +9,14 @@ import { MODES } from '~/constants/modes';
import { Tooltip } from '~/components/panels/Tooltip';
import { selectMap } from '~/redux/map/selectors';
import { selectEditor } from '~/redux/editor/selectors';
import { IState } from '~/redux/store';
const mapStateToProps = state => ({
map: selectMap(state),
editor: selectEditor(state),
});
const mapStateToProps = (state: IState) => {
const { provider, logo } = selectMap(state);
const { markers_shown, editing } = selectEditor(state);
return { provider, logo, markers_shown, editing };
};
const mapDispatchToProps = {
editorSetMode: EDITOR_ACTIONS.editorSetMode,
@ -22,8 +25,10 @@ const mapDispatchToProps = {
type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
const TopRightPanelUnconnected = ({
map: { provider, logo },
editor: { markers_shown, editing },
provider,
logo,
markers_shown,
editing,
editorSetMode,
}: Props) => {
const startProviderMode = useCallback(() => editorSetMode(MODES.PROVIDER), [editorSetMode]);

View file

@ -19,21 +19,18 @@ import { CLIENT } from '~/config/frontend';
import { DIALOGS, TABS } from '~/constants/dialogs';
import { Tooltip } from '~/components/panels/Tooltip';
import { TitleDialog } from '~/components/dialogs/TitleDialog';
import { IState } from '~/redux/store';
const mapStateToProps = ({
user: { user },
editor: { dialog, dialog_active },
map: { route, stickers },
}) => ({
editor: {
dialog,
dialog_active,
},
user: { user },
map: {
route,
stickers,
}
}: IState) => ({
dialog,
dialog_active,
user,
route,
stickers,
});
const mapDispatchToProps = {
@ -100,7 +97,7 @@ export class UserPanelUnconnected extends PureComponent<Props, State> {
openAppInfoDialog = () => {
this.setMenuOpened();
this.props.editorSetDialog(DIALOGS.APP_INFO);
this.props.editorSetDialogActive(this.props.editor.dialog !== DIALOGS.APP_INFO);
this.props.editorSetDialogActive(this.props.dialog !== DIALOGS.APP_INFO);
};
openOauthFrame = () => {
@ -118,7 +115,7 @@ export class UserPanelUnconnected extends PureComponent<Props, State> {
render() {
const {
props: { user: { user }, editor: { dialog, dialog_active }, map: { route, stickers } },
props: { user, dialog, dialog_active, route, stickers },
state: { menuOpened },
} = this;

View file

@ -1,25 +1,22 @@
// @flow
import React from "react";
import { UserPicture } from "~/components/user/UserPicture";
import { IUser } from "~/constants/auth";
import React, { FC, memo } from 'react';
import { UserPicture } from '~/components/user/UserPicture';
import { IUser } from '~/constants/auth';
interface Props {
user: IUser;
setMenuOpened: () => void;
}
export const UserButton = ({
setMenuOpened,
user: { uid, photo, name }
}: Props) => (
export const UserButton: FC<Props> = memo(({ setMenuOpened, user: { uid, photo, name } }) => (
<div className="control-bar user-bar">
<div className="user-button" onClick={setMenuOpened}>
<UserPicture photo={photo} />
<div className="user-button-fields">
<div className="user-button-name">{name || uid || "..."}</div>
<div className="user-button-text">{uid || "пользователь"}</div>
<div className="user-button-name">{name || uid || '...'}</div>
<div className="user-button-text">{uid || 'пользователь'}</div>
</div>
</div>
</div>
);
));

View file

@ -32,7 +32,7 @@ type Props = {
editorSetDialogActive: typeof editorSetDialogActive;
};
const Component = (props: Props) => (
const AppUnconnected = (props: Props) => (
<div>
<Fills />
<UserPanel />
@ -72,6 +72,8 @@ const mapStateToProps = ({
set,
});
const mapDispatchToProps = dispatch =>
bindActionCreators({ editorHideRenderer, editorSetDialogActive }, dispatch);
export const App = connect(mapStateToProps, mapDispatchToProps)(hot(module)(Component));
const mapDispatchToProps = { editorHideRenderer, editorSetDialogActive };
const App = connect(mapStateToProps, mapDispatchToProps)(hot(module)(AppUnconnected));
export { App };

View file

@ -1,4 +1,4 @@
import React, { createElement } from 'react';
import React, { createElement, FC, memo } from 'react';
import { DIALOGS, IDialogs } from '~/constants/dialogs';
import classnames from 'classnames';
import { AppInfoDialog } from '~/components/dialogs/AppInfoDialog';
@ -17,7 +17,7 @@ const LEFT_DIALOGS = {
[DIALOGS.APP_INFO]: AppInfoDialog,
};
const LeftDialog = ({ dialog, dialog_active, editorSetDialogActive }: Props) => (
const LeftDialog: FC<Props> = memo(({ dialog, dialog_active, editorSetDialogActive }) => (
<React.Fragment>
{Object.keys(LEFT_DIALOGS).map(item => (
<div
@ -26,16 +26,22 @@ const LeftDialog = ({ dialog, dialog_active, editorSetDialogActive }: Props) =>
>
{dialog && LEFT_DIALOGS[item] && createElement(LEFT_DIALOGS[item], {})}
<div className="dialog-close-button desktop-only" onClick={() => editorSetDialogActive(false)}>
<div
className="dialog-close-button desktop-only"
onClick={() => editorSetDialogActive(false)}
>
<Icon icon="icon-cancel-1" />
</div>
<div className="dialog-close-button mobile-only" onClick={() => editorSetDialogActive(false)}>
<div
className="dialog-close-button mobile-only"
onClick={() => editorSetDialogActive(false)}
>
<Icon icon="icon-chevron-down" />
</div>
</div>
))}
</React.Fragment>
);
));
export { LeftDialog };

View file

@ -1,6 +1,6 @@
import React, { FC, useEffect, memo, useState, useCallback } from 'react';
import { IMapRoute } from '../../../redux/map/types';
import { InteractivePoly } from '~/utils/polyline';
import { InteractivePoly } from '~/utils/map/InteractivePoly';
import { isMobile } from '~/utils/window';
import { LatLng, Map, LeafletEvent } from 'leaflet';
import { selectEditor } from '~/redux/editor/selectors';

View file

@ -1,6 +1,6 @@
import React, { FC, useEffect, useMemo, useCallback, memo } from 'react';
import { FC, useEffect, useCallback, memo } from 'react';
import pick from 'ramda/es/pick';
import { OsrmRouter } from '~/utils/osrm';
import { OsrmRouter } from '~/utils/map/OsrmRouter';
import { connect } from 'react-redux';
import { selectMap } from '~/redux/map/selectors';
import { selectEditorRouter, selectEditorMode } from '~/redux/editor/selectors';

View file

@ -4,7 +4,7 @@ import { IStickerDump } from '~/redux/map/types';
import { STICKERS } from '~/constants/stickers';
import { StickerDesc } from '~/components/StickerDesc';
import classNames from 'classnames';
import { DomMarker } from '~/utils/DomMarker';
import { DomMarker } from '~/utils/map/DomMarker';
import { createPortal } from 'react-dom';
import { MapContainer, MainMap } from '~/constants/map';

View file

@ -42,7 +42,7 @@ import { LOGOS } from '~/constants/logos';
import { loadMapFromPath } from '../map/sagas';
import { mapClicked, mapSetRoute } from '../map/actions';
import { MAP_ACTIONS } from '../map/constants';
import { OsrmRouter } from '~/utils/osrm';
import { OsrmRouter } from '~/utils/map/OsrmRouter';
import path from 'ramda/es/path';
import { MainMap } from '~/constants/map';
import { EDITOR_INITIAL_STATE } from '.';
@ -236,7 +236,7 @@ function* mapClick({ latlng }: ReturnType<typeof mapClicked>) {
function* routerSubmit() {
const route: ReturnType<typeof selectMapRoute> = yield select(selectMapRoute);
const latlngs = path(['_routes', 0, 'coordinates'], OsrmRouter);
const latlngs: LatLng[] = path(['_routes', 0, 'coordinates'], OsrmRouter);
const coordinates = simplify({ map: MainMap, latlngs });

View file

@ -23,11 +23,6 @@ export const createArrow = (latlng: LatLngLiteral, angle: number): Marker => mar
export const arrowClusterIcon = (cluster): DivIcon => {
const markers = cluster.getAllChildMarkers();
// search for nearest marker to cluster (slow)
// const nearest = markers.sort((a, b) => (
// dist2(a.getLatLng(), cluster.getLatLng()) - dist2(b.getLatLng(), cluster.getLatLng())
// ));
// faster way
cluster.setLatLng(markers[markers.length - 1].getLatLng());
return markers[markers.length - 1].options.icon;

View file

@ -15,7 +15,7 @@ export const DomMarker = DivIcon.extend({
this._setIconStyles(element, 'icon');
return element;
return element;
}
});

View file

@ -576,7 +576,6 @@ InteractivePoly.addInitHook(function() {
});
});
// export const InteractivePoly = Component;
/*
events:
vertexdragstart,

View file

@ -1,7 +1,7 @@
import { Marker } from 'leaflet';
import * as Routing from 'leaflet-routing-machine/src/index';
import { CLIENT } from '~/config/frontend';
import { DomMarker } from '~/utils/DomMarker';
import { DomMarker } from '~/utils/map/DomMarker';
import { MainMap } from '~/constants/map';
const createWaypointMarker = (): DomMarker => {