UNPKG

@seasketch/geoprocessing

Version:

Geoprocessing and reporting framework for SeaSketch 2.0

486 lines • 16.3 kB
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