@seasketch/geoprocessing
Version:
Geoprocessing and reporting framework for SeaSketch 2.0
115 lines (104 loc) • 3.53 kB
text/typescript
import {
Sketch,
SketchCollection,
Polygon,
MultiPolygon,
GeoprocessingHandler,
getFirstFromParam,
DefaultExtraParams,
Feature,
isVectorDatasource,
overlapFeatures,
getFeaturesForSketchBBoxes,
} from "@seasketch/geoprocessing";
import project from "../../project/projectClient.js";
import {
Metric,
ReportResult,
rekeyMetrics,
sortMetrics,
} from "@seasketch/geoprocessing/client-core";
import { clipToGeography } from "../util/clipToGeography.js";
/**
* vectorFunction: A geoprocessing function that calculates overlap metrics for vector datasources
* @param sketch - A sketch or collection of sketches
* @param extraParams
* @returns Calculated metrics and a null sketch
*/
export async function vectorFunction(
sketch:
| Sketch<Polygon | MultiPolygon>
| SketchCollection<Polygon | MultiPolygon>,
extraParams: DefaultExtraParams = {},
): Promise<ReportResult> {
// Check for client-provided geography, fallback to first geography assigned as default-boundary in metrics.json
const geographyId = getFirstFromParam("geographyIds", extraParams);
const curGeography = project.getGeographyById(geographyId, {
fallbackGroup: "default-boundary",
});
// Clip portion of sketch outside geography features
const clippedSketch = await clipToGeography(sketch, curGeography);
const featuresByDatasource: Record<
string,
Feature<Polygon | MultiPolygon>[]
> = {};
// Calculate overlap metrics for each class in metric group
const metricGroup = project.getMetricGroup("vectorFunction");
const metrics = (
await Promise.all(
metricGroup.classes.map(async (curClass) => {
const ds = project.getMetricGroupDatasource(metricGroup, {
classId: curClass.classId,
});
if (!isVectorDatasource(ds))
throw new Error(`Expected vector datasource for ${ds.datasourceId}`);
const url = project.getDatasourceUrl(ds);
// Fetch features overlapping with sketch, if not already fetched
const features =
featuresByDatasource[ds.datasourceId] ||
(await getFeaturesForSketchBBoxes(sketch, url));
featuresByDatasource[ds.datasourceId] = features;
// Get classKey for current data class
const classKey = project.getMetricGroupClassKey(metricGroup, {
classId: curClass.classId,
});
let finalFeatures: Feature<Polygon | MultiPolygon>[] = [];
if (classKey === undefined)
// Use all features
finalFeatures = features;
else {
// Filter to features that are a member of this class
finalFeatures = features.filter(
(feat) =>
feat.geometry &&
feat.properties &&
feat.properties[classKey] === curClass.classId,
);
}
// Calculate overlap metrics
const overlapResult = await overlapFeatures(
metricGroup.metricId,
finalFeatures,
clippedSketch,
);
return overlapResult.map(
(metric): Metric => ({
...metric,
classId: curClass.classId,
geographyId: curGeography.geographyId,
}),
);
}),
)
).flat();
return {
metrics: sortMetrics(rekeyMetrics(metrics)),
};
}
export default new GeoprocessingHandler(vectorFunction, {
title: "vectorFunction",
description: "Function description",
timeout: 500, // seconds
memory: 1024, // megabytes
executionMode: "async",
});