diff --git a/workflows/raster/README.md b/workflows/raster/README.md index 504d9056..15c9c8d2 100644 --- a/workflows/raster/README.md +++ b/workflows/raster/README.md @@ -3,6 +3,7 @@ - [Standardising](#Standardising) - [copy](#copy) - [publish-odr](#Publish-odr) +- [National DEM](#national-dem) - [tests](#Tests) # Standardising @@ -280,6 +281,35 @@ graph TD; See the [copy template](#copy) for more information. +# national-dem + +This workflow combines a set of DEMs datasets in order to create a single national dataset composed of 50k tiles. + +Upon completion all standardised TIFF and STAC files will be located with the ./flat/ directory of the workflow in the artifacts scratch bucket. In addition, a Basemaps link is produced enabling visual QA. + +Publishing to the AWS Registry of Open Data is an optional step [publish-odr](#Publish-odr) that can be run automatically after standardisation. + +## Workflow Input Parameters + +| Parameter | Type | Default | Description | +| ------------------ | ---- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| config-file | str | https://raw.githubusercontent.com/linz/basemaps-config/master/config/tileset/elevation.json | Location of the configuration file listing the source datasets to merge. | +| compare | str | | Existing collection.json to compare with the new merging. | +| source_epsg | str | 2193 | The EPSG code of the source imagery | +| target_epsg | str | 2193 | The target EPSG code - if different to source the imagery will be reprojected | +| group | 4 | | How many output tiles to process in each standardising task "pod". Change if you have resource or performance issues when standardising a dataset. | +| collection_id | 4 | | Collection ID of the existing National DEM collection. | +| publish_to_odr | str | false | Run [publish-odr](#Publish-odr) after standardising has completed successfully | +| target_bucket_name | enum | | Used only if `publish_to_odr` is true. The bucket name of the target ODR location | +| copy_option | enum | --no-clobber | Used only if `publish_to_odr` is true.
`--no-clobber`
Skip overwriting existing files.
`--force`
Overwrite all files.
`--force-no-clobber`
Overwrite only changed files, skip unchanged files.
| + +### Example Input Parameters + +| Parameter | Value | +| ------------- | --------------------------------------------------------------------- | +| compare | s3://nz-elevation/new-zealand/new-zealand/dem_1m/2193/collection.json | +| collection_id | 01J6TK9HHNDHJEG8QRSF98WH11 | + # Tests ## How To Use the Test Workflow diff --git a/workflows/raster/national-dem.yaml b/workflows/raster/national-dem.yaml new file mode 100644 index 00000000..e21698c6 --- /dev/null +++ b/workflows/raster/national-dem.yaml @@ -0,0 +1,436 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/argoproj/argo-workflows/v3.5.5/api/jsonschema/schema.json + +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: national-dem + labels: + linz.govt.nz/category: raster + linz.govt.nz/data-type: raster +spec: + parallelism: 50 + nodeSelector: + karpenter.sh/capacity-type: 'spot' + entrypoint: main + onExit: exit-handler + workflowMetadata: + labels: + linz.govt.nz/region: 'new-zealand' + podMetadata: + labels: + linz.govt.nz/category: raster + linz.govt.nz/data-type: raster + linz.govt.nz/region: 'new-zealand' + arguments: + parameters: + - name: version_argo_tasks + description: 'Specify a version of the argo-tasks image to use, e.g. "v4.1" or "latest"' + value: 'v4' + - name: version_basemaps_cli + description: 'Specify a version of the basemaps-cli image to use, e.g. "v7.1" or "latest"' + value: 'v7' + - name: version_topo_imagery + description: 'Specify a version of the topo-imagery image to use, e.g. "v4.8" or "latest"' + value: 'v5' + - name: config-file + description: 'Location of the configuration file listing the source datasets to merge.' + value: 'https://raw.githubusercontent.com/linz/basemaps-config/master/config/tileset/elevation.json' + - name: compare + description: 'Existing collection.json to compare with the new merging.' + value: '' + - name: source_epsg + description: 'EPSG of the source files' + value: '2193' + - name: target_epsg + description: 'EPSG of the standardised output files' + value: '2193' + - name: group + description: 'How many output tiles to process in each standardising task "pod". Change if you have resource or performance issues when standardising a dataset.' + value: '4' + - name: collection_id + description: 'Collection ID of the existing National DEM Collection.' + value: '' + - name: publish_to_odr + description: 'Create a Pull Request for publishing to imagery or elevation ODR bucket' + value: 'false' + enum: + - 'false' + - 'true' + - name: copy_option + description: 'Do not overwrite existing files with "no-clobber", or "force" overwriting files in the target location' + value: '--no-clobber' + enum: + - '--no-clobber' + - '--force' + - '--force-no-clobber' + templateDefaults: + container: + imagePullPolicy: Always + image: '' + templates: + - name: main + retryStrategy: + expression: 'false' + inputs: + parameters: + - name: config-file + dag: + tasks: + - name: get-location + template: get-location + + - name: create-mapsheet + template: create-mapsheet + arguments: + parameters: + - name: config-file + value: '{{workflow.parameters.config-file}}' + - name: compare + # Workaround --compare with empty value will fail so we add the flag inside the input parameter + value: '{{= workflow.parameters.compare == "" ? "" : "--compare=" + workflow.parameters.compare}}' + - name: bucket + value: '{{tasks.get-location.outputs.parameters.bucket}}' + - name: key + value: '{{tasks.get-location.outputs.parameters.key}}' + depends: 'get-location.Succeeded' + + - name: group + templateRef: + name: tpl-at-group + template: main + arguments: + artifacts: + - name: input + from: '{{ tasks.create-mapsheet.outputs.artifacts.files }}' + parameters: + - name: size + value: '{{workflow.parameters.group}}' + - name: version + value: '{{= workflow.parameters.version_argo_tasks}}' + depends: 'create-mapsheet' + when: "{{= len(sprig.fromJson(tasks['create-mapsheet'].outputs.parameters.file_list)) > 0 }}" + + - name: collection-id-setup + template: collection-id-setup + depends: 'group.Succeeded' + + - name: standardise-validate + template: standardise-validate + arguments: + parameters: + - name: group_id + value: '{{item}}' + - name: collection-id + value: '{{tasks.collection-id-setup.outputs.parameters.collection-id}}' + - name: target + value: '{{tasks.get-location.outputs.parameters.location}}flat/' + artifacts: + - name: group_data + from: '{{ tasks.group.outputs.artifacts.output }}' + depends: 'collection-id-setup.Succeeded && get-location.Succeeded' + withParam: '{{ tasks.group.outputs.parameters.output }}' + + - name: create-collection + template: create-collection + arguments: + parameters: + - name: collection-id + value: '{{tasks.collection-id-setup.outputs.parameters.collection-id}}' + - name: location + value: '{{tasks.get-location.outputs.parameters.location}}' + depends: 'standardise-validate.Succeeded' + + - name: stac-validate + templateRef: + name: tpl-at-stac-validate + template: main + arguments: + parameters: + - name: uri + value: '{{tasks.get-location.outputs.parameters.location}}flat/collection.json' + artifacts: + - name: stac-result + raw: + data: '{{tasks.stac-validate.outputs.result}}' + depends: 'create-collection.Succeeded' + + - name: create-config + when: "'{{workflow.parameters.target_epsg}}' =~ '2193|3857'" + arguments: + parameters: + - name: location + value: '{{tasks.get-location.outputs.parameters.location}}' + - name: bucket + value: '{{tasks.get-location.outputs.parameters.bucket}}' + - name: key + value: '{{tasks.get-location.outputs.parameters.key}}' + template: create-config + depends: 'standardise-validate' + + - name: publish-odr + templateRef: + name: publish-odr + template: main + when: "'{{workflow.parameters.publish_to_odr}}' == 'true'" + arguments: + parameters: + - name: source + value: '{{tasks.get-location.outputs.parameters.location}}flat/' + - name: target_bucket_name + value: 'nz-elevation' + - name: copy_option + value: '{{workflow.parameters.copy_option}}' + - name: ticket + value: '{{=sprig.trim(workflow.parameters.ticket)}}' + - name: add_date_in_survey_path + value: 'false' + depends: 'stac-validate.Succeeded && create-config.Succeeded' + + outputs: + parameters: + - name: target + valueFrom: + parameter: '{{tasks.get-location.outputs.parameters.location}}' + default: '' + # END TEMPLATE `main` + + - name: collection-id-setup + script: + image: '019359803926.dkr.ecr.ap-southeast-2.amazonaws.com/topo-imagery:{{=sprig.trim(workflow.parameters.version_topo_imagery)}}' + args: [python] + source: | + import ulid + collection_id = "{{workflow.parameters.collection_id}}" + with open("/tmp/collection-id", "w") as f: + if not collection_id: + f.write(str(ulid.ULID())) + else: + f.write(collection_id) + outputs: + parameters: + - name: collection-id + valueFrom: + path: '/tmp/collection-id' + + - name: create-mapsheet + inputs: + parameters: + - name: config-file + - name: compare + - name: bucket + - name: key + container: + image: '019359803926.dkr.ecr.ap-southeast-2.amazonaws.com/argo-tasks:{{=sprig.trim(workflow.parameters.version_argo_tasks)}}' + resources: + requests: + cpu: 3000m + memory: 7.8Gi + command: [node, /app/index.js] + env: + - name: AWS_ROLE_CONFIG_PATH + value: s3://linz-bucket-config/config.json + args: + [ + 'mapsheet-coverage', + '--verbose', + '--location', + '{{inputs.parameters.config-file}}', + '--epsg-code', + '{{workflow.parameters.source_epsg}}', + '{{inputs.parameters.compare}}', + ] + outputs: + parameters: + - name: file_list + valueFrom: + path: /tmp/mapsheet-coverage/file-list.json + artifacts: + # List of tiff files that need to be processed + - name: files + path: /tmp/mapsheet-coverage/file-list.json + optional: true + archive: + none: {} + - name: layers_source + path: /tmp/mapsheet-coverage/layers-source.geojson.gz + optional: true + archive: + none: {} + - name: layers_combined + path: /tmp/mapsheet-coverage/layers-combined.geojson.gz + optional: true + archive: + none: {} + # Provenance information for the collection + - name: capture_dates + path: /tmp/mapsheet-coverage/capture-dates.geojson + s3: + bucket: '{{inputs.parameters.bucket}}' + key: '{{inputs.parameters.key}}flat/capture-dates.geojson' + archive: + none: {} + + - name: standardise-validate + nodeSelector: + karpenter.sh/capacity-type: 'spot' + inputs: + parameters: + - name: group_id + - name: collection-id + - name: target + artifacts: + - name: group_data + path: /tmp/input/ + container: + image: '019359803926.dkr.ecr.ap-southeast-2.amazonaws.com/topo-imagery:{{=sprig.trim(workflow.parameters.version_topo_imagery)}}' + resources: + requests: + memory: 7.8Gi + cpu: 15000m + ephemeral-storage: 3Gi + volumeMounts: + - name: ephemeral + mountPath: '/tmp' + args: + - python + - '/app/scripts/standardise_validate.py' + - '--from-file' + - '/tmp/input/{{inputs.parameters.group_id}}.json' + - '--target' + - '{{inputs.parameters.target}}' + - '--preset' + - 'dem_lerc' + - '--collection-id' + - '{{inputs.parameters.collection-id}}' + - '--create-footprints' + - 'true' + - '--source-epsg' + - '{{=sprig.trim(workflow.parameters.source_epsg)}}' + - '--target-epsg' + - '{{=sprig.trim(workflow.parameters.target_epsg)}}' + - '--gsd' + - '1' + + - name: create-collection + nodeSelector: + karpenter.sh/capacity-type: 'spot' + inputs: + parameters: + - name: collection-id + - name: location + outputs: + artifacts: + - name: capture-area + path: '/tmp/capture-area.geojson' + optional: true + archive: + none: {} + container: + image: '019359803926.dkr.ecr.ap-southeast-2.amazonaws.com/topo-imagery:{{=sprig.trim(workflow.parameters.version_topo_imagery)}}' + resources: + requests: + memory: 7.8Gi + cpu: 15000m + args: + - python + - '/app/scripts/collection_from_items.py' + - '--uri' + - '{{inputs.parameters.location}}flat/' + - '--collection-id' + - '{{inputs.parameters.collection-id}}' + - '--category' + - 'dem' + - '--region' + - 'new-zealand' + - '--gsd' + - '1' + - '--lifecycle' + - 'ongoing' + - '--producer' + - 'Toitū Te Whenua Land Information New Zealand' + - '--producer-list' + - '' + - '--licensor' + - 'Toitū Te Whenua Land Information New Zealand' + - '--licensor-list' + - '' + - '--concurrency' + - '25' + - '--capture-dates' + + - name: get-location + script: + image: '019359803926.dkr.ecr.ap-southeast-2.amazonaws.com/argo-tasks:{{=sprig.trim(workflow.parameters.version_argo_tasks)}}' + command: [node] + source: | + const fs = require('fs'); + const loc = JSON.parse(process.env['ARGO_TEMPLATE']).archiveLocation.s3; + const key = loc.key.replace('{{pod.name}}',''); + fs.writeFileSync('/tmp/location', `s3://${loc.bucket}/${key}`); + fs.writeFileSync('/tmp/bucket', `${loc.bucket}`); + fs.writeFileSync('/tmp/key', `${key}`); + outputs: + parameters: + - name: location + valueFrom: + path: '/tmp/location' + - name: bucket + valueFrom: + path: '/tmp/bucket' + - name: key + valueFrom: + path: '/tmp/key' + + - name: create-config + inputs: + parameters: + - name: location + description: 'Location of the imagery to create config for' + - name: bucket + - name: key + container: + image: 'ghcr.io/linz/basemaps/cli:{{=sprig.trim(workflow.parameters.version_basemaps_cli)}}' + command: [node, /app/node_modules/@basemaps/cogify/dist/index.cjs] + env: + - name: AWS_ROLE_CONFIG_PATH + value: s3://linz-bucket-config/config.basemaps.json + args: + - 'config' + - '{{ inputs.parameters.location }}flat/' + outputs: + parameters: + - name: url + description: 'Basemaps URL to view the imagery' + valueFrom: + path: '/tmp/cogify/config-url' + - name: config + description: 'Location of the config file' + valueFrom: + path: '/tmp/cogify/config-path' + artifacts: + - name: url + path: '/tmp/cogify/config-url' + s3: + bucket: '{{inputs.parameters.bucket}}' + key: '{{inputs.parameters.key}}flat/config-url' + archive: + none: {} + + - name: exit-handler + retryStrategy: + limit: '0' # `tpl-exit-handler` retries itself + steps: + - - name: exit + templateRef: + name: tpl-exit-handler + template: main + arguments: + parameters: + - name: workflow_status + value: '{{workflow.status}}' + - name: workflow_parameters + value: '{{workflow.parameters}}' + + volumes: + - name: ephemeral + emptyDir: {} diff --git a/workflows/raster/publish-odr.yaml b/workflows/raster/publish-odr.yaml index 9ab974b9..bacc0eee 100644 --- a/workflows/raster/publish-odr.yaml +++ b/workflows/raster/publish-odr.yaml @@ -93,6 +93,8 @@ spec: - name: source - name: target_bucket_name - name: ticket + - name: add_date_in_survey_path + default: 'true' dag: tasks: - name: generate-path @@ -107,6 +109,8 @@ spec: value: '{{inputs.parameters.target_bucket_name}}' - name: source value: '{{inputs.parameters.source}}' + - name: add_date_in_survey_path + value: '{{inputs.parameters.add_date_in_survey_path}}' - name: push-to-github templateRef: