UNPKG

mapillary-js

Version:

A WebGL interactive street imagery library

237 lines (193 loc) 6.84 kB
import { Geometry } from "./Geometry"; import { GeometryTagError } from "../error/GeometryTagError"; import { Transform } from "../../../geo/Transform"; import { isSpherical } from "../../../geo/Geo"; /** * @class PointsGeometry * * @classdesc Represents a point set in the 2D basic image coordinate system. * * @example * ```js * var points = [[0.5, 0.3], [0.7, 0.3], [0.6, 0.5]]; * var pointsGeometry = new PointsGeometry(points); * ``` */ export class PointsGeometry extends Geometry { private _points: number[][]; /** * Create a points geometry. * * @constructor * @param {Array<Array<number>>} points - Array of 2D points on the basic coordinate * system. The number of points must be greater than or equal to two. * * @throws {GeometryTagError} Point coordinates must be valid basic coordinates. */ constructor(points: number[][]) { super(); const pointsLength: number = points.length; if (pointsLength < 2) { throw new GeometryTagError("A points geometry must have two or more positions."); } this._points = []; for (const point of points) { if (point[0] < 0 || point[0] > 1 || point[1] < 0 || point[1] > 1) { throw new GeometryTagError("Basic coordinates of points must be on the interval [0, 1]."); } this._points.push(point.slice()); } } /** * Get points property. * @returns {Array<Array<number>>} Array of 2d points. */ public get points(): number[][] { return this._points; } /** * Add a point to the point set. * * @param {Array<number>} point - Point to add. * @ignore */ public addPoint2d(point: number[]): void { const clamped: number[] = [ Math.max(0, Math.min(1, point[0])), Math.max(0, Math.min(1, point[1])), ]; this._points.push(clamped); this._notifyChanged$.next(this); } /** * Get the coordinates of a point from the point set representation of the geometry. * * @param {number} index - Point index. * @returns {Array<number>} Array representing the 2D basic coordinates of the point. * @ignore */ public getPoint2d(index: number): number[] { return this._points[index].slice(); } /** * Remove a point from the point set. * * @param {number} index - The index of the point to remove. * @ignore */ public removePoint2d(index: number): void { if (index < 0 || index >= this._points.length || this._points.length < 3) { throw new GeometryTagError("Index for removed point must be valid."); } this._points.splice(index, 1); this._notifyChanged$.next(this); } /** @ignore */ public setVertex2d(index: number, value: number[], transform: Transform): void { this.setPoint2d(index, value, transform); } /** @ignore */ public setPoint2d(index: number, value: number[], transform: Transform): void { const changed: number[] = [ Math.max(0, Math.min(1, value[0])), Math.max(0, Math.min(1, value[1])), ]; this._points[index] = changed; this._notifyChanged$.next(this); } /** @ignore */ public getPoints3d(transform: Transform): number[][] { return this._getPoints3d(this._points, transform); } /** @ignore */ public getPoint3d(index: number, transform: Transform): number[] { return transform.unprojectBasic(this._points[index], 200); } /** @ignore */ public getPoints2d(): number[][] { return this._points.slice(); } /** @ignore */ public getCentroid2d(transform?: Transform): number[] { if (!transform) { throw new GeometryTagError("Get centroid must be called with a transform for points geometries."); } const [minX, minY, maxX, maxY]: number[] = this.getRect2d(transform); const centroidX: number = minX < maxX ? (minX + maxX) / 2 : ((minX + maxX + 1) / 2) % 1; const centroidY: number = (minY + maxY) / 2; return [centroidX, centroidY]; } /** @ignore */ public getCentroid3d(transform: Transform): number[] { let centroid2d: number[] = this.getCentroid2d(); return transform.unprojectBasic(centroid2d, 200); } /** @ignore */ public getRect2d(transform: Transform): number[] { let minX: number = 1; let maxX: number = 0; let minY: number = 1; let maxY: number = 0; const points: number[][] = this._points; for (const point of points) { if (point[0] < minX) { minX = point[0]; } if (point[0] > maxX) { maxX = point[0]; } if (point[1] < minY) { minY = point[1]; } if (point[1] > maxY) { maxY = point[1]; } } if (isSpherical(transform.cameraType)) { const indices: number[] = []; for (let i: number = 0; i < points.length; i++) { indices[i] = i; } indices.sort( (a, b): number => { return points[a][0] < points[b][0] ? -1 : points[a][0] > points[b][0] ? 1 : a < b ? -1 : 1; }); let maxDistanceX: number = points[indices[0]][0] + 1 - points[indices[indices.length - 1]][0]; let leftMostIndex: number = 0; for (let i: number = 0; i < indices.length - 1; i++) { const index1: number = indices[i]; const index2: number = indices[i + 1]; const distanceX: number = points[index2][0] - points[index1][0]; if (distanceX > maxDistanceX) { maxDistanceX = distanceX; leftMostIndex = i + 1; } } if (leftMostIndex > 0) { minX = points[indices[leftMostIndex]][0]; maxX = points[indices[leftMostIndex - 1]][0]; } } return [minX, minY, maxX, maxY]; } /** @ignore */ public setCentroid2d(value: number[], transform: Transform): void { throw new Error("Not implemented"); } private _getPoints3d(points2d: number[][], transform: Transform): number[][] { return points2d .map( (point: number[]) => { return transform.unprojectBasic(point, 200); }); } }