UNPKG

@allmaps/render

Version:

Render functions for WebGL and image buffers

330 lines (329 loc) 12.9 kB
import { triangulateToUnique } from "@allmaps/triangulate"; import { mixNumbers, mixPoints, getPropertyFromTripleCacheOrComputation, getPropertyFromCacheOrComputation } from "@allmaps/stdlib"; import { WarpedMap } from "./WarpedMap.js"; const DEFAULT_DISTORTION_MEASURES = [ "log2sigma", "twoOmega" ]; function createDefaultTriangulatedWarpedMapOptions() { return {}; } class TriangulatedWarpedMap extends WarpedMap { previousResourceResolution; resourceResolution; triangulateErrorCount = 0; projectedGcpPreviousTriangulation; projectedGcpTriangulation; resourceTriangulationCache; projectedGcpTriangulationCache; resourceTrianglePoints = []; projectedGeoPreviousTrianglePoints = []; projectedGeoTrianglePoints = []; previousTrianglePointsDistortion = []; trianglePointsDistortion = []; projectedGeoPreviousTriangulationMask = []; projectedGeoTriangulationMask = []; /** * Creates an instance of a TriangulatedWarpedMap. * * @param mapId - ID of the map * @param georeferencedMap - Georeferenced map used to construct the WarpedMap * @param options - Options */ constructor(mapId, georeferencedMap, options) { options = { ...createDefaultTriangulatedWarpedMapOptions(), ...options }; super(mapId, georeferencedMap, options); this.resourceTriangulationCache = /* @__PURE__ */ new Map(); this.projectedGcpTriangulationCache = /* @__PURE__ */ new Map(); this.updateTriangulation(); } /** * Update the ground control points loaded from a georeferenced map to new ground control points. * * @param gcps - the new ground control points */ setGcps(gcps) { super.setGcps(gcps); this.clearResourceTriangulationCaches(); this.updateTriangulation(); } /** * Update the resource mask loaded from a georeferenced map to a new mask. * * @param resourceMask - the new mask */ setResourceMask(resourceMask) { super.setResourceMask(resourceMask); this.clearResourceTriangulationCaches(); this.updateTriangulation(); } /** * Set the distortionMeasure * * @param distortionMeasure - the disortion measure */ setDistortionMeasure(distortionMeasure) { super.setDistortionMeasure(distortionMeasure); this.updateTrianglePointsDistortion(); } /** * Set the internal projection * * @param projection - the internal projection */ setInternalProjection(projection) { super.setInternalProjection(projection); this.updateTriangulation(); } /** * Set the projection * * @param projection - the projection */ setProjection(projection) { super.setProjection(projection); this.clearProjectedTriangulationCaches(); this.updateTriangulation(); } /** * Reset previous transform properties to new ones (when completing a transformer transitions). */ resetPrevious() { super.resetPrevious(); this.previousResourceResolution = this.resourceResolution; this.projectedGcpPreviousTriangulation = this.projectedGcpTriangulation; this.projectedGeoPreviousTrianglePoints = this.projectedGeoTrianglePoints; this.previousTrianglePointsDistortion = this.trianglePointsDistortion; this.projectedGeoPreviousTriangulationMask = this.projectedGeoTriangulationMask; } /** * Mix previous transform properties with new ones (when changing an ongoing transformer transition). * * @param t - animation progress */ mixPreviousAndNew(t) { super.mixPreviousAndNew(t); if (this.projectedGcpPreviousTriangulation && this.projectedGcpTriangulation) { const projectedGcpPreviousTriangulation = this.projectedGcpPreviousTriangulation; const projectedGcpTriangulation = this.projectedGcpTriangulation; this.previousResourceResolution = this.resourceResolution; this.projectedGcpPreviousTriangulation = { resourceResolution: projectedGcpPreviousTriangulation.resourceResolution, gcpUniquePoints: projectedGcpPreviousTriangulation.gcpUniquePoints.map( (projectedGcp, index) => { return { resource: projectedGcp.resource, geo: mixPoints( projectedGcpTriangulation.gcpUniquePoints[index].geo, projectedGcp.geo, t ), // Note: Not mixing the distortions Map, only the active distortion distortions: projectedGcpTriangulation.gcpUniquePoints[index].distortions, distortion: mixNumbers( projectedGcpTriangulation.gcpUniquePoints[index].distortion || 0, projectedGcp.distortion || 0, t ) }; } ), uniquePointIndices: projectedGcpPreviousTriangulation.uniquePointIndices, uniquePointIndexInterpolatedPolygon: projectedGcpPreviousTriangulation.uniquePointIndexInterpolatedPolygon }; this.projectedGeoPreviousTrianglePoints = projectedGcpPreviousTriangulation.uniquePointIndices.map( (i) => projectedGcpPreviousTriangulation.gcpUniquePoints[i].geo ); this.previousTrianglePointsDistortion = projectedGcpPreviousTriangulation.uniquePointIndices.map( (i) => projectedGcpPreviousTriangulation.gcpUniquePoints[i].distortion ); } this.projectedGeoPreviousTriangulationMask = this.projectedGeoTriangulationMask.map((point, index) => { return mixPoints( point, this.projectedGeoPreviousTriangulationMask[index], t ); }); } /** * Update the (previous and new) triangulation of the resourceMask. Use cache if available. */ updateTriangulation() { if (!this.resourceTriangulationCache || !this.projectedGcpTriangulationCache) { return; } const resourceResolution = this.projectedTransformer.getToGeoTransformationResolution( this.resourceMaskBbox ); let refinePrevious = false; if (resourceResolution && this.previousResourceResolution) { refinePrevious = this.previousResourceResolution < resourceResolution; this.resourceResolution = Math.min( resourceResolution, this.previousResourceResolution ); } if (resourceResolution && !this.previousResourceResolution) { refinePrevious = true; this.resourceResolution = resourceResolution; } if (!resourceResolution && this.previousResourceResolution) { this.resourceResolution = this.previousResourceResolution; } else if (!resourceResolution && !this.previousResourceResolution) { this.resourceResolution = void 0; } this.projectedGcpTriangulation = getPropertyFromTripleCacheOrComputation( this.projectedGcpTriangulationCache, this.resourceResolution, this.transformationType, this.internalProjection, () => { const { uniquePoints, uniquePointIndexTriangles, uniquePointIndexInterpolatedPolygon } = getPropertyFromCacheOrComputation( this.resourceTriangulationCache, this.resourceResolution, () => triangulateToUnique([this.resourceMask], this.resourceResolution, { steinerPoints: this.gcps.map((gcp) => gcp.resource) }) ); const resourceResolution2 = this.resourceResolution; const resourceUniquePoints = uniquePoints; const gcpUniquePoints = resourceUniquePoints.map( (resourcePoint) => this.projectedTransformer.transformToGeo( resourcePoint, { distortionMeasures: DEFAULT_DISTORTION_MEASURES, referenceScale: this.getReferenceScale() }, (gcpPartialDistortion) => gcpPartialDistortion ) ); const uniquePointIndices = uniquePointIndexTriangles.flat(); return { resourceResolution: resourceResolution2, gcpUniquePoints, uniquePointIndices, uniquePointIndexInterpolatedPolygon }; } ); if (!this.projectedGcpPreviousTriangulation) { this.projectedGcpPreviousTriangulation = this.projectedGcpTriangulation; } if (refinePrevious) { this.previousResourceResolution = this.resourceResolution; this.projectedGcpPreviousTriangulation = getPropertyFromTripleCacheOrComputation( this.projectedGcpTriangulationCache, this.previousResourceResolution, this.previousTransformationType, this.previousInternalProjection, () => { if (!this.projectedGcpTriangulation) { throw new Error("No projectedGcpTriangulation"); } const projectedGcpTriangulation = this.projectedGcpTriangulation; return { resourceResolution: this.projectedGcpTriangulation.resourceResolution, gcpUniquePoints: this.projectedGcpTriangulation.gcpUniquePoints.map( (projectedGcp) => this.projectedPreviousTransformer.transformToGeo( projectedGcp.resource, { distortionMeasures: DEFAULT_DISTORTION_MEASURES, referenceScale: this.getReferenceScale() }, (gcpPartialDistortion) => gcpPartialDistortion ) ), uniquePointIndices: this.projectedGcpTriangulation.uniquePointIndices, uniquePointIndexInterpolatedPolygon: projectedGcpTriangulation.uniquePointIndexInterpolatedPolygon }; }, () => !this.mixed, () => !this.mixed ); } this.updateTrianglePoints(); } /** * Derive the (previous and new) resource and projectedGeo points from their corresponding triangulations. * * Also derive the (previous and new) triangulation-refined resource and projectedGeo mask */ updateTrianglePoints() { if (!this.projectedGcpPreviousTriangulation || !this.projectedGcpTriangulation) { return; } const projectedGcpPreviousTriangulation = this.projectedGcpPreviousTriangulation; const projectedGcpTriangulation = this.projectedGcpTriangulation; this.resourceTrianglePoints = this.projectedGcpTriangulation.uniquePointIndices.map( (i) => projectedGcpTriangulation.gcpUniquePoints[i].resource ); this.projectedGeoPreviousTrianglePoints = this.projectedGcpPreviousTriangulation.uniquePointIndices.map( (i) => projectedGcpPreviousTriangulation.gcpUniquePoints[i].geo ); this.projectedGeoTrianglePoints = this.projectedGcpTriangulation.uniquePointIndices.map( (i) => projectedGcpTriangulation.gcpUniquePoints[i].geo ); this.projectedGeoPreviousTriangulationMask = this.projectedGcpPreviousTriangulation.uniquePointIndexInterpolatedPolygon.map( (typedRing) => typedRing.map( (i) => projectedGcpPreviousTriangulation.gcpUniquePoints[i].geo ) ).flat(); this.projectedGeoTriangulationMask = this.projectedGcpTriangulation.uniquePointIndexInterpolatedPolygon.map( (typedRing) => typedRing.map((i) => projectedGcpTriangulation.gcpUniquePoints[i].geo) ).flat(); this.updateTrianglePointsDistortion(); } /** * Derive the (previous and new) distortions from their corresponding triangulations. */ updateTrianglePointsDistortion() { if (!this.projectedGcpPreviousTriangulation || !this.projectedGcpTriangulation) { return; } const projectedGcpPreviousTriangulation = this.projectedGcpPreviousTriangulation; const projectedGcpTriangulation = this.projectedGcpTriangulation; this.previousTrianglePointsDistortion = projectedGcpPreviousTriangulation.uniquePointIndices.map((i) => { const distortions = projectedGcpPreviousTriangulation.gcpUniquePoints[i].distortions; if (!this.previousDistortionMeasure || !distortions) { return 0; } else { return distortions.get(this.previousDistortionMeasure); } }); this.trianglePointsDistortion = projectedGcpTriangulation.uniquePointIndices.map((i) => { const distortions = projectedGcpTriangulation.gcpUniquePoints[i].distortions; if (!this.distortionMeasure || !distortions) { return 0; } else { return distortions.get(this.distortionMeasure); } }); } updateProjectedTransformerProperties() { super.updateProjectedTransformerProperties(); this.updateTriangulation(); } clearProjectedTransformerCaches() { super.clearProjectedTransformerCaches(); this.clearResourceTriangulationCaches(); } clearResourceTriangulationCaches() { this.resourceTriangulationCache = /* @__PURE__ */ new Map(); this.clearProjectedTriangulationCaches(); } clearProjectedTriangulationCaches() { this.projectedGcpTriangulationCache = /* @__PURE__ */ new Map(); } } export { TriangulatedWarpedMap }; //# sourceMappingURL=TriangulatedWarpedMap.js.map