@seasketch/geoprocessing
Version:
Geoprocessing and reporting framework for SeaSketch 2.0
336 lines • 11.5 kB
JavaScript
import { describe, test, expect } from "vitest";
import { genSampleSketch } from "../helpers/index.js";
import parseGeoraster from "georaster";
import { overlapRasterClass } from "./overlapRasterClass.js";
import { classIdMapping } from "../datasources/index.js";
import { featureCollection, bbox } from "@turf/turf";
import fix from "../testing/fixtures/sketches.js";
// bbox - [xmin, ymin, xmax, ymax]
// pixel - [left, bottom, right, top]
const bottomLeftPoly = genSampleSketch({
type: "Polygon",
coordinates: [
[
[],
[],
[],
[],
[],
],
],
});
const bottomRightPoly = genSampleSketch({
type: "Polygon",
coordinates: [
[
[],
[],
[],
[],
[],
],
],
});
const topRightPoly = genSampleSketch({
type: "Polygon",
coordinates: [
[
[],
[],
[],
[],
[],
],
],
});
const multiPoly1 = genSampleSketch({
type: "MultiPolygon",
coordinates: [topRightPoly.geometry.coordinates],
});
const multiPoly2 = genSampleSketch({
type: "MultiPolygon",
coordinates: [
topRightPoly.geometry.coordinates,
bottomRightPoly.geometry.coordinates,
],
});
const mixedCollectionId = "MMMM";
const mixedPolySketchCollection = {
type: "FeatureCollection",
properties: {
id: mixedCollectionId,
name: "Collection 1",
updatedAt: "2021-11-20T00:00:34.269Z",
createdAt: "2021-11-19T23:34:12.889Z",
sketchClassId: "615b65a2aac8c8285d50d9f3",
isCollection: true,
userAttributes: [],
},
bbox: bbox(featureCollection([bottomRightPoly, multiPoly1])),
features: [bottomRightPoly, multiPoly1],
};
const overlappingPolySketchCollection = {
type: "FeatureCollection",
properties: {
id: mixedCollectionId,
name: "Collection 1",
updatedAt: "2021-11-20T00:00:34.269Z",
createdAt: "2021-11-19T23:34:12.889Z",
sketchClassId: "615b65a2aac8c8285d50d9f3",
isCollection: true,
userAttributes: [],
},
bbox: bbox(featureCollection([topRightPoly, multiPoly1])),
features: [topRightPoly, multiPoly1],
};
// Multi-class raster (categorical)
const classes = Array.from({ length: 2 }, (v, i) => ({
numericClassId: i + 1,
classId: `${i + 1}`,
display: `Group ${i + 1}`,
}));
describe("overlapRasterClass test", () => {
test("overlapRasterClass - undefined sketch should return class sum for whole raster", async () => {
const raster = await parseGeoraster([
[
[],
[],
],
], {
noDataValue: 0,
projection: 4326,
xmin: 0, // left
ymax: 20, // top
pixelWidth: 10,
pixelHeight: 10,
});
const metrics = await overlapRasterClass("test", raster, undefined, classIdMapping(classes));
// only cell in polygon should have been nodata in bottom left
expect(metrics.length).toBe(2);
expect(metrics[0].sketchId).toBe(null);
expect(metrics[0].classId).toBe("1");
expect(metrics[0].value).toBe(2);
expect(metrics[1].classId).toBe("2");
expect(metrics[1].sketchId).toBe(null);
expect(metrics[1].value).toBe(1);
});
test("overlapRasterClass - can assign categories to alternate metric dimension", async () => {
const raster = await parseGeoraster([
[
[],
[],
],
], {
noDataValue: 0,
projection: 4326,
xmin: 0, // left
ymax: 20, // top
pixelWidth: 10,
pixelHeight: 10,
});
const metrics = await overlapRasterClass("test", raster, undefined, classIdMapping(classes), "groupId");
expect(metrics.length).toBe(2);
expect(metrics[0].sketchId).toBe(null);
expect(metrics[0].groupId).toBe("1");
expect(metrics[0].value).toBe(2);
});
test("overlapRasterClass - single polygon sketch bottom left", async () => {
const raster = await parseGeoraster([
[
[],
[],
],
], {
noDataValue: 0,
projection: 4326,
xmin: 0, // left
ymax: 20, // top
pixelWidth: 10,
pixelHeight: 10,
});
const metrics = await overlapRasterClass("test", raster, bottomLeftPoly, classIdMapping(classes));
// only cell in polygon should have been nodata in bottom left
expect(metrics.length).toBe(2);
expect(metrics[0].classId).toBe("1");
expect(metrics[0].value).toBe(0);
expect(metrics[1].classId).toBe("2");
expect(metrics[1].value).toBe(0);
});
test("overlapRasterClass - single polygon sketch top right", async () => {
const raster = await parseGeoraster([
[
[],
[],
],
], {
noDataValue: 0,
projection: 4326,
xmin: 0, // left
ymax: 20, // top
pixelWidth: 10,
pixelHeight: 10,
});
const metrics = await overlapRasterClass("test", raster, topRightPoly, classIdMapping(classes));
expect(metrics.length).toBe(2);
expect(metrics[0].classId).toBe("1");
expect(metrics[0].value).toBe(0);
expect(metrics[1].classId).toBe("2");
expect(metrics[1].value).toBe(1);
});
test("overlapRasterClass - single polygon sketch bottom right", async () => {
const raster = await parseGeoraster([
[
[],
[],
],
], {
noDataValue: 0,
projection: 4326,
xmin: 0, // left
ymax: 20, // top
pixelWidth: 10,
pixelHeight: 10,
});
const metrics = await overlapRasterClass("test", raster, bottomRightPoly, classIdMapping(classes));
expect(metrics.length).toBe(2);
expect(metrics[0].classId).toBe("1");
expect(metrics[0].value).toBe(1);
expect(metrics[1].classId).toBe("2");
expect(metrics[1].value).toBe(0);
});
// multipoly test
test("overlapRasterClass - multi polygon top and bottom right", async () => {
const raster = await parseGeoraster([
[
[],
[],
],
], {
noDataValue: 0,
projection: 4326,
xmin: 0, // left
ymax: 20, // top
pixelWidth: 10,
pixelHeight: 10,
});
const metrics = await overlapRasterClass("test", raster, multiPoly2, classIdMapping(classes));
expect(metrics.length).toBe(2);
expect(metrics[0].classId).toBe("1");
expect(metrics[0].value).toBe(1);
expect(metrics[1].classId).toBe("2");
expect(metrics[1].value).toBe(1);
});
// multipoly test
test("overlapRasterClass - poly and multi polygon top and bottom right", async () => {
const raster = await parseGeoraster([
[
[],
[],
],
], {
noDataValue: 0,
projection: 4326,
xmin: 0, // left
ymax: 20, // top
pixelWidth: 10,
pixelHeight: 10,
});
const metrics = await overlapRasterClass("test", raster, multiPoly2, classIdMapping(classes));
expect(metrics.length).toBe(2);
expect(metrics[0].classId).toBe("1");
expect(metrics[0].value).toBe(1);
expect(metrics[1].classId).toBe("2");
expect(metrics[1].value).toBe(1);
});
test("overlapRasterClass - mixed collection", async () => {
const raster = await parseGeoraster([
[
[],
[],
],
], {
noDataValue: 0,
projection: 4326,
xmin: 0, // left
ymax: 20, // top
pixelWidth: 10,
pixelHeight: 10,
});
const metrics = await overlapRasterClass("test", raster, mixedPolySketchCollection, classIdMapping(classes));
expect(metrics.length).toBe(6);
expect(metrics[0].classId).toBe("1");
expect(metrics[0].value).toBe(1);
expect(metrics[1].classId).toBe("2");
expect(metrics[1].value).toBe(0);
expect(metrics[2].classId).toBe("1");
expect(metrics[2].value).toBe(0);
expect(metrics[3].classId).toBe("2");
expect(metrics[3].value).toBe(1);
expect(metrics[4].classId).toBe("1");
expect(metrics[4].value).toBe(1);
expect(metrics[5].classId).toBe("2");
expect(metrics[5].value).toBe(1);
});
// multipoly test
test("overlapRasterClass - overlapping mixed collect", async () => {
const raster = await parseGeoraster([
[
[],
[],
],
], {
noDataValue: 0,
projection: 4326,
xmin: 0, // left
ymax: 20, // top
pixelWidth: 10,
pixelHeight: 10,
});
const metrics = await overlapRasterClass("test", raster, overlappingPolySketchCollection, classIdMapping(classes));
expect(metrics.length).toBe(6);
expect(metrics[0].classId).toBe("1");
expect(metrics[0].value).toBe(0);
expect(metrics[1].classId).toBe("2");
expect(metrics[1].value).toBe(1);
expect(metrics[2].classId).toBe("1");
expect(metrics[2].value).toBe(0);
expect(metrics[3].classId).toBe("2");
expect(metrics[3].value).toBe(1);
expect(metrics[4].classId).toBe("1");
expect(metrics[4].value).toBe(0);
expect(metrics[5].classId).toBe("2");
expect(metrics[5].value).toBe(1); // collection should not double count class 2
});
test("overlapRasterClass - should handle multiple holes in collection", async () => {
const raster = await parseGeoraster([
[
[],
[],
],
], {
noDataValue: 0,
projection: 4326,
xmin: 0, // left
ymax: 20, // top
pixelWidth: 10,
pixelHeight: 10,
});
const metrics = await overlapRasterClass("test", raster, fix.holeMixedSC, classIdMapping(classes));
// Remember
expect(metrics.length).toBe(6);
expect(metrics[0].classId).toBe("1");
expect(metrics[0].value).toBe(2); // hole covered bottom left 1
expect(metrics[1].classId).toBe("2");
expect(metrics[1].value).toBe(1);
expect(metrics[2].classId).toBe("1");
expect(metrics[2].value).toBe(3); // hole covered top right 1
expect(metrics[3].classId).toBe("2");
expect(metrics[3].value).toBe(0);
// collection together should cancel holes
expect(metrics[4].classId).toBe("1");
expect(metrics[4].value).toBe(3);
expect(metrics[5].classId).toBe("2");
expect(metrics[5].value).toBe(1);
});
});
//# sourceMappingURL=overlapRasterClass.test.js.map