-
Notifications
You must be signed in to change notification settings - Fork 0
/
clipToOceanEez.ts
102 lines (92 loc) · 3.04 KB
/
clipToOceanEez.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import {
ValidationError,
PreprocessingHandler,
VectorDataSource,
intersect,
difference,
isPolygonFeature,
Feature,
Polygon,
MultiPolygon,
} from "@seasketch/geoprocessing";
import area from "@turf/area";
import bbox from "@turf/bbox";
import { featureCollection as fc } from "@turf/helpers";
import combine from "@turf/combine";
import flatten from "@turf/flatten";
const MAX_SIZE = 500000 * 1000 ** 2;
type OsmLandFeature = Feature<Polygon, { gid: number }>;
type EezLandUnion = Feature<Polygon, { gid: number; UNION: string }>;
// Defined at module level for potential caching/reuse by serverless process
const SubdividedOsmLandSource = new VectorDataSource<OsmLandFeature>(
"https://d3p1dsef9f0gjr.cloudfront.net/"
);
const SubdividedEezLandUnionSource = new VectorDataSource<EezLandUnion>(
"https://d3muy0hbwp5qkl.cloudfront.net"
);
export async function clipLand(feature: Feature<Polygon | MultiPolygon>) {
const landFeatures = await SubdividedOsmLandSource.fetchUnion(
bbox(feature),
"gid"
);
const combined = combine(landFeatures).features[0] as Feature<MultiPolygon>;
return difference(feature, combined);
}
export async function clipOutsideEez(
feature: Feature<Polygon | MultiPolygon>,
eezFilterByNames: string[] = []
) {
let eezFeatures = await SubdividedEezLandUnionSource.fetch(bbox(feature));
// Optionally filter down to a single country/union EEZ boundary
if (eezFilterByNames.length > 0) {
eezFeatures = eezFeatures.filter((e) =>
eezFilterByNames.includes(e.properties.UNION)
);
}
const combined = combine(fc(eezFeatures))
.features[0] as Feature<MultiPolygon>;
return intersect(feature, combined);
}
/**
* Takes a Polygon feature and returns the portion that is in the ocean and within an EEZ boundary
* If results in multiple polygons then returns the largest
*/
export async function clipToOceanEez(
feature: Feature,
eezFilterByNames?: string[]
): Promise<Feature> {
if (!isPolygonFeature(feature)) {
throw new ValidationError("Input must be a polygon");
}
if (area(feature) > MAX_SIZE) {
throw new ValidationError(
"Please limit sketches to under 500,000 square km"
);
}
let clipped = await clipLand(feature);
if (clipped) clipped = await clipOutsideEez(clipped, eezFilterByNames);
if (!clipped || area(clipped) === 0) {
throw new ValidationError("Sketch is outside of project boundaries");
} else {
if (clipped.geometry.type === "MultiPolygon") {
const flattened = flatten(clipped);
let biggest = [0, 0];
for (var i = 0; i < flattened.features.length; i++) {
const a = area(flattened.features[i]);
if (a > biggest[0]) {
biggest = [a, i];
}
}
return flattened.features[biggest[1]] as Feature<Polygon>;
} else {
return clipped;
}
}
}
export default new PreprocessingHandler(clipToOceanEez, {
title: "clipToOceanEez",
description:
"Erases portion of sketch overlapping with land or extending into ocean outsize EEZ boundary",
timeout: 40,
requiresProperties: [],
});