Newer
Older
Bucknell, Mary S.
committed
import {bisector} from 'd3-array';
import {select} from 'd3-selection';
/**
* Determine the unicode variant of an HTML decimal entity
*
* @param someString
* @returns {string}
*/
let numericValue = parseInt(someString.slice(2, -1), 10);
if (numericValue) {
return String.fromCharCode(numericValue);
/**
* Determine if a string contains an HTML decimal entity
*
* @param someString
* @returns {array} or {null}
*/
let re = /&(?:[a-z]+|#\d+);/g;
return someString.match(re);
/**
* Replace html entities with unicode entities
*
* @param someString
* @returns {*}
*/
let entities = getHtmlFromString(someString);
if (entities) {
for (let entity of entities) {
let unicodeEntity = unicodeHtmlEntity(entity);
someString = someString.replace(entity, unicodeEntity);
}
/**
* Determine if two sets are equal
*
* @param set1
* @param set2
* @returns {boolean}
*/
let sizeEqual = set1.size === set2.size;
Naab, Daniel James
committed
let itemsEqual = Array.from(set1).every(x => {
const TEXT_WRAP_LINE_HEIGHT = 1.1; // ems
Naab, Daniel James
committed
//const TEXT_WRAP_BREAK_CHARS = ['/', '&', '-'];
const TEXT_WRAP_BREAK_CHARS = [];
/**
* Wrap long svg text labels into multiple lines.
* Based on: https://bl.ocks.org/ericsoco/647db6ebadd4f4756cae
* @param {Array of Strings} break_chars - these along with spaces are acceptable places to break on}
export const wrap = function(text, width, break_chars = TEXT_WRAP_BREAK_CHARS) {
const elem = select(this);
// To determine line breaks, add a space after each break character
let textContent = elem.text();
Naab, Daniel James
committed
break_chars.forEach(char => {
textContent = textContent.replace(char, char + ' ');
});
let x = elem.attr('x');
let y = elem.attr('y');
let dy = parseFloat(elem.attr('dy') || 0);
let tspan = elem
.text(null)
.append('tspan')
.attr('x', x)
.attr('y', y)
.attr('dy', dy + 'em');
// Iteratively add each word to the line until we exceed the maximum width.
let line = [];
let lineCount = 0;
const words = textContent.split(/\s+/);
for (const word of words) {
// Add this word to the line
line.push(word);
tspan.text(line.join(' '));
// If we exceeded the line width, remove the last word from the array
// and append this tspan to the DOM node.
let tspanNode = tspan.node();
if (tspanNode.getComputedTextLength() > width) {
// Remove the last word and put it on the next line.
let spanContent = line.join(' ');
line = [word];
// Remove the spaces trailing break characters that were added above
Naab, Daniel James
committed
break_chars.forEach(char => {
spanContent = spanContent.replace(char + ' ', char);
});
// Insert this text as a tspan
lineCount++;
tspan = elem
.append('tspan')
.attr('x', x)
.attr('y', y)
.attr('dy', lineCount * TEXT_WRAP_LINE_HEIGHT + dy + 'em')
.text(word);
};
Naab, Daniel James
committed
/**
* This will execute the equivalent media query as the USWDS given a minWidth.
* For example, this SASS:
* @media($medium-screen)
* is equivalent to:
Naab, Daniel James
committed
* mediaQuery(mediumScreenPx)
Naab, Daniel James
committed
* @param {Number} minWidth
* @return {Boolean} true if the media query is active at the given width
*/
Naab, Daniel James
committed
return window.matchMedia(`screen and (min-width: ${minWidth}px)`).matches;
Naab, Daniel James
committed
};
/**
* Returns a function that will be a no-op if a condition is false.
* Use to insert conditionals into declarative-style D3-chained method calls.
* @param {Boolean} condition If true, will run `func`
* @param {Function} func
*/
export const callIf = function(condition, func) {
return function(...args) {
if (condition) {
func(...args);
}
};
};
Bucknell, Mary S.
committed
/**
* Function to parse RDB to Objects
Bucknell, Mary S.
committed
* @param {String} rdbData - containing RDB data
* @returns {Array of Objects} - one for each data line in the RDB.
Bucknell, Mary S.
committed
*/
export const parseRDB = function(rdbData) {
const rdbLines = rdbData.split('\n');
let dataLines = rdbLines.filter(rdbLine => rdbLine[0] !== '#').filter(rdbLine => rdbLine.length > 0);
// remove the useless row
dataLines.splice(1, 1);
let recordData = [];
if (dataLines.length > 0) {
let headers = dataLines.shift().split('\t');
for (let dataLine of dataLines) {
let data = dataLine.split('\t');
let dataObject = {};
for (let i = 0; i < headers.length; i++) {
Bucknell, Mary S.
committed
dataObject[headers[i]] = data[i];
}
recordData.push(dataObject);
}
}
return recordData;
Bucknell, Mary S.
committed
};
/*
* Convert a temperature measurement in fahrenheit to celsius.
* @param {String or Number} fahrenheit
* @returns {Number} measurement in celsius
*/
export const convertFahrenheitToCelsius = function(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
};
/*
* Convert a temperature measurement in celsius to fahrenheit.
Bucknell, Mary S.
committed
* @param {String or Number} celsius
* @returns {Number} measurement in fahrenheit
*/
export const convertCelsiusToFahrenheit = function(celsius) {
return celsius * 9 / 5 + 32;
};
/*
* Return the variables sorted with the ones we care about first
Bucknell, Mary S.
committed
* @param {Array of variable Object}
* @return {Array of variable Object}
Bucknell, Mary S.
committed
export const sortedParameters = function(parameters) {
const PARAM_PERTINENCE = {
'72019': 2
};
Bucknell, Mary S.
committed
const allParameters = parameters ? Object.values(parameters) : [];
const pertinentParmCds = Object.keys(PARAM_PERTINENCE);
const highPertinenceVars = allParameters
.filter(x => pertinentParmCds.includes(x.parameterCode))
.sort((a, b) => {
Bucknell, Mary S.
committed
const aPertinence = PARAM_PERTINENCE[a.parameterCode];
const bPertinence = PARAM_PERTINENCE[b.parameterCode];
if (aPertinence < bPertinence) {
return -1;
} else {
return 1;
}
});
const lowPertinenceVars = allParameters
.filter(x => !pertinentParmCds.includes(x.parameterCode))
.sort((a, b) => {
Bucknell, Mary S.
committed
const aDesc = a.description.toLowerCase();
const bDesc = b.description.toLowerCase();
if (aDesc < bDesc) {
return -1;
} else {
return 1;
}
});
return highPertinenceVars.concat(lowPertinenceVars);
};
Bucknell, Mary S.
committed
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
* Return the data point nearest to time and its index.
* @param {Array} data - array of Object where one of the keys is dateTime in Unix epoch.
* @param {Date} time
* @return {Object} - datum
*/
export const getNearestTime = function(data, time) {
// Function that returns the left bounding point for a given chart point.
if (data.length === 0) {
return null;
}
const bisectDate = bisector(d => d.dateTime).left;
let index = bisectDate(data, time, 1);
let datum;
let d0 = data[index - 1];
let d1 = data[index];
if (d0 && d1) {
datum = time - d0.dateTime > d1.dateTime - time ? d1 : d0;
} else {
datum = d0 || d1;
}
// Return the nearest data point and its index.
return datum;
};
* When given an approval code, will return the text equivalent
* @param {String} approvalCode - Usually a letter such as 'A'
* @return {String} - an easy to understand text version of an approval code
*/
export const approvalCodeText = function(approvalCode) {
const approvalText = {
P: 'Provisional',
A: 'Approved',
default: `unknown code: ${approvalCode}`
};
return approvalText[approvalCode] || approvalText.default;
};