@allmaps/render
Version:
Render functions for WebGL and image buffers
330 lines (329 loc) • 12.9 kB
JavaScript
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