@deck.gl-community/layers
Version:
Add-on layers for deck.gl
123 lines (102 loc) • 3.19 kB
text/typescript
// deck.gl-community
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import {Vector2} from '@math.gl/core';
/** GeoJSON style position coordinate vector */
export type Position = [number, number] | [number, number, number];
/** [red, green, blue, alpha] in premultiplied alpha format */
export type Color = [number, number, number, number];
export interface PathMarker {
position: Position;
angle: number;
color: Color;
object: unknown;
}
function getLineLength(vPoints) {
// calculate total length
let lineLength = 0;
for (let i = 0; i < vPoints.length - 1; i++) {
lineLength += vPoints[i].distance(vPoints[i + 1]);
}
return lineLength;
}
const DEFAULT_COLOR = [0, 0, 0, 255];
const DEFAULT_DIRECTION = {forward: true, backward: false};
export function createPathMarkers({
data,
getPath = (x, context) => x.path,
getDirection = (x) => x.direction,
getColor = (x) => DEFAULT_COLOR,
getMarkerPercentages = (x, info) => [0.5],
projectFlat
}): PathMarker[] {
const markers: PathMarker[] = [];
for (const object of data) {
const path = getPath(object, null);
const direction = getDirection(object) || DEFAULT_DIRECTION;
const color = getColor(object);
const vPoints = path.map((p) => new Vector2(p));
const vPointsReverse = vPoints.slice(0).reverse();
// calculate total length
const lineLength = getLineLength(vPoints);
// Ask for where to put markers
const percentages = getMarkerPercentages(object, {lineLength});
// Create the markers
for (const percentage of percentages) {
if (direction.forward) {
const marker = createMarkerAlongPath({
path: vPoints,
percentage,
lineLength,
color,
object,
projectFlat
});
markers.push(marker);
}
if (direction.backward) {
const marker = createMarkerAlongPath({
path: vPointsReverse,
percentage,
lineLength,
color,
object,
projectFlat
});
markers.push(marker);
}
}
}
return markers;
}
function createMarkerAlongPath({
path,
percentage,
lineLength,
color,
object,
projectFlat
}): PathMarker {
const distanceAlong = lineLength * percentage;
let currentDistance = 0;
let previousDistance = 0;
let i = 0;
for (i = 0; i < path.length - 1; i++) {
currentDistance += path[i].distance(path[i + 1]);
if (currentDistance > distanceAlong) {
break;
}
previousDistance = currentDistance;
}
// If reached the end of the loop without exiting early,
// undo the final increment to avoid a null-pointer exception
if (i === path.length - 1) {
i -= 1;
}
const vDirection = path[i + 1].clone().subtract(path[i]).normalize();
const along = distanceAlong - previousDistance;
const vCenter = vDirection.clone().multiply(new Vector2(along, along)).add(path[i]);
const vDirection2 = new Vector2(projectFlat(path[i + 1])).subtract(projectFlat(path[i]));
const angle = (vDirection2.verticalAngle() * 180) / Math.PI;
return {position: [vCenter.x, vCenter.y, 0], angle, color, object};
}