@seasketch/geoprocessing
Version:
Geoprocessing and reporting framework for SeaSketch 2.0
486 lines • 16.3 kB
JavaScript
import { featureCollection, polygon, bbox } from "@turf/turf";
import { hasOwnProperty, isObject } from "./native.js";
import { isFeature, isFeatureCollection, collectionHasGeometry, isMultiPolygonFeature, isPolygonFeature, } from "./geo.js";
import { v4 as uuid } from "uuid";
export function getUserAttribute(sketchOrProps, exportid, defaultValue) {
const props = (() => {
if (isSketch(sketchOrProps)) {
return sketchOrProps.properties;
}
else if (isSketchCollection(sketchOrProps)) {
return sketchOrProps.properties;
}
else {
return sketchOrProps;
}
})();
const found = props.userAttributes.find((a) => a.exportId === exportid);
return found && found.value !== undefined && found.value !== null
? found.value
: defaultValue;
}
export function getJsonUserAttribute(sketchOrProps, exportid, defaultValue) {
const value = getUserAttribute(sketchOrProps, exportid, defaultValue);
if (typeof value === "string") {
return JSON.parse(value);
}
else {
return value;
}
}
/**
* Converts array of sketches to an array of their SketchProperties
*/
export function toSketchPropertiesArray(sketchArray) {
return sketchArray.map((s) => s.properties);
}
/**
* Returns SketchProperties for each child sketch in a SketchCollection
*/
export function toChildProperties(sketchCollection) {
return sketchCollection.features.map((sketch) => sketch.properties);
}
/**
* Converts a Sketch or SketchCollection to a Sketch array, maintaining geometry type
* Useful for putting in a consistent form that can be iterated over
* @param input sketch or sketch collection
* @returns array of sketches, if input is a sketch collection then it is the child sketches
*/
export function toSketchArray(input) {
if (isSketch(input)) {
return [input];
}
else if (isSketchCollection(input)) {
return input.features;
}
throw new Error("invalid input, must be Sketch or SketchCollection");
}
/** Helper to convert a NullSketch or NullSketchCollection to a NullSketch array */
export function toNullSketchArray(input) {
if (isSketch(input)) {
return [input];
}
else if (isSketchCollection(input)) {
return input.features;
}
throw new Error("invalid input, must be NullSketch or NullSketchCollection");
}
/**
* Returns sketch or sketch collection with null geometry
*/
export function toNullSketch(sketch, useNull = false) {
if (isSketchCollection(sketch)) {
return {
...sketch,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
features: sketch.features.map(({ geometry, ...nonGeom }) => ({
...nonGeom,
...(useNull ? { geometry: null } : {}),
})),
};
}
else {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { geometry, ...nonGeom } = sketch;
return {
...nonGeom,
...(useNull ? { geometry: null } : {}),
};
}
}
/**
* Checks if object is a Sketch. Any code inside a block guarded by a conditional call to this function will have type narrowed to Sketch
*/
export const isSketch = (feature) => {
return (feature &&
isFeature(feature) &&
hasOwnProperty(feature, "type") &&
hasOwnProperty(feature, "properties") &&
feature.properties &&
feature.properties.name);
};
/**
* Checks if sketch is a Polygon
*/
export const isPolygonSketch = (sketch) => {
return sketch && isSketch(sketch) && isPolygonFeature(sketch);
};
/**
* Checks if sketch is a MultiPolygon. Any code inside a block guarded by a conditional call to this function will have type narrowed to Sketch
*/
export const isMultiPolygonSketch = (sketch) => {
return sketch && isSketch(sketch) && isMultiPolygonFeature(sketch);
};
/**
* Check if object is a SketchCollection. Any code inside a block guarded by a conditional call to this function will have type narrowed to SketchCollection
*/
export const isSketchCollection = (collection) => {
return (collection &&
isFeatureCollection(collection) &&
hasOwnProperty(collection, "properties") &&
isObject(collection.properties) &&
hasOwnProperty(collection.properties, "name") &&
hasOwnProperty(collection.properties, "sketchClassId") &&
collection.features.map(isSketch).reduce((acc, cur) => acc && cur, true));
};
/**
* Checks if object is a NullSketch. Any code inside a block guarded by a conditional call to this function will have type narrowed to NullSketch
*/
export const isNullSketch = (feature) => {
return (feature &&
isFeature(feature) &&
hasOwnProperty(feature, "type") &&
hasOwnProperty(feature, "properties") &&
feature.properties &&
feature.properties.name &&
!feature.geometry);
};
/**
* Check if object is a NullSketchCollection. Any code inside a block guarded by a conditional call to this function will have type narrowed to NullSketchCollection
*/
export const isNullSketchCollection = (collection) => {
return (collection &&
isFeatureCollection(collection) &&
hasOwnProperty(collection, "properties") &&
isObject(collection.properties) &&
hasOwnProperty(collection.properties, "name") &&
collection.features.map(isNullSketch).reduce((acc, cur) => acc && cur, true));
};
export const isPolygonSketchCollection = (collection) => {
return (collection &&
isSketchCollection(collection) &&
collectionHasGeometry(collection, "Polygon"));
};
export const isMultiPolygonSketchCollection = (collection) => {
return (collection &&
isSketchCollection(collection) &&
collectionHasGeometry(collection, "MultiPolygon"));
};
export const isPolygonAllSketchCollection = (collection) => {
return (collection &&
isSketchCollection(collection) &&
collectionHasGeometry(collection, ["Polygon", "MultiPolygon"]));
};
export const isLineStringSketchCollection = (collection) => {
return (collection &&
isSketchCollection(collection) &&
collectionHasGeometry(collection, "LineString"));
};
export const isPointSketchCollection = (collection) => {
return (collection &&
isSketchCollection(collection) &&
collectionHasGeometry(collection, "Point"));
};
export const genSampleUserAttributes = () => {
return [
{
label: "single",
fieldType: "ChoiceField",
exportId: "SINGLE",
value: "single",
},
{
label: "multi",
fieldType: "ChoiceField",
exportId: "MULTI",
value: ["one", "two"],
},
{
label: "multiJson",
fieldType: "ChoiceField",
exportId: "MULTISTRING",
value: JSON.stringify(["one", "two"]),
},
{
label: "boolean",
value: false,
exportId: "BOOLEAN",
fieldType: "YesNo",
},
];
};
/**
* Returns a Sketch with given features geometry and properties. Reasonable defaults are given for properties not provided
* Default geometry is a square from 0,0 to 1,1
*/
export const genSketch = (options = {}) => {
const { feature = polygon([
[
[0, 0],
[0, 1],
[1, 1],
[1, 0],
[0, 0],
],
]), name = `sketch-${uuid()}`, id = uuid(), userAttributes = [
{
label: "Type of Sketch",
fieldType: "ChoiceField",
exportId: "TYPE_OF_SKETCH",
value: "sample",
},
{
label: "Notes",
fieldType: "TextArea",
exportId: "NOTES",
value: "This is a sample sketch",
},
], sketchClassId = uuid(), createdAt = new Date().toISOString(), updatedAt = new Date().toISOString(), } = options;
return {
...feature,
id,
properties: {
id,
isCollection: false,
userAttributes,
sketchClassId,
createdAt,
updatedAt,
name,
},
bbox: feature.geometry ? bbox(feature.geometry) : undefined,
};
};
/**
* Given array of sketches, return a sketch collection with given properties.
* Generates reasonable default values for any properties not passed in
* The geometry type of the returned collection will match the one passed in
* Properties of sketches are retained
*/
export const genSketchCollection = (sketches, options = {}) => {
const collId = options.id || uuid();
const { name = `sketch-${collId}`, id = collId, userAttributes = [], sketchClassId = uuid(), createdAt = new Date().toISOString(), updatedAt = new Date().toISOString(), } = options;
return {
type: "FeatureCollection",
features: sketches.map((sk, index) => {
const skId = uuid();
return {
...sk,
id: skId,
properties: {
...sk.properties,
id: skId,
name: sk.properties.name || `${name}-${index}`,
},
};
}),
properties: {
id,
isCollection: true,
userAttributes,
sketchClassId,
createdAt,
updatedAt,
name,
},
bbox: bbox(featureCollection(sketches)),
};
};
/**
* Returns a Sketch with given geometry and Geometry type, Properties are reasonable random
*/
export const genSampleSketch = (geometry, name) => ({
type: "Feature",
properties: {
id: name || uuid(),
isCollection: false,
userAttributes: genSampleUserAttributes(),
sketchClassId: uuid(),
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
name: name || "genSampleSketch",
},
geometry,
bbox: bbox(geometry),
});
/**
* Returns a Sketch with given geometry and Geometry type, Properties are reasonable random
*/
export const genSampleNullSketch = (name) => ({
type: "Feature",
properties: {
id: name || uuid(),
isCollection: false,
userAttributes: genSampleUserAttributes(),
sketchClassId: uuid(),
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
name: name || "genSampleNullSketch",
},
});
/**
* Given feature collection, return a sketch collection with reasonable random props.
* The geometry type of the returned collection will match the one passed in
* @param geometry
*/
export const genSampleSketchCollection = (fc, name) => {
// Convert features to sketches
const sketches = fc.features.map((f) => genSampleSketch(f.geometry));
// Rebuild into sketch collection
return {
...fc,
features: sketches,
properties: {
id: name || uuid(),
isCollection: true,
userAttributes: genSampleUserAttributes(),
sketchClassId: uuid(),
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
name: name || `genSampleSketchCollection_${uuid()}`,
},
bbox: bbox(fc),
};
};
/**
* Given feature collection, return a sketch collection with reasonable random props.
* The geometry type of the returned collection will match the one passed in
* @param geometry
*/
export const genSampleSketchCollectionFromSketches = (sketches, name) => {
// Rebuild into sketch collection
return {
type: "FeatureCollection",
features: sketches,
properties: {
id: name || uuid(),
isCollection: true,
userAttributes: genSampleUserAttributes(),
sketchClassId: uuid(),
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
name: name || `genSampleSketchCollection_${uuid()}`,
},
bbox: bbox(featureCollection(sketches)),
};
};
/**
* Given feature collection, return a sketch collection with reasonable random props.
* The geometry type of the returned collection will match the one passed in
* @param geometry
*/
export const genSampleNullSketchCollection = (sketches, name) => {
// Rebuild into sketch collection
return {
type: "FeatureCollection",
features: sketches,
properties: {
id: name || uuid(),
isCollection: true,
userAttributes: genSampleUserAttributes(),
sketchClassId: name || uuid(),
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
name: name || `genSampleSketchCollection_${uuid()}`,
},
};
};
export const genSampleSketchContext = () => ({
sketchProperties: {
name: "My Sketch",
id: "abc123",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
sketchClassId: "efg345",
isCollection: false,
userAttributes: [
{
exportId: "DESIGNATION",
fieldType: "ChoiceField",
label: "Designation",
value: "Marine Reserve",
},
{
exportId: "COMMENTS",
fieldType: "TextArea",
label: "Comments",
value: "This is my MPA and it is going to be the greatest. Amazing.",
},
{
label: "Include this?",
value: false,
exportId: "include_false",
fieldType: "YesNo",
},
],
},
geometryUri: "",
projectUrl: "https://example.com/project",
exampleOutputs: [
{
functionName: "ranked",
sketchName: "My Sketch",
results: {},
},
],
visibleLayers: [],
language: "en",
});
/**
* Given sketch or sketch collection, returns just the individual sketch features inside.
* @param sketch
*/
export function getSketchFeatures(sketch) {
if (isSketch(sketch) || isNullSketch(sketch)) {
return [sketch];
}
else if (isSketchCollection(sketch)) {
return sketch.features.filter((feat) => !feat.properties.isCollection);
}
else if (isNullSketchCollection(sketch)) {
return sketch.features.filter((feat) => !feat.properties.isCollection);
}
else {
throw new Error("Not a valid sketch");
}
}
/**
* Converts Feature to Sketch with reasonable defaults given for sketch properties if not provided
*/
export const featureToSketch = (feat, name = "sketches", sketchProperties = {}) => {
const sk = genSketch({
feature: feat,
name,
...feat.properties,
...sketchProperties,
id: uuid(),
});
sk.properties.userAttributes = [];
return sk;
};
/**
* Converts FeatureCollection to SketchCollection with reasonable defaults given for sketch properties if not provided
*/
export const featureToSketchCollection = (fc, name = "sketches", sketchProperties = {}) => {
const sketchFeatures = fc.features.map((feat, idx) => {
const idValue = feat.properties?.id || idx + 1;
const featureName = (() => {
if (name) {
if (feat.properties && feat.properties[name]) {
return feat.properties[name];
}
else {
return `${name}-${idValue}`;
}
}
else {
return `Area-${idValue}`;
}
})();
const sk = genSketch({
feature: feat,
name: featureName,
...feat.properties,
...sketchProperties,
id: `${idValue}`,
});
sk.properties.userAttributes = [];
return sk;
});
const sc = genSketchCollection(sketchFeatures, {
name,
});
sc.properties.userAttributes = [];
return sc;
};
//# sourceMappingURL=sketch.js.map