diff --git a/server/models/views/lib/find-min-threshold.js b/server/models/views/lib/find-min-threshold.js index cf0909c36..360990474 100644 --- a/server/models/views/lib/find-min-threshold.js +++ b/server/models/views/lib/find-min-threshold.js @@ -31,7 +31,7 @@ function filterImtdThresholds (imtdThresholds) { return { alert: minObjectA ? minObjectA.value : null, - warning: minObjectW ? minObjectW.value : null + warning: minObjectW || null } } diff --git a/server/models/views/lib/process-imtd-thresholds.js b/server/models/views/lib/process-imtd-thresholds.js index 0d84f7e8f..549b6c17f 100644 --- a/server/models/views/lib/process-imtd-thresholds.js +++ b/server/models/views/lib/process-imtd-thresholds.js @@ -17,17 +17,26 @@ function processThreshold (threshold, stationStageDatum, stationSubtract, postPr function processImtdThresholds (imtdThresholds, stationStageDatum, stationSubtract, postProcess) { const thresholds = [] - const imtdThresholdWarning = processThreshold(imtdThresholds?.warning, stationStageDatum, stationSubtract, postProcess) + const imtdThresholdWarning = processThreshold(imtdThresholds?.warning?.value, stationStageDatum, stationSubtract, postProcess) + // Correct threshold value if value > zero (Above Ordnance Datum) [FSR-595] if (imtdThresholdWarning) { - // Correct threshold value if value > zero (Above Ordnance Datum) [FSR-595] - thresholds.push({ - id: 'warningThreshold', - description: 'Property flooding is possible above this level. One or more flood warnings may be issued', - shortname: 'Possible flood warnings', - value: imtdThresholdWarning - }) + if (imtdThresholds.warning.severity_value) { + const warningType = imtdThresholds.warning.severity_value === 3 ? 'Severe Flood Warning' : 'Flood Warning' + thresholds.push({ + id: 'warningThreshold', + description: `${warningType} issued: ${imtdThresholds.warning.ta_name}`, + shortname: 'Possible flood warnings', + value: imtdThresholdWarning + }) + } else { + thresholds.push({ + id: 'warningThreshold', + description: 'Property flooding is possible above this level. One or more flood warnings may be issued', + shortname: 'Possible flood warnings', + value: imtdThresholdWarning + }) + } } - const imtdThresholdAlert = processThreshold(imtdThresholds?.alert, stationStageDatum, stationSubtract, postProcess) if (imtdThresholdAlert) { thresholds.push({ diff --git a/server/models/views/station.js b/server/models/views/station.js index 904cd05da..0c1886542 100644 --- a/server/models/views/station.js +++ b/server/models/views/station.js @@ -170,7 +170,7 @@ class ViewModel { oneHourAgo.setHours(oneHourAgo.getHours() - 1) - // check if recent value is over one hour old0 + // check if recent value is over one hour old this.dataOverHourOld = new Date(this.recentValue.ts) < oneHourAgo this.recentValue.dateWhen = 'on ' + moment.tz(this.recentValue.ts, tz).format('D/MM/YY') @@ -248,7 +248,7 @@ class ViewModel { id: 'highest', value: this.station.porMaxValue, description: this.station.thresholdPorMaxDate - ? 'Water reaches the highest level recorded at this measuring station (recorded on ' + this.station.thresholdPorMaxDate + ')' + ? `Water reaches the highest level recorded at this measuring station (${this.station.thresholdPorMaxDate})` : 'Water reaches the highest level recorded at this measuring station', shortname: 'Highest level on record' }) @@ -268,7 +268,7 @@ class ViewModel { thresholds.push(...processedImtdThresholds) if (this.station.percentile5) { - // Only push typical range if it has a percentil5 + // Only push typical range if it has a percentile5 thresholds.push({ id: 'pc5', value: this.station.percentile5, diff --git a/server/routes/station.js b/server/routes/station.js index 907d37d64..23664da2e 100644 --- a/server/routes/station.js +++ b/server/routes/station.js @@ -10,6 +10,7 @@ module.exports = { handler: async (request, h) => { const { id } = request.params let { direction } = request.params + // const thresholdId = request.query.tid? // Convert human readable url to service parameter direction = direction === 'downstream' ? 'd' : 'u' diff --git a/server/src/js/components/chart.js b/server/src/js/components/chart.js index 227a6ee4e..914f90f42 100644 --- a/server/src/js/components/chart.js +++ b/server/src/js/components/chart.js @@ -2,13 +2,13 @@ // Chart component import '../utils' +import { area as d3Area, line as d3Line, curveMonotoneX } from 'd3-shape' import { axisBottom, axisLeft } from 'd3-axis' import { scaleLinear, scaleBand, scaleTime } from 'd3-scale' import { timeFormat } from 'd3-time-format' +import { timeHour, timeMinute } from 'd3-time' import { select, selectAll, pointer } from 'd3-selection' import { max, bisector, extent } from 'd3-array' -import { timeHour, timeMinute } from 'd3-time' -import { area as d3Area, line as d3Line, curveMonotoneX } from 'd3-shape' const { forEach, simplify } = window.flood.utils @@ -776,6 +776,9 @@ function LineChart (containerId, stationId, data, options = {}) { .attr('class', 'threshold__line') .attr('aria-hidden', true) .attr('x2', xScale(xExtent[1])).attr('y2', 0) + + // Label + const copy = `${threshold.level}m ${threshold.name}`.match(/[\s\S]{1,35}(?!\S)/g, '$&\n') const label = thresholdContainer.append('g') .attr('class', 'threshold-label') const path = label.append('path') @@ -784,10 +787,13 @@ function LineChart (containerId, stationId, data, options = {}) { const text = label.append('text') .attr('class', 'threshold-label__text') text.append('tspan').attr('font-size', 0).text('Threshold: ') - text.append('tspan').attr('x', 10).attr('y', 22).text(`${threshold.level}m ${threshold.name}`) + copy.map((l, i) => text.append('tspan').attr('x', 10).attr('y', (i + 1) * 22).text(l.trim())) const textWidth = Math.round(text.node().getBBox().width) + const textHeight = Math.round(text.node().getBBox().height) path.attr('d', `m-0.5,-0.5 l${textWidth + 20},0 l0,36 l-${((textWidth + 20) / 2) - 7.5},0 l-7.5,7.5 l-7.5,-7.5 l-${((textWidth + 20) / 2) - 7.5},0 l0,-36 l0,0`) - label.attr('transform', `translate(${Math.round(width / 2 - ((textWidth + 20) / 2))}, -46)`) + label.attr('transform', `translate(${Math.round(width / 2 - ((textWidth + 20) / 2))}, -${29 + textHeight})`) + + // Remove button const remove = thresholdContainer.append('a') .attr('role', 'button') .attr('class', 'threshold__remove') @@ -800,6 +806,7 @@ function LineChart (containerId, stationId, data, options = {}) { remove.append('circle').attr('class', 'threshold__remove-button').attr('r', 11) remove.append('line').attr('x1', -3).attr('y1', -3).attr('x2', 3).attr('y2', 3) remove.append('line').attr('y1', -3).attr('x2', -3).attr('x1', 3).attr('y2', 3) + // Set individual elements size and position thresholdContainer.attr('transform', 'translate(0,' + Math.round(yScale(threshold.level)) + ')') }) @@ -931,7 +938,7 @@ function LineChart (containerId, stationId, data, options = {}) { const showTooltip = (tooltipY = 10) => { if (!dataPoint) return // Hide threshold label - thresholdsContainer.select('.threshold--selected .threshold-label').style('visibility', 'hidden') + // thresholdsContainer.select('.threshold--selected .threshold-label').style('visibility', 'hidden') // Set tooltip text const value = dataCache.type === 'river' && (Math.round(dataPoint.value * 100) / 100) <= 0 ? '0' : dataPoint.value.toFixed(2) // *DBL below zero addition tooltipValue.text(`${value}m`) // *DBL below zero addition @@ -1109,7 +1116,8 @@ function LineChart (containerId, stationId, data, options = {}) { // const defaults = { - btnAddThresholdClass: 'defra-button-text-s' + btnAddThresholdClass: 'defra-button-text-s', + btnAddThresholdText: 'Show on chart (Visual only)' } options = Object.assign({}, defaults, options) @@ -1170,16 +1178,30 @@ function LineChart (containerId, stationId, data, options = {}) { const tooltipDescription = tooltipText.append('tspan').attr('class', 'tooltip-text').attr('x', 12).attr('dy', '1.4em') // Add optional 'Add threshold' buttons - document.querySelectorAll('[data-threshold-add]').forEach(container => { - const button = document.createElement('button') - button.className = options.btnAddThresholdClass - button.innerHTML = `Show ${container.getAttribute('data-level')}m threshold on chart (Visual only)` - button.setAttribute('aria-controls', `${containerId}-visualisation`) - button.setAttribute('data-id', container.getAttribute('data-id')) - button.setAttribute('data-threshold-add', '') - button.setAttribute('data-level', container.getAttribute('data-level')) - button.setAttribute('data-name', container.getAttribute('data-name')) - container.parentElement.replaceChild(button, container) + document.querySelectorAll('[data-threshold-add]').forEach((container, i) => { + const tooltip = document.createElement('div') + tooltip.className = 'defra-tooltip defra-tooltip--left' + tooltip.setAttribute('data-tooltip', '') + tooltip.innerHTML = ` + +
The River Pinn level at Eastcote Road was 0.35 metres. Property flooding is possible when it goes above 1.40 metres.') Code.expect(response.payload).to.contain('
The River Pinn level at Avenue Road was 0.18 metres. Property flooding is possible when it goes above 1.46 metres.') - Code.expect(response.payload).to.contain(' Monitor the River Pinn level at Avenue Road') + Code.expect(response.payload).to.contain('Monitor the River Pinn level at Avenue Road') Code.expect(response.payload).to.contain('
The River Pinn level at Moss Close was 0.13 metres. Property flooding is possible when it goes above 1.15 metres.') - Code.expect(response.payload).to.contain('Monitor the River Pinn level at Moss Close') + Code.expect(response.payload).to.contain('Monitor the River Pinn level at Moss Close') }) lab.test('Check flood severity banner link for Flood warning', async () => { const floodService = require('../../server/services/flood')