@seasketch/geoprocessing
Version:
Geoprocessing and reporting framework for SeaSketch 2.0
99 lines • 4.8 kB
JavaScript
import { toSketchArray, isSketchCollection, roundDecimal, } from "../helpers/index.js";
import { clip } from "./clip.js";
import { createMetric } from "../metrics/index.js";
import { featureCollection, truncate as truncateGeom } from "@turf/turf";
/**
* Calculates area overlap between sketch(es) and an array of polygon features.
* Truncates input geometry coordinates down to 6 decimal places (~1m accuracy) before intersection to avoid floating point precision issues.
* If sketch collection, then calculates area per sketch and for sketch collection
* @param metricId unique metric identifier to assign to each metric
* @param features to intersect and get overlap metrics
* @param sketch the sketches. If empty will return 0 result.
* @param options.truncate truncate results to 6 digits after decimal point, defaults to true
* @param options.includeChildMetrics if false and sketch is collection, child sketch metrics will not be included in results, defaults to true
* @param options.sumProperty Property in features with value to sum, if not defined each feature will count as 1
* @returns array of Metric objects
*/
export async function overlapPolygonSum(metricId, features, sketch, options = {}) {
const { includeChildMetrics = true, truncate = true, sumProperty } = options;
// Collect indices of features that intersect with sketch(es) for later calculation of collection level sum
const featureIndices = new Set();
const truncatedSketches = (Array.isArray(sketch) ? sketch : toSketchArray(sketch)).map((s) => truncateGeom(s));
const truncatedFeatures = features.map((f) => truncateGeom(f));
// Individual sketch metrics
const sketchMetrics = truncatedSketches.map((curSketch) => {
const intersections = intersectSum(curSketch, truncatedFeatures, sumProperty);
// Accumulate feature indices that intersect with collection
for (const index of intersections.indices)
featureIndices.add(index);
return createMetric({
metricId,
sketchId: curSketch.properties.id,
value: truncate
? roundDecimal(intersections.sum, 6, { keepSmallValues: true })
: intersections.sum,
extra: {
sketchName: curSketch.properties.name,
},
});
});
const metrics = includeChildMetrics ? sketchMetrics : [];
// Collection level metrics
if (isSketchCollection(sketch)) {
let collValue = 0;
// Iterate through feature indices and accumulate collection level sum value
for (const index of featureIndices) {
const feature = features[index];
if (sumProperty &&
feature.properties &&
feature.properties[sumProperty]) {
collValue += feature.properties[sumProperty];
}
else {
collValue += 1;
}
}
metrics.push(createMetric({
metricId,
sketchId: sketch.properties.id,
value: truncate
? roundDecimal(collValue, 6, { keepSmallValues: true })
: collValue,
extra: {
sketchName: sketch.properties.name,
isCollection: true,
},
}));
}
return metrics;
}
/**
* Returns an object containing the sum value of features in B that intersect with featureA,
* and the indices of the features in B that intersect with featureA
* No support for partial overlap, counts the whole feature if it intersects.
* @param featureA single feature to intersect with featuresB
* @param featuresB array of features
* @param sumProperty Property in featuresB with value to sum, if not defined each feature will count as 1
* @returns Sum of features/feature property which overlap with the sketch, and a list of
* indices for features that overlap with the sketch to be used in calculating total sum of
* the sketch collection
*/
export const intersectSum = (featureA, featuresB, sumProperty) => {
const indices = [];
// intersect and get sum of remainder
const sketchValue = featuresB
.map((curFeature, index) => {
// Optimization: can this be done with turf.booleanIntersects?
const rem = clip(featureCollection([featureA, curFeature]), "intersection");
if (!rem)
return 0;
indices.push(index);
let featureValue = 1;
if (sumProperty && curFeature.properties[sumProperty] >= 0)
featureValue = curFeature.properties[sumProperty];
return featureValue;
})
.reduce((valueSoFar, curValue) => valueSoFar + curValue, 0);
return { sum: sketchValue, indices: indices };
};
//# sourceMappingURL=overlapPolygonSum.js.map