optimized marks calculation speed

This commit is contained in:
Fedor Katurov 2020-01-14 12:29:05 +07:00
parent 0ea9710497
commit d8b51e0b1a

View file

@ -1,29 +1,30 @@
import { divIcon, LatLngLiteral, Layer, LayerGroup, Map, marker, Marker } from "leaflet"; import { divIcon, LatLngLiteral, Layer, LayerGroup, Map, marker, Marker } from 'leaflet';
import { arrowClusterIcon, createArrow } from "~/utils/arrow"; import { arrowClusterIcon, createArrow } from '~/utils/arrow';
import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js'; import { MarkerClusterGroup } from 'leaflet.markercluster/dist/leaflet.markercluster-src.js';
import { allwaysPositiveAngleDeg, angleBetweenPoints, distKmHaversine } from "~/utils/geom"; import { allwaysPositiveAngleDeg, angleBetweenPoints, distKmHaversine } from '~/utils/geom';
import classNames from 'classnames'; import classNames from 'classnames';
interface KmMarksOptions { interface KmMarksOptions {
showMiddleMarkers: boolean, showMiddleMarkers: boolean;
showEndMarker: boolean, showEndMarker: boolean;
kmMarksStep: number, kmMarksStep: number;
} }
class KmMarksLayer extends LayerGroup { class KmMarksLayer extends LayerGroup {
constructor(latlngs?: LatLngLiteral[], options?: KmMarksOptions){ constructor(latlngs?: LatLngLiteral[], options?: KmMarksOptions) {
super(); super();
this.options = { this.options = {
showMiddleMarkers: true, showMiddleMarkers: true,
showEndMarker: true, showEndMarker: true,
kmMarksStep: 10, kmMarksStep: 20,
...(options || {}), ...(options || {}),
} as KmMarksOptions; } as KmMarksOptions;
} }
setLatLngs = (latlngs: LatLngLiteral[]): void => { setLatLngs = (latlngs: LatLngLiteral[]): void => {
if (!this.map) return; if (!this.map) return;
this.marksLayer.clearLayers(); this.marksLayer.clearLayers();
this.endMarker.clearLayers(); this.endMarker.clearLayers();
@ -36,7 +37,7 @@ class KmMarksLayer extends LayerGroup {
}; };
drawMiddleMarkers = (latlngs: LatLngLiteral[]) => { drawMiddleMarkers = (latlngs: LatLngLiteral[]) => {
const kmMarks = {}; const marks = [];
let last_km_mark = 0; let last_km_mark = 0;
this.distance = latlngs.reduce((dist, current, index) => { this.distance = latlngs.reduce((dist, current, index) => {
@ -51,20 +52,20 @@ class KmMarksLayer extends LayerGroup {
if (rounded > last_km_mark) { if (rounded > last_km_mark) {
const angle = angleBetweenPoints( const angle = angleBetweenPoints(
this.map.latLngToContainerPoint(current), this.map.latLngToContainerPoint(current),
this.map.latLngToContainerPoint(next), this.map.latLngToContainerPoint(next)
); );
// if segment is too long, we'll add multiple markers on it
for (let i = 1; i <= count; i += 1) { for (let i = 1; i <= count; i += 1) {
const step = last_km_mark + (i * this.options.kmMarksStep); const step = last_km_mark + i * this.options.kmMarksStep;
const shift = (step - dist) / diff; const shift = (step - dist) / diff;
const coords = { const coords = {
lat: current.lat - ((current.lat - next.lat) * shift), lat: current.lat - (current.lat - next.lat) * shift,
lng: current.lng - ((current.lng - next.lng) * shift), lng: current.lng - (current.lng - next.lng) * shift,
}; };
kmMarks[step] = { ...coords, angle }; marks.push(this.createMiddleMarker(coords, angle, step));
this.marksLayer.addLayer(this.createMiddleMarker(coords, angle, step));
} }
last_km_mark = rounded; last_km_mark = rounded;
@ -72,49 +73,54 @@ class KmMarksLayer extends LayerGroup {
return sum; return sum;
}, 0); }, 0);
this.marksLayer.addLayers(marks);
}; };
createMiddleMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, { createMiddleMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker =>
draggable: false, marker(latlng, {
interactive: false, draggable: false,
icon: divIcon({ interactive: false,
html: ` icon: divIcon({
<div class="leaflet-km-dist" style="transform: translate(-50%, -50%) rotate(${allwaysPositiveAngleDeg(angle)}deg);"> html: `
<div class="leaflet-km-dist" style="transform: translate(-50%, -50%) rotate(${allwaysPositiveAngleDeg(
angle
)}deg);">
${distance} ${distance}
</div> </div>
`, `,
className: 'leaflet-km-marker', className: 'leaflet-km-marker',
iconSize: [11, 11], iconSize: [11, 11],
iconAnchor: [6, 6] iconAnchor: [6, 6],
}) }),
}); });
createEndMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, { createEndMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker =>
draggable: false, marker(latlng, {
interactive: false, draggable: false,
icon: divIcon({ interactive: false,
html: ` icon: divIcon({
html: `
<div class="leaflet-km-dist"> <div class="leaflet-km-dist">
${parseFloat(distance.toFixed(1))} ${parseFloat(distance.toFixed(1))}
</div> </div>
`, `,
className: classNames('leaflet-km-marker end-marker', { right: (angle > -90 && angle < 90) }), className: classNames('leaflet-km-marker end-marker', { right: angle > -90 && angle < 90 }),
iconSize: [11, 11], iconSize: [11, 11],
iconAnchor: [6, 6] iconAnchor: [6, 6],
}), }),
zIndexOffset: -100, zIndexOffset: -100,
}); });
drawEndMarker = (latlngs: LatLngLiteral[]): void => { drawEndMarker = (latlngs: LatLngLiteral[]): void => {
this.endMarker.clearLayers(); this.endMarker.clearLayers();
const current = latlngs[latlngs.length - 2]; const current = latlngs[latlngs.length - 2];
const next = latlngs[latlngs.length - 1 const next = latlngs[latlngs.length - 1];
];
const angle = angleBetweenPoints( const angle = angleBetweenPoints(
this.map.latLngToContainerPoint(current), this.map.latLngToContainerPoint(current),
this.map.latLngToContainerPoint(next), this.map.latLngToContainerPoint(next)
); );
this.endMarker.addLayer(this.createEndMarker(next, angle, this.distance)); this.endMarker.addLayer(this.createEndMarker(next, angle, this.distance));
@ -134,9 +140,8 @@ class KmMarksLayer extends LayerGroup {
distance: number = 0; distance: number = 0;
} }
KmMarksLayer.addInitHook(function() {
KmMarksLayer.addInitHook(function () { this.once('add', event => {
this.once('add', (event) => {
if (event.target instanceof KmMarksLayer) { if (event.target instanceof KmMarksLayer) {
this.map = event.target._map; this.map = event.target._map;
this.marksLayer.addTo(this.map); this.marksLayer.addTo(this.map);
@ -144,7 +149,7 @@ KmMarksLayer.addInitHook(function () {
} }
}); });
this.once('remove', (event) => { this.once('remove', event => {
if (event.target instanceof KmMarksLayer) { if (event.target instanceof KmMarksLayer) {
this.marksLayer.removeFrom(this.map); this.marksLayer.removeFrom(this.map);
this.endMarker.removeFrom(this.map); this.endMarker.removeFrom(this.map);
@ -152,4 +157,4 @@ KmMarksLayer.addInitHook(function () {
}); });
}); });
export { KmMarksLayer }; export { KmMarksLayer };