s2maps-gpu
Version:
S2 Maps GPU - An open source, high-performance, and GPU-accelerated map engine for rendering large-scale, interactive maps.
190 lines (189 loc) • 5.92 kB
JavaScript
import { flattenGeometryToLines, lineLength } from './lineTools.js';
/**
* Flatten all geometry types to points
* @param geometry - vector geometry
* @param type - geometry type
* @returns vector points
*/
export function flattenGeometryToPoints(geometry, type) {
if (type === 'Point')
return [geometry];
if (type === 'MultiPoint')
return geometry;
const res = [];
const lines = flattenGeometryToLines(geometry, type);
for (const line of lines) {
for (const point of line)
res.push(point);
}
return res;
}
/**
* Get the center points of the geometry
* @param geometry - vector geometry
* @param type - geometry type
* @returns vector points at the center of the geometry
*/
export function getCenterPoints(geometry, type) {
if (type === 'Point')
return [geometry];
if (type === 'MultiPoint')
return geometry;
return findCenterPoints(geometry, type, 0).map((sp) => sp.point);
}
/**
* Get the spaced points of the geometry
* @param geometry - vector geometry
* @param type - geometry type
* @param spacing - distance between points
* @param extent - extent is the tile "pixel" size
* @returns vector points spaced along the line
*/
export function getSpacedPoints(geometry, type, spacing, extent) {
if (type === 'Point')
return [geometry];
if (type === 'MultiPoint')
return geometry;
return findSpacedPoints(geometry, type, spacing, extent).map((sp) => sp.point);
}
/**
* Find center points of the geometry
* @param geometry - vector geometry
* @param type - geometry type
* @param extent - extent is the tile "pixel" size
* @returns vector points at the centers of the geometry(s)
*/
export function findCenterPoints(geometry, type, extent) {
const res = [];
if (type === 'Point' || type === 'MultiPoint')
return res;
const lines = flattenGeometryToLines(geometry, type);
for (const line of lines) {
const { length, distIndex } = lineLength(line);
const center = Math.floor(length / 2);
const { point, pathLeft, pathRight } = buildPointAtDistance(line, distIndex, center, extent);
res.push({
point,
distance: center,
pathLeft,
pathRight,
});
}
return res;
}
/**
* Find points along the line at a given distance
* @param geometry - vector geometry
* @param type - geometry type
* @param spacing - distance between points
* @param extent - extent is the tile "pixel" size
* @returns vector points spaced along the line
*/
export function findSpacedPoints(geometry, type, spacing, extent) {
const res = [];
if (type === 'Point' || type === 'MultiPoint')
return res;
// safety check
if (spacing <= 50)
return res;
const lines = flattenGeometryToLines(geometry, type);
for (const line of lines) {
const { length, distIndex } = lineLength(line);
// every spacing distance, add a point
const distances = [];
let distance = spacing;
while (distance < length) {
distances.push(distance);
distance += spacing;
}
for (const d of distances) {
const { point, pathLeft, pathRight } = buildPointAtDistance(line, distIndex, d, extent);
res.push({
point,
distance: d,
pathLeft,
pathRight,
});
}
}
return res;
}
/**
* NOTE: Currently assumes the line is longer then the distance
* @param line - the line
* @param index - index of the line
* @param distance - distance along the line
* @param extent - extent is the tile "pixel" size
* @returns path structure
*/
function buildPointAtDistance(line, index, distance, extent) {
const fourthExtent = extent * 0.25;
let i = 0;
while (i < index.length - 1 && index[i + 1] < distance)
i++;
const p1 = line[i];
const p2 = line[i + 1];
const d1 = index[i];
const d2 = index[i + 1];
const t = (distance - d1) / (d2 - d1);
const point = {
x: p1.x + (p2.x - p1.x) * t,
y: p1.y + (p2.y - p1.y) * t,
};
// store either 7 points or as many as possible
const pathLeft = [];
const pathRight = [];
let l = i;
let r = i + 1;
let curAngle = pointAngle(point, line[l]) ?? 0;
while (l >= 0 && pathLeft.length < 3) {
pathLeft.push(duplicatePoint(line[l]));
l--;
curAngle = pointAngle(line[l + 1], line[l]) ?? curAngle;
}
// pathLeft length needs to be 4; add 1 at pathAngle
while (pathLeft.length < 4) {
const { x, y } = pathLeft[pathLeft.length - 1];
pathLeft.push({
x: x + fourthExtent * Math.cos(curAngle),
y: y + fourthExtent * Math.sin(curAngle),
});
}
curAngle = pointAngle(point, line[r]) ?? 0;
while (r < line.length && pathRight.length < 3) {
pathRight.push(duplicatePoint(line[r]));
r++;
curAngle = pointAngle(line[r - 1], line[r]) ?? curAngle;
}
while (pathRight.length < 4) {
const { x, y } = pathRight[pathRight.length - 1];
pathRight.push({
x: x + fourthExtent * Math.cos(curAngle),
y: y + fourthExtent * Math.sin(curAngle),
});
}
return {
point,
pathLeft: pathLeft,
pathRight: pathRight,
};
}
/**
* Duplicate a point
* @param point - the point to duplicate
* @returns the duplicated point
*/
export function duplicatePoint(point) {
return { x: point.x, y: point.y, m: point.m, t: point.t };
}
/**
* Get the angle between 2 points
* @param a - first point
* @param b - second point
* @returns the angle
*/
export function pointAngle(a, b) {
if (b === undefined || (a.x === b.x && a.y === b.y))
return undefined;
return Math.atan2(b.y - a.y, b.x - a.x);
}