@ -725,6 +725,104 @@ function do_grid_monthly(myid, month, year, data, apiUrl) {
function do _grid _monthly _one ( myid , month , year , data , apiUrl ) {
const margin = { top : 60 , right : 20 , bottom : 20 , left : 20 } ;
const cellSize = 35 ;
const width = cellSize * 7 ; // Number of columns (days)
const height = cellSize * 7 ; // Number of rows (days)
// Create a tooltip element
const tooltip = d3 . select ( "body" )
. append ( "div" )
. attr ( "class" , "tooltip" )
. style ( "opacity" , 0 )
. style ( "position" , "absolute" ) ; // Set tooltip position to absolute
const svg = d3 . select ( ` # ${ myid } ` )
. append ( "svg" )
. attr ( "class" , "heatmap" )
. attr ( "width" , width + margin . left + margin . right )
. attr ( "height" , height + margin . top + margin . bottom )
. append ( "g" )
. attr ( "transform" , ` translate( ${ margin . left } , ${ margin . top } ) ` ) ;
// Convert data values to numbers
const numericData = data . map ( value => parseFloat ( value ) ) ;
// Create a color scale based on numeric data
const colorScale = d3 . scaleLinear ( )
. domain ( [ d3 . min ( numericData ) , d3 . max ( numericData ) ] )
. range ( [ "#0571b0" , "#ca0020" ] ) ; // Replace with your desired color range
const rect = svg . selectAll ( "rect" )
. data ( numericData )
. enter ( ) . append ( "rect" )
. attr ( "width" , cellSize )
. attr ( "height" , cellSize )
. attr ( "x" , ( d , i ) => ( ( i + getDayOffset ( month , year ) ) % 7 ) * cellSize )
. attr ( "y" , ( d , i ) => Math . floor ( ( i + getDayOffset ( month , year ) ) / 7 ) * cellSize )
. attr ( "fill" , d => colorScale ( d ) ) // Apply the color scale to data values
. on ( "mouseover" , function ( d ) {
// Show the tooltip on hover
const [ x , y ] = d3 . mouse ( this ) ;
const pageX = x + window . scrollX ; // Adjust for horizontal scroll
const pageY = y + window . scrollY ; // Adjust for vertical scroll
tooltip
. style ( "left" , ( d3 . event . pageX + 10 ) + "px" )
. style ( "top" , ( d3 . event . pageY - 10 ) + "px" )
. style ( "background-color" , "#c3c3c3" )
. style ( "opacity" , 1 )
. html ( "Value: " + d . toFixed ( 2 ) ) ;
} )
. on ( "mouseout" , ( ) => {
tooltip . style ( "opacity" , 0 ) ;
} ) ;
svg . selectAll ( ".day-number" )
. data ( numericData )
. enter ( ) . append ( "text" )
. attr ( "class" , "day-number" )
. attr ( "x" , ( d , i ) => ( ( i + getDayOffset ( month , year ) ) % 7 ) * cellSize + cellSize / 2 )
. attr ( "y" , ( d , i ) => Math . floor ( ( i + getDayOffset ( month , year ) ) / 7 ) * cellSize + cellSize / 2 )
. attr ( "text-anchor" , "middle" )
. attr ( "alignment-baseline" , "middle" )
. text ( ( d , i ) => i + 1 ) ;
const weekDays = [ "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" , "Sun" ] ;
svg . selectAll ( ".weekday" )
. data ( weekDays )
. enter ( ) . append ( "text" )
. attr ( "class" , "weekday" )
. attr ( "x" , ( d , i ) => i * cellSize + cellSize / 2 )
. attr ( "y" , - 10 )
. attr ( "text-anchor" , "middle" )
. text ( ( d ) => d ) ;
const firstDayOfMonth = new Date ( year , month - 1 , 1 ) ;
const dayOffset = ( firstDayOfMonth . getDay ( ) + 6 ) % 7 ; // Calculate the day offset for the first day of the month
const monthName = getMonth ( month ) ;
svg . append ( "text" )
. attr ( "class" , "year-label" )
. attr ( "x" , width )
. attr ( "y" , - 30 )
. attr ( "text-anchor" , "end" )
. text ( monthName + ", " + year ) ;
function getDayOffset ( month , year ) {
const firstDayOfMonth = new Date ( year , month - 1 , 1 ) ;
const dayOffset = ( firstDayOfMonth . getDay ( ) + 6 ) % 7 ; // Calculate the day offset for the first day of the month
return dayOffset ;
}
function getMonth ( month ) {
const months = [
"January" , "February" , "March" , "April" , "May" , "June" ,
"July" , "August" , "September" , "October" , "November" , "December"
] ;
return months [ month - 1 ] ;
}
}
//--------------------------------
@ -775,8 +873,8 @@ function do_grid_yearly(myid, year, myData) {
// Create a color scale based on the min, max, and range of values
const colorScale = d3 . scaleLinear ( )
. domain ( [ minValue , maxValue ] )
. range ( [ "#eff3ff" , "#08519c" ] ) ; // Specify the range of colors
. domain ( [ minValue , maxValue ] )
. range ( [ 0 , 1 ] ) ;
// Create a group for month labels
const monthLabelsGroup = svg . append ( "g" )
@ -810,96 +908,112 @@ function do_grid_yearly(myid, year, myData) {
const x = col * cellWidth + borderWidth ;
const y = row * cellHeight + borderWidth ;
// Month Label
const monthLabel = monthLabelsGroup . append ( "text" )
. attr ( "x" , x + cellWidth / 2 )
. attr ( "y" , y + 20 ) // Increased vertical position
. attr ( "font-size" , "18" )
. attr ( "text-anchor" , "middle" )
. text ( ` ${ month . name } ${ year } ` ) ;
// Create a border around the month
const border = svg . append ( "rect" )
. attr ( "x" , x )
. attr ( "y" , y + 30 ) // Increased vertical position
. attr ( "width" , cellWidth - 2 * borderWidth )
. attr ( "height" , cellHeight - 2 * borderWidth - 30 ) // Adjusted height to avoid overlap
. attr ( "fill" , "none" )
. attr ( "stroke" , "#ffffff" ) // Border
. attr ( "stroke-width" , borderWidth ) ;
// Calculate the y position for each month label
const monthLabelY = y + 20 + ( row * 20 ) ; // Add 20px margin top for each row
// Month Label
const monthLabel = monthLabelsGroup . append ( "text" )
. attr ( "x" , x + cellWidth / 2 )
. attr ( "y" , monthLabelY )
. attr ( "font-size" , "18" )
. attr ( "text-anchor" , "middle" )
. text ( ` ${ month . name } ${ year } ` ) ;
// Calendar Days
for ( let day = 1 ; day <= month . days ; day ++ ) {
// Get the value for the current day
const value = myData [ month . days * i + day - 1 ] ; // Calculate index based on month and day
// Weekday labels
const weekdays = [ 'Mon' , 'Tue' , 'Wed' , 'Thu' , 'Fri' , 'Sat' , 'Sun' ] ;
// Calculate the color based on the value
const color = d3 . interpolateViridis ( colorScale ( value ) ) ;
for ( let w = 0 ; w < weekdays . length ; w ++ ) {
const weekdayLabel = svg . append ( "text" )
. attr ( "x" , x + ( w * ( cellWidth / 7 ) ) + cellWidth / 14 )
. attr ( "y" , y + 55 ) // Increased vertical position
. attr ( "font-size" , "12" )
. attr ( "text-anchor" , "middle" )
. text ( weekdays [ w ] ) ;
}
// Calculate the position of the current day
const dayRow = Math . floor ( ( day - 1 ) / 7 ) ;
const dayCol = ( day - 1 ) % 7 ;
// Calculate the y position for each border rectangle
const borderY = y + 30 + ( row * 20 ) ; // Add 20px margin top for each row
// Create a border around the month
const border = svg . append ( "rect" )
. attr ( "x" , x )
. attr ( "y" , borderY )
. attr ( "width" , cellWidth - 2 * borderWidth )
. attr ( "height" , cellHeight - 10 ) // Decreased height to fit the day rectangles
. attr ( "fill" , "none" )
. attr ( "stroke" , "black" )
. attr ( "stroke-width" , borderWidth ) ;
// Calculate the number of weeks in the month
const numWeeks = Math . ceil ( month . days / 7 ) ;
function getDayOffset ( month , year ) {
const firstDayOfMonth = new Date ( year , month - 1 , 1 ) ;
const dayOffset = ( firstDayOfMonth . getDay ( ) + 6 ) % 7 ; // Calculate the day offset for the first day of the month
return dayOffset ;
}
// Loop through each week and create the day rectangles
for ( let week = 0 ; week < numWeeks ; week ++ ) {
for ( let day = 0 ; day < 7 ; day ++ ) {
const dayNumber = week * 7 + day + 1 ; // Calculate the day number
}
// Weekday labels
const weekdays = [ 'Mon' , 'Tue' , 'Wed' , 'Thu' , 'Fri' , 'Sat' , 'Sun' ] ;
// Calculate the x and y positions for each day rectangle
const rectX = x + ( day * ( cellWidth / 7 ) ) ;
const rectY = y + 30 + ( ( week + 1 ) * ( cellHeight / numWeeks ) ) ;
// Loop through each month and create the calendar heatmap
for ( let i = 0 ; i < months . length ; i ++ ) {
const month = months [ i ] ;
// Calculate row and column positions
const row = Math . floor ( i / columns ) ;
const col = i % columns ;
// Calculate the value index for the current day
const valueIndex = ( i * month . days ) + ( week * 7 ) + day ;
// Starting positions for months
const x = col * cellWidth + borderWidth ;
const y = row * cellHeight + borderWidth ;
// Get the value for the current day
const value = myData [ valueIndex ] ;
// Calculate the color based on the value
const color = colorScale ( value ) ;
// Create the day rectangle
const rect = svg . append ( "rect" )
. attr ( "x" , rectX )
. attr ( "y" , rectY )
. attr ( "width" , cellWidth / 7 )
. attr ( "height" , cellHeight / numWeeks )
. attr ( "fill" , value ? color : "white" ) // Set white background for empty values
. attr ( "stroke" , "none" )
. on ( "mouseover" , ( ) => handleMouseover ( dayNumber , value ) )
. on ( "mouseout" , ( ) => tooltip . style ( "opacity" , 0 ) ) ;
// Add the day number as text inside the day rectangle
const dayText = svg . append ( "text" )
. attr ( "x" , rectX + ( cellWidth / 14 ) )
. attr ( "y" , rectY + ( cellHeight / ( 2 * numWeeks ) ) )
. attr ( "font-size" , "12" )
. attr ( "text-anchor" , "middle" )
. text ( dayNumber ) ;
}
}
// Weekday labels
for ( let w = 0 ; w < weekdays . length ; w ++ ) {
const weekdayLabel = svg . append ( "text" )
. attr ( "x" , x + ( w * ( cellWidth / 7 ) ) + cellWidth / 14 )
. attr ( "y" , y + 55 ) // Increased vertical position
. attr ( "font-size" , "12" )
. attr ( "text-anchor" , "middle" )
. text ( weekdays [ w ] ) ;
}
}
// Calendar Days
for ( let day = 1 ; day <= month . days ; day ++ ) {
// Get the value for the current day
const value = myData [ month . days * i + day - 1 ] ; // Calculate index based on month and day
// Calculate the color based on the value
const color = d3 . interpolateViridis ( colorScale ( value ) ) ;
// Calculate the position of the current day
const dayRow = Math . floor ( ( day - 1 ) / 7 ) ;
const dayCol = ( day - 1 ) % 7 ;
// Create a rectangle for each day
svg . append ( "rect" )
. attr ( "x" , x + ( dayCol * ( cellWidth / 7 ) ) )
. attr ( "y" , y + 60 + ( dayRow * ( cellHeight / 6 ) ) ) // Increased vertical position
. attr ( "width" , cellWidth / 7 )
. attr ( "height" , cellHeight / 6 )
. attr ( "fill" , color )
. attr ( "stroke" , "#ffffff" ) // white border
. attr ( "class" , "day" )
. on ( "mouseover" , ( ) => {
const dayValue = myData [ month . days * i + day - 1 ] ; // Calculate index based on month and day
// Call the handleMouseover function with the correct value
handleMouseover ( day , dayValue ) ;
} )
. on ( "mouseout" , ( ) => {
tooltip . style ( "opacity" , 0 ) ;
} )
. append ( "text" ) // Add text to display the day number
. attr ( "x" , x + ( dayCol * ( cellWidth / 7 ) ) + cellWidth / 14 )
. attr ( "y" , y + 85 + ( dayRow * ( cellHeight / 6 ) ) ) // Increased vertical position
. attr ( "font-size" , "12" )
. attr ( "text-anchor" , "middle" )
. text ( day ) ;
}
}
}
}
function do _grid _yearlyXX ( myid , year , myData ) {
@ -1133,8 +1247,6 @@ for (let i = 0; i < months.length; i++) {
//--------------------------------
// HORIZON d3 diagram
function do _horizon _row ( data , myid , title , myDates ) {
var horizonChart = d3 . horizonChart ( )
. height ( 50 )
@ -1215,7 +1327,7 @@ function getApiUrls() {
document . querySelectorAll ( '#col-left div' ) . forEach ( function ( element , index ) {
var apiUrl = element . getAttribute ( 'apiurl' ) ;
var id = element . id ;
var order = apiUrlsCenter . length ;
var order = apiUrlsLeft . length ;
if ( apiUrl === 'txt' ) {
var clone = element . cloneNode ( true ) ;
clone . querySelectorAll ( 'span' ) . forEach ( function ( span ) {
@ -1254,7 +1366,7 @@ function getApiUrls() {
var apiUrl = element . getAttribute ( 'apiurl' ) ;
var id = element . id ;
var text = element . innerHTML . trim ( ) ; // Assuming the text is the innerHTML of the element
var order = apiUrlsCenter . length ;
var order = apiUrlsRight . length ;
if ( apiUrl === 'txt' ) {
var clone = element . cloneNode ( true ) ;
clone . querySelectorAll ( 'span' ) . forEach ( function ( span ) {
@ -1272,7 +1384,10 @@ function getApiUrls() {
// Similar modifications for #col-center and #col-right
var result = {
"name" : myHouse . xname ,
// FIXME : boardName will be entered in the save board form
"boardName" : "my Form name test" ,
"name" : boardName ,
"hus" : myParam ,
"description" : myHouse . subtitle ,
"col-left" : apiUrlsLeft ,
"col-center" : apiUrlsCenter ,
@ -1699,9 +1814,42 @@ var firstOpen = true;
// > makeApiRequiest
// > submit nuttons listener
//function to prepare the tha API request upon a board JSON
function loadBoards ( myJSON ) {
myJSON = '' ;
function getFormId ( api _url ) { return api _url [ 0 ] . toUpperCase ( ) + api _url . split ( "/" ) [ 1 ] [ 0 ] . toUpperCase ( ) ; } ;
function loadBoards ( ) {
//JAUME
// Test JSON Board:
myJSON = { "name" : "Charlie" , "hus" : "Charlie" , "description" : "Lorem <a href=\"#\">Ipsum is</a> simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen." , "col-left" : [ { "order" : 0 , "id" : "P-txt-728265" , "text" : "Here a text" , "api_url" : null } , { "order" : 1 , "id" : "parallel-8001586" , "text" : "" , "api_url" : "parallel/monthly?hus=Charlie&family=MP1_1&type=celsius&year=2020&month=1" } ] , "col-center" : [ { "order" : 0 , "id" : "G-txt-956767" , "text" : "Here a text" , "api_url" : null } , { "order" : 1 , "id" : "grid-190619" , "text" : "" , "api_url" : "grid/monthly?hus=Charlie&sensor=Temp_MP1_1_Pos1&type=celsius&year=2023&month=01" } ] , "col-right" : [ { "order" : 0 , "id" : "H-txt-518613" , "text" : "Here a text" , "api_url" : null } , { "order" : 1 , "id" : "horizon-986338" , "text" : "" , "api_url" : "horizon/daily?hus=Charlie&family=MP1_1&type=celsius&day=2023-04-12" } ] } ;
var cols = [ "col-left" , "col-center" , "col-right" ] ;
cols . forEach ( col => {
if ( col . length > 0 ) {
myJSON [ col ] . forEach ( element => {
// Get necessary variables:
var myColumn = col ;
var myid = element . id ;
var myTitle = element . text ;
// Create DIV
createMyDiv ( myid , myColumn , myTitle ) ;
// Check if is TXT or Diagram:
if ( element . text == "" ) {
cl ( element [ "api_url" ] ) ;
var apiUrl = element [ "api_url" ] ;
var formId = getFormId ( apiUrl ) ;
//createParallel(myid, formId, formData);
makeApiRequest ( formId , apiUrl , myid ) ;
} else {
var formId = "TXT" ;
createTXT ( myid , element . text ) ;
}
} ) ;
}
} ) ;
//Prepara the variables:
// formId, apiUrl, myid, myTitle, ...args
@ -1833,25 +1981,13 @@ function makeApiRequest(formId, apiUrl, myid, myTitle, ...args) {
case "HY" :
data . sensor _names . forEach ( element => {
var dataRow = data [ element ]
var element1 = [ 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.70 , 31.70 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.80 , 31.70 , 31.70 , 31.70 , 31.70 , 31.70 , 31.70 ] ;
do _horizon _row ( dataRow , "#" + myid , element , data . days ) ;
} ) ;
break ;
}
} )
. catch ( ( error ) => {
console . error ( "Fetch error:" , error ) ;
} ) ;
}
async function createHiddenVauesDiv ( myid , dataName , data ) {
// final childs for each data var
var mainDiv = document . getElementById ( myid ) ;
const childDiv = document . createElement ( "div" ) ;
const lastValueElement = document . createElement ( "div" ) ;
lastValueElement . id = dataName ;
var data2 = JSON . stringify ( data ) ;
lastValueElement . innerHTML = data2 ;
}