mirror of
https://github.com/muerwre/orchidmap-front.git
synced 2025-04-25 02:56:41 +07:00
added km marks
This commit is contained in:
parent
bc34cf3876
commit
0b0b8b9cc0
3 changed files with 195 additions and 0 deletions
37
src/containers/map/KmMarks/index.tsx
Normal file
37
src/containers/map/KmMarks/index.tsx
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import React, { FC, useEffect, useState } from 'react';
|
||||||
|
import { KmMarksLayer } from '~/utils/marks';
|
||||||
|
import { MainMap } from '~/constants/map';
|
||||||
|
import { selectMap } from '~/redux/map/selectors';
|
||||||
|
import pick from 'ramda/es/pick';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
map: pick(['route'], selectMap(state)),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = {};
|
||||||
|
type Props = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
|
||||||
|
|
||||||
|
const KmMarksUnconnected: FC<Props> = ({
|
||||||
|
map: { route },
|
||||||
|
}) => {
|
||||||
|
const [layer, setLayer] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const layer = new KmMarksLayer([]);
|
||||||
|
layer.addTo(MainMap);
|
||||||
|
setLayer(layer);
|
||||||
|
return () => MainMap.removeLayer(layer);
|
||||||
|
}, [MainMap]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!layer) return;
|
||||||
|
|
||||||
|
layer.setLatLngs(route);
|
||||||
|
}, [layer, route]);
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const KmMarks = connect(mapStateToProps, mapDispatchToProps)(KmMarksUnconnected);
|
||||||
|
|
||||||
|
export { KmMarks };
|
|
@ -10,6 +10,7 @@ import { Route } from '~/containers/map/Route';
|
||||||
import { Router } from '~/containers/map/Router';
|
import { Router } from '~/containers/map/Router';
|
||||||
import { TileLayer } from '~/containers/map/TileLayer';
|
import { TileLayer } from '~/containers/map/TileLayer';
|
||||||
import { Stickers } from '~/containers/map/Stickers';
|
import { Stickers } from '~/containers/map/Stickers';
|
||||||
|
import { KmMarks } from '~/containers/map/KmMarks';
|
||||||
|
|
||||||
import 'leaflet/dist/leaflet.css';
|
import 'leaflet/dist/leaflet.css';
|
||||||
import { selectEditorEditing } from '~/redux/editor/selectors';
|
import { selectEditorEditing } from '~/redux/editor/selectors';
|
||||||
|
@ -73,6 +74,7 @@ const MapUnconnected: React.FC<IProps> = ({
|
||||||
<Route />
|
<Route />
|
||||||
|
|
||||||
<Router />
|
<Router />
|
||||||
|
<KmMarks />
|
||||||
</div>,
|
</div>,
|
||||||
document.getElementById('canvas')
|
document.getElementById('canvas')
|
||||||
);
|
);
|
||||||
|
|
156
src/utils/marks.ts
Normal file
156
src/utils/marks.ts
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
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 KmMarksLayer 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
KmMarksLayer.addInitHook(function () {
|
||||||
|
this.once('add', (event) => {
|
||||||
|
if (event.target instanceof KmMarksLayer) {
|
||||||
|
this.map = event.target._map;
|
||||||
|
this.marksLayer.addTo(this.map);
|
||||||
|
this.endMarker.addTo(this.map);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.once('remove', (event) => {
|
||||||
|
if (event.target instanceof KmMarksLayer) {
|
||||||
|
this.marksLayer.removeFrom(this.map);
|
||||||
|
this.endMarker.removeFrom(this.map);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export { KmMarksLayer };
|
Loading…
Add table
Add a link
Reference in a new issue