@seasketch/geoprocessing
Version:
Geoprocessing and reporting framework for SeaSketch 2.0
80 lines (67 loc) • 3.22 kB
text/typescript
import fs from "fs-extra";
import { area, bbox, featureCollection as fc } from "@turf/turf";
import { $ } from "zx";
import { getDatasourceFeatures } from "../../../src/dataproviders/index.js";
import { chunk, roundDecimal } from "../../../src/helpers/index.js";
import project from "./ProjectClientGlobal.js";
import { clip } from "../../../src/toolbox/clip.js";
const outfile = "./mr-eez-land-union-precalc.json";
const infile = "/mnt/c/data/EEZ_land_union_v3_202003/EEZ_Land_v3_202030.shp";
// THIS SCRIPT IS NOT VIABLE AS-IS
// Problem - turf falling over on large eez and/or land features, even when chunking
// Suggest solutions
// Option 1 - In QGIS, subtract whole OSM land from EEZ
// Option 2 - crosswalk eez to osm land, fetch land features, filter to one country, calc with each land feature, then area, sum the result
// start fresh
await $`rm -f ${outfile}`;
// calculate equal area 6933 using sqlite, and strip unneeded properties
//const query = `SELECT "UNION", GEOMETRY, round(ST_Area(ST_Transform(GEOMETRY, 6933)), 0) as area_6933 FROM EEZ_Land_v3_202030 ORDER BY "UNION"`;
// Convert shapefile to geojson and pare down attributes
const query = `SELECT "UNION", GEOMETRY FROM EEZ_Land_v3_202030 ORDER BY "UNION"`;
await $`ogr2ogr -f GeoJSON -dialect sqlite -sql ${query} ${outfile} ${infile}`;
// Calculate turf area (after subtracting osm land) and bbox, strip geometry to save space
const eezFeatures = fs.readJSONSync(outfile);
// calculate turf area = eez - osm land
const landDs = project.getExternalVectorDatasourceById(
"global-clipping-osm-land",
);
const landUrl = project.getDatasourceUrl(landDs);
for (const eezFeat of eezFeatures.features) {
// Get land features that overlap with eez
const eezBbox = bbox(eezFeat);
const landFeatures = await getDatasourceFeatures(landDs, landUrl, {
unionProperty: "gid", // gid is assigned per country
bbox: eezBbox,
});
const eezNoLandArea = area(eezFeat);
let remEezArea = 0;
console.log(eezFeat.properties.UNION);
console.log("eezNoLandArea:", eezNoLandArea);
console.log("numLandFeatures:", landFeatures.length);
const featToChunkSize = (numFeatures) => {
if (numFeatures > 8000) return 100;
else if (numFeatures > 6000) return 500;
else if (numFeatures > 4000) return 4000;
else return numFeatures;
};
const chunkSize = featToChunkSize(landFeatures.length);
const chunks = chunk(landFeatures, chunkSize);
console.log(`${chunks.length} chunks, ${chunkSize} features each`);
// Start with whole eezFeat and subtract one chunk of land features at a time to not blow up clip
let remainder = eezFeat;
for (const [idx, curChunk] of chunks.entries()) {
console.log(`chunk ${idx + 1} of ${chunks.length}`);
remainder = clip(fc([remainder, ...curChunk]), "difference");
}
remEezArea = remainder ? area(remainder) : remEezArea;
console.log("remEezArea:", eezNoLandArea);
console.log(
`${roundDecimal((remEezArea / eezNoLandArea) * 100, 1)}% decrease`,
);
console.log(" ");
console.log(" ");
eezFeat.area_turf = remEezArea;
eezFeat.bbox = bbox(eezFeat);
eezFeat.geometry = null; // remove geometry to save space
fs.writeJsonSync(outfile, eezFeatures);
}