removed all modules

This commit is contained in:
Fedor Katurov 2020-01-08 12:07:36 +07:00
parent aa8fd14517
commit 0c321f2bb3
21 changed files with 677 additions and 77 deletions

View file

@ -1,65 +0,0 @@
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;

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

@ -16,7 +16,7 @@ import {
LeafletMouseEvent,
latLng,
LatLngLiteral
} from "leaflet";
} from "leaflet";
import { distKm, distToSegment, getPolyLength, pointInArea } from "$utils/geom";

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, distKm } 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 = 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),
);
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 * as React from 'react';
import { DomMarker } from '$utils/DomMarker';
import { STICKERS } from '$constants/stickers';
import * as 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'];
}