diff --git a/SedimentDataExplorer.html b/SedimentDataExplorer.html
new file mode 100644
index 0000000..fc676a3
--- /dev/null
+++ b/SedimentDataExplorer.html
@@ -0,0 +1,139 @@
+
+
+
+
+
+ Sediment Template Data Explorer
+
+
+
+
+ Sediment Template Data Explorer - v0.20240203
+ Excel files -
+
+
+
+
+
+
+
+
+ Selection tools -
+
+
+
+
+
+
Select Samples
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Select Chemicals
+
+
+
+
+
+
+
+
+
+
+
+ Sheetanmes -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Chart types -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Location co-ordinate files -
+
+
+
+
+
+
+ Status -
+
+
+
+ Load -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SedimentDataExplorer.js b/SedimentDataExplorer.js
new file mode 100644
index 0000000..8225926
--- /dev/null
+++ b/SedimentDataExplorer.js
@@ -0,0 +1,988 @@
+
+// import {parse, stringify, toJSON, fromJSON} from 'flatted';
+ const autocolors = window['chartjs-plugin-autocolors'];
+ Chart.register(autocolors);
+ const annotationPlugin = window['chartjs-plugin-annotation'];
+ Chart.register(annotationPlugin);
+
+ markerPngs = ['marker-icon-red.png', 'marker-icon-orange.png', 'marker-icon-yellow.png',
+ 'marker-icon-green.png', 'marker-icon-blue.png', 'marker-icon-violet.png',
+ 'marker-icon-grey.png', 'marker-icon-gold.png','marker-icon-black.png'];
+
+// Define the projection for British National Grid (OSGB 1936)
+ proj4.defs("EPSG:27700", "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894 +units=m +no_defs");
+
+ let lastInstanceNo = 0;
+ let noInstances = 16;
+ let chartInstance = [];
+ let instanceType = [];
+ let instanceSheet = [];
+ let highlighted = [];
+ for (i = 1; i < noInstances; i++) {
+ chartInstance[i] = null;
+ instanceType[i] = null;
+ instanceSheet[i] = null;
+ }
+ dataSheetNames = ['Physical Data','Trace metal data','PAH data','PCB data','BDE data','Organotins data','Organochlorine data'];
+ dataSheetNamesCheckboxes = [];
+ for (let i = 0; i < dataSheetNames.length; i++) {
+ dataSheetNamesCheckboxes[i] = dataSheetNames[i].replace(/\s/g, '').toLowerCase();
+ }
+ sheetsToDisplay = {};
+ for (i = 0; i < dataSheetNames.length; i++) {
+ sheetName = dataSheetNames[i];
+ sheetsToDisplay[sheetName] = true;
+ }
+ subChartNames = ['samplegroup','chemicalgroup','gorhamtest','totalHC','congenertest']
+ subsToDisplay = {};
+ for (i = 0; i < subChartNames.length; i++) {
+ subName = subChartNames[i];
+ subsToDisplay[sheetName] = true;
+ }
+ calcSheetNames = ['Physical Stats','PSA Charts','Metals calcs','PAH calcs','PCB calcs','BDE calcs','Organotin calcs','Organochlorine calcs'];
+ let map; // Declare map as a global variable
+ let fred;
+ let sampleMeasurements = {};
+ let selectedSampleMeasurements = {};
+ let sampleInfo = {};
+ let selectedSampleInfo = {};
+ let blankMeasurements = {};
+ let namedLocations = {};
+ //All actions level mg/kg
+ const actionLevels = {};
+ actionLevels['Trace metal data'] = {
+ 'Arsenic (As)':[20,100],
+ 'Cadmium (Cd)': [0.4,5],
+ 'Chromium (Cr)': [40,400],
+ 'Copper (Cu)': [40,400],
+ 'Mercury (Hg)': [0.3,3],
+ 'Nickel (Ni)': [20,200],
+ 'Lead (Pb)': [50,500],
+ 'Zinc (Zn)': [130,800]
+ };
+ actionLevels['Organotins data'] = {
+ 'Dibutyltine (DBT)': [0.1,1],
+ 'Tributyltin (TBT)': [0.1,1]
+ };
+ actionLevels['PAH data'] = {
+ 'Acenapthene': [0.1,0],
+ 'Acenapthylene': [0.1,0],
+ 'Anthracene': [0.1,0],
+ 'Benz[a]anthracene': [0.1,0],
+ 'Benzo[a]pyrene': [0.1,0],
+ 'Benzo[b]fluoranthene': [0.1,0],
+ 'Benzo[g,h,i]perylene': [0.1,0],
+ 'Benzo[e]pyrene': [0.1,0],
+ 'Benzo[k]fluoranthene': [0.1,0],
+ 'C1-Napthalenes': [0.1,0],
+ 'C1-Phenanthrenes': [0.1,0],
+ 'C2-Napthalenes': [0.1,0],
+ 'C3-Napthalenes': [0.1,0],
+ 'Chrysene': [0.1,0],
+ 'Dibenz[a,h]anthracene': [0.01,0],
+ 'Fluoranthene': [0.1,0],
+ 'Fluorene': [0.1,0],
+ 'Indeno[123-c,d]pyrene': [0.1,0],
+ 'Napthalene': [0.1,0],
+ 'Perylene': [0.1,0],
+ 'Phenanthrene': [0.1,0],
+ 'Pyrene': [0.1,0]
+ };
+ actionLevels['Organochlorine data'] = {
+ 'Dieldrin': [0.005,0],
+ 'Dichlorodiphenyltrichloroethane (PPDDT)': [0.001,0.2]
+ };
+ let actionLevelColors = ['rgba(255, 255, 0, 1)','rgba(255, 0, 0, 0.5)'];
+ let actionLevelDashes = [[3,3],[5,5]];
+
+ firstTime = true;
+
+ importData();
+
+ function saveSnapShot() {
+ const fileSave = document.getElementById('fileSave');
+ const fileName = fileSave.value;
+ saveStatus(fileName);
+ }
+
+ function loadSnapShotURL() {
+ const urlLoad = document.getElementById('urlLoad');
+ const fileUrl = urlLoad.value;
+ loadStatus(fileUrl);
+ }
+
+ function loadSnapShotFile() {
+ const fileInput = document.getElementById('fileLoad');
+ const file = fileInput.files[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.onload = function (e) {
+ // Read file as text
+ const textData = e.target.result;
+ // Decode the base64 data (if it was encoded)
+ const decodedData = decodeURIComponent(escape(atob(textData)));
+ // Parse the JSON data
+ const jsonData = JSON.parse(decodedData);
+ // Use the loaded data
+ // Now jsonData contains the loaded data
+ sampleInfo = jsonData.sampleInfo;
+ sampleMeasurements = jsonData.sampleMeasurements;
+ selectedSampleInfo = jsonData.selectedSampleInfo;
+ selectedSampleMeasurements = jsonData.selectedSampleMeasurements;
+ updateChart();
+ };
+
+ reader.readAsText(file);
+ }
+ }
+
+ function saveStatus(fileName) {
+ // Save data to a file
+ const dataBlob = new Blob([btoa(unescape(encodeURIComponent(JSON.stringify({ sampleInfo, sampleMeasurements, selectedSampleInfo, selectedSampleMeasurements }))))], { type: 'application/octet-stream' });
+ const downloadLink = document.createElement('a');
+ downloadLink.href = URL.createObjectURL(dataBlob);
+ downloadLink.download = fileName;
+ downloadLink.click();
+ }
+
+ // Function to load data from a file URL
+ async function loadStatus(fileURL) {
+ try {
+ // Fetch the data from the URL
+ const response = await fetch(fileURL);
+
+ // Check if the fetch was successful (status code 200)
+ if (!response.ok) {
+ throw new Error(`Failed to fetch data. Status: ${response.status}`);
+ }
+
+ // Read the response as text
+ const textData = await response.text();
+
+ // Decode the base64 data (if it was encoded)
+ const decodedData = decodeURIComponent(escape(atob(textData)));
+
+ // Parse the JSON data
+ const jsonData = JSON.parse(decodedData);
+
+ // Now jsonData contains the loaded data
+ sampleInfo = jsonData.sampleInfo;
+ sampleMeasurements = jsonData.sampleMeasurements;
+ selectedSampleInfo = jsonData.selectedSampleInfo;
+ selectedSampleMeasurements = jsonData.selectedSampleMeasurements;
+ updateChart();
+ return jsonData;
+ } catch (error) {
+ console.error('Error loading data:', error.message);
+ }
+ }
+
+ function clearData() {
+ sampleMeasurements = {};
+ selectedSampleMeasurements = {};
+ sampleInfo = {};
+ selectedSampleInfo = {};
+/* if (map) {
+ map.remove();
+ }*/
+ const canvas = [];
+ for (i = 1; i < noInstances; i++) {
+ canvas[i] = document.getElementById('chart' + i);
+ clearCanvasAndChart(canvas[i], i);
+ }
+ }
+
+ function importLocations() {
+ const fileInput = document.getElementById('fileLocations');
+ const urlInput = document.getElementById('urlLocations');
+ const files = fileInput.files; // Files is now a FileList object containing multiple files
+ const urls = urlInput.value.trim().split(',').map(url => url.trim()); // Split comma-separated URLs
+
+ if (files.length === 0 && urls.length === 0) {
+ alert('Please select files or enter URLs.');
+ return;
+ }
+ // Process files
+ for (let i = 0; i < files.length; i++) {
+ filename = files[i].name;
+ const reader = new FileReader();
+ reader.onload = function (e) {
+ const data = new Uint8Array(e.target.result);
+ processExcelLocations(data,filename);
+ };
+ reader.readAsArrayBuffer(files[i]);
+ }
+
+ // Process URLs only if URLs are supplied
+ if (urls.length > 0) {
+ urls.forEach(url => {
+ // Check if the URL is a valid URL before fetching
+ if (!/^https?:\/\//i.test(url)) {
+ console.error('Invalid URL:', url);
+ return;
+ }
+
+ fetch(url)
+ .then(response => response.arrayBuffer())
+ .then(data => {
+ processExcelLocations(new Uint8Array(data),url);
+ })
+ .catch(error => {
+ console.error('Error fetching the locations file:', error);
+ });
+ });
+ }
+ // Clear the input field after reading locations
+ fileInput.value = '';
+ urlInput.value = '';
+ }
+
+ function processExcelLocations(data,url) {
+ // Based on simple Excel data in first sheet
+ // row 1 column titles
+ // column 1 location as per name used as sample in MMO templates
+ // column 2 latitude in decimal degrees
+ // column 3 longitude in decimal degrees
+// console.log('processexcellocations',url);
+//console.log('prcoessing ',url);
+ const workbook = XLSX.read(data, { type: 'array' });
+//console.log(workbook);
+ sheetData = workbook.Sheets['Sheet1'];
+//console.log(sheetData);
+ const df = XLSX.utils.sheet_to_json(sheetData, { header: 1 });
+ for (let r = 1; r < df.length; r++) {
+ const sample = df[r][0];
+ namedLocations[sample] = {};
+ namedLocations[sample].latitude = df[r][1];
+ namedLocations[sample].longitude = df[r][2];
+console.log(sample,namedLocations[sample]);
+ }
+ }
+
+ function importData() {
+ urls = {};
+ if (firstTime) {
+ firstTime = false;
+ files = {};
+ // Get the current URL
+ const currentURL = window.location.href;
+
+ // Parse the URL to get the search parameters
+ const suppliedParams = new URLSearchParams(window.location.search);
+ // Get the value of the 'file' parameter
+ const statusParam = suppliedParams.get('status');
+ if (statusParam) {
+ loadStatus(statusParam);
+ } else {
+ const urlParam = suppliedParams.get('urls');
+ if (urlParam) {
+ urls = urlParam.split(',').map(url => url.trim()); // Split comma-separated URLs
+ }
+ }
+ const selParam = suppliedParams.get('selcharts');
+ if (selParam) {
+ selcharts = selParam.split(',').map(sel => sel.trim()); // Split comma-separated URLs
+ if (selcharts) {
+ // Blank all the checkboxes
+ for (let i = 0; i < dataSheetNamesCheckboxes.length; i++) {
+console.log(i,dataSheetNamesCheckboxes[i]);
+ const checkbox = document.getElementById(dataSheetNamesCheckboxes[i]);
+ checkbox.checked = false;
+ }
+ // Check all the boxes set in url
+ for (let i = 0; i < selcharts.length; i++) {
+ const checkbox = document.getElementById(selcharts[i]);
+ checkbox.checked = true;
+ }
+ }
+ }
+ const subParam = suppliedParams.get('subcharts');
+ if (subParam) {
+ subcharts = subParam.split(',').map(subch => subch.trim()); // Split comma-separated URLs
+ if (subcharts) {
+ // Blank all the checkboxes
+ for (let i = 0; i < subChartNames.length; i++) {
+ const checkbox = document.getElementById(subChartNames[i]);
+ checkbox.checked = false;
+ }
+ // Check all the boxes set in url
+ for (let i = 0; i < subcharts.length; i++) {
+ const checkbox = document.getElementById(subcharts[i]);
+ checkbox.checked = true;
+ }
+ }
+ }
+
+ } else {
+ const fileInput = document.getElementById('fileInput');
+ const urlInput = document.getElementById('urlInput');
+ files = fileInput.files; // Files is now a FileList object containing multiple files
+ urls = urlInput.value.trim().split(',').map(url => url.trim()); // Split comma-separated URLs
+ }
+ if (files.length === 0 && urls.length === 0) {
+ alert('Please select files or enter URLs.');
+ return;
+ }
+ // Process files
+ for (let i = 0; i < files.length; i++) {
+ const filename = files[i].name;
+console.log(filename);
+ const reader = new FileReader();
+
+ reader.onload = function (e) {
+ const data = new Uint8Array(e.target.result);
+ processExcelData(data,filename);
+ };
+ reader.readAsArrayBuffer(files[i]);
+ }
+
+ // Process URLs only if URLs are supplied
+ if (urls.length > 0) {
+ urls.forEach(url => {
+ // Check if the URL is a valid URL before fetching
+ if (!/^https?:\/\//i.test(url)) {
+ console.error('Invalid URL:', url);
+ return;
+ }
+
+ fetch(url)
+ .then(response => response.arrayBuffer())
+ .then(data => {
+ processExcelData(new Uint8Array(data),url);
+ })
+ .catch(error => {
+ console.error('Error fetching the file:', error);
+ });
+ });
+ }
+ // Clear the input field after reading data
+ fileInput.value = '';
+ urlInput.value = '';
+ }
+
+ function processExcelData(data, url) {
+// console.log('processexceldata',url);
+ const workbook = XLSX.read(data, { type: 'array' });
+
+ function extractDataFromSheet(sheetName, sheetData, dateSampled) {
+ if (sheetData === null || sheetData == undefined) {
+ return null;
+ }
+//console.log('ext data ',sheetData);
+ const df = XLSX.utils.sheet_to_json(sheetData, { header: 1 });
+// const df = XLSX.utils.sheet_to_json(sheetData, { header: 1, cellText: true });
+ let startRow = -1;
+ let startCol = -1;
+ let measurementUnit = 'Not set';
+ let totalSum = 0;
+ meas = {};
+ // Read in date of analysis
+//console.log('df ', df);
+ if (df[18][1] === 'Date of analysis:') {
+ row = 18;
+ column = 2;
+ } else {
+ if (df[17][1] === 'Date of analysis:') {
+ row = 17;
+ column = 2;
+ } else {
+ if (df[18][2] === 'Date of analysis:') {
+ row = 18;
+ column = 3;
+ } else {
+ if (df[17][2] === 'Date of analysis:') {
+ row = 17;
+ column = 3;
+ } else {
+ row = null;
+ dataAnalysed = 'AD: missing';
+ contractor = 'Not listed';
+ }
+ }
+ }
+ }
+ dateAnalysed = 'AD: missing';
+ contractor = 'Not listed';
+ for (let cc = 0; cc < 30; cc++) {
+ for (let r = 0; r < 30; r++) {
+ const cellValue = df[r][cc];
+//console.log(sheetName,r,cc);
+ if (typeof cellValue === 'string' && cellValue.includes('Date of analysis')) {
+ dateAnalysed = parseDates(df[r][cc + 1])[0];
+ contractor = df[r - 1][cc + 1];
+ break;
+ }
+ }
+ }
+
+ for (let r = 0; r < df.length; r++) {
+ for (let cc = 0; cc < df[r].length; cc++) {
+ const cellValue = df[r][cc];
+ if (typeof cellValue === 'string' && cellValue.includes('Dredge Area')) {
+ c = cc - 1;
+ measurementUnit = df[r][c+4];
+ corec = 0;
+ extraValue = df[r][c];
+ if (typeof extraValue === 'string' && extraValue.includes('Laboratory sample number')) {
+console.log('Lab sampl numb');
+ corec = 0;
+ measurementUnit = df[r][c+4];
+ } else {
+console.log('No Lab sampl numb');
+ corec = 0;
+ measurementUnit = df[r][c+3];
+ }
+ //}
+//console.log('unit ',measurementUnit);
+ if (!(sheetName === 'Physical Data')) {
+ if(!(sheetName === 'PCB data')) {
+ startRow = r + 2;
+ } else {
+ startRow = r + 3;
+ }
+ startCol = c;
+ for (let col = startCol + 1; col < df[startRow].length; col++) {
+ if(!(sheetName === 'PCB data')) {
+ chemical = df[startRow - 1][col];
+ } else {
+ chemical = df[startRow - 2][col];
+ congener = df[startRow - 1][col];
+ }
+ if (chemical !== null && chemical !== undefined) {
+ if (!meas.chemicals){
+ meas.chemicals = {};
+ meas.total = {};
+ if(sheetName === 'PCB data') {
+ meas.congeners = {};
+ }
+ }
+ if (!meas.chemicals[chemical]) {
+ meas.chemicals[chemical] = {};
+ if(sheetName === 'PCB data') {
+ meas.congeners[chemical] = congener;
+ }
+ }
+ for (let row = startRow; row < df.length; row++) {
+ sample = df[row][startCol+2]; //bodge to pick up sample id
+ // If sample id not present then use Laboratory sample number instead
+ if (sample == undefined || sample == null) {
+ sample = df[row][startCol];
+ }
+ if (!(sample == undefined || sample == null)) {
+//console.log(sample);
+ const concentration = df[row][col+corec];
+ if(sample.includes('detection')) {
+ meas.chemicals[chemical][sample] = concentration;
+ //.push(parseFloat(concentration) || 0);
+ } else {
+ if (!meas.chemicals[chemical].samples) {
+ meas.chemicals[chemical].samples = {};
+ }
+ if (!meas.chemicals[chemical].samples[sample]) {
+ meas.chemicals[chemical].samples[sample] = {};
+ }
+ if (!meas.total[sample]) {
+ meas.total[sample] = 0;
+ }
+ if (concentration !== null && concentration !== undefined) {
+ meas.chemicals[chemical].samples[sample]= parseFloat(concentration);
+ meas.total[sample] += parseFloat(concentration) || 0;
+ totalSum += parseFloat(concentration) || 0;
+ }
+ }
+ }
+ }
+ } else if (sheetName === 'PAH data') {
+ hydrocarbon = df[startRow - 2][col];
+ if (hydrocarbon !== null && hydrocarbon !== undefined) {
+ if (hydrocarbon === 'Total hydrocarbon content (mg/kg)') {
+ for (let row = startRow; row < df.length; row++) {
+ sample = df[row][startCol+2]; //bodge to pick up sample id
+ // If sample id not present then use Laboratory sample number instead
+ if (sample == undefined || sample == null) {
+ sample = df[row][startCol];
+ }
+ if (!(sample == undefined || sample == null)) {
+ const concentration = df[row][col+corec];
+ if(sample.includes('detection')) {
+ meas[sample] = concentration;
+ //.push(parseFloat(concentration) || 0);
+ } else {
+ if (!meas.totalHC) {
+ meas.totalHC = {};
+ meas.totalHCUnit = hydrocarbon;
+ }
+ meas.totalHC[sample] = parseFloat(concentration);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ measurementUnit = df[r][c+7];
+ meas.samples = {};
+ meas.sizes = [];
+ startRow = r + 3;
+ startCol = c;
+ for (let col = startCol + 7; col < df[startRow - 1].length; col++) {
+ meas.sizes.push(parseFloat(df[startRow - 2][col]) || 0);
+ for (let row = startRow; row < 38; row++) {
+ sample = df[row][startCol+2]; //bodge to pick up sample id
+ // If sample id not present then use Laboratory sample number instead
+ if (sample == undefined || sample == null) {
+ sample = df[row][startCol];
+ }
+ if (!(sample == undefined || sample == null)) {
+//console.log(sheetName, col, row, sample);
+ if (!meas.samples) {
+ meas.samples = {}
+ }
+ if (!meas.samples[sample]) {
+ meas.samples[sample] = {};
+ meas.samples[sample].psd = [];
+ meas.samples[sample]['Visual Appearance'] = df[row][startCol+3];
+ meas.samples[sample]['Total solids (% total sediment)'] = df[row][startCol+5];
+ meas.samples[sample]['Organic matter (total organic carbon)'] = df[row][startCol+6];
+ }
+
+//console.log(sample);
+ meas.samples[sample].psd.push(parseFloat(df[row][col]) || 0);
+//console.log('meas.psd ',df[row][col]);
+ }
+ }
+ }
+//console.log(sheetName, ' in else ','meas ', meas);
+ }
+ break;
+ }
+ }
+ if (startRow !== -1 && startCol !== -1) {
+ break;
+ }
+ }
+ meas['Date analysed'] = dateAnalysed;
+ meas['Unit of measurement'] = measurementUnit;
+ meas['Laboratory/contractor'] = contractor;
+ if(!totalSum>0 && !(sheetName === 'Physical Data')) {
+ return 'No data for ' + sheetName;
+ }
+//console.log(sheetName, 'meas ', meas);
+ sampleMeasurements[dateSampled][sheetName] = meas;
+ const sums = {};
+ if (sheetName === "PCB data") {
+ const ICES7 = ["2,2',5,5'-Tetrachlorobiphenyl","2,4,4'-Trichlorobiphenyl","2,2',3,4,4',5,5'-Heptachlorobiphenyl",
+ "2,2',4,4',5,5'-Hexachlorobiphenyl","2,2',3,4,4',5'-Hexachlorobiphenyl",
+ "2,3',4,4',5-Pentachlorobiphenyl","2,2',4,5,5'-Pentachlorobiphenyl"];
+
+ for (const chemical in meas.chemicals) {
+ for (const sample in meas.chemicals[chemical].samples) {
+ //console.log(chemical,sample);
+ if (!sums[sample]) {
+ sums[sample] = {
+ ICES7: 0,
+ All: 0
+ };
+ }
+ //console.log(meas[chemical][sample]);
+// const congenerConcentration = meas.chemicals[chemical].samples[sample].reduce((acc, val) => acc + val, 0);
+ const congenerConcentration = meas.chemicals[chemical].samples[sample] || 0;
+ if (ICES7.includes(chemical)) {
+ sums[sample].ICES7 += congenerConcentration;
+ }
+ sums[sample].All += congenerConcentration;
+ }
+ }
+ sampleMeasurements[dateSampled][sheetName].congenerTest = sums;
+ }
+ if (sheetName === "PAH data") {
+ // Goring Test protocol here, but results stored by sample
+ const lmw = ['Acenaphthene', 'Acenaphthylene', 'Anthracene', 'Fluorene', 'C1-Naphthalenes', 'Naphthalene', 'Phenanthrene'];
+ const hmw = ['Benz[a]anthracene', 'Benzo[a]pyrene', 'Chrysene', 'Dibenz[a,h]anthracene', 'Fluoranthene', 'Pyrene'];
+
+ for (const chemical in meas.chemicals) {
+ for (const sample in meas.chemicals[chemical].samples) {
+ //console.log(chemical,sample);
+ if (!sums[sample]) {
+ sums[sample] = {
+ lmwSum: 0,
+ hmwSum: 0
+ };
+ }
+ //console.log(meas[chemical][sample]);
+ if (lmw.includes(chemical)) {
+ const lmwConcentrationSum = meas.chemicals[chemical].samples[sample] || 0;
+ sums[sample].lmwSum += lmwConcentrationSum;
+ } else if (hmw.includes(chemical)) {
+ const hmwConcentrationSum = meas.chemicals[chemical].samples[sample] || 0;
+ sums[sample].hmwSum += hmwConcentrationSum;
+ }
+ }
+ }
+ sampleMeasurements[dateSampled][sheetName].gorhamTest = sums;
+ }
+ return dateAnalysed;
+ }
+
+function parseDates(dateString) {
+// Check if the date field is empty
+if (!dateString) {
+ return ['Missing'];
+}
+
+const dates = [];
+
+// Split the input by commas or hyphens
+const dateParts = dateString.split(/,|-/);
+dateParts.forEach(part => {
+ // Trim leading/trailing spaces
+ const trimmedPart = part.trim();
+
+ // Check if it's a range (contains a hyphen)
+ if (trimmedPart.includes('/')) {
+ const ukDate = convertToUKFormat(trimmedPart);
+ if (ukDate) {
+ dates.push(ukDate);
+ }
+ } else {
+ // Single date
+ const ukDate = convertToUKFormat(trimmedPart);
+ if (ukDate) {
+ dates.push(ukDate);
+ }
+ }
+});
+
+return dates.length > 0 ? dates : ['Missing'];
+}
+
+function convertToUKFormat(dateString) {
+const parts = dateString.split('/');
+if (parts.length === 3) {
+ // Assuming the format is mm/dd/yy
+ const mm = parts[0].padStart(2, '0');
+ const dd = parts[1].padStart(2, '0');
+ const yy = parts[2].padStart(2, '0');
+
+ // Construct the UK format: dd/mm/yy
+ return `${yy}/${mm}/${dd}`;
+}
+
+// Return null for invalid date formats
+return null;
+}
+
+ function extractApplicationDataFromSheet(sheetName, sheetData, url) {
+// console.log('extractappdata',url);
+ const df = XLSX.utils.sheet_to_json(sheetData, { header: 1 });
+// const df = XLSX.utils.sheet_to_json(sheetData, { header: 1, cellText: true });
+
+
+// console.log(sheetName); //Output each cell value to console
+// console.log(df.length);
+ let startRow = -1;
+ let startCol = -1;
+
+ // This code should have found Applicant: but doesn't so bodged above
+/* if (typeof cellValue === 'string' && cellValue.includes('Applicant:')) {
+ console.log('found applicant');
+ const applicant = df[r][c+1];
+ const applicationNumber = df[r+1][c+1];
+ const applicationTitle = df[r+2][c+1];
+ const dateSampled = df[r+3][c+1];
+ const samplingLocation = df[r+4][c+1];
+ }
+*/
+
+
+/* // This relies on template first page not changing at all
+ const applicant = df[14][4];
+ const applicationNumber = df[15][4];
+ const applicationTitle = df[16][4];*/
+ for (i = 16; 19; i++) {
+ dateRow = i;
+ if (df[dateRow][2].includes('Date sampled:')) {
+ break;
+ }
+ dateRow = 0;
+// dateSampled = 'SD: missing';
+ }
+ if (dateRow > 0) {
+ applicant = df[dateRow-3][4];
+ applicationNumber = df[dateRow-2][4];
+ applicationTitle = df[dateRow-1][4];
+ dateSampled = parseDates(df[dateRow][4])[0];
+ } else {
+ applicant = df[14][4];
+ applicationNumber = df[15][4];
+ applicationTitle = df[16][4];
+ dateSampled = 'SD: missing';
+ dateRow = 17;
+ }
+ sampleInfo[dateSampled] = {};
+ sampleInfo[dateSampled]['Applicant'] = applicant;
+ sampleInfo[dateSampled]['Application number'] = applicationNumber;
+ sampleInfo[dateSampled]['Application title'] = applicationTitle;
+ sampleInfo[dateSampled]['Date sampled'] = dateSampled;
+ sampleInfo[dateSampled]['fileURL'] = url;
+console.log('extractapplicationdatafromsheet ', dateSampled,url);
+ sampleInfo[dateSampled].position = {};
+ sampleMeasurements[dateSampled] = {};
+
+ const samplingLocation = df[dateRow + 1][4];
+ for (let c = 0; c < df.length; c++) {
+
+ //Bodge as didn't read enough of columns when set by length alone
+// for (let r = 0; r < df[c].length; r++) {
+ if (df[c].length < 40 ) {
+ cdepth = 40;
+ } else {
+ cdepth = df[c].length;
+ }
+ for (let r = 0; r < cdepth; r++) {
+ const cellValue = df[r][c];
+// console.log(c,r,cellValue);
+// if (typeof cellValue === 'string' && cellValue.includes('Excluded sample (MMO use)')) {
+ if (typeof cellValue === 'string' && cellValue.includes('Sample location (decimal degrees, WGS84)')) {
+console.log('Sample Location');
+ const extraValue = df[r][c-1]
+console.log(extraValue);
+ if (typeof extraValue === 'string' && extraValue.includes('Excluded sample (MMO use)')) {
+ startRow = r + 2;
+ startCol = c-2;
+ samCol = startCol;
+ excCol = startCol + 1;
+ latCol = startCol + 2;
+ lonCol = startCol + 3;
+ namCol = startCol + 4;
+ depCol = startCol + 5;
+ dreCol = startCol + 6;
+ } else {
+ startRow = r + 2;
+ startCol = c-1;
+ samCol = startCol;
+ excCol = startCol + 6; // not present here
+ dreCol = startCol + 5;
+ latCol = startCol + 1;
+ lonCol = startCol + 2;
+ namCol = startCol + 3;
+ depCol = startCol + 4;
+ }
+
+ for (let row = startRow; row < df.length; row++) {
+ const sample = df[row][samCol]; //bodge to pick up sample id
+ if (!(sample == undefined || sample == null)) {
+ sInfo = {};
+ sInfo['Excluded sample (MMO use)'] = df[row][excCol];
+ sInfo['Dredge area'] = df[row][dreCol];
+ point = parseCoordinates(df[row][latCol],df[row][lonCol]);
+ if (point === null || point === undefined) {
+ // latitude and longitude aren't specified so try to retrieve latlon from previously entered locations
+ if (namedLocations[sample] !== null && namedLocations[sample] !== undefined) {
+ point = {};
+ point['latitude'] = namedLocations[sample].latitude;
+ point['longitude'] = namedLocations[sample].longitude;
+ } else {
+//console.log('lat and long undefined does not match', sample);
+ point = {};
+ point['latitude'] = undefined;
+ point['longitude'] = undefined;
+ }
+ }
+ sInfo['Position latitude'] = point['latitude'];
+ sInfo['Position longitude'] = point['longitude'];
+ sInfo['Location name (as per sampling plan)'] = df[row][namCol];
+ sInfo['Sampling depth (m)'] = processDepth(df[row][depCol]);
+ sInfo['Sampling location']= samplingLocation;
+ sampleInfo[dateSampled].position[sample] = sInfo;
+ }
+ }
+ break;
+ }
+ }
+ if (startRow !== -1 && startCol !== -1) {
+// console.log('Breaking up');
+ break;
+ }
+ }
+ return dateSampled;
+ }
+
+
+// workbook.SheetNames.forEach(sheetName => {
+// const sheetData = workbook.Sheets[sheetName];
+// extractDataFromSheet(sheetName, sheetData);
+// });
+ sheetName = 'Application info';
+ sheetData = workbook.Sheets[sheetName];
+ dateSampled = extractApplicationDataFromSheet(sheetName, sheetData, url);
+ sheetName = 'PAH data';
+ sheetData = workbook.Sheets[sheetName];
+ dateAnalysed = extractDataFromSheet(sheetName, sheetData, dateSampled);
+ if (dateSampled.includes('SD: Missing')) {
+ dateSampled = dateAnalysed + 'ADMSD';
+// sampleMeasurements[dateSampled] = sampleMeasurements['holder'];
+// delete sampleMeasurements['holder'];
+// sampleInfo[dateSampled] = sampleInfo['holder'];
+// delete sampleInfo['holder'];
+ sampleMeasurements[dateSampled] = sampleMeasurements['SD: Missing'];
+ delete sampleMeasurements['SD: Missing'];
+ sampleInfo[dateSampled] = sampleInfo['SD: Missing'];
+ delete sampleInfo['SD: Missing'];
+ } else if (dateSampled > dateAnalysed) {
+ sampleMeasurements[dateAnalysed + 'ADWSD'] = sampleMeasurements[dateSampled];
+ delete sampleMeasurements[dateSampled];
+ sampleInfo[dateAnalysed + 'ADWSD'] = sampleInfo[dateSampled];
+ delete sampleInfo[dateSampled];
+ dateSampled = dateAnalysed + 'ADWSD';
+ }
+ sheetName = 'PCB data';
+ sheetData = workbook.Sheets[sheetName];
+ dateAnalysed = extractDataFromSheet(sheetName, sheetData, dateSampled);
+ sheetName = 'Trace metal data';
+ sheetData = workbook.Sheets[sheetName];
+ dateAnalysed = extractDataFromSheet(sheetName, sheetData, dateSampled);
+ sheetName = 'BDE data';
+ sheetData = workbook.Sheets[sheetName];
+ dateAnalysed = extractDataFromSheet(sheetName, sheetData, dateSampled);
+ sheetName = 'Organotins data';
+ sheetData = workbook.Sheets[sheetName];
+ dateAnalysed = extractDataFromSheet(sheetName, sheetData, dateSampled);
+ sheetName = 'Organochlorine data';
+ sheetData = workbook.Sheets[sheetName];
+ dateAnalysed = extractDataFromSheet(sheetName, sheetData, dateSampled);
+
+ sheetName = 'Physical Data';
+ sheetData = workbook.Sheets[sheetName];
+ dateAnalysed = extractDataFromSheet(sheetName, sheetData, dateSampled);
+
+
+ selectedSampleMeasurements = sampleMeasurements;
+ selectedSampleInfo = sampleInfo;
+ updateChart();
+ };
+
+ function processDepth(enteredDepth) {
+// console.log(enteredDepth);
+ if (!enteredDepth) {
+ return {
+ minDepth: 0.0,
+ maxDepth: 0.0,
+ };
+ }
+ // Remove any non-numeric characters, except for dots and hyphens
+ const cleanedDepth = enteredDepth.replace(/[^0-9.\-]/g, '');
+
+ // Split the cleaned depth by hyphens to handle ranges
+ const depthParts = cleanedDepth.split('-');
+
+ // Convert the parts to numbers
+ const numericDepths = depthParts.map(parseFloat);
+
+ // Determine min and max depths
+ const minDepth = Math.min(...numericDepths);
+ const maxDepth = Math.max(...numericDepths);
+
+ // Return the result
+ return {
+ minDepth: isNaN(minDepth) ? 0.0 : minDepth,
+ maxDepth: isNaN(maxDepth) ? 0.0 : maxDepth,
+ };
+ }
+
+// Function to create a new canvas for a chart
+function createCanvas(instanceNo) {
+const container = document.getElementById('chartContainer');
+const canvas = document.createElement('canvas');
+canvas.id = 'chart' + instanceNo; // Unique chart ID
+container.appendChild(canvas); // Append the canvas to the container
+}
+
+// Function to create a button for resetting zoom
+function createResetZoomButton(chart,instanceNo) {
+const button = document.createElement('button');
+button.id = 'button'+instanceNo
+button.textContent = 'Reset Zoom';
+button.addEventListener('click', () => {
+ chart.resetZoom();
+});
+const container = document.getElementById('chartContainer');
+container.appendChild(button);
+}
+
+function clearCanvasAndChart(canvas, chartInstanceNo) {
+ if (canvas) {
+ const ctx = canvas.getContext('2d');
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ if (chartInstance[chartInstanceNo]) {
+ chartInstance[chartInstanceNo].destroy();
+ chartInstance[chartInstanceNo] = null;
+ }
+ /* // Hide the canvas
+ const convas = document.getElementById("chart" + chartInstanceNo);
+ convas.style.display = "none";*/
+ // Remove the canvas
+ const convas = document.getElementById("chart" + chartInstanceNo);
+ convas.remove();
+ // Remove a reset button if created
+ const buttonToRemove = document.getElementById('button' + chartInstanceNo);
+ // Check if the button exists
+ if (buttonToRemove) {
+ // Remove the button
+ buttonToRemove.remove();
+ } else {
+ console.log('Button not found', chartInstanceNo);
+ }
+ }
+}
+
+function chemicalTypeHasData(sheetName) {
+ chemicalTypeData = false;
+ for (const ds in selectedSampleMeasurements) {
+ const chemicalTypes = Object.keys(selectedSampleMeasurements[ds]);
+ //console.log(ds, sheetName, chemicalTypes);
+ if (chemicalTypes.includes(sheetName)) {
+ chemicalTypeData = true;
+ return chemicalTypeData;
+ }
+ }
+}
+
+function filenameDisplay() {
+
+ const fileDisplayDiv = document.getElementById("fileDisplay");
+ // blank it each time
+ fileDisplayDiv.innerHTML = "";
+
+ iconNo = 0;
+ for (dateSampled in selectedSampleInfo) {
+ currentIcon = markerPngs[iconNo];
+ iconNo = (iconNo + 1) % 9;
+
+ const fileURL = sampleInfo[dateSampled].fileURL;
+
+ // Create an image element for the icon
+ const iconElement = document.createElement("img");
+ iconElement.src = currentIcon;
+ iconElement.alt = "Marker Icon";
+ iconElement.style.width = "20px"; // Adjust the width as needed
+
+ // Create a link element for each file and append it to the div
+ const linkElement = document.createElement("a");
+ linkElement.href = fileURL;
+ // console.log(fileURL);
+ // console.log(dateSampled);
+ linkElement.textContent = `File for ${dateSampled}: ${fileURL}`;
+ linkElement.target = "_blank"; // Open link in a new tab/window
+
+ // Append the icon before the link
+ fileDisplayDiv.appendChild(iconElement);
+ fileDisplayDiv.appendChild(linkElement);
+
+ // Add a line break for better readability
+ fileDisplayDiv.appendChild(document.createElement("br"));
+ };
+}
+
+
diff --git a/markers/marker-icon-black.png b/markers/marker-icon-black.png
new file mode 100644
index 0000000..d262ae4
Binary files /dev/null and b/markers/marker-icon-black.png differ
diff --git a/markers/marker-icon-blue.png b/markers/marker-icon-blue.png
new file mode 100644
index 0000000..e2e9f75
Binary files /dev/null and b/markers/marker-icon-blue.png differ
diff --git a/markers/marker-icon-gold.png b/markers/marker-icon-gold.png
new file mode 100644
index 0000000..162fada
Binary files /dev/null and b/markers/marker-icon-gold.png differ
diff --git a/markers/marker-icon-green.png b/markers/marker-icon-green.png
new file mode 100644
index 0000000..56db5ea
Binary files /dev/null and b/markers/marker-icon-green.png differ
diff --git a/markers/marker-icon-grey.png b/markers/marker-icon-grey.png
new file mode 100644
index 0000000..ebbab8e
Binary files /dev/null and b/markers/marker-icon-grey.png differ
diff --git a/markers/marker-icon-orange.png b/markers/marker-icon-orange.png
new file mode 100644
index 0000000..fbbce7b
Binary files /dev/null and b/markers/marker-icon-orange.png differ
diff --git a/markers/marker-icon-red.png b/markers/marker-icon-red.png
new file mode 100644
index 0000000..3e64e06
Binary files /dev/null and b/markers/marker-icon-red.png differ
diff --git a/markers/marker-icon-violet.png b/markers/marker-icon-violet.png
new file mode 100644
index 0000000..28efc3c
Binary files /dev/null and b/markers/marker-icon-violet.png differ
diff --git a/markers/marker-icon-yellow.png b/markers/marker-icon-yellow.png
new file mode 100644
index 0000000..b011eea
Binary files /dev/null and b/markers/marker-icon-yellow.png differ
diff --git a/markers/marker-shadow.png b/markers/marker-shadow.png
new file mode 100644
index 0000000..9fd2979
Binary files /dev/null and b/markers/marker-shadow.png differ
diff --git a/sdeCharts.js b/sdeCharts.js
new file mode 100644
index 0000000..7bbc9c4
--- /dev/null
+++ b/sdeCharts.js
@@ -0,0 +1,971 @@
+function updateChart(){
+ if (lastInstanceNo > 0) {
+ const canvas = [];
+ for (i = 1; i < lastInstanceNo + 1; i++) {
+ canvas[i] = document.getElementById('chart' + i);
+ clearCanvasAndChart(canvas[i], i);
+ }
+ }
+ const yScaleType = document.getElementById('scaleType').checked ? 'logarithmic' : 'linear'; // Check the checkbox state
+ for (i = 0; i < dataSheetNames.length; i++) {
+ sheetName = dataSheetNames[i];
+ sheetsToDisplay[dataSheetNames[i]] = document.getElementById(dataSheetNamesCheckboxes[i]).checked ? true : false; // Check the checkbox state
+ }
+ for (i = 0; i < subChartNames.length; i++) {
+ subName = subChartNames[i];
+ subsToDisplay[subName] = document.getElementById(subName).checked ? true : false; // Check the checkbox state
+ }
+ lastInstanceNo = 0;
+ blankSheets = {};
+ setBlanksForCharting(selectedSampleMeasurements);
+ for (sheetName in sheetsToDisplay) {
+ if (sheetsToDisplay[sheetName] && chemicalTypeHasData(sheetName)) {
+ lastInstanceNo = displayCharts(sheetName, lastInstanceNo, yScaleType);
+ }
+ }
+console.log('lastInstanceNo ',lastInstanceNo);
+ sampleMap(selectedMeas, yScaleType);
+ filenameDisplay();
+}
+
+function displayCharts(sheetName, instanceNo, yScaleType) {
+// const { unitTitle, selectedMeas } = dataForCharting(selectedSampleMeasurements, sheetName);
+ if(sheetName === 'Physical Data') {
+ retData = dataForPSDCharting(selectedSampleMeasurements, sheetName);
+ unitTitle = retData['unitTitle'];
+//console.log('unitTitle displayCharts ',unitTitle);
+ sizes = retData['sizes'];
+ selectedMeas = retData['measChart'];
+//console.log(sizes);
+//console.log('selectedMeas ', selectedMeas);
+ instanceNo += 1;
+ displayPSDChart(sizes, selectedMeas, sheetName, instanceNo, yScaleType, unitTitle);
+ } else {
+ retData = dataForCharting(selectedSampleMeasurements, sheetName);
+ unitTitle = retData['unitTitle'];
+//console.log('unitTitle displayCharts ',unitTitle);
+ selectedMeas = retData['measChart'];
+
+//console.log('selectedMeas ', selectedMeas);
+ if (subsToDisplay['samplegroup']) {
+ instanceNo += 1;
+ displaySampleChart(selectedMeas, sheetName, instanceNo, yScaleType, unitTitle);
+ }
+ if (subsToDisplay['chemicalgroup']) {
+ instanceNo += 1;
+ displayChemicalChart(selectedMeas, sheetName, instanceNo, yScaleType, unitTitle);
+ }
+ if (sheetName === 'PAH data' && subsToDisplay['gorhamtest']) {
+ instanceNo += 1;
+ selectedSums = sumsForGorhamCharting(selectedSampleMeasurements);
+ displayGorhamTest(selectedSums, sheetName, instanceNo, yScaleType, unitTitle);
+ }
+ if (sheetName === 'PAH data' && subsToDisplay['totalHC']) {
+ instanceNo += 1;
+ retData = sumsForTotalHCCharting(selectedSampleMeasurements);
+ unitTitle = retData['unitTitle'];
+ selectedSums = retData['measChart'];
+ displayTotalHC(selectedSums, sheetName, instanceNo, yScaleType, unitTitle);
+ }
+ if (sheetName === 'PCB data' && subsToDisplay['congenertest']) {
+ instanceNo += 1;
+ selectedSums = sumsForCongenerCharting(selectedSampleMeasurements);
+ displayCongener(selectedSums, sheetName, instanceNo, yScaleType, unitTitle);
+ }
+ }
+ // Display the canvas
+//console.log('Display the canvas ',instanceNo);
+ return instanceNo
+}
+
+function dataForCharting(selected, sheetName) {
+ const datesSampled = Object.keys(selected);
+ ct = sheetName;
+ unitTitle = blankSheets[ct]['Unit of measurement'];
+ measChart = {};
+// for (const ds in selected) {
+ datesSampled.sort();
+ datesSampled.forEach (ds => {
+ if (!(selected[ds][ct] == undefined || selected[ds][ct] == null)) {
+ for (const c in selected[ds][ct].chemicals) {
+ if (measChart[c] == undefined || measChart[c] == null) {
+ measChart[c] = {};
+ }
+// for (const s in selected[ds][ct].chemicals[c].samples) {
+ for (const s in selectedSampleInfo[ds].position) {
+ if (selected[ds][ct].chemicals[c].samples[s] == undefined || selected[ds][ct].chemicals[c].samples[s] == null) {
+ measChart[c][ds + ': ' + s] = 0.0;
+ } else {
+ measChart[c][ds + ': ' + s] = selected[ds][ct].chemicals[c].samples[s];
+ }
+ }
+ }
+ } else {
+ // Have to deal with samples without measurements set everything to zero
+ for (const c in blankSheets[ct].chemicals) {
+ if (measChart[c] == undefined || measChart[c] == null) {
+ measChart[c] = {};
+ }
+ for (const s in selectedSampleInfo[ds].position) {
+ measChart[c][ds + ': ' + s] = 0.0;
+ }
+ }
+ }
+ });
+ unitTitle = blankSheets[ct]['Unit of measurement'];
+ return {unitTitle, measChart}
+}
+
+function sumsForCongenerCharting(selected) {
+ const datesSampled = Object.keys(selected);
+ measChart = {};
+// for (const ds in selected) {
+ datesSampled.sort();
+ datesSampled.forEach (ds => {
+ if (!(selected[ds]['PCB data'] == undefined || selected[ds]['PCB data'] == null)) {
+// for (const s in selected[ds]['PCB data'].congenerTest) {
+ for (const s in selectedSampleInfo[ds].position) {
+console.log(ds,s);
+ if (selected[ds]['PCB data'].congenerTest[s] == undefined || selected[ds]['PCB data'].congenerTest[s] == null) {
+ measChart[ds + ': ' + s] = { ICES7 : 0.0, All : 0.0 };
+ } else {
+ measChart[ds + ': ' + s] = selected[ds]['PCB data'].congenerTest[s];
+ }
+ }
+ } else {
+ for (const s in selectedSampleInfo[ds].position) {
+ measChart[ds + ': ' + s] = { All : 0.0, ICES7 : 0.0};
+ }
+ }
+ });
+ return measChart
+}
+
+function sumsForGorhamCharting(selected) {
+ const datesSampled = Object.keys(selected);
+ measChart = {};
+// for (const ds in selected) {
+ datesSampled.sort();
+ datesSampled.forEach (ds => {
+ if (!(selected[ds][ct] == undefined || selected[ds][ct] == null)) {
+ for (const s in selected[ds]['PAH data'].gorhamTest) {
+ if (selected[ds]['PAH data'].gorhamTest[s] == undefined || selected[ds]['PAH data'].gorhamTest[s] == null) {
+ measChart[ds + ': ' + s] = { hmwSum : 0.0, lmwSum : 0.0};
+ } else {
+ measChart[ds + ': ' + s] = selected[ds]['PAH data'].gorhamTest[s];
+ }
+ }
+ } else {
+ for (const s in selectedSampleInfo[ds].position) {
+ measChart[ds + ': ' + s] = { hmwSum : 0.0, lmwSum : 0.0};
+ }
+ }
+ });
+ return measChart
+}
+
+function sumsForTotalHCCharting(selected) {
+ const datesSampled = Object.keys(selected);
+ unitTitle = blankSheets[ct]['totalHCUnit'];
+ measChart = {};
+// for (const ds in selected) {
+ datesSampled.sort();
+ datesSampled.forEach (ds => {
+ if (!(selected[ds][ct] == undefined || selected[ds][ct] == null)) {
+ for (const s in selected[ds]['PAH data'].totalHC) {
+ if (selected[ds]['PAH data'].total[s] == undefined || selected[ds]['PAH data'].totalHC[s] == null) {
+ measChart[ds + ': ' + s] = { totalHC : 0.0, fractionPAH : 0.0};
+ } else {
+// measChart[ds + ': ' + s] = {totalHC : selected[ds]['PAH data'].totalHC[s], fractionPAH : (selected[ds]['PAH data'].total[s] / (1000 * selected[ds]['PAH data'].totalHC[s]))};
+ measChart[ds + ': ' + s] = {totalHC : selected[ds]['PAH data'].totalHC[s], fractionPAH : selected[ds]['PAH data'].total[s] / 1000};
+ }
+ }
+ } else {
+ for (const s in selectedSampleInfo[ds].position) {
+ measChart[ds + ': ' + s] = { totalHC : 0.0, fractionPAH : 0.0};
+ }
+ }
+ });
+ return {unitTitle, measChart}
+}
+
+
+
+function setBlanksForCharting(selected) {
+ const datesSampled = Object.keys(selected);
+ // Have to deal with samples without measurements set everything to zero
+ for (const ds in selected) {
+ for (const ct in selected[ds]) {
+ if (blankSheets[ct] == null || blankSheets[ct] == undefined) {
+ if (!(selected[ds][ct] == undefined || selected[ds][ct] == null)) {
+ blankSheets[ct] = selected[ds][ct];
+ }
+ }
+ }
+ }
+ return
+}
+
+function dataForPSDCharting(selected, sheetName) {
+ const datesSampled = Object.keys(selected);
+ ct = sheetName;
+// unitTitle = selected[datesSampled[0]][ct]['Unit of measurement'];
+ unitTitle = blankSheets[ct]['Unit of measurement'];
+ measChart = {};
+ sizes = null;
+// for (const ds in selected) {
+ datesSampled.sort();
+ datesSampled.forEach (ds => {
+ if (!(selected[ds][ct] == undefined || selected[ds][ct] == null)) {
+ if (!sizes) {
+ sizes = selected[ds][ct].sizes;
+ sizes = sizes.map(phiSize => Math.pow(2, -phiSize));
+ }
+ for (const s in selected[ds][ct].samples) {
+ measChart[ds + ': ' + s] = selected[ds][ct].samples[s].psd;
+ }
+ } else {
+ for (const s in selectedSampleInfo[ds].position) {
+ measChart[ds + ': ' + s] = new Array(42).fill(0.0);
+ }
+ }
+ });
+//console.log('dataforPSD ', unitTitle,sizes,measChart);
+ return {unitTitle, sizes, measChart}
+}
+
+function displayPSDChart(sizes, meas, sheetName, instanceNo, yLogLin, unitTitle) {
+ createCanvas(instanceNo);
+ const convas = document.getElementById("chart" + instanceNo);
+ convas.style.display = "block";
+ instanceType[instanceNo] = 'PSD';
+ instanceSheet[instanceNo] = sheetName;
+ // Extract sample names from the PSD data structure
+ const sampleNames = Object.keys(meas);
+
+ // Create datasets for each sample
+ const datasets = sampleNames.map((sampleName, index) => {
+ return {
+ label: sampleName,
+ data: meas[sampleName],
+// borderColor: getRandomColor(), // Function to generate random color
+ borderWidth: 2,
+ fill: false,
+ };
+ });
+
+ // Chart configuration
+ const chartConfig = {
+ type: 'line',
+ data: {
+ labels: sizes,
+ datasets: datasets,
+ },
+ options: {
+ plugins: {
+ title: {
+ display: true,
+ text: sheetName
+ },
+ legend: {
+ display: false,
+ position: 'bottom',
+ labels: {
+ font: { // Customize legend label font
+ size: 14,
+ weight: 'italic',
+ padding: 10
+ }
+ }
+ },
+ // Add a custom plugin for interactivity
+ selectSample: {
+ highlightedSample: null,
+ },
+ },
+ scales: {
+ x: {
+ type: 'logarithmic',
+ position: 'bottom',
+ title: {
+ display: true,
+ text: 'mm'
+ }
+ },
+ y: {
+ beginAtZero: true,
+ type: yLogLin,
+ title: {
+ display: true,
+ text: unitTitle
+ }
+ }
+ },
+ autocolors: {
+ mode: 'label'
+ }
+ }
+ };
+// };
+
+
+const ctx = document.getElementById('chart' + instanceNo).getContext('2d');
+chartInstance[instanceNo] = new Chart(ctx, chartConfig);
+
+Chart.register({
+id: 'selectSample',
+afterDraw: function (chart, args, options) {
+ const highlightedSample = chart.options.plugins.selectSample.highlightedSample;
+
+ if (highlightedSample) {
+//console.log('highlightedSample ', highlightedSample);
+ const datasetIndex = chart.data.datasets.findIndex(dataset => dataset.label === highlightedSample);
+
+ if (datasetIndex !== -1) {
+ const dataset = chart.data.datasets[datasetIndex];
+ dataset.borderWidth = 4;
+ dataset.borderColor = 'red';
+ }
+ }
+},
+});
+}
+
+// Function to generate a random color
+function getRandomColor() {
+const letters = '0123456789ABCDEF';
+let color = '#';
+for (let i = 0; i < 6; i++) {
+color += letters[Math.floor(Math.random() * 16)];
+}
+return color;
+}
+
+function displayPSDHighlight(meas, yLogLin, instanceNo, clickedMapSample) {
+ clickedSamples = findSamplesInSameLocation(clickedMapSample);
+ const allChemicals = Object.keys(meas);
+ let clickedIndexes = [];
+ clickedSamples.forEach (clickedSample => {
+ index = -1;
+ for (const sample in meas[allChemicals[0]]) {
+ index += 1;
+ if (sample.includes(clickedSample)) {
+ clickedIndexes.push(index);
+ }
+ }
+ });
+ clickedIndexes.forEach(item => {
+//console.log('displayPSDHighlight',clickedIndexes);
+//console.log('item ',item);
+chartInstance[instanceNo].options.plugins.selectSample.highlightedSample = item;
+ });
+ // Update the chart
+ chartInstance[instanceNo].update();
+}
+
+// Helper function to remove highlighting
+function removePSDHighlight() {
+chartInstance.options.plugins.selectSample.highlightedSample = null;
+chartInstance.update();
+}
+
+function displaySampleChart(meas, sheetName, instanceNo, yLogLin, unitTitle) {
+ createCanvas(instanceNo);
+ const convas = document.getElementById("chart" + instanceNo);
+ convas.style.display = "block";
+ instanceType[instanceNo] = 'chemical';
+ instanceSheet[instanceNo] = sheetName;
+ const allChemicals = Object.keys(meas);
+ const allSamples = Object.keys(meas[allChemicals[0]]); // Assuming all samples have the same chemicals
+ const datasets = allChemicals.map((chemical, index) => {
+ const data = allSamples.map(sample => meas[chemical][sample]); // Using the first concentration value for simplicity
+ return {
+ label: chemical,
+ data: data,
+ borderWidth: 1,
+ yAxisID: 'y',
+ };
+ });
+ displayAnyChart(meas, allSamples,datasets,instanceNo,sheetName,unitTitle,yLogLin);
+}
+
+function highlightMapLocation(clickedIndex) {
+ console.log(clickedIndex);
+ return
+}
+
+function displayAnyChart(meas, all, datasets, instanceNo, title, yTitle, yLogLin) {
+ //console.log('chartInstance ', instanceNo);
+ // const ctx = document.getElementById('chart' + instanceNo).getContext('2d');
+ const ctx = document.getElementById('chart' + instanceNo);
+ //console.log(sheetName, instanceNo);
+ stanGraph = {
+ type: 'bar',
+ data: {
+ labels: all,
+ datasets: datasets
+ },
+ options: {
+ interaction: {
+ mode: 'index',
+ axis: 'xy'
+ },
+ plugins: {
+ title: {
+ display: true,
+ text: title
+ },
+ legend: {
+ display: false,
+ position: 'top',
+ labels: {
+ font: { // Customize legend label font
+ size: 14,
+ weight: 'italic',
+ padding: 10
+ }
+ }
+ },
+ zoom: {
+ pan: {
+ // pan options and/or events
+ enabled: true,
+ mode: 'xy',
+ modifierKey: 'shift',
+ },
+ limits: {
+ y: { min: 0 }
+ // axis limits
+ },
+ zoom: {
+ // zoom options and/or events
+ wheel: {
+ enabled: true,
+ },
+ drag: {
+ enabled: true,
+ },
+ mode: 'xy'
+ }
+ },
+ },
+ indexAxis: 'x',
+ scales: {
+ x: {
+ beginAtZero: true,
+ ticks: {
+ maxRotation: 90,
+ minRotation: 90,
+ autoSkip: false,
+ }
+ },
+ y: {
+ beginAtZero: true,
+ type: yLogLin,
+ title: {
+ display: true,
+ text: yTitle,
+ position: 'left',
+ }
+ },
+ },
+ autocolors: {
+ mode: 'label'
+ }
+ }
+ };
+ if (title.includes('hydrocarbon')) {
+ stanGraph.options.scales.y1 = {
+ beginAtZero: true,
+ type: yLogLin,
+ position: 'right',
+ title: {
+ display: true,
+ text: 'Total PAHs as Fraction of Total Hydrocarbons',
+ position: 'right',
+ }
+ };
+ console.log(stanGraph);
+ };
+ chartInstance[instanceNo] = new Chart(ctx, stanGraph);
+ //clickableScales(chartInstance[instanceNo], 1);
+ function clickableScales(chart, canvas, click) {
+ //console.log(chart);
+ const height = chart.scales.x.height;
+ const top = chart.scales.x.top;
+ const bottom = chart.scales.x.bottom;
+ const left = chart.scales.x.left;
+ const right = chart.scales.x.maxWidth / chart.scales.x.ticks.length;
+ //console.log('click scales - height,top,bottom,left,right', height,top,bottom,left,right);
+ let resetCoordinates = canvas.getBoundingClientRect();
+ //console.log('click - raw x y', click.clientX, click.clientY);
+ const x = click.clientX - resetCoordinates.left;
+ const y = click.clientY - resetCoordinates.top;
+ //console.log('click - corrrected x y', x, y);
+ //console.log('chart.scales.x.ticks.length',chart.scales.x.ticks.length);
+ if (y >= top && y <= bottom) {
+ for (let i = 0; i < chart.scales.x.ticks.length; i++) {
+ if (x >= left + (right * i) && x <= left + (right * (i + 1))) {
+ console.log('x label', i);
+ const regexPattern = /^(\S+): (.+)$/;
+ const matchResult = all[i].match(regexPattern);
+ if (matchResult) {
+ // Extracted parts
+ const dateSampled = matchResult[1];
+ const sample = matchResult[2];
+
+ // Output the results
+ console.log("Date Sampled: ", dateSampled);
+ console.log("Sample:", sample);
+ createHighlights(meas, yLogLin, dateSampled, sample, null);
+ } else {
+ console.log("String format doesn't match the expected pattern.");
+ };
+ }
+ }
+ }
+
+ }
+ ctx.addEventListener('click', (e) => {
+ clickableScales(chartInstance[instanceNo], ctx, e);
+ chartInstance[instanceNo].resize();
+ //chartInstance[instanceNo].updtae();
+ });
+ const xLabels = document.querySelectorAll('#chart' + instanceNo + '.chartjs-axis-x .chartjs-axis-label');
+ xLabels.forEach((label, index) => {
+ label.addEventListener('click', () => {
+ console.log('about to toggle');
+ toggleHighlightMapLocation(index);
+ });
+ });
+ createResetZoomButton(chartInstance[instanceNo], instanceNo);
+}
+
+
+function displayChemicalChart(meas, sheetName, instanceNo, yLogLin, unitTitle) {
+createCanvas(instanceNo);
+const convas = document.getElementById("chart" + instanceNo);
+convas.style.display = "block";
+instanceType[instanceNo] = 'sample';
+instanceSheet[instanceNo] = sheetName;
+const allChemicals = Object.keys(meas);
+const allSamples = Object.keys(meas[allChemicals[0]]); // Assuming all samples have the same chemicals
+const datasets = allSamples.map((sample, index) => {
+ const data = allChemicals.map(chemical => meas[chemical][sample]); // Using the first concentration value for simplicity
+ return {
+ label: sample,
+ data: data,
+ borderWidth: 1,
+ yAxisID: 'y',
+ };
+});
+displayAnyChart(meas, allChemicals,datasets,instanceNo,sheetName,unitTitle,yLogLin);
+chartInstance[instanceNo].options.plugins.annotation.annotations = {};
+let allal = actionLevels[sheetName];
+
+
+if(allal) {
+ allChemicals.forEach (chemical => {
+ let al = allal[chemical] ? allal[chemical].slice() : null;
+ alMax = 0;
+ al2 = false;
+ if(al) {
+ item = allChemicals.indexOf(chemical);
+ for (i = 0; i < 2; i++) {
+ if (al[i] > 0.0) {
+// borderColor = actionLevelColors[i];
+ // Get the units right between action levels and sample measurements
+ // Action levels are all in mg/kg but PAHs in ug/kg
+ if (sheetName === 'PAH data') {
+ al[i] = al[i] * 1000;
+ }
+ if (i === 1) {
+ al2 = true;
+ }
+ chartLine(instanceNo,chemical + i,item-0.5,item+0.5,al[i],al[i],actionLevelColors[i],actionLevelDashes[i]);
+ }
+ }
+ if (al[1] > alMax) {
+ alMax = al[1];
+ }
+ }
+ });
+ maxConc = findMaxConcentration(meas);
+//console.log('maxConc ',maxConc);
+//console.log(meas);
+ if (maxConc > alMax) {
+ alMax = maxConc;
+ }
+ alX = allChemicals.length * 0.03;
+ chartLabel(instanceNo,alX,0.8*alMax,actionLevelColors[0],'Action Level 1 ');
+ chartLine(instanceNo,'Legend - Action Level 1',alX*1.4,alX*2.5,0.8*alMax,0.8*alMax,actionLevelColors[0],actionLevelDashes[0]);
+ if (al2) {
+ chartLabel(instanceNo,alX,0.9*alMax,actionLevelColors[1],'Action Level 2 ');
+ chartLine(instanceNo,'Legend - Action Level 2',alX*1.4,alX*2.5,0.9*alMax,0.9*alMax,actionLevelColors[1],actionLevelDashes[1]);
+ }
+}
+ // Update the chart
+ chartInstance[instanceNo].update();
+}
+
+// chartInstance[3].resetZoom();
+//
+
+// Function to find the maximum concentration
+function findMaxConcentration(data) {
+let maxConcentration = -Infinity;
+
+for (const chemical in data) {
+for (const sample in data[chemical]) {
+ const concentration = data[chemical][sample];
+ if (concentration > maxConcentration) {
+ maxConcentration = concentration;
+ }
+}
+}
+
+return maxConcentration;
+}
+
+function chartLine(instanceNo,name,xMin,xMax,yMin,yMax,borderColor,borderDash) {
+// chartInstance[instanceNo].options.plugins.annotation.annotations.push({
+ chartInstance[instanceNo].options.plugins.annotation.annotations[('line-' + instanceNo + '-' + name)] = {
+ type: 'line',
+ yMin: yMin,
+ yMax: yMax,
+ xMin: xMin,
+ xMax: xMax,
+ borderColor: borderColor,
+ borderDash: borderDash,
+ borderWidth: 2,
+ };
+}
+
+function chartLabel(instanceNo,xValue,yValue,borderColor,label) {
+// chartInstance[instanceNo].options.plugins.annotation.annotations.push({
+ chartInstance[instanceNo].options.plugins.annotation.annotations[('label-' + instanceNo + '-'+label)] = {
+ type: 'label',
+ enabled: true,
+ xValue: xValue,
+ yValue: yValue,
+ color: borderColor,
+ backgroundColor: 'rgba(200,200,200)',
+ content: [label],
+ font: {
+ size: 10
+ }
+ };
+}
+
+function displayGorhamTest(sums, sheetName, instanceNo, yLogLin, unitTitle) {
+ createCanvas(instanceNo);
+ const convas = document.getElementById("chart" + instanceNo);
+ convas.style.display = "block";
+ instanceType[instanceNo] = 'gorham';
+instanceSheet[instanceNo] = sheetName;
+ const lmw = ['Acenaphthene', 'Acenaphthylene', 'Anthracene', 'Fluorene', 'C1-Naphthalenes', 'Naphthalene', 'Phenanthrene'];
+ const hmw = ['Benz[a]anthracene', 'Benzo[a]pyrene', 'Chrysene', 'Dibenz[a,h]anthracene', 'Fluoranthene', 'Pyrene'];
+ const LMW = {
+ ERL: 552,
+ ERM: 3160
+ };
+ const HMW = {
+ ERL: 1700,
+ ERM: 9600
+ };
+
+ const samples = Object.keys(sums);
+ const lmwSumData = samples.map(sample => sums[sample].lmwSum);
+ const hmwSumData = samples.map(sample => sums[sample].hmwSum);
+ datasets = [
+ {
+ label: 'LMW Sums',
+ backgroundColor: 'rgba(54, 162, 235, 0.5)',
+ borderColor: 'rgba(54, 162, 235, 1)',
+ borderWidth: 1,
+ data: lmwSumData,
+ yAxisID: 'y',
+ },
+ {
+ label: 'HMW Sums',
+ backgroundColor: 'rgba(255, 99, 132, 0.5)',
+ borderColor: 'rgba(255, 99, 132, 1)',
+ borderWidth: 1,
+ data: hmwSumData,
+ yAxisID: 'y',
+ },
+ ];
+
+displayAnyChart(sums, samples,datasets,instanceNo,sheetName + ': Gorham Test Protocol',unitTitle,yLogLin);
+
+ chartInstance[instanceNo].options.plugins.annotation.annotations = {};
+ chartLine(instanceNo,'LMW.ERL',0,samples.length,LMW.ERL,LMW.ERL,'rgba(0, 0, 255, 0.5)',[3,3]);
+ chartLine(instanceNo,'LMW.ERM',0,samples.length,LMW.ERM,LMW.ERM,'rgba(0, 0, 255, 0.5)',[5,5]);
+ chartLine(instanceNo,'HMW.ERL',0,samples.length,HMW.ERL,HMW.ERL,'rgba(255, 0, 0, 0.5)',[3,3]);
+ chartLine(instanceNo,'HMW.ERM',0,samples.length,HMW.ERM,HMW.ERM,'rgba(255, 0, 0, 0.5)',[5,5]);
+ gorX = samples.length * 0.05;
+ gorMax = HMW.ERM;
+ for (const sample in sums) {
+ if (sums[sample].lmwSum > gorMax) {
+ gorMax = sums[sample].lmwSum;
+ }
+ if (sums[sample].hmwSum > gorMax) {
+ gorMax = sums[sample].hmwSum;
+ }
+ }
+ chartLabel(instanceNo,gorX,0.75*gorMax,'rgba(0, 0, 255, 0.5)','LMW ERL ');
+ chartLine(instanceNo,'Legend - LMW.ERL',gorX*1.2,gorX*2.2,0.75*gorMax,0.75*gorMax,'rgba(0, 0, 255, 0.5)',actionLevelDashes[0]);
+ chartLabel(instanceNo,gorX,0.8*gorMax,'rgba(0, 0, 255, 0.5)','LMW ERM ');
+ chartLine(instanceNo,'Legend - LMW.ERM',gorX*1.2,gorX*2.2,0.8*gorMax,0.8*gorMax,'rgba(0, 0, 255, 0.5)',actionLevelDashes[1]);
+ chartLabel(instanceNo,gorX,0.9*gorMax,'rgba(255, 0, 0, 0.5)','HMW ERL ');
+ chartLine(instanceNo,'Legend - HMW.ERL',gorX*1.2,gorX*2.2,0.9*gorMax,0.9*gorMax,'rgba(255, 0, 0, 0.5)',actionLevelDashes[0]);
+ chartLabel(instanceNo,gorX,0.95*gorMax,'rgba(255, 0, 0, 0.5)','HMW ERM ');
+ chartLine(instanceNo,'Legend - HMW.ERM',gorX*1.2,gorX*2.2,0.95*gorMax,0.95*gorMax,'rgba(255, 0, 0, 0.5)',actionLevelDashes[1]);
+ // Update the chart
+ chartInstance[instanceNo].update();
+}
+
+function displayCongener(sums, sheetName, instanceNo, yLogLin, unitTitle) {
+ createCanvas(instanceNo);
+ const convas = document.getElementById("chart" + instanceNo);
+ convas.style.display = "block";
+ instanceType[instanceNo] = 'congener';
+instanceSheet[instanceNo] = sheetName;
+ const samples = Object.keys(sums);
+ const ICES7SumData = samples.map(sample => sums[sample].ICES7);
+ const allSumData = samples.map(sample => sums[sample].All);
+ const datasets = [
+ {
+ label: 'ICES7',
+ backgroundColor: 'rgba(54, 162, 235, 0.5)',
+ borderColor: 'rgba(54, 162, 235, 1)',
+ borderWidth: 1,
+ data: ICES7SumData,
+ yAxisID: 'y',
+ },
+ {
+ label: 'All',
+ backgroundColor: 'rgba(255, 99, 132, 0.5)',
+ borderColor: 'rgba(255, 99, 132, 1)',
+ borderWidth: 1,
+ data: allSumData,
+ yAxisID: 'y',
+ },
+ ];
+displayAnyChart(sums, samples,datasets,instanceNo,sheetName + ': Congener Sums',unitTitle,yLogLin);
+ chartInstance[instanceNo].options.plugins.annotation.annotations = {};
+ chartLine(instanceNo,'ICES7 Action Level 1',0,samples.length,0.01,0.01,'rgba(0, 0, 255, 0.5)',[3,3]);
+ chartLine(instanceNo,'All Action Level 1',0,samples.length,0.02,0.02,'rgba(255, 0, 0, 0.5)',[3,3]);
+ chartLine(instanceNo,'All Action Level 2',0,samples.length,0.2,0.2,'rgba(255, 0, 0, 0.5)',[5,5]);
+ gorX = samples.length * 0.1;
+ gorMax = 0.2;
+ for (const sample in sums) {
+ if (sums[sample].ICES7 > gorMax) {
+ gorMax = sums[sample].ICES7;
+ }
+ if (sums[sample].All > gorMax) {
+ gorMax = sums[sample].All;
+ }
+ }
+ chartLabel(instanceNo,gorX,0.75*gorMax,'rgba(0, 0, 255, 0.5)','ICES7 Action Level 1 ');
+ chartLine(instanceNo,'Legend - ICES7 AL1',gorX*1.2,gorX*2.2,0.75*gorMax,0.75*gorMax,'rgba(0, 0, 255, 0.5)',actionLevelDashes[0]);
+ chartLabel(instanceNo,gorX,0.9*gorMax,'rgba(255, 0, 0, 0.5)','All Action Level 1 ');
+ chartLine(instanceNo,'Legend - All AL1',gorX*1.2,gorX*2.2,0.9*gorMax,0.9*gorMax,'rgba(255, 0, 0, 0.5)',actionLevelDashes[0]);
+ chartLabel(instanceNo,gorX,0.95*gorMax,'rgba(255, 0, 0, 0.5)','All Action Level 2 ');
+ chartLine(instanceNo,'Legend - All AL2',gorX*1.2,gorX*2.2,0.95*gorMax,0.95*gorMax,'rgba(255, 0, 0, 0.5)',actionLevelDashes[1]);
+ // Update the chart
+ chartInstance[instanceNo].update();
+}
+
+function displayTotalHC(sums, sheetName, instanceNo, yLogLin, unitTitle) {
+ createCanvas(instanceNo);
+ const convas = document.getElementById("chart" + instanceNo);
+ convas.style.display = "block";
+ instanceType[instanceNo] = 'totalHC';
+instanceSheet[instanceNo] = sheetName;
+ const samples = Object.keys(sums);
+ const totalHC = samples.map(sample => sums[sample].totalHC);
+ const fractionPAH = samples.map(sample => sums[sample].fractionPAH);
+ const datasets = [
+ {
+ label: 'totalHC',
+ backgroundColor: 'rgba(54, 162, 235, 0.5)',
+ borderColor: 'rgba(54, 162, 235, 1)',
+ borderWidth: 1,
+ data: totalHC,
+ yAxisID: 'y',
+ },
+ {
+ label: 'fractionPAH',
+ backgroundColor: 'rgba(255, 99, 132, 0.5)',
+ borderColor: 'rgba(255, 99, 132, 1)',
+ borderWidth: 1,
+ data: fractionPAH,
+ yAxisID: 'y1',
+ },
+ ];
+displayAnyChart(sums, samples,datasets,instanceNo,sheetName + ': Total hydrocarbon & Total PAH/THC',unitTitle,yLogLin);
+y1Title = 'Fraction PAH of THC';
+/* chartInstance[instanceNo].options.scales.push({
+ y1: { beginAtZero: true,
+ type: yLogLin,
+ title: {
+ display: true,
+ text: y1Title,
+ position: 'right',
+ }
+ }
+ });*/
+
+/* chartInstance[instanceNo].scales[('y1')] = {
+ y1: { beginAtZero: true,
+ type: yLogLin,
+ position: 'right',
+ title: {
+ display: true,
+ text: 'Something',
+ position: 'right',
+ }
+ },
+ };*/
+chartInstance[instanceNo].options.plugins.legend.display = true;
+
+ /*chartInstance[instanceNo].options.plugins.annotation.annotations = {};
+ chartLine(instanceNo,'ICES7 Action Level 1',0,samples.length,0.01,0.01,'rgba(0, 0, 255, 0.5)',[3,3]);
+ chartLine(instanceNo,'All Action Level 1',0,samples.length,0.02,0.02,'rgba(255, 0, 0, 0.5)',[3,3]);
+ chartLine(instanceNo,'All Action Level 2',0,samples.length,0.2,0.2,'rgba(255, 0, 0, 0.5)',[5,5]);
+ gorX = samples.length * 0.1;
+ gorMax = 0.2;
+ for (const sample in sums) {
+ if (sums[sample].totalHC > gorMax) {
+ gorMax = sums[sample].ICES7;
+ }
+ if (sums[sample].All > gorMax) {
+ gorMax = sums[sample].All;
+ }
+ }
+ chartLabel(instanceNo,gorX,0.75*gorMax,'rgba(0, 0, 255, 0.5)','ICES7 Action Level 1 ');
+ chartLine(instanceNo,'Legend - ICES7 AL1',gorX*1.2,gorX*2.2,0.75*gorMax,0.75*gorMax,'rgba(0, 0, 255, 0.5)',actionLevelDashes[0]);
+ chartLabel(instanceNo,gorX,0.9*gorMax,'rgba(255, 0, 0, 0.5)','All Action Level 1 ');
+ chartLine(instanceNo,'Legend - All AL1',gorX*1.2,gorX*2.2,0.9*gorMax,0.9*gorMax,'rgba(255, 0, 0, 0.5)',actionLevelDashes[0]);
+ chartLabel(instanceNo,gorX,0.95*gorMax,'rgba(255, 0, 0, 0.5)','All Action Level 2 ');
+ chartLine(instanceNo,'Legend - All AL2',gorX*1.2,gorX*2.2,0.95*gorMax,0.95*gorMax,'rgba(255, 0, 0, 0.5)',actionLevelDashes[1]);*/
+ // Update the chart
+ chartInstance[instanceNo].update();
+}
+
+
+function findSamplesInSameLocation(clickedMapSample) {
+ for (ds in selectedSampleInfo) {
+ for (s in selectedSampleInfo[ds].position) {
+ if (clickedMapSample === s) {
+ lat = selectedSampleInfo[ds].position[s]['Position latitude'];
+ lon = selectedSampleInfo[ds].position[s]['Position longitude'];
+ }
+ }
+ }
+ clickedMapSamples = [];
+ for (ds in selectedSampleInfo) {
+ for (s in selectedSampleInfo[ds].position) {
+ const testPos = selectedSampleInfo[ds].position[s];
+ if (testPos['Position latitude'] === lat && testPos['Position longitude'] === lon) {
+ clickedMapSamples.push(ds + ': ' + s);
+ }
+ }
+ }
+ return clickedMapSamples
+}
+
+function createHighlights(meas, linLog, dateSampled, hoveredSample, isMarked) {
+ const datesSampled = Object.keys(selectedSampleInfo);
+ datesSampled.sort();
+ samples = [];
+ datesSampled.forEach(dateSampled => {
+ // for (const dateSampled in selectedSampleInfo) {
+ const ok = Object.keys(selectedSampleInfo[dateSampled].position);
+ // noSamples += Object.keys(selectedSampleInfo[dateSampled].position).length;
+ noSamples += ok.length;
+ for (const sample in selectedSampleInfo[dateSampled].position) {
+ samples.push(dateSampled + ': ' + sample);
+ }
+ // }
+ });
+ console.log(hoveredSample);
+ if (!dateSampled) {
+ clickedSamples = findSamplesInSameLocation(hoveredSample);
+ console.log('Not dateSampled');
+ } else {
+ clickedSamples = [];
+ clickedSamples[0] = dateSampled + ': ' + hoveredSample;
+ console.log('dateSampled');
+ }
+ console.log(clickedSamples);
+ const allChemicals = Object.keys(meas);
+ let clickedIndexes = [];
+ console.log('samples', samples);
+ console.log('meas[allChemicals[0]]', Object.keys(meas[allChemicals[0]]));
+ clickedSamples.forEach(clickedSample => {
+ index = -1;
+ samples.forEach(sample => {
+ index += 1;
+ if (sample === clickedSample) {
+ clickedIndexes.push(index);
+ }
+ });
+ });
+ console.log(clickedIndexes);
+ clickedIndexes.forEach(item => {
+ if (isMarked === null) {
+ console.log('doing the null bit');
+ if (highlighted[item]) {
+ highlighted[item] = false;
+ } else {
+ highlighted[item] = true;
+ }
+ } else {
+ highlighted[item] = !isMarked;
+ }
+ });
+ console.log(highlighted);
+ clickedIndexes.forEach(item => {
+ console.log(item);
+ for (let i = 1; i < lastInstanceNo + 1; i++) {
+ if (instanceType[i] === 'gorham' || instanceType[i] === 'chemical' || instanceType[i] === 'congener' || instanceType[i] === 'totalHC') {
+ if (highlighted[item]) {
+ displayChartHighlight(meas, linLog, i, dateSampled, item);
+ } else {
+ removeChartHighlight(meas, linLog, i, dateSampled, item);
+ }
+ }
+ }
+ });
+}
+
+// Function to display the chart based on the clicked sample
+function displayChartHighlight(meas, yLogLin, instanceNo, dateSampled, item) {
+ // Draw a rectangle around the clicked data
+console.log('about to highlight',instanceNo,item);
+ chartInstance[instanceNo].options.plugins.annotation.annotations[('tempBox-' + instanceNo + '-'+item)] = {
+ type: 'box',
+ xScaleID: 'x',
+ yScaleID: 'y',
+ xMin: item - 0.5, // Adjust based on your data and preferences
+ xMax: item + 0.5,
+ borderWidth: 2,
+ borderColor: 'red',
+ backgroundColor: 'rgba(255, 0, 0, 0.1)',
+ id: `tempBox-$(instanceNo)-$(item)`,
+ };
+ // Update the chart
+ chartInstance[instanceNo].update();
+}
+
+function removeChartHighlight(meas, yLogLin, instanceNo, dateSampled, item) {
+console.log('about to remove highlight',instanceNo,item);
+ delete chartInstance[instanceNo].options.plugins.annotation.annotations['tempBox-' + instanceNo + '-'+item];
+ // Update the chart
+ chartInstance[instanceNo].update();
+}
+
+
+
diff --git a/sdeMaps.js b/sdeMaps.js
new file mode 100644
index 0000000..840652a
--- /dev/null
+++ b/sdeMaps.js
@@ -0,0 +1,226 @@
+function sampleMap(meas, linLog) {
+ // Check if there's an existing map and remove it
+ if (map) {
+ map.remove();
+ }
+ // SampleInfo data structure
+ // Initialize the map
+ map = L.map('map').setView([54.596, -1.177], 13); // Set the initial center and zoom level
+
+ // Add OpenStreetMap tile layer
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+ attribution: '© OpenStreetMap contributors'
+ }).addTo(map);
+
+ latSum = 0;
+ lonSum = 0;
+ noLocations = 0;
+ minLat = null;
+ maxLat = null;
+ minLon = null;
+ maxLon = null;
+ let hoveredSample = null;
+
+ var greenIcon = L.icon({
+ iconUrl: 'blue-marker-icon.png', // Replace with the path to your marker icon
+ shadowUrl: 'marker-shadow.png',
+
+ iconSize: [38, 95], // size of the icon
+ shadowSize: [50, 64], // size of the shadow
+ iconAnchor: [22, 94], // point of the icon which will correspond to marker's location
+ shadowAnchor: [4, 62], // the same for the shadow
+ popupAnchor: [-3, -76] // point from which the popup should open relative to the iconAnchor
+ });
+
+ // Define a custom marker icon with a specific color
+ var CustomIcon = L.Icon.extend({
+ options: {
+ shadowUrl: 'marker-shadow.png',
+ iconSize: [25, 41], // Replace with the size of your marker icon
+ iconAnchor: [12, 41], // Replace with the anchor point of your marker icon
+ popupAnchor: [1, -34], // Replace with the popup anchor point of your marker icon
+ }
+ });
+ // Add markers for each sample
+ iconNo = 0;
+
+ const datesSampled = Object.keys(selectedSampleInfo);
+ datesSampled.sort();
+ datesSampled.forEach(dateSampled => {
+ currentIcon = new CustomIcon({ iconUrl: markerPngs[iconNo] });
+ iconNo = (iconNo + 1) % 9;
+ noSamples = 0;
+ for (const dateSampled in selectedSampleInfo) {
+ noSamples += Object.keys(selectedSampleInfo[dateSampled].position).length;
+ }
+ console.log('noSamples', noSamples);
+ highlighted = Array(noSamples).fill(false);
+ for (const sample in selectedSampleInfo[dateSampled].position) {
+ if (selectedSampleInfo[dateSampled].position[sample]['Position latitude']) {
+ lat = selectedSampleInfo[dateSampled].position[sample]['Position latitude'];
+ lon = selectedSampleInfo[dateSampled].position[sample]['Position longitude'];
+ // Create a marker for each sample
+ if (lat !== undefined && lon !== undefined) {
+ lat = parseFloat(lat);
+ lon = parseFloat(lon);
+ if (maxLat === null) {
+ minLat = lat;
+ maxLat = lat;
+ minLon = lon;
+ maxLon = lon;
+ } else {
+ if (lat > maxLat) {
+ maxLat = lat;
+ } else if (lat < minLat) {
+ minLat = lat;
+ }
+ if (lon > maxLon) {
+ maxLon = lon;
+ } else if (lon < minLon) {
+ minLon = lon;
+ }
+ }
+ // Create a marker for each sample
+ // const marker = L.marker([lat, lon]).addTo(map).bindPopup(`${sample}
Latitude: ${lat}
Longitude: ${lon}`);
+ const marker = L.marker([lat, lon], { icon: currentIcon }).addTo(map).bindPopup(`${sample}
Latitude: ${lat}
Longitude: ${lon}`);
+ /* const marker = L.circleMarker([lat, lon],
+ {radius: 4, color: 'white', fillColor: 'red', fillOpacity: 1}
+ ).addTo(map).bindPopup(`${sample}
Latitude: ${lat}
Longitude: ${lon}`);
+ marker.bindTooltip(sample, { permanent: false, direction: 'top' });*/
+ marker.isMarked = false;
+
+ // Add a click event listener to the marker
+ marker.on('click', function () {
+ hoveredSample = sample;
+ createHighlights(meas, linLog, null, hoveredSample, marker.isMarked);
+ if (!marker.isMarked) {
+ marker.isMarked = true;
+ } else {
+ marker.isMarked = false;
+ }
+ // Update the chart - in routintes
+ //console.log('update ',sample,i);
+ // chartInstance[i].update();
+ });
+
+ noLocations += 1;
+ latSum += parseFloat(lat);
+ lonSum += parseFloat(lon);
+ };
+ }
+
+ }
+ /* iconNo += 1;
+ if (iconNo > 8) {
+ iconNo = 0;
+ }*/
+ console.log(iconNo, dateSampled);
+ });
+
+ if (noLocations > 0) {
+ const centreLat = latSum / noLocations;
+ const centreLon = lonSum / noLocations;
+ var bounds = L.latLngBounds([minLat, minLon], [maxLat, maxLon]);
+ map.fitBounds(bounds);
+ //console.log('lat,lon ',minLat,minLon, maxLat,maxLon);
+ /* if (centreLat !== undefined && centreLon !== undefined) {
+ map.setView(new L.LatLng(centreLat, centreLon), 13);
+ }*/
+ // }
+ }
+
+}
+
+function randomColor() {
+ return '#' + Math.floor(Math.random() * 16777215).toString(16);
+}
+
+function exportChart() {
+ const now = new Date();
+ const formattedDate = now
+ .toISOString()
+ .slice(2, 16)
+ .replace(/[-T:]/g, ''); // Format: yymmddhhmm
+
+ for (i = 1; i 360) {
+// Use proj4js library to convert British National Grid to latitude and longitude
+proj4.defs("EPSG:27700", "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894 +units=m +no_defs");
+const point = proj4("EPSG:27700", "EPSG:4326", [parseInt(latitude, 10), parseInt(longitude, 10)]);
+
+return { latitude: point[1], longitude: point[0] };
+} else {
+return { latitude: parseCoordinate(latitude), longitude: parseCoordinate(longitude) };
+}
+
+// If the input doesn't match any recognized format, return null or handle accordingly
+return null;
+}
+
diff --git a/sdeSelections.js b/sdeSelections.js
new file mode 100644
index 0000000..642f1cd
--- /dev/null
+++ b/sdeSelections.js
@@ -0,0 +1,442 @@
+function openChemicalSelection(sampleMeasurements) {
+ const chemicalModal = document.getElementById('chemicalModal');
+ chemicalModal.style.display = 'block';
+
+ const sampleCheckboxes = document.getElementById('chemicalCheckboxes');
+ chemicalCheckboxes.innerHTML = '';
+
+ const datesSampled = Object.keys(selectedSampleMeasurements);
+
+ for (chemicalType in selectedSampleMeasurements[datesSampled[0]]) {
+ if (chemicalType !== 'Physical Data') {
+ const chemicals = Object.keys(selectedSampleMeasurements[datesSampled[0]][chemicalType].chemicals);
+
+ const checkboxContainer = document.createElement('div');
+ checkboxContainer.className = 'checkbox-container';
+
+ chemicals.forEach(chemical => {
+ const checkbox = document.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.id = `chemical`;
+ checkbox.name = 'chemical';
+ checkbox.value = chemical;
+ checkbox.checked = true; // Initially all chemicals are checked
+
+ const label = document.createElement('label');
+ label.htmlFor = `chemical_${chemical}`;
+ label.appendChild(document.createTextNode(chemical));
+
+ checkboxContainer.appendChild(checkbox);
+ checkboxContainer.appendChild(label);
+ checkboxContainer.appendChild(document.createElement('br'));
+ });
+
+ chemicalCheckboxes.appendChild(checkboxContainer);
+ }
+ }
+ }
+
+ function flipChemicalSelections(selection) {
+ const checkboxes = document.querySelectorAll('#chemicalCheckboxes input[type="checkbox"]');
+ checkboxes.forEach(checkbox => {
+ if (selection) {
+ checkbox.checked = true;
+ } else {
+ checkbox.checked = false;
+ };
+ });
+ }
+
+ function applyChemicalFilter() {
+ const containsText = document.getElementById('containsTextChemical').value.toLowerCase();
+ const checkboxes = document.querySelectorAll('#chemicalCheckboxes input[type="checkbox"]');
+ checkboxes.forEach(checkbox => {
+ if (containsText.length > 0) {
+ if (checkbox.nextSibling.textContent.toLowerCase().includes(containsText)) {
+ checkbox.checked = true;
+ } else {
+ checkbox.checked = false;
+ }
+ }
+ });
+ }
+
+ function closeChemicalSelection() {
+ selectChemicals();
+ const chemicalModal = document.getElementById('chemicalModal');
+ chemicalModal.style.display = 'none';
+ }
+
+ function selectChemicals() {
+ const checkboxes = document.querySelectorAll('input[name="chemical"]:checked');
+ const selectedChemicals = Array.from(checkboxes)
+ .filter(checkbox => checkbox.checked)
+ .map(checkbox => checkbox.value);
+ selectedSampleMeasurements = getSelectedChemicalSampleMeasurements(selectedChemicals);
+ selectedSampleInfo = getSelectedChemicalSampleInfo(selectedChemicals);
+ updateChart();
+ }
+
+ function getSelectedChemicalSampleMeasurements(selectedChemicals) {
+ selectedMeas = {};
+ for (dateSampled in sampleMeasurements) {
+ for (const chemicalType in selectedSampleMeasurements[dateSampled]) {
+ for (const chemical in selectedSampleMeasurements[dateSampled][chemicalType].chemicals) {
+ if (selectedChemicals.includes(chemical)) {
+ // Put here as if no chemicals selected then don't need chemical type
+ if (!selectedMeas[dateSampled]) {
+ selectedMeas[dateSampled] = {};
+ }
+ if (!selectedMeas[dateSampled][chemicalType]) {
+ selectedMeas[dateSampled][chemicalType] = {};
+ selectedMeas[dateSampled][chemicalType].chemicals = {};
+ if (chemicalType == 'PAH data') {
+ // Just copy all common data even if only 1 PAH is selected
+ selectedMeas[dateSampled][chemicalType].gorhamTest = selectedSampleMeasurements[dateSampled][chemicalType].gorhamTest;
+ selectedMeas[dateSampled][chemicalType].total = selectedSampleMeasurements[dateSampled][chemicalType].total;
+ selectedMeas[dateSampled][chemicalType].totalHC = selectedSampleMeasurements[dateSampled][chemicalType].totalHC;
+ selectedMeas[dateSampled][chemicalType].totalHCUnit = selectedSampleMeasurements[dateSampled][chemicalType].totalHCUnit;
+ }
+ }
+ selectedMeas[dateSampled][chemicalType].chemicals[chemical] = selectedSampleMeasurements[dateSampled][chemicalType].chemicals[chemical];
+ }
+ }
+ }
+ }
+ return selectedMeas;
+ }
+
+ function getSelectedChemicalSampleInfo(selectedChemicals) {
+ selectedSamps = {};
+ for (const dateSampled in selectedSampleMeasurements) {
+ for (const chemicalType in selectedSampleMeasurements[dateSampled]) {
+ selectedSamps[dateSampled] = {};
+ selectedSamps[dateSampled]['Date sampled'] = selectedSampleInfo[dateSampled]['Date sampled'];
+ selectedSamps[dateSampled].fileURL = selectedSampleInfo[dateSampled].fileURL;
+ selectedSamps[dateSampled].Applicant = selectedSampleInfo[dateSampled].Applicant;
+ selectedSamps[dateSampled]['Application number'] = selectedSampleInfo[dateSampled]['Application number'];
+ selectedSamps[dateSampled]['Application title'] = selectedSampleInfo[dateSampled]['Application title'];
+ selectedSamps[dateSampled].position = {};
+ for (const chemical in selectedSampleMeasurements[dateSampled][chemicalType].chemicals) {
+ for (const sample in selectedSampleMeasurements[dateSampled][chemicalType].chemicals[chemical].samples){
+ selectedSamps[dateSampled].position[sample] = selectedSampleInfo[dateSampled].position[sample];
+ }
+ }
+ }
+ }
+ return selectedSamps;
+ }
+
+ function openSampleSelection(sampleMeasurements) {
+ const sampleModal = document.getElementById('sampleModal');
+ sampleModal.style.display = 'block';
+
+ const sampleCheckboxes = document.getElementById('sampleCheckboxes');
+ sampleCheckboxes.innerHTML = '';
+
+ const datesSampled = Object.keys(sampleInfo);
+
+
+ datesSampled.sort();
+ datesSampled.forEach (dateSampled => {
+
+ // for (dateSampled in sampleInfo) {
+ const samples = Object.keys(sampleInfo[dateSampled].position);
+ const checkboxContainer = document.createElement('div');
+ checkboxContainer.className = 'checkbox-container';
+
+ samples.forEach(sample => {
+ const checkbox = document.createElement('input');
+ checkbox.type = 'checkbox';
+ checkbox.id = `sample_${dateSampled + ': ' + sample}`;
+ checkbox.name = 'sample';
+ checkbox.value = dateSampled + ': ' + sample;
+ checkbox.checked = true; // Initially all samples are checked
+
+ const label = document.createElement('label');
+ label.htmlFor = `sample_${sample}`;
+ label.appendChild(document.createTextNode(dateSampled + ': ' + sample));
+
+ checkboxContainer.appendChild(checkbox);
+ checkboxContainer.appendChild(label);
+ checkboxContainer.appendChild(document.createElement('br'));
+ });
+
+ sampleCheckboxes.appendChild(checkboxContainer);
+ });
+ }
+
+ function flipSampleSelections(selection) {
+ const checkboxes = document.querySelectorAll('#sampleCheckboxes input[type="checkbox"]');
+ checkboxes.forEach(checkbox => {
+ if (selection) {
+ checkbox.checked = true;
+ } else {
+ checkbox.checked = false;
+ };
+ });
+ }
+
+ function applySampleFilter() {
+ const containsText = document.getElementById('containsText').value.toLowerCase();
+ const minDepth = parseFloat(document.getElementById('minDepth').value);
+ const maxDepth = parseFloat(document.getElementById('maxDepth').value);
+ centreLat = parseFloat(document.getElementById('centreLat').value);
+ centreLon = parseFloat(document.getElementById('centreLon').value);
+ const centreDist = parseFloat(document.getElementById('centreDist').value);
+ const checkboxes = document.querySelectorAll('#sampleCheckboxes input[type="checkbox"]');
+ const checkboxesIn = document.querySelectorAll('input[name="sample"]:checked');
+ checkboxes.forEach(checkbox => {
+ if (containsText.length > 0) {
+ if (checkbox.nextSibling.textContent.toLowerCase().includes(containsText)) {
+ checkbox.checked = true;
+ } else {
+ checkbox.checked = false;
+ }
+ }
+ });
+ if (!(isNaN(minDepth) || isNaN(maxDepth)) && (minDepth <= maxDepth)) {
+ checkboxes.forEach(checkbox => {
+ checkbox.checked = false;
+ });
+ for (const dateSelected in sampleInfo) {
+ for (const sample in sampleInfo[dateSelected].position) {
+ const minSample = sampleInfo[dateSelected].position[sample]['Sampling depth (m)'].minDepth;
+ if (minDepth <= minSample) {
+ const maxSample = sampleInfo[dateSelected].position[sample]['Sampling depth (m)'].maxDepth;
+ if (maxDepth >= maxSample) {
+ const checkName = `sample_${dateSelected + ': ' + sample}`;
+ const checkbox = document.getElementById(checkName);
+ checkbox.checked = true;
+ }
+ }
+ }
+ }
+ }
+ // going to find samples close to a set of coordinates
+ if (!isNaN(centreDist)) {
+ // latitude and longitude not supplied
+ if ((isNaN(centreLat) || isNaN(centreLon))) {
+ // so assuming only one checkbox is ticked then find all samples near to that position
+ const selectedSamples = Array.from(checkboxesIn)
+ .filter(checkbox => checkbox.checked)
+ .map(checkbox => checkbox.value);
+ if (selectedSamples.length === 1) {
+ for (const dateSampled in sampleInfo) {
+ for (const sample in sampleInfo[dateSampled].position) {
+ if (selectedSamples.includes(dateSampled + ': ' + sample)) {
+ centreLat = sampleInfo[dateSampled].position[sample]['Position latitude'];
+ centreLon = sampleInfo[dateSampled].position[sample]['Position longitude'];
+ }
+ }
+ }
+ } else {
+ return
+ }
+ }
+ checkboxes.forEach(checkbox => {
+ checkbox.checked = false;
+ });
+ for (const dateSelected in sampleInfo) {
+ for (const sample in sampleInfo[dateSelected].position) {
+ const sampleLat = sampleInfo[dateSelected].position[sample]['Position latitude'];
+ const sampleLon = sampleInfo[dateSelected].position[sample]['Position longitude'];
+ distance = 1000 * haversineDistance(sampleLat, sampleLon, centreLat, centreLon);
+ if (distance <= centreDist) {
+ const checkName = `sample_${dateSelected + ': ' + sample}`;
+ const checkbox = document.getElementById(checkName);
+ checkbox.checked = true;
+ }
+ }
+ }
+ }
+ }
+
+ function haversineDistance(lat1, lon1, lat2, lon2) {
+ const R = 6371; // Radius of the Earth in kilometers
+ const dLat = toRadians(lat2 - lat1);
+ const dLon = toRadians(lon2 - lon1);
+ const a =
+ Math.sin(dLat / 2) * Math.sin(dLat / 2) +
+ Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+ const distance = R * c; // Distance in kilometers
+ return distance;
+ }
+
+ function toRadians(degrees) {
+ return degrees * (Math.PI / 180);
+ }
+
+
+ function closeSampleSelection() {
+ selectSamples();
+ const sampleModal = document.getElementById('sampleModal');
+ sampleModal.style.display = 'none';
+ }
+
+ function selectSamples() {
+ const checkboxes = document.querySelectorAll('input[name="sample"]:checked');
+ //console.log(checkboxes);
+ const selectedSamples = Array.from(checkboxes)
+ .filter(checkbox => checkbox.checked)
+ .map(checkbox => checkbox.value);
+ //console.log(selectedSamples);
+ selectedSampleMeasurements = getselectedSampleMeasurements(selectedSamples);
+ selectedSampleInfo = getSelectedSamples(selectedSamples);
+ updateChart();
+ }
+
+
+ function getselectedSampleMeasurements(selectedSamples) {
+ selectedMeas = {};
+ //console.log(selectedSamples);
+ for (dateSampled in sampleMeasurements) {
+ //console.log(dateSampled);
+ const chemicalTypes = Object.keys(sampleMeasurements[dateSampled]);
+ const chemicals = Object.keys(sampleMeasurements[dateSampled][chemicalTypes[0]].chemicals);
+ for (const chemicalType in sampleMeasurements[dateSampled]) {
+ if (chemicalType === 'Physical Data') {
+ for (const sample in sampleMeasurements[dateSampled][chemicalType].samples) {
+ if (selectedSamples.includes(dateSampled + ': ' + sample)) {
+ if (!selectedMeas[dateSampled]) {
+ selectedMeas[dateSampled] = {};
+ selectedMeas[dateSampled][chemicalType] = {};
+ selectedMeas[dateSampled][chemicalType].samples = {};
+ selectedMeas[dateSampled][chemicalType]['Unit of measurement'] = sampleMeasurements[dateSampled][chemicalType]['Unit of measurement'];
+ selectedMeas[dateSampled][chemicalType].sizes = sampleMeasurements[dateSampled][chemicalType].sizes;
+ //console.log('1 psd selectedMeas ',dateSampled,chemicalType,selectedMeas);
+ } else {
+ if (!selectedMeas[dateSampled][chemicalType]) {
+ selectedMeas[dateSampled][chemicalType] = {};
+ selectedMeas[dateSampled][chemicalType].samples = {};
+ selectedMeas[dateSampled][chemicalType]['Unit of measurement'] = sampleMeasurements[dateSampled][chemicalType]['Unit of measurement'];
+ selectedMeas[dateSampled][chemicalType].sizes = sampleMeasurements[dateSampled][chemicalType].sizes;
+ //console.log('2 psd selectedMeas ',dateSampled,chemicalType,selectedMeas);
+ }
+ //console.log('3 psd selectedMeas ',dateSampled,chemicalType,sample,selectedMeas);
+ selectedMeas[dateSampled][chemicalType].samples[sample] = {};
+ selectedMeas[dateSampled][chemicalType].samples[sample].psd = sampleMeasurements[dateSampled][chemicalType].samples[sample].psd;
+ }
+ }
+ }
+
+ //console.log('Create ', dateSampled, chemicalType);
+ //console.log('should do Gorham Test here');
+
+ } else {
+ for (const chemical in sampleMeasurements[dateSampled][chemicalType].chemicals) {
+ for (const sample in sampleMeasurements[dateSampled][chemicalType].chemicals[chemical].samples) {
+ //console.log(sample);
+ if (selectedSamples.includes(dateSampled + ': ' + sample)) {
+ //console.log('Found one');
+ if (!selectedMeas[dateSampled]) {
+ //console.log('Create ', dateSampled);
+ selectedMeas[dateSampled] = {};
+ // for (const chemicalType in sampleMeasurements[dateSampled]) {
+ selectedMeas[dateSampled][chemicalType] = {};
+ selectedMeas[dateSampled][chemicalType]['Unit of measurement'] = sampleMeasurements[dateSampled][chemicalType]['Unit of measurement'];
+ selectedMeas[dateSampled][chemicalType].chemicals = {};
+ //console.log('Create ', dateSampled, chemicalType);
+ //console.log('should do Gorham Test here');
+ if (chemicalType == 'PAH data') {
+ //console.log('Create ', dateSampled, chemicalType,'Gorham Test');
+ selectedMeas[dateSampled][chemicalType].gorhamTest = {};
+ selectedMeas[dateSampled][chemicalType].gorhamTest[sample] = sampleMeasurements[dateSampled][chemicalType].gorhamTest[sample];
+ selectedMeas[dateSampled][chemicalType].total = {};
+ selectedMeas[dateSampled][chemicalType].total[sample] = selectedSampleMeasurements[dateSampled][chemicalType].total[sample];
+ selectedMeas[dateSampled][chemicalType].totalHC = {};
+ selectedMeas[dateSampled][chemicalType].totalHCUnit = selectedSampleMeasurements[dateSampled][chemicalType].totalHCUnit;
+ selectedMeas[dateSampled][chemicalType].totalHC[sample] = selectedSampleMeasurements[dateSampled][chemicalType].totalHC[sample];
+ }
+ if (chemicalType == 'PCB data') {
+ //console.log('Create ', dateSampled, chemicalType,'Gorham Test');
+ selectedMeas[dateSampled][chemicalType].congenerTest = {};
+ selectedMeas[dateSampled][chemicalType].congenerTest[sample] = sampleMeasurements[dateSampled][chemicalType].congenerTest[sample];
+ }
+ for (const chemical in sampleMeasurements[dateSampled][chemicalType].chemicals) {
+ //console.log('Create ', dateSampled, chemicalType,chemical);
+ selectedMeas[dateSampled][chemicalType].chemicals[chemical] = {};
+ selectedMeas[dateSampled][chemicalType].chemicals[chemical].samples = {};
+ selectedMeas[dateSampled][chemicalType].chemicals[chemical].samples[sample] = sampleMeasurements[dateSampled][chemicalType].chemicals[chemical].samples[sample];
+ }
+ // }
+ } else {
+ // for (const chemicalType in sampleMeasurements[dateSampled]) {
+ //console.log('ch ', dateSampled,sample,chemicalType);
+ if (!selectedMeas[dateSampled][chemicalType]) {
+ selectedMeas[dateSampled][chemicalType] = {};
+ selectedMeas[dateSampled][chemicalType]['Unit of measurement'] = sampleMeasurements[dateSampled][chemicalType]['Unit of measurement'];
+ selectedMeas[dateSampled][chemicalType].chemicals = {};
+ if (chemicalType === 'PAH data') {
+ //console.log('Create ', dateSampled, chemicalType,'Gorham Test');
+ selectedMeas[dateSampled][chemicalType].gorhamTest = {};
+ selectedMeas[dateSampled][chemicalType].gorhamTest[sample] = sampleMeasurements[dateSampled][chemicalType].gorhamTest[sample];
+ }
+ if (chemicalType == 'PCB data') {
+ //console.log('Create ', dateSampled, chemicalType,'Gorham Test');
+ selectedMeas[dateSampled][chemicalType].congenerTest = {};
+ selectedMeas[dateSampled][chemicalType].congenerTest[sample] = sampleMeasurements[dateSampled][chemicalType].congenerTest[sample];
+ }
+ for (const chemical in sampleMeasurements[dateSampled][chemicalType].chemicals) {
+ //console.log('Create ', dateSampled, chemicalType,chemical);
+ selectedMeas[dateSampled][chemicalType].chemicals[chemical] = {};
+ selectedMeas[dateSampled][chemicalType].chemicals[chemical].samples = {};
+ selectedMeas[dateSampled][chemicalType].chemicals[chemical].samples[sample] = sampleMeasurements[dateSampled][chemicalType].chemicals[chemical].samples[sample];
+ }
+ } else {
+ if (chemicalType === 'PAH data') {
+ //console.log('Create ', dateSampled, chemicalType,'Gorham Test');
+ selectedMeas[dateSampled][chemicalType].gorhamTest[sample] = sampleMeasurements[dateSampled][chemicalType].gorhamTest[sample];
+ selectedMeas[dateSampled][chemicalType].total[sample] = selectedSampleMeasurements[dateSampled][chemicalType].total[sample];
+ selectedMeas[dateSampled][chemicalType].totalHC[sample] = selectedSampleMeasurements[dateSampled][chemicalType].totalHC[sample];
+ }
+ if (chemicalType == 'PCB data') {
+ //console.log('Create ', dateSampled, chemicalType,'Gorham Test');
+ selectedMeas[dateSampled][chemicalType].congenerTest[sample] = sampleMeasurements[dateSampled][chemicalType].congenerTest[sample];
+ }
+ for (const chemical in sampleMeasurements[dateSampled][chemicalType].chemicals) {
+ selectedMeas[dateSampled][chemicalType].chemicals[chemical].samples[sample] = sampleMeasurements[dateSampled][chemicalType].chemicals[chemical].samples[sample];
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // console.log(selectedMeas); // Output each cell value to console
+ return selectedMeas;
+ }
+
+ function getSelectedSamples(selectedSamples) {
+ selectedSamps = {};
+ for (const dateSampled in sampleInfo) {
+ for (const sample in sampleInfo[dateSampled].position) {
+ if (selectedSamples.includes(dateSampled + ': ' + sample)) {
+ // console.log('a point ' + dateSampled + ': ' + sample);
+ if (!selectedSamps[dateSampled]) {
+ selectedSamps[dateSampled] = {};
+ selectedSamps[dateSampled]['Date sampled'] = sampleInfo[dateSampled]['Date sampled'];
+ selectedSamps[dateSampled].fileURL = sampleInfo[dateSampled].fileURL;
+ selectedSamps[dateSampled].Applicant = sampleInfo[dateSampled].Applicant;
+ selectedSamps[dateSampled]['Application number'] = sampleInfo[dateSampled]['Application number'];
+ selectedSamps[dateSampled]['Application title'] = sampleInfo[dateSampled]['Application title'];
+ }
+ if (!selectedSamps[dateSampled].position) {
+ selectedSamps[dateSampled].position = {};
+ }
+ selectedSamps[dateSampled].position[sample] = sampleInfo[dateSampled].position[sample];
+ }
+ }
+ }
+ // console.log(selectedSamps); // Output each cell value to console
+ return selectedSamps;
+ }
+
+ function clearSelections() {
+ selectedSampleMeasurements = sampleMeasurements;
+ selectedSampleInfo = sampleInfo;
+ }
+