UNPKG

@seasketch/geoprocessing

Version:

Geoprocessing and reporting framework for SeaSketch 2.0

441 lines • 20.5 kB
import { ProjectClientBase, firstMatchingMetric, geographySchema, metricsSchema, } from "../../../src/index.js"; import fs from "fs-extra"; import path from "node:path"; import { precalcDatasources } from "./precalcDatasources.js"; import { importDatasource } from "./importDatasource.js"; import { writeGeographies } from "../geographies/geographies.js"; import configFixtures from "../../../src/testing/fixtures/projectConfig.js"; import { describe, beforeEach, expect, test } from "vitest"; const projectClient = new ProjectClientBase(configFixtures.simple); const srcPath = "data/in"; const dstPath = "data/out"; const port = 8080; const eezSrc = "eez"; const multiEezSrc = "two-samoas-eez"; const reefSrc = "samoa_benthic_reef_sand"; describe("precalcRasterDatasource", () => { beforeEach(() => { // Ensure test data folder fs.mkdirsSync(dstPath); }); test("precalcRasterDatasource - single file, single class should write geography and precalc raster metrics", async () => { const dsFilename = "datasources_precalc_raster_test_1.json"; const dsFilePath = path.join(dstPath, dsFilename); const datasourceId = "samoa_benthic_reef_sand"; const geogDatasourceId = "eez_raster1"; const geogFilename = "geographies_precalc_raster_test_1.json"; const geogFilePath = path.join(dstPath, geogFilename); const geographyId = "eez_raster1"; const precalcFilename = "precalc_raster_test_1.json"; const precalcFilePath = path.join(dstPath, precalcFilename); // Start off with clean empty datasources file fs.writeJSONSync(dsFilePath, []); // First import the datasources await importDatasource(projectClient, { geo_type: "vector", src: path.join(srcPath, `${eezSrc}.json`), datasourceId: geogDatasourceId, classKeys: [], formats: ["fgb", "json"], propertiesToKeep: [], precalc: true, explodeMulti: true, }, { newDatasourcePath: dsFilePath, newDstPath: dstPath, doPublish: false, }); await importDatasource(projectClient, { geo_type: "raster", src: path.join(srcPath, `${datasourceId}.tif`), datasourceId, classKeys: [], formats: ["tif"], noDataValue: 0, band: 1, measurementType: "quantitative", precalc: true, }, { newDatasourcePath: dsFilePath, newDstPath: dstPath, doPublish: false, }); // Create geography const eezGeog = { geographyId: geographyId, datasourceId: geogDatasourceId, display: geographyId, precalc: true, }; writeGeographies([eezGeog], geogFilePath); const savedGeos = fs.readJSONSync(geogFilePath); expect(Array.isArray(savedGeos) && savedGeos.length === 1).toBe(true); geographySchema.parse(savedGeos[0]); await precalcDatasources(projectClient, { newDatasourcePath: dsFilePath, newGeographyPath: geogFilePath, newPrecalcPath: precalcFilePath, newDstPath: dstPath, port, }); // Verify precalc const metrics = fs.readJSONSync(precalcFilePath); metricsSchema.parse(metrics); // Should create metrics for both the geography datasource and the raster datasource expect(metrics.length).toBe(6); const areaMetrics = metrics.filter((m) => m.metricId === "area"); // vector ds area and raster ds area expect(areaMetrics.length).toEqual(2); const countMetric = firstMatchingMetric(metrics, (m) => m.metricId === "valid"); expect(countMetric).toBeTruthy(); expect(countMetric.value).toBe(53); const sumMetric = firstMatchingMetric(metrics, (m) => m.metricId === "sum"); expect(sumMetric).toBeTruthy(); expect(sumMetric.value).toBe(53); fs.removeSync(dsFilePath); fs.removeSync(path.join(dstPath, `${geogDatasourceId}.fgb`)); fs.removeSync(path.join(dstPath, `${geogDatasourceId}.json`)); fs.removeSync(geogFilePath); fs.removeSync(path.join(dstPath, `${datasourceId}.tif`)); fs.removeSync(precalcFilePath); }, 60_000); test("precalcRasterDatasource - multiple geog scenarios with external subdivided datasource", async () => { const dsFilename = "datasources_precalc_raster_test_9.json"; const dsFilePath = path.join(dstPath, dsFilename); const rasterDatasourceId = "samoa_benthic_reef_sand9"; const geogDatasourceId = "global-clipping-eez-land-union"; const geogFilename = "geographies_precalc_raster_test_9.json"; const geogFilePath = path.join(dstPath, geogFilename); const precalcFilename = "precalc_raster_test_9.json"; const precalcFilePath = path.join(dstPath, precalcFilename); // Start off with clean empty datasources file //TODO: remove this once global datasources are updated, because they'll be added by default on datasource import fs.writeJSONSync(dsFilePath, [ { datasourceId: geogDatasourceId, geo_type: "vector", url: "https://d3muy0hbwp5qkl.cloudfront.net", formats: ["subdivided"], classKeys: [], idProperty: "UNION", nameProperty: "UNION", precalc: false, }, ]); // Import raster to test against with known counts (both Samoa and American Samoa) await importDatasource(projectClient, { geo_type: "raster", src: path.join(srcPath, `${reefSrc}.tif`), datasourceId: rasterDatasourceId, classKeys: [], formats: ["tif"], noDataValue: 0, band: 1, measurementType: "quantitative", precalc: true, }, { newDatasourcePath: dsFilePath, newDstPath: dstPath, doPublish: false, }); // Create geographies that reference this datasource // Box filter should give all polygons within bounding box (more than 2) const geogBoxFilter = { geographyId: "geog-box-filter", datasourceId: geogDatasourceId, display: "geog-box-filter", bboxFilter: [ -174.511_394_471_577_574_4, -17.555_268_752_861_550_8, -165.200_833_333_191_610_6, -10.024_476_331_539_347, ], precalc: true, }; // Filter to single polygon geography const geogSingleFilter = { geographyId: "geog-single-filter", datasourceId: geogDatasourceId, propertyFilter: { property: "UNION", values: ["Samoa"], }, bboxFilter: [ -173.774_690_650_053_3, -17.555_268_752_861_55, -165.200_833_333_191_6, -10.024_476_331_539_347, ], display: "geog-single-filter", precalc: true, }; // Filter should give two Samoan polygons const geogDoubleFilter = { geographyId: "geog-double-filter", datasourceId: geogDatasourceId, propertyFilter: { property: "UNION", values: ["Samoa", "American Samoa"], // Should include two polygons }, bboxFilter: [ -174.511_394_471_577_574_4, -17.555_268_752_861_550_8, -165.200_833_333_191_610_6, -10.024_476_331_539_347, ], display: "geog-double-filter", precalc: true, }; writeGeographies([geogBoxFilter, geogSingleFilter, geogDoubleFilter], geogFilePath); await precalcDatasources(projectClient, { newDatasourcePath: dsFilePath, newGeographyPath: geogFilePath, newPrecalcPath: precalcFilePath, newDstPath: dstPath, port, }); const savedGeos = fs.readJSONSync(geogFilePath); expect(Array.isArray(savedGeos) && savedGeos.length === 3).toBe(true); geographySchema.parse(savedGeos[0]); // Verify precalc const metrics = fs.readJSONSync(precalcFilePath); metricsSchema.parse(metrics); expect(metrics.length).toBe(12); // because precalc false for geog datasource const boxFilterSumMetric = firstMatchingMetric(metrics, (m) => m.geographyId === "geog-box-filter" && m.metricId === "sum"); expect(boxFilterSumMetric.value).toEqual(101); const boxFilterAreaMetric = firstMatchingMetric(metrics, (m) => m.geographyId === "geog-box-filter" && m.metricId === "area"); expect(boxFilterAreaMetric.value).toEqual(2_617_017_582.013_417); // Very close to QGIS calculated value const singleFilterSumMetric = firstMatchingMetric(metrics, (m) => m.geographyId === "geog-single-filter" && m.metricId === "sum"); expect(singleFilterSumMetric.value).toEqual(76); const singleFilterAreaMetric = firstMatchingMetric(metrics, (m) => m.geographyId === "geog-single-filter" && m.metricId === "area"); expect(singleFilterAreaMetric.value).toBeCloseTo((2_617_017_582.013_417 / 101) * 76); const doubleFilterSumMetric = firstMatchingMetric(metrics, (m) => m.geographyId === "geog-double-filter" && m.metricId === "sum"); expect(doubleFilterSumMetric.value).toEqual(98); fs.removeSync(dsFilePath); fs.removeSync(path.join(dstPath, `${rasterDatasourceId}.tif`)); fs.removeSync(path.join(dstPath, `${geogDatasourceId}.fgb`)); fs.removeSync(path.join(dstPath, `${geogDatasourceId}.json`)); fs.removeSync(geogFilePath); fs.removeSync(precalcFilePath); }, 10_000); test("precalcRasterDatasource - multiple geog scenarios with external flatgeobuf datasource", async () => { const dsFilename = "datasources_precalc_raster_test_8.json"; const dsFilePath = path.join(dstPath, dsFilename); const rasterDatasourceId = "samoa_benthic_reef_sand8"; const geogDatasourceId = "global-eez-mr-v12"; const geogFilename = "geographies_precalc_raster_test_8.json"; const geogFilePath = path.join(dstPath, geogFilename); const precalcFilename = "precalc_raster_test_8.json"; const precalcFilePath = path.join(dstPath, precalcFilename); // Start off with clean empty datasources file //TODO: remove this once global datasources are updated, because they'll be added by default on datasource import fs.writeJSONSync(dsFilePath, [ { datasourceId: geogDatasourceId, geo_type: "vector", url: `https://gp-global-datasources-datasets.s3.us-west-1.amazonaws.com/${geogDatasourceId}.fgb`, formats: ["fgb", "json"], classKeys: [], idProperty: "GEONAME", nameProperty: "GEONAME", precalc: false, }, ]); // Import raster to test against with known counts (both Samoa and American Samoa) await importDatasource(projectClient, { geo_type: "raster", src: path.join(srcPath, `${reefSrc}.tif`), datasourceId: rasterDatasourceId, classKeys: [], formats: ["tif"], noDataValue: 0, band: 1, measurementType: "quantitative", precalc: true, }, { newDatasourcePath: dsFilePath, newDstPath: dstPath, doPublish: false, }); // Create geographies that reference this datasource // Box filter should give all polygons within bounding box (more than 2) const geogBoxFilter = { geographyId: "geog-box-filter", datasourceId: geogDatasourceId, display: "geog-box-filter", bboxFilter: [ -174.511_394_471_577_574_4, -17.555_268_752_861_550_8, -165.200_833_333_191_610_6, -10.024_476_331_539_347, ], precalc: true, }; // Filter to single polygon geography const geogSingleFilter = { geographyId: "geog-single-filter", datasourceId: geogDatasourceId, propertyFilter: { property: "GEONAME", values: ["Samoan Exclusive Economic Zone"], }, bboxFilter: [ -173.774_690_650_053_3, -17.555_268_752_861_55, -165.200_833_333_191_6, -10.024_476_331_539_347, ], display: "geog-single-filter", precalc: true, }; // Filter should give two Samoan polygons const geogDoubleFilter = { geographyId: "geog-double-filter", datasourceId: geogDatasourceId, propertyFilter: { property: "GEONAME", values: [ "Samoan Exclusive Economic Zone", "United States Exclusive Economic Zone (American Samoa)", ], // Should include two polygons }, bboxFilter: [ -174.511_394_471_577_574_4, -17.555_268_752_861_550_8, -165.200_833_333_191_610_6, -10.024_476_331_539_347, ], display: "geog-double-filter", precalc: true, }; writeGeographies([geogBoxFilter, geogSingleFilter, geogDoubleFilter], geogFilePath); await precalcDatasources(projectClient, { newDatasourcePath: dsFilePath, newGeographyPath: geogFilePath, newPrecalcPath: precalcFilePath, newDstPath: dstPath, port, }); // Verify geography const savedGeos = fs.readJSONSync(geogFilePath); expect(Array.isArray(savedGeos) && savedGeos.length === 3).toBe(true); geographySchema.parse(savedGeos[0]); // Verify precalc const metrics = fs.readJSONSync(precalcFilePath); metricsSchema.parse(metrics); expect(metrics.length).toBe(12); // because precalc false for geog datasource const boxFilterMetric = firstMatchingMetric(metrics, (m) => m.geographyId === "geog-box-filter" && m.metricId === "sum"); expect(boxFilterMetric.value).toEqual(68); const singleFilterMetric = firstMatchingMetric(metrics, (m) => m.geographyId === "geog-single-filter" && m.metricId === "sum"); expect(singleFilterMetric.value).toEqual(53); const doubleFilterMetric = firstMatchingMetric(metrics, (m) => m.geographyId === "geog-double-filter" && m.metricId === "sum"); expect(doubleFilterMetric.value).toEqual(67); fs.removeSync(dsFilePath); fs.removeSync(path.join(dstPath, `${rasterDatasourceId}.tif`)); fs.removeSync(path.join(dstPath, `${geogDatasourceId}.fgb`)); fs.removeSync(path.join(dstPath, `${geogDatasourceId}.json`)); fs.removeSync(geogFilePath); fs.removeSync(precalcFilePath); }, 30_000); test("precalcRasterDatasource - multiple geog scenarios with internal geojson datasource", async () => { const dsFilename = "datasources_precalc_raster_test_7.json"; const dsFilePath = path.join(dstPath, dsFilename); const rasterDatasourceId = "samoa_benthic_reef_sand7"; const geogDatasourceId = "two-samoas-eez"; const geogFilename = "geographies_precalc_raster_test_7.json"; const geogFilePath = path.join(dstPath, geogFilename); const precalcFilename = "precalc_raster_test_7.json"; const precalcFilePath = path.join(dstPath, precalcFilename); // Start off with clean empty datasources file fs.writeJSONSync(dsFilePath, []); // First import the two-samoas as internal datasource for geographies await importDatasource(projectClient, { geo_type: "vector", src: path.join(srcPath, `${multiEezSrc}.json`), datasourceId: geogDatasourceId, classKeys: [], formats: ["fgb", "json"], propertiesToKeep: [], precalc: false, explodeMulti: true, }, { newDatasourcePath: dsFilePath, newDstPath: dstPath, doPublish: false, }); // Import raster to test against with known counts (both Samoa and American Samoa) await importDatasource(projectClient, { geo_type: "raster", src: path.join(srcPath, `${reefSrc}.tif`), datasourceId: rasterDatasourceId, classKeys: [], formats: ["tif"], noDataValue: 0, band: 1, measurementType: "quantitative", precalc: true, }, { newDatasourcePath: dsFilePath, newDstPath: dstPath, doPublish: false, }); // Create geographies that reference this datasource // No filter should give all polygons (two) in geography const geoBoxFilter = { geographyId: "geog-box-filter", datasourceId: geogDatasourceId, display: "geog-box-filter", precalc: true, }; // Filter to single polygon geography const geogSingleFilter = { geographyId: "geog-single-filter", datasourceId: geogDatasourceId, propertyFilter: { property: "GEONAME", values: ["Samoan Exclusive Economic Zone"], }, bboxFilter: [ -173.774_690_650_053_3, -17.555_268_752_861_55, -165.200_833_333_191_6, -10.024_476_331_539_347, ], display: "geog-single-filter", precalc: true, }; // Filter should give all polygons (both countries) const geogDoubleFilter = { geographyId: "geog-double-filter", datasourceId: geogDatasourceId, propertyFilter: { property: "GEONAME", values: [ "Samoan Exclusive Economic Zone", "United States Exclusive Economic Zone (American Samoa)", ], // Should include two polygons }, bboxFilter: [ -174.511_394_471_577_574_4, -17.555_268_752_861_550_8, -165.200_833_333_191_610_6, -10.024_476_331_539_347, ], display: "geog-double-filter", precalc: true, }; writeGeographies([geoBoxFilter, geogSingleFilter, geogDoubleFilter], geogFilePath); await precalcDatasources(projectClient, { newDatasourcePath: dsFilePath, newGeographyPath: geogFilePath, newPrecalcPath: precalcFilePath, newDstPath: dstPath, port, }); const savedGeos = fs.readJSONSync(geogFilePath); expect(Array.isArray(savedGeos) && savedGeos.length === 3).toBe(true); geographySchema.parse(savedGeos[0]); // Verify precalc const metrics = fs.readJSONSync(precalcFilePath); metricsSchema.parse(metrics); expect(metrics.length).toBe(12); // because precalc false for geog datasource // check each metric for expected value const noFilterMetric = firstMatchingMetric(metrics, (m) => m.geographyId === "geog-box-filter" && m.metricId === "sum"); expect(noFilterMetric.value).toEqual(69); const singleFilterMetric = firstMatchingMetric(metrics, (m) => m.geographyId === "geog-single-filter" && m.metricId === "sum"); expect(singleFilterMetric.value).toEqual(53); const doubleFilterMetric = firstMatchingMetric(metrics, (m) => m.geographyId === "geog-double-filter" && m.metricId === "sum"); expect(doubleFilterMetric.value).toEqual(69); fs.removeSync(dsFilePath); fs.removeSync(path.join(dstPath, `${rasterDatasourceId}.tif`)); fs.removeSync(path.join(dstPath, `${geogDatasourceId}.fgb`)); fs.removeSync(path.join(dstPath, `${geogDatasourceId}.json`)); fs.removeSync(geogFilePath); fs.removeSync(precalcFilePath); }, 20_000); }); //# sourceMappingURL=precalcRasterDatasource.test.e2e.js.map