faster distance calc

This commit is contained in:
Fedor Katurov 2020-01-14 10:40:33 +07:00
parent 2e9b332012
commit 34f98fb08b
8 changed files with 101 additions and 80 deletions

View file

@ -18,7 +18,7 @@ import {
LatLngLiteral
} from "leaflet";
import { distKm, distToSegment, getPolyLength, pointInArea } from "~/utils/geom";
import { distKmHaversine, distToSegment, getPolyLength, pointInArea } from "~/utils/geom";
interface InteractivePolylineOptions extends PolylineOptions {
maxMarkers?: number;
@ -271,11 +271,11 @@ export class InteractivePoly extends Polyline {
const prev = this.markers[index];
const next = this.markers[index + 1];
const initial_distance = distKm(prev.getLatLng(), next.getLatLng());
const initial_distance = distKmHaversine(prev.getLatLng(), next.getLatLng());
const current_distance =
((prev && distKm(prev.getLatLng(), current)) || 0) +
((next && distKm(next.getLatLng(), current)) || 0);
((prev && distKmHaversine(prev.getLatLng(), current)) || 0) +
((next && distKmHaversine(next.getLatLng(), current)) || 0);
this.distance += current_distance - initial_distance;
@ -377,12 +377,12 @@ export class InteractivePoly extends Polyline {
index <= this.markers.length + 1 ? this.markers[index + 1] : null;
const initial_distance =
((prev && distKm(prev.getLatLng(), initial)) || 0) +
((next && distKm(next.getLatLng(), initial)) || 0);
((prev && distKmHaversine(prev.getLatLng(), initial)) || 0) +
((next && distKmHaversine(next.getLatLng(), initial)) || 0);
const current_distance =
((prev && distKm(prev.getLatLng(), current)) || 0) +
((next && distKm(next.getLatLng(), current)) || 0);
((prev && distKmHaversine(prev.getLatLng(), current)) || 0) +
((next && distKmHaversine(next.getLatLng(), current)) || 0);
this.distance += current_distance - initial_distance;
@ -460,7 +460,7 @@ export class InteractivePoly extends Polyline {
? latlngs[latlngs.length - 1]
: latlngs[0];
this.distance += distKm(point, latlng);
this.distance += distKmHaversine(point, latlng);
this.fire("distancechange", { distance: this.distance });
};
@ -505,10 +505,10 @@ export class InteractivePoly extends Polyline {
const next = index <= latlngs.length + 1 ? latlngs[index + 1] : null;
const initial_distance =
((prev && distKm(prev, current)) || 0) +
((next && distKm(next, current)) || 0);
((prev && distKmHaversine(prev, current)) || 0) +
((next && distKmHaversine(next, current)) || 0);
const current_distance = (prev && next && distKm(prev, next)) || 0;
const current_distance = (prev && next && distKmHaversine(prev, next)) || 0;
this.distance += current_distance - initial_distance;

View file

@ -1,7 +1,7 @@
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 { allwaysPositiveAngleDeg, angleBetweenPoints, distKmHaversine } from "~/utils/geom";
import classNames from 'classnames';
interface KmMarksOptions {
@ -43,7 +43,7 @@ class Component extends LayerGroup {
if (index >= latlngs.length - 1) return dist;
const next = latlngs[index + 1];
const diff = distKm(current, next);
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);

View file

@ -1,4 +1,4 @@
import { Map, LayerGroup, Layer, FeatureGroup } from 'leaflet';
import { Map, FeatureGroup, FitBoundsOptions } from 'leaflet';
export class MapContainer extends Map {
constructor(props) {
@ -21,8 +21,16 @@ export class MapContainer extends Map {
const bounds = layers[i].getBounds();
if (Object.keys(bounds).length == 2) return bounds;
}
return null;
};
fitVisibleBounds = (options: FitBoundsOptions) => {
const bounds = this.getVisibleBounds();
if (!bounds) return;
this.fitBounds(bounds, options)
}
public clickable = true;
public routeLayer = new FeatureGroup();

View file

@ -183,7 +183,7 @@ function* locationChangeSaga({ location }: ReturnType<typeof editorLocationChang
if (!ready) return;
yield call(loadMapFromPath);
MainMap.fitBounds(MainMap.getVisibleBounds(), { animate: true });
MainMap.fitVisibleBounds({ animate: true });
}
function* keyPressedSaga({ key, target }: ReturnType<typeof editorKeyPressed>) {

View file

@ -29,8 +29,7 @@ import {
editorSetSave,
editorClearAll,
} from '~/redux/editor/actions';
import { pushLoaderState, getUrlData, pushPath, replacePath } from '~/utils/history';
import { searchSetSagaWorker } from '~/redux/user/sagas';
import { pushLoaderState, getUrlData, pushPath } from '~/utils/history';
import { getStoredMap, postMap } from '~/utils/api';
import { Unwrap } from '~/utils/middleware';
import { selectMap, selectMapProvider } from './selectors';
@ -150,7 +149,7 @@ export function* mapInitSaga() {
yield call(loadMapFromPath);
yield call(setReadySaga);
MainMap.fitBounds(MainMap.getVisibleBounds(), { animate: false });
MainMap.fitVisibleBounds({ animate: false });
pushLoaderState(100);
}

View file

@ -1,25 +1,26 @@
import { LatLng, LatLngLiteral, point, Point, PointExpression } from "leaflet";
import { LatLng, LatLngLiteral, point, Point, PointExpression } from 'leaflet';
interface ILatLng {
lat: number,
lng: number,
lat: number;
lng: number;
}
export const middleCoord = (l1: ILatLng, l2: ILatLng): ILatLng => ({
lat: (l2.lat + ((l1.lat - l2.lat) / 2)),
lng: (l2.lng + ((l1.lng - l2.lng) / 2))
lat: l2.lat + (l1.lat - l2.lat) / 2,
lng: l2.lng + (l1.lng - l2.lng) / 2,
});
export const middleCoordPx = (p1: Point, p2: Point): Point => point({
x: (p1.x + ((p2.x - p1.x) / 2)),
y: (p1.y + ((p2.y - p1.y) / 2))
});
export const middleCoordPx = (p1: Point, p2: Point): Point =>
point({
x: p1.x + (p2.x - p1.x) / 2,
y: p1.y + (p2.y - p1.y) / 2,
});
export const deg2rad = (deg: number): number => ((deg * Math.PI) / 180);
export const rad2deg = (rad: number): number => ((rad / Math.PI) * 180);
export const deg2rad = (deg: number): number => (deg * Math.PI) / 180;
export const rad2deg = (rad: number): number => (rad / Math.PI) * 180;
export const findDistancePx = (p1: Point, p2: Point): number => {
return Math.sqrt(((p1.x - p2.x) ** 2) + ((p1.y - p2.y) ** 2));
return Math.sqrt((p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2);
};
export const findDistance = (t1: number, n1: number, t2: number, n2: number): number => {
@ -34,40 +35,56 @@ export const findDistance = (t1: number, n1: number, t2: number, n2: number): nu
const dlon = lon2 - lon1;
// here's the heavy lifting
const a = (Math.sin(dlat / 2) ** 2) +
(Math.cos(lat1) * Math.cos(lat2) * (Math.sin(dlon / 2) ** 2));
const a = Math.sin(dlat / 2) ** 2 + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dlon / 2) ** 2;
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); // great circle distance in radians
const dk = c * 6373; // great circle distance in km
return (Math.round(dk * 1000) / 1000);
return Math.round(dk * 1000) / 1000;
};
export const distKm = (A: LatLngLiteral, B: LatLngLiteral): number => findDistance(A.lat, A.lng, B.lat, B.lng);
// probably faster one
export const findDistanceHaversine = (t1: number, n1: number, t2: number, n2: number): number => {
const R = 6371; // km
const dLat = ((t2 - t1) * Math.PI) / 180;
var dLon = ((n2 - n1) * Math.PI) / 180;
var a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos((t1 * Math.PI) / 180) *
Math.cos((t2 * Math.PI) / 180) *
Math.sin(dLon / 2) *
Math.sin(dLon / 2);
var c = 2 * Math.asin(Math.sqrt(a));
return R * c;
};
export const getLabelDirection = (angle: number): 'left' | 'right' => (
((angle % Math.PI) >= -(Math.PI / 2) && (angle % Math.PI) <= (Math.PI / 2)) ? 'left' : 'right'
);
export const distKm = (A: LatLngLiteral, B: LatLngLiteral): number =>
findDistance(A.lat, A.lng, B.lat, B.lng);
export const distKmHaversine = (A: LatLngLiteral, B: LatLngLiteral): number =>
findDistanceHaversine(A.lat, A.lng, B.lat, B.lng);
export const getLabelDirection = (angle: number): 'left' | 'right' =>
angle % Math.PI >= -(Math.PI / 2) && angle % Math.PI <= Math.PI / 2 ? 'left' : 'right';
export const getPolyLength = (latlngs: LatLngLiteral[]): number => {
if (latlngs.length < 2) return 0;
return latlngs.reduce((dist, item, index) => (
index < (latlngs.length - 1)
? dist + distKm(item, latlngs[index + 1])
: dist
), 0)
return latlngs.reduce(
(dist, item, index) =>
index < latlngs.length - 1 ? dist + distKmHaversine(item, latlngs[index + 1]) : dist,
0
);
};
// if C between A and B
export const pointInArea = (A: LatLng, B: LatLng, C: LatLng, radius: number = 0.001): boolean => (
export const pointInArea = (A: LatLng, B: LatLng, C: LatLng, radius: number = 0.001): boolean =>
C.lng <= Math.max(A.lng + radius, B.lng + radius) &&
C.lat <= Math.max(A.lat + radius, B.lat + radius) &&
C.lat >= Math.min(A.lat - radius, B.lat - radius) &&
C.lng >= Math.min(A.lng - radius, B.lng - radius)
);
C.lng >= Math.min(A.lng - radius, B.lng - radius);
export const dist2 = (A: LatLngLiteral, B: LatLngLiteral): number => (((A.lat - B.lat) ** 2) + ((A.lng - B.lng) ** 2));
export const dist2 = (A: LatLngLiteral, B: LatLngLiteral): number =>
(A.lat - B.lat) ** 2 + (A.lng - B.lng) ** 2;
const distToSegmentSquared = (A: LatLng, B: LatLng, C: LatLng): number => {
const l2 = dist2(A, B);
@ -75,29 +92,27 @@ const distToSegmentSquared = (A: LatLng, B: LatLng, C: LatLng): number => {
const t = Math.max(
0,
Math.min(
1,
(((C.lat - A.lat) * (B.lat - A.lat) + (C.lng - A.lng) * (B.lng - A.lng)) / l2)
)
Math.min(1, ((C.lat - A.lat) * (B.lat - A.lat) + (C.lng - A.lng) * (B.lng - A.lng)) / l2)
);
return dist2(
C,
{
return dist2(C, {
lat: A.lat + t * (B.lat - A.lat),
lng: A.lng + t * (B.lng - A.lng)
lng: A.lng + t * (B.lng - A.lng),
});
};
export const distToSegment = (A: LatLng, B: LatLng, C: LatLng): number => Math.sqrt(distToSegmentSquared(A, B, C));
export const distToSegment = (A: LatLng, B: LatLng, C: LatLng): number =>
Math.sqrt(distToSegmentSquared(A, B, C));
export const pointBetweenPoints = (A: LatLng, B: LatLng, C: LatLng): boolean => (distToSegment(A, B, C) < 0.01);
export const pointBetweenPoints = (A: LatLng, B: LatLng, C: LatLng): boolean =>
distToSegment(A, B, C) < 0.01;
export const angleBetweenPoints = (A: Point, B: Point): number => parseFloat(((Math.atan2(B.y - A.y, B.x - A.x))* 180 / Math.PI).toFixed());
export const angleBetweenPointsRad = (A: Point, B: Point): number => ((Math.atan2(B.x - A.x, B.y - A.y)));
export const angleBetweenPoints = (A: Point, B: Point): number =>
parseFloat(((Math.atan2(B.y - A.y, B.x - A.x) * 180) / Math.PI).toFixed());
export const angleBetweenPointsRad = (A: Point, B: Point): number =>
Math.atan2(B.x - A.x, B.y - A.y);
export const allwaysPositiveAngleDeg = (angle: number): number => (
(angle >= -90 && angle <= 90) ? angle : (180 + angle)
);
export const allwaysPositiveAngleDeg = (angle: number): number =>
angle >= -90 && angle <= 90 ? angle : 180 + angle;
export const nearestInt = (value: number, parts: number): number => (value - (value % parts));
export const nearestInt = (value: number, parts: number): number => value - (value % parts);

View file

@ -1,7 +1,7 @@
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 { allwaysPositiveAngleDeg, angleBetweenPoints, distKmHaversine } from "~/utils/geom";
import classNames from 'classnames';
interface KmMarksOptions {
@ -43,7 +43,7 @@ class KmMarksLayer extends LayerGroup {
if (index >= latlngs.length - 1) return dist;
const next = latlngs[index + 1];
const diff = distKm(current, next);
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);
@ -72,7 +72,6 @@ class KmMarksLayer extends LayerGroup {
return sum;
}, 0);
};
createMiddleMarker = (latlng: LatLngLiteral, angle: number, distance: number): Marker => marker(latlng, {

View file

@ -18,7 +18,7 @@ import {
LatLngLiteral,
} from 'leaflet';
import { distKm, distToSegment, getPolyLength, pointInArea } from '~/utils/geom';
import { distKmHaversine, distToSegment, getPolyLength, pointInArea } from '~/utils/geom';
interface InteractivePolylineOptions extends PolylineOptions {
maxMarkers?: number;
@ -268,11 +268,11 @@ export class InteractivePoly extends Polyline {
const prev = this.markers[index];
const next = this.markers[index + 1];
const initial_distance = distKm(prev.getLatLng(), next.getLatLng());
const initial_distance = distKmHaversine(prev.getLatLng(), next.getLatLng());
const current_distance =
((prev && distKm(prev.getLatLng(), current)) || 0) +
((next && distKm(next.getLatLng(), current)) || 0);
((prev && distKmHaversine(prev.getLatLng(), current)) || 0) +
((next && distKmHaversine(next.getLatLng(), current)) || 0);
this.distance += current_distance - initial_distance;
@ -369,12 +369,12 @@ export class InteractivePoly extends Polyline {
const next = index <= this.markers.length + 1 ? this.markers[index + 1] : null;
const initial_distance =
((prev && distKm(prev.getLatLng(), initial)) || 0) +
((next && distKm(next.getLatLng(), initial)) || 0);
((prev && distKmHaversine(prev.getLatLng(), initial)) || 0) +
((next && distKmHaversine(next.getLatLng(), initial)) || 0);
const current_distance =
((prev && distKm(prev.getLatLng(), current)) || 0) +
((next && distKm(next.getLatLng(), current)) || 0);
((prev && distKmHaversine(prev.getLatLng(), current)) || 0) +
((next && distKmHaversine(next.getLatLng(), current)) || 0);
this.distance += current_distance - initial_distance;
@ -448,7 +448,7 @@ export class InteractivePoly extends Polyline {
const point = this.drawing_direction === 'forward' ? latlngs[latlngs.length - 1] : latlngs[0];
this.distance += distKm(point, latlng);
this.distance += distKmHaversine(point, latlng);
this.fire('distancechange', { distance: this.distance });
};
@ -493,9 +493,9 @@ export class InteractivePoly extends Polyline {
const next = index <= latlngs.length + 1 ? latlngs[index + 1] : null;
const initial_distance =
((prev && distKm(prev, current)) || 0) + ((next && distKm(next, current)) || 0);
((prev && distKmHaversine(prev, current)) || 0) + ((next && distKmHaversine(next, current)) || 0);
const current_distance = (prev && next && distKm(prev, next)) || 0;
const current_distance = (prev && next && distKmHaversine(prev, next)) || 0;
this.distance += current_distance - initial_distance;