From 9cce4cff385f3f25974fc18c049966729d139aa4 Mon Sep 17 00:00:00 2001 From: Fritz Hoeing Date: Wed, 7 Feb 2024 18:27:59 +0100 Subject: [PATCH 1/5] feat: init contour-lines worker --- .../Dockerfile | 0 .../README.md | 0 .../add-to-table.js | 23 +++------ .../child-logger.js | 0 .../connect.js | 0 src/create-contour/contour.js | 51 +++++++++++++++++++ .../get-client.js | 0 .../index.js | 46 +++++++---------- .../package-lock.json | 0 .../package.json | 0 10 files changed, 76 insertions(+), 44 deletions(-) rename src/{create-polygons => create-contour}/Dockerfile (100%) rename src/{create-polygons => create-contour}/README.md (100%) rename src/{create-polygons => create-contour}/add-to-table.js (50%) rename src/{create-polygons => create-contour}/child-logger.js (100%) rename src/{create-polygons => create-contour}/connect.js (100%) create mode 100644 src/create-contour/contour.js rename src/{create-polygons => create-contour}/get-client.js (100%) rename src/{create-polygons => create-contour}/index.js (76%) rename src/{create-polygons => create-contour}/package-lock.json (100%) rename src/{create-polygons => create-contour}/package.json (100%) diff --git a/src/create-polygons/Dockerfile b/src/create-contour/Dockerfile similarity index 100% rename from src/create-polygons/Dockerfile rename to src/create-contour/Dockerfile diff --git a/src/create-polygons/README.md b/src/create-contour/README.md similarity index 100% rename from src/create-polygons/README.md rename to src/create-contour/README.md diff --git a/src/create-polygons/add-to-table.js b/src/create-contour/add-to-table.js similarity index 50% rename from src/create-polygons/add-to-table.js rename to src/create-contour/add-to-table.js index 3ac9e8c..fc845b1 100644 --- a/src/create-polygons/add-to-table.js +++ b/src/create-contour/add-to-table.js @@ -4,36 +4,29 @@ import logger from './child-logger.js'; // Add data to table export const addData = async ( datasetTimestamp, - polygon, + contourLine, region ) => { // Check if polygon object contains all necessary properties - if (!('geom' in polygon)) { + if (!('geometry' in contourLine)) { // timestamp of dataset not valid logger.error('Object is missing geometry'); throw 'Object is missing geometry.'; } - if (!('temp' in polygon)) { + if (!('properties' in contourLine)) { // timestamp of dataset not valid - logger.error('Object is missing temperature value'); - throw 'Object is missing temperature value.'; - } - - if (!('band' in polygon)) { - // timestamp of dataset not valid - logger.error('Object is missing band'); - throw 'Object is missing band.'; + logger.error('Object is missing properties'); + throw 'Object is missing properties.'; } // Add to table const client = await getClient(); // TODO process.argv can potentially be removed const timestamp = process.argv[2] ?? datasetTimestamp; - const geom = process.argv[2] ?? polygon.geom; - const temp = process.argv[2] ?? polygon.temp; - const band = process.argv[2] ?? polygon.band; - let insertRow = await client.query(`INSERT INTO ${region}_polygons(timestamp, ST_GeomFromText(geom), temp, band) VALUES($1);`, [timestamp, geom, temp, band]); + const geom = process.argv[2] ?? contourLine.geometry; + const temp = process.argv[2] ?? contourLine.properties.TEMP; + let insertRow = await client.query(`INSERT INTO ${region}_contourLines(timestamp, geom, temp) VALUES($1);`, [timestamp, geom, temp]); logger.info(`Inserted ${insertRow.rowCount} row`); await client.end(); }; diff --git a/src/create-polygons/child-logger.js b/src/create-contour/child-logger.js similarity index 100% rename from src/create-polygons/child-logger.js rename to src/create-contour/child-logger.js diff --git a/src/create-polygons/connect.js b/src/create-contour/connect.js similarity index 100% rename from src/create-polygons/connect.js rename to src/create-contour/connect.js diff --git a/src/create-contour/contour.js b/src/create-contour/contour.js new file mode 100644 index 0000000..90ae613 --- /dev/null +++ b/src/create-contour/contour.js @@ -0,0 +1,51 @@ +import { exec } from 'child_process'; +import fs from 'fs'; + +/** + * Executes a shell command and return it as a Promise. + * Kudos to https://ali-dev.medium.com/how-to-use-promise-with-exec-in-node-js-a39c4d7bbf77 + * + * @param cmd {String} The command to execute + * @return {Promise} A Promise returning the console output + */ +const execShellCommand = (cmd) => { + return new Promise((resolve, reject) => { + exec(cmd, (error, stdout, stderr) => { + if (error) { + console.warn(error); + reject(error); + } + resolve(stdout ? stdout : stderr); + }); + }); +} + +// todo fix naming for the comments + +/** + * Reclassify a GeoTIFF according to custom levels. + * + * Relies on GRASS + * + * @param {String} inputPath The path of the GeoTIFF to convert + * @param {String} fileName The path where the created COG shall be stored + * @param {String} fileName The levels according to which the GeoTIFF shall be reclassified + * + * @returns {Promise} A Promise that resolves to the console output of the underlying GDAL process + * + * @throws If provided paths are invalid or conversion fails, an error is thrown + */ +const createContourLines = async (inputPath, fileName, interval) => { + // valdate inputPath + if (! await fs.existsSync(inputPath)) { + throw `Input file does not exist: ${inputPath}/`; + } + + // build command for sub-process + // -q: prevent non-error output + const contourCmd = `gdal_contour -b 2 -a TEMP -i ${interval} -f "GeoJSON" /${inputPath}/${fileName} /tmp/output.geojson`; + + return await execShellCommand(contourCmd); +} + +export default createContourLines; diff --git a/src/create-polygons/get-client.js b/src/create-contour/get-client.js similarity index 100% rename from src/create-polygons/get-client.js rename to src/create-contour/get-client.js diff --git a/src/create-polygons/index.js b/src/create-contour/index.js similarity index 76% rename from src/create-polygons/index.js rename to src/create-contour/index.js index 8488650..fdc78e8 100644 --- a/src/create-polygons/index.js +++ b/src/create-contour/index.js @@ -7,6 +7,7 @@ import fetch from 'node-fetch'; import dayjs from 'dayjs'; import customParseFormat from 'dayjs/plugin/customParseFormat.js'; import utc from 'dayjs/plugin/utc.js'; +import createContourLines from './contour.js'; dayjs.extend(customParseFormat); dayjs.extend(utc); @@ -16,34 +17,21 @@ const rabbitHost = process.env.RABBITHOST; const rabbitUser = process.env.RABBITUSER; const rabbitPass = process.env.RABBITPASS; -const fetchPolygons = async (fileUrlOnWebspace) => { - const body = { - inputs: { - cogUrl: fileUrlOnWebspace, - interval: 1, - bands: [1, 2, 3] - } - }; - const url = 'https://klips-dev.terrestris.de/processes/contour-polygons/execution'; - const response = await fetch(url, { - body: JSON.stringify(body), - method: 'POST', - headers: { - 'Content-Type': 'application/json' - } - }); - - - if (!response.ok) { - throw new Error(`HTTP error status: ${response.status}`); - } - - return await response.json(); -}; const polygonsWorker = async (workerJob, inputs) => { + // todo put these information into converter.ts + const inputPath = inputs[0]; + const fileName = inputs[1]; + const interval = inputs[2]; + + await createContourLines(inputPath, fileName, interval); + // array aus multipolygonen (geoJSON?) - const polygons = await fetchPolygons(inputs[0]); + // todo check if it needs a relative path + const contourLines = fetch("/tmp/output.geojson") + .then((response) => response.json()) + .then(data => { return (data) }) + // get region and timestamp from input (example format: langenfeld_20230629T0500Z.tif) const regex = /^([^_]+)_(\d{8}T\d{4}Z)/; @@ -61,7 +49,7 @@ const polygonsWorker = async (workerJob, inputs) => { // get timestamp for current hour //const currentTimestamp = dayjs.utc().startOf('hour'); - if (datasetTimestamp ) { + if (datasetTimestamp) { // timestamp of dataset not valid logger.info('Could not parse dataset timestamp.'); throw 'Could not parse dataset timestamp.'; @@ -72,7 +60,7 @@ const polygonsWorker = async (workerJob, inputs) => { (async () => { const client = await getClient(); let createTableQuery = ` - CREATE TABLE IF NOT EXISTS ${region}_polygons( + CREATE TABLE IF NOT EXISTS ${region}_contourLines( id BIGSERIAL PRIMARY KEY NOT NULL , timestamp timestamp without timezone, geom geometry, @@ -88,9 +76,9 @@ const polygonsWorker = async (workerJob, inputs) => { // Add rows to table - polygons.forEach(polygon => addData( + contourLines.features.forEach(contourLine => addData( datasetTimestamp, - polygon, + contourLine, region )); diff --git a/src/create-polygons/package-lock.json b/src/create-contour/package-lock.json similarity index 100% rename from src/create-polygons/package-lock.json rename to src/create-contour/package-lock.json diff --git a/src/create-polygons/package.json b/src/create-contour/package.json similarity index 100% rename from src/create-polygons/package.json rename to src/create-contour/package.json From b5f82d4bdf2abc7fc01e3b9dd769b3e8e5db99af Mon Sep 17 00:00:00 2001 From: Fritz Hoeing Date: Wed, 7 Feb 2024 18:33:09 +0100 Subject: [PATCH 2/5] fix: renaming to contourLines --- README.md | 4 ++-- docker-compose.yml | 6 +++--- src/create-contour/Dockerfile | 10 +++++----- src/create-contour/child-logger.js | 2 +- src/create-contour/index.js | 6 +++--- src/packagesToBuild.json | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index e53a986..47f12b6 100644 --- a/README.md +++ b/README.md @@ -150,8 +150,8 @@ The Docker images are built automatically by a GitHub Action. For local developm ```shell # make sure you are in the root of the project docker image build \ - --file src/create-polygons/Dockerfile \ - --tag create-polygons:my-custom-tag \ + --file src/create-contour/Dockerfile \ + --tag create-contour:my-custom-tag \ . ``` diff --git a/docker-compose.yml b/docker-compose.yml index 6f637b7..4d0003d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -97,8 +97,8 @@ services: context: ./ dockerfile: ./src/create-file/Dockerfile - create-polygons: - image: ghcr.io/klips-project/mqm-worker/create-polygons:latest + create-contour: + image: ghcr.io/klips-project/mqm-worker/create-contour:latest build: context: ./ - dockerfile: ./src/create-polygons/Dockerfile \ No newline at end of file + dockerfile: ./src/create-contour/Dockerfile \ No newline at end of file diff --git a/src/create-contour/Dockerfile b/src/create-contour/Dockerfile index c08cb89..92e0faa 100644 --- a/src/create-contour/Dockerfile +++ b/src/create-contour/Dockerfile @@ -2,17 +2,17 @@ FROM node:16-alpine RUN apk add --update nodejs npm -COPY src/create-polygons/package.json src/create-polygons/package-lock.json /home/ +COPY src/create-contour/package.json src/create-contour/package-lock.json /home/ COPY util/start-worker.sh util/wait-for.sh /home/ RUN chmod +x /home/start-worker.sh /home/wait-for.sh WORKDIR /home/ RUN npm install -COPY src/create-polygons/index.js worker/ -COPY src/create-polygons/get-client.js worker/ -COPY src/create-polygons/add-to-table.js worker/ +COPY src/create-contour/index.js worker/ +COPY src/create-contour/get-client.js worker/ +COPY src/create-contour/add-to-table.js worker/ COPY src/workerTemplate.js . -COPY src/create-polygons/child-logger.js worker/ +COPY src/create-contour/child-logger.js worker/ COPY src/logger.js . HEALTHCHECK CMD pgrep node || exit 1 diff --git a/src/create-contour/child-logger.js b/src/create-contour/child-logger.js index 5b6d16c..6efd7f6 100644 --- a/src/create-contour/child-logger.js +++ b/src/create-contour/child-logger.js @@ -1,3 +1,3 @@ import { logger } from '../logger.js'; -export default logger.child({ type: 'create-polygons' }); +export default logger.child({ type: 'create-contour' }); diff --git a/src/create-contour/index.js b/src/create-contour/index.js index fdc78e8..d580009 100644 --- a/src/create-contour/index.js +++ b/src/create-contour/index.js @@ -18,7 +18,7 @@ const rabbitUser = process.env.RABBITUSER; const rabbitPass = process.env.RABBITPASS; -const polygonsWorker = async (workerJob, inputs) => { +const contourLinesWorker = async (workerJob, inputs) => { // todo put these information into converter.ts const inputPath = inputs[0]; const fileName = inputs[1]; @@ -90,9 +90,9 @@ const polygonsWorker = async (workerJob, inputs) => { (async () => { try { // Initialize and start the worker process - await initialize(rabbitHost, rabbitUser, rabbitPass, workerQueue, resultQueue, polygonsWorker); + await initialize(rabbitHost, rabbitUser, rabbitPass, workerQueue, resultQueue, contourLinesWorker); } catch (error) { logger.error({ error: error }, `Problem when initializing`); } })(); -export default polygonsWorker; +export default contourLinesWorker; diff --git a/src/packagesToBuild.json b/src/packagesToBuild.json index e6af711..4e3b398 100644 --- a/src/packagesToBuild.json +++ b/src/packagesToBuild.json @@ -1 +1 @@ -["download-file","gunzip-file","geoserver-publish-sld","geoserver-create-and-apply-sld","geoserver-publish-layer-from-db","geonetwork-publish-metadata","send-email","dispatcher","zip-handler","upload-file", "geoserver-publish-geotiff", "geotiff-validator", "send-mattermost-message", "geoserver-publish-imagemosaic", "geoserver-create-imagemosaic-datastore", "create-file", "geotiff-optimizer", "dataset-rotation", "dataset-archive", "create-polygons"] +["download-file","gunzip-file","geoserver-publish-sld","geoserver-create-and-apply-sld","geoserver-publish-layer-from-db","geonetwork-publish-metadata","send-email","dispatcher","zip-handler","upload-file", "geoserver-publish-geotiff", "geotiff-validator", "send-mattermost-message", "geoserver-publish-imagemosaic", "geoserver-create-imagemosaic-datastore", "create-file", "geotiff-optimizer", "dataset-rotation", "dataset-archive", "create-contour"] From 64cf0228b745922d23d659c4a722a8cc1c871f91 Mon Sep 17 00:00:00 2001 From: Fritz Hoeing Date: Thu, 8 Feb 2024 10:42:39 +0100 Subject: [PATCH 3/5] fix: fixing comments --- src/create-contour/README.md | 11 ++++++----- src/create-contour/add-to-table.js | 2 +- src/create-contour/contour.js | 7 ++----- src/create-contour/index.js | 3 +-- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/create-contour/README.md b/src/create-contour/README.md index 97a8cd9..f4c64aa 100644 --- a/src/create-contour/README.md +++ b/src/create-contour/README.md @@ -4,10 +4,10 @@ Manages archiving of incoming data. ### Mechanism -- Gets array of polygons using the contour_polygons-process +- Gets array of lines using the contour_polygons-process - connects to postgres-database - creates new table in postgres-database (if it does not exist) -- loops through polygons and adds them to table +- loops through lines and adds them to table - ToDo: Delete older data - ToDo: Add worker to publish on geoserver in WFS @@ -24,10 +24,11 @@ Manages archiving of incoming data. "job": [ { "id": 1, - "type": "create-polygons", + "type": "create-contour", "inputs": [ "http://nginx/cog/dresden/dresden_temperature/dresden_20240202T0300Z.tif", - "dresden_20240202T0300Z.tif" + "dresden_20240202T0300Z.tif", + "2" ] } ] @@ -58,5 +59,5 @@ Manages archiving of incoming data. ### Send example job ```bash -cat workflows/create-polygon-test.json | rabbitmqadmin -u rabbit -p rabbit publish exchange=amq.default routing_key=dispatcher +cat workflows/create-contour.json | rabbitmqadmin -u rabbit -p rabbit publish exchange=amq.default routing_key=dispatcher ``` \ No newline at end of file diff --git a/src/create-contour/add-to-table.js b/src/create-contour/add-to-table.js index fc845b1..acc30fc 100644 --- a/src/create-contour/add-to-table.js +++ b/src/create-contour/add-to-table.js @@ -7,7 +7,7 @@ export const addData = async ( contourLine, region ) => { - // Check if polygon object contains all necessary properties + // Check if line-vector contains all necessary properties if (!('geometry' in contourLine)) { // timestamp of dataset not valid logger.error('Object is missing geometry'); diff --git a/src/create-contour/contour.js b/src/create-contour/contour.js index 90ae613..9352609 100644 --- a/src/create-contour/contour.js +++ b/src/create-contour/contour.js @@ -20,16 +20,14 @@ const execShellCommand = (cmd) => { }); } -// todo fix naming for the comments - /** * Reclassify a GeoTIFF according to custom levels. * * Relies on GRASS * * @param {String} inputPath The path of the GeoTIFF to convert - * @param {String} fileName The path where the created COG shall be stored - * @param {String} fileName The levels according to which the GeoTIFF shall be reclassified + * @param {String} fileName The name of the GeoTIFF + * @param {String} interval Elevation interval between contours * * @returns {Promise} A Promise that resolves to the console output of the underlying GDAL process * @@ -42,7 +40,6 @@ const createContourLines = async (inputPath, fileName, interval) => { } // build command for sub-process - // -q: prevent non-error output const contourCmd = `gdal_contour -b 2 -a TEMP -i ${interval} -f "GeoJSON" /${inputPath}/${fileName} /tmp/output.geojson`; return await execShellCommand(contourCmd); diff --git a/src/create-contour/index.js b/src/create-contour/index.js index d580009..a0dabde 100644 --- a/src/create-contour/index.js +++ b/src/create-contour/index.js @@ -26,7 +26,7 @@ const contourLinesWorker = async (workerJob, inputs) => { await createContourLines(inputPath, fileName, interval); - // array aus multipolygonen (geoJSON?) + // array aus multiLines als geoJSON // todo check if it needs a relative path const contourLines = fetch("/tmp/output.geojson") .then((response) => response.json()) @@ -65,7 +65,6 @@ const contourLinesWorker = async (workerJob, inputs) => { timestamp timestamp without timezone, geom geometry, temp number, - band int, ); `; const res = await client.query(createTableQuery); From d8a13adf9e55896ccda48c09ff176635c01eb3f4 Mon Sep 17 00:00:00 2001 From: Fritz Hoeing Date: Thu, 8 Feb 2024 10:51:08 +0100 Subject: [PATCH 4/5] fix: adds missing detail in readme --- src/create-contour/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/create-contour/README.md b/src/create-contour/README.md index f4c64aa..beca930 100644 --- a/src/create-contour/README.md +++ b/src/create-contour/README.md @@ -16,6 +16,7 @@ Manages archiving of incoming data. 1. path to input file in cog webspace (fileUrlOnWebspace) 2. file name (fileNameWithSuffix) +3. Interval ## Example Worker Job JSON From ff0e593ed124484b8af2528cdf8a0e15569378aa Mon Sep 17 00:00:00 2001 From: sdobbert Date: Thu, 8 Feb 2024 15:06:26 +0100 Subject: [PATCH 5/5] adjust dockerfile and remove filepath --- src/create-contour/Dockerfile | 1 + src/create-contour/contour.js | 4 ++-- src/create-contour/index.js | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/create-contour/Dockerfile b/src/create-contour/Dockerfile index 92e0faa..53c2b99 100644 --- a/src/create-contour/Dockerfile +++ b/src/create-contour/Dockerfile @@ -11,6 +11,7 @@ RUN npm install COPY src/create-contour/index.js worker/ COPY src/create-contour/get-client.js worker/ COPY src/create-contour/add-to-table.js worker/ +COPY src/create-contour/contour.js worker/ COPY src/workerTemplate.js . COPY src/create-contour/child-logger.js worker/ COPY src/logger.js . diff --git a/src/create-contour/contour.js b/src/create-contour/contour.js index 9352609..fc6ed1f 100644 --- a/src/create-contour/contour.js +++ b/src/create-contour/contour.js @@ -33,14 +33,14 @@ const execShellCommand = (cmd) => { * * @throws If provided paths are invalid or conversion fails, an error is thrown */ -const createContourLines = async (inputPath, fileName, interval) => { +const createContourLines = async (inputPath, interval) => { // valdate inputPath if (! await fs.existsSync(inputPath)) { throw `Input file does not exist: ${inputPath}/`; } // build command for sub-process - const contourCmd = `gdal_contour -b 2 -a TEMP -i ${interval} -f "GeoJSON" /${inputPath}/${fileName} /tmp/output.geojson`; + const contourCmd = `gdal_contour -b 2 -a TEMP -i ${interval} -f "GeoJSON" /${inputPath} /tmp/output.geojson`; return await execShellCommand(contourCmd); } diff --git a/src/create-contour/index.js b/src/create-contour/index.js index a0dabde..66468d2 100644 --- a/src/create-contour/index.js +++ b/src/create-contour/index.js @@ -21,10 +21,9 @@ const rabbitPass = process.env.RABBITPASS; const contourLinesWorker = async (workerJob, inputs) => { // todo put these information into converter.ts const inputPath = inputs[0]; - const fileName = inputs[1]; const interval = inputs[2]; - await createContourLines(inputPath, fileName, interval); + await createContourLines(inputPath, interval); // array aus multiLines als geoJSON // todo check if it needs a relative path