diff --git a/server.js b/server.js index 3aee006..8edb0ed 100644 --- a/server.js +++ b/server.js @@ -1,21 +1,21 @@ import express from 'express'; import dotenv from 'dotenv'; -//import { fileURLToPath } from 'url'; -//import { dirname } from 'path'; -//import fs from 'fs'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; +import fs from 'fs'; dotenv.config(); -// const __filename = fileURLToPath(import.meta.url); -// const __dirname = dirname(__filename); +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); const app = express(); const port = process.env.PORT; // Read the data file and parse it once when the server starts -//const dataPath = new URL('../Insect-dashboard/static/data/data.json', import.meta.url); -//const rawData = fs.readFileSync(fileURLToPath(dataPath)); -//const data = JSON.parse(rawData); +const dataPath = new URL('../Insect-dashboard/static/data/data.json', import.meta.url); +const rawData = fs.readFileSync(fileURLToPath(dataPath)); +const data = JSON.parse(rawData); app.set('view engine', 'ejs'); app.use(express.urlencoded({ extended: true })); @@ -32,31 +32,33 @@ app.get('/', (req, res) => { app.get('/vergelijken', (req, res) => { try { - console.log('every item:', data); - res.render('pages/compare.ejs', { data }); + // console.log('every item:', data); + res.render('pages/compare.ejs'); } catch (error) { console.error('Error reading JSON file:', error); res.status(500).render('pages/error.ejs', { error }); } }); -//app.get('/filteredData', (req, res) => { - //const selectedLocation = req.query.location; - //const selectedIdentification = req.query.identification; +app.get('/filteredData', (req, res) => { + const selectedProvince = req.query.province; + const selectedLocation = req.query.location; + const selectedIdentification = req.query.identification; // Implement your logic to filter data based on selectedLocation and selectedIdentification // Assuming your data is stored in the 'data' variable - // const filteredData = data.filter(item => { - //return ( - //(selectedLocation === 'All' || item.location_name === selectedLocation) && - // (selectedIdentification === 'All' || item.identification_nl === selectedIdentification) - //); - // }); + const filteredData = data.filter(item => { + return ( + (selectedLocation === 'All' || item.location_name === selectedLocation) && + (selectedIdentification === 'All' || item.identification_nl === selectedIdentification) && + (selectedProvince === 'All' || item.province === selectedProvince) + ); + }); - // console.log('Filtered Data:', filteredData); // Log the filtered data + console.log('Filtered Data:', filteredData); // Log the filtered data -// res.json(filteredData); -//}); + res.json(filteredData); +}); diff --git a/static/css/style-map.css b/static/css/style-map.css index d635a52..9d7e1e8 100644 --- a/static/css/style-map.css +++ b/static/css/style-map.css @@ -39,7 +39,6 @@ #tooltip { position: absolute; width: 200px; - /* height: 70px; */ z-index: 1099; top: 0; left: 300px; @@ -68,7 +67,7 @@ body, height: 100%; padding: 0; margin: 0; - font-family: 'Roboto', 'sans-serif'; + font-family: "Avenir", sans-serif; color: #24337A; } @@ -114,7 +113,6 @@ aside, width: 500px; z-index: 1100; position: absolute; - /* top: 0; */ background: #fff; box-shadow: 5px 5px 10px -4px rgba(0,0,0,0.1); } @@ -130,7 +128,6 @@ aside h3 { } aside:nth-of-type(1) { - /* left: -280px; */ left: 0px; height: 100%; transition: 0.8s all; @@ -255,7 +252,6 @@ aside:nth-of-type(1) { padding: 30px; } .gemeente-stats { - /* height: 55px; */ margin: 0 -30px; padding: 10px 25px; display: flex; @@ -460,9 +456,6 @@ section.bottomSearch { color: var(--switches-label-color); } -/* switch highlighters wrapper (sliding left / right) - - need wrapper to enable the even margins around the highlight box -*/ .switch-wrapper { position: absolute; top: 0; @@ -471,20 +464,12 @@ section.bottomSearch { padding: 0.15rem; z-index: 3; transition: transform .5s cubic-bezier(.77, 0, .175, 1); - /* transition: transform 1s; */ } - -/* switch box highlighter */ .switch { border-radius: 3rem; background: var(--switch-bg-color); height: 100%; } - -/* switch box labels - - default setup - - toggle afterwards based on radio:checked status -*/ .switch div { width: 100%; text-align: center; @@ -502,12 +487,10 @@ section.bottomSearch { .switches-container input:nth-of-type(1):checked~.switch-wrapper { transform: translateX(0%); } - /* slide the switch box from left to right */ .switches-container input:nth-of-type(2):checked~.switch-wrapper { transform: translateX(100%); } - /* toggle the switch box labels - first checkbox:checked - show first switch div */ .switches-container input:nth-of-type(1):checked~.switch-wrapper .switch div:nth-of-type(1) { opacity: 1; @@ -518,266 +501,166 @@ section.bottomSearch { opacity: 1; } -/* .toggleSwitch { - display: flex; - justify-content: space-between; - width: 200px; - margin: 0 auto; - } - - .toggleButton { - flex-grow: 1; - padding: 10px; - font-size: 16px; - cursor: pointer; - border: none; - border-radius: 5px; - } - - .toggleButton:nth-child(1) { - background-color: #3498db; - color: #fff; - } - - .toggleButton:nth-child(2) { - background-color: #ddd; - color: #333; - } */ - - /* .toggleButton.active { - Styling for the active state - background-color: #3498db; - color: #fff; - } */ - -/* .initiatives { - display: flex; - flex-direction: row; - margin-bottom: 10px; -} - -.initiatives p:nth-child(1) { - margin: 0; - font-size: 0.9em; - font-weight: 500; -} - -.initiatives p:nth-child(2) { - margin: 2px 0 0 0; - color: #6A67F6; - font-size: 0.6em; - font-weight: 500; -} - -.initiatives img { - width: 35px; - height: 35px; - margin-right: 10px; -} - -.more-button { - margin: 20px 10px; - text-align: center; - border-radius: 100px; - border: 3px solid #6A67F6; - color: #6A67F6; - padding: 8px 0; - transition: 0.5s all; -} - -.more-button:hover { - background-color: #9694FF; - border: 3px solid #9694FF; - color: #fff; -} - -.energy-buttons > div { - overflow: hidden; - height: 40px; - border-radius: 50px; - margin: 10px 0; - position: relative; - transition: 0.5s all; -} - -.energy-button-text { - position: absolute; - width: 160px; - text-align: right; - padding: 11px 20px 10px; - border-radius: 50px; - z-index: 1000; - transition: 0.3s all; - background-color: #9694FF; - color: #fff; - font-weight: 500; - font-size: 1em; - left: -120px; -} - -.energy-buttons div:nth-child(1) p { - width: 140px; -} - -.energy-buttons div:nth-child(4) p { - width: 125px; -} - -.round-button { - height: 40px; - width: 40px; - border-radius: 100px; - background-color: #fff; - z-index: 1100; - cursor: pointer; - transition: 0.5s all; - position: relative; - color: #24337A; - z-index: 1001; -} - -.round-button:hover { - background-color: #6A67F6; -} - -.round-button:hover img { - filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(182deg) brightness(158%) contrast(102%); - transition: 0.5s all; -} - -.energy-buttons > div:hover .energy-button-text { - left: 0px; -} - -.energy-buttons > div:hover { - width: 160px; +.no-display { + display: none; } -.round-button img { - width: 28px; - height: 28px; - margin: 6px 6px; -} */ - -/* #wind img { - width: 25px; - height: 25px; - margin: 6px 9px; +/*Id because it needs to overwrite the display of item itself*/ +#display-none { + display: none; } -#biogas img { - width: 27px; - margin: 8px 7px; -} */ - -/* #initiative-title { - margin: 40px 0 10px; - font-size: 1.2em; -} */ - -/*------- Classes from Teunis------ */ -/* #tabMenu { - z-index: 1100; - position: absolute; - bottom: 26px; - height: 50px; - right: 40px; - background-color: rgba(255, 255, 255, 0); - display: flex; - margin-top: 50px; -} +/* ---------------------------------------------------COMPARE-PAGE-STYLING--------------------------------------------------- */ -.tabBar { - background-color: rgba(247,247,247, 0.87); - width: 260px; - padding: 5px 20px; - max-height: 70px; - border-radius: 100px; - display: flex; - justify-content: space-around; - box-shadow: 0px 0px 25px rgba(195, 208, 222, 0.5); -} +aside.filter-sidebar { + width: 450px; + } -.tab { - text-align: center; -} +.home { + position: relative; + left: 500px; + height: 100vh; + width: calc(80% - 50px); + background-color: var(--body-color); + transition: var(--tran-05); + padding: 20px; + } -.tabBar div.active { - background-color: white; - border-radius: 100px; - padding: 0 15px 0 10px; -} + /* ToolTIP styling */ + .home #tooltip { + top: auto; + bottom: 0; + left: 10px; + height: fit-content; + width: fit-content; + background-color: #008c38; + border-bottom-right-radius: 0; + border-top-left-radius: 10px; + color: #fff; + } + + .home #tooltip h3{ + color: #fff; + } -.active p { - float: right; - font-size: 1em; + /* ----------------------Chart-styling----------------------- */ + + #chart { + width: calc(100% - 90px); + position: relative; + border: 2px solid #f2f2f2; + border-radius: 6px; + top: 20px; + } + + section.no-data { + display: grid; + align-items: center; + justify-content: center; + justify-items: center; + height: 290px; + align-content: center; + } + + section.no-data h1, + section.no-data h3{ + color: #008c38; font-weight: 400; - color: #6A67F6; - margin: 10px 0 auto 10px; -} - -.active img { - margin-top: 3px; - filter: invert(37%) sepia(70%) saturate(2530%) hue-rotate(226deg) brightness(101%) contrast(93%); -} */ - -/* .tabBar img { - width: 25px; - margin-top: 7px; -} - -.tabBar img:hover { - filter: brightness(2) saturate(5) hue-rotate(20deg);; -} */ - -/*------- End Teunis' classes------ */ - -/*JS classes*/ -/* .active-year { - background-color: #fff; - color: #6A67F6; - font-weight: 700; - box-shadow: 3px 3px 10px -2px rgba(0,0,0,0.15); - transition: 0.8s all; -} - -.active-energy { - background-color: #6A67F6; - color: #fff; -} - -.active-energy img { - filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(182deg) brightness(158%) contrast(102%); -} */ + } + + section.no-data h1{ + font-weight: 800; + } -.no-display { - display: none; -} + /* ----------------------filter-styling----------------------- */ -/*Id because it needs to overwrite the display of item itself*/ -#display-none { - display: none; -} + #filterForm { + position: relative; + width: 100%; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + } + + .form-field { + display: grid; + width: 100%; + margin-bottom: 18px; + border: 2px solid #f2f2f2; + border-radius: 6px; + padding: 12px; + } + .form-field .accordion i { + line-height: 15px; + font-size: 15px; + padding: 5px; + background: #f2f2f2; + color: #008c38; + border-radius: 6px; + display: inline-block; + position: absolute; + right: 15px; + } + .form-field button{ + border: none; + background: none; + } + .form-field button i { + line-height: 20px; + font-size: 20px; + color: #fff; + border-radius: 6px; + cursor: pointer; + } + + .form-field span:nth-child(2) { + display: grid; + margin-left: 15px; + } + + .filter-sidebar .bottomSearch{ + background-color: #008c38; + } + + section.bottomSearch .form-field { + border: none; + margin-bottom: 0; + padding: 0; + } + + .additional-item{ + cursor: pointer; + } + + .accordion { + cursor: pointer; + width: 100%; + text-align: left; + transition: all .6s; + } + + .panel { + display: none; + padding: 10px; + border-radius: 0 0 5px 5px; + width: 100%; + flex-wrap: wrap; + } + + .panel span { + width: calc(50% - 10px); + } + + .accordion.open { + background-color: #fff; + } -/*@media screen and (min-height: 800px) {*/ -/* #tabMenu { - right: calc(50vw - 80px); - z-index: 1100; - position: absolute; - bottom: 40px; - height: 50px; - background-color: rgba(255, 255, 255, 0); - display: flex; - margin-top: 50px; - }*/ - -/* #legenda { - left: auto; - right: 0; - }*/ - -/* .year-selector { - bottom: 0; - top: auto; - }*/ -/*}*/ \ No newline at end of file + #filterSummary { + box-shadow: 5px 5px 10px -4px rgba(0,0,0,0.1); + /* height: 20px; */ + width: fit-content; + padding: 15px; + border-radius: 6px; + background: #f2f2f2; + opacity: 0; + } \ No newline at end of file diff --git a/static/css/style.css b/static/css/style.css deleted file mode 100644 index d524e02..0000000 --- a/static/css/style.css +++ /dev/null @@ -1,537 +0,0 @@ -/* ---------------------------------------------------STANDARD-STYLING--------------------------------------------------- */ -*{ - margin: 0; - padding: 0; - box-sizing: border-box; -} - -:root { - /* ===== Colors ===== */ - --body-color: #fff; - --sidebar-color: #75F; - --primary-color: #695cfe; - --primary-color-light: #f6f5ff; - --toggle-color: #ddd; - --text-color: #fff; - --text-color-2: #2a2a2a; - - /* ====== Transition ====== */ - --tran-03: all 0.2s ease; - --tran-03: all 0.3s ease; - --tran-04: all 0.3s ease; - --tran-05: all 0.3s ease; -} - -::selection { - color: #fff; - background: #6A67F6; -} - -::-moz-selection { - color: #fff; - background: #6A67F6; -} - -body { - min-height: 100vh; - background-color: var(--body-color); - transition: var(--tran-05); - display: flex; - flex-wrap: nowrap; - font-family: "Avenir", sans-serif; -} - -::selection { - background-color: var(--primary-color); - color: #fff; -} - -body.dark { - --body-color: #18191a; - --sidebar-color: #242526; - --primary-color: #3a3b3c; - --primary-color-light: #3a3b3c; - --toggle-color: #fff; - --text-color: #ccc; - --text-color-2: #ccc; -} - -/* ---------------------------------------------------HAMBURGER-MENU--------------------------------------------------- */ - -/* ===== Sidebar ===== */ -.sidebar { - position: fixed; - top: 0; - left: 0; - height: 100%; - width: 88px; - padding: 10px 14px; - /* background: var(--sidebar-color); */ - transition: var(--tran-05); - z-index: 100; - border-right: solid 2px #f2f2f2; -} - - -/* ===== Reusable code - Here ===== */ -.sidebar li { - height: 50px; - list-style: none; - display: flex; - align-items: center; - margin-top: 10px; -} - -.sidebar header .image { - min-width: 60px; - border-radius: 6px; -} - -.sidebar .text { - color: var(--text-color); - transition: var(--tran-03); -} - -.sidebar .text-2 { - color: var(--text-color-2); - transition: var(--tran-03); - margin-left: 20px; -} - -.sidebar .text { - font-size: 17px; - font-weight: 500; - white-space: nowrap; - opacity: 1; -} -/* =========================== */ - -.sidebar header { - position: relative; -} - -.sidebar header .image-text { - display: flex; - align-items: center; -} -.sidebar header .logo-text { - display: flex; - flex-direction: column; -} -header .image-text .name { - margin-top: 2px; - font-size: 18px; - font-weight: 600; -} - -.sidebar header .image { - display: flex; - align-items: center; - justify-content: center; -} - -.sidebar header .image img { - width: 40px; - border-radius: 6px; -} - -.sidebar header .toggle { - position: absolute; - top: 50%; - right: -25px; - transform: translateY(-50%) rotate(180deg); - height: 25px; - width: 25px; - background-color: var(--primary-color); - color: var(--sidebar-color); - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; - cursor: pointer; - transition: var(--tran-05); -} - -body.dark .sidebar header .toggle { - color: var(--text-color); -} - -.sidebar .menu { - margin-top: 40px; -} -.sidebar li a { - list-style: none; - background-color: transparent; - display: flex; - align-items: center; - height: 20px; - width: 100%; - border-radius: 6px; - text-decoration: none; - transition: var(--tran-03); - padding: 15px; - color: var(--text-color-2); -} - -.sidebar li a i{ - font-size: 25px; -} - -.sidebar li a:after { - content: ""; - position: absolute; - height: 60px; - width: 3px; - background-color: var(--sidebar-color); - right: -2px; -} - -.sidebar li a:hover { - list-style: none; - background-color: var(--sidebar-color); - border-color: none; - color: var(--toggle-color); -} - -.sidebar .menu-bar { - height: calc(100% - 55px); - display: flex; - flex-direction: column; - justify-content: space-between; - overflow-y: scroll; -} -.menu-bar::-webkit-scrollbar { - display: none; -} -.sidebar .menu-bar .mode { - border-radius: 6px; - background-color: var(--primary-color-light); - position: relative; - transition: var(--tran-05); -} - -.menu-bar .mode .sun-moon { - height: 50px; - width: 60px; -} - -.mode .sun-moon i { - position: absolute; -} -.mode .sun-moon i.sun { - opacity: 0; -} -body.dark .mode .sun-moon i.sun { - opacity: 1; -} -body.dark .mode .sun-moon i.moon { - opacity: 0; -} - -.menu-bar .bottom-content .toggle-switch { - position: absolute; - right: 0; - height: 100%; - min-width: 60px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 6px; - cursor: pointer; -} -.toggle-switch .switch { - position: relative; - height: 22px; - width: 40px; - border-radius: 25px; - background-color: var(--toggle-color); - transition: var(--tran-05); -} - -.switch::before { - content: ""; - position: absolute; - height: 15px; - width: 15px; - border-radius: 50%; - top: 50%; - left: 5px; - transform: translateY(-50%); - background-color: var(--sidebar-color); - transition: var(--tran-04); -} - -body.dark .switch::before { - left: 20px; -} - -.home { - position: relative; - left: 89px; - height: calc(100vh - 60px); - width: 80%; - background-color: var(--body-color); - transition: var(--tran-05); - padding: 20px; -} -.home .text { - font-size: 30px; - font-weight: 500; - color: var(--text-color); - padding: 12px 30px; -} - -body.dark .home .text { - color: var(--text-color); -} - -/* ---------------------------------------------------SIDEBAR-MENU--------------------------------------------------- */ -nav menuitem { - position:relative; - display:block; - opacity:0; - cursor:pointer; -} - -nav menuitem > menu { - position: absolute; - pointer-events:none; -} -nav > menu { display:flex; } - -nav > menu > menuitem { pointer-events: all; opacity:1; } -menu menuitem a { white-space:nowrap; display:block; } - -menuitem:hover > menu { - pointer-events:initial; -} -menuitem:hover > menu > menuitem, -menu:hover > menuitem{ - opacity:1; -} -nav > menu > menuitem menuitem menu { - transform:translateX(100%); - top:0; right:0; -} - -sidebar { - position: absolute; - top: 0; - right:20px; -} - -nav.sideGraph a { - background:var(--sidebar-color);; - color:#FFF; - min-width:190px; - transition: background 0.5s, color 0.5s, transform 0.5s; - margin:0px 6px 6px 0px; - padding:20px 40px; - box-sizing:border-box; - border-radius:3px; - box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.5); - position:relative; -} - -nav.sideGraph a:nth-child(1){ - border-top-right-radius: 0; - border-top-left-radius: 0; -} - -nav.sideGraph a:hover:before { - content: ''; - top:0;left:0; - position:absolute; - background:rgba(0, 0, 0, 0.2); - width:100%; - height:100%; -} - -nav > menu > menuitem > a + menu:after{ - content: ''; - position:absolute; - border:10px solid transparent; - border-top: 10px solid white; - left:12px; - top: -40px; -} -nav menuitem > menu > menuitem > a + menu:after{ - content: ''; - position:absolute; - border:10px solid transparent; - border-left: 10px solid white; - top: 20px; - left:-180px; - transition: opacity 0.6, transform 0s; -} - -nav > menu > menuitem > menu > menuitem{ - transition: transform 0.6s, opacity 0.6s; - transform:translateY(150%); - opacity:0; -} -nav > menu > menuitem:hover > menu > menuitem, -nav > menu > menuitem.hover > menu > menuitem{ - transform:translateY(0%); - opacity: 1; -} - -menuitem > menu > menuitem > menu > menuitem{ - transition: transform 0.6s, opacity 0.6s; - transform:translateX(195px) translateY(0%); - opacity: 0; -} -menuitem > menu > menuitem:hover > menu > menuitem, -menuitem > menu > menuitem.hover > menu > menuitem{ - transform:translateX(0) translateY(0%); - opacity: 1; -} - - - - -#filterForm { - position: relative; - width: 80%; - display: flex; - flex-wrap: wrap; - justify-content: flex-start; -} - -.form-field { - display: flex; - align-items: center; - margin-right: 18px; - border: 2px solid #f2f2f2; - border-radius: 6px; - padding: 12px; -} - -.form-field i { - line-height: 20px; - font-size: 20px; - padding: 15px; - background: #f2f2f2; - color: var(--sidebar-color); - border-radius: 6px; -} -.form-field button{ - border: none; - background: none; -} -.form-field button i { - line-height: 20px; - font-size: 20px; - padding: 15px; - background: var(--sidebar-color); - color: var(--toggle-color); - border-radius: 6px; - cursor: pointer; -} - -.form-field span:nth-child(2) { - display: grid; - margin-left: 15px; -} - - -#chart { - width: calc(100% - 90px); - position: relative; - border: 2px solid #f2f2f2; - border-radius: 6px; - top: 20px; -} - -section.sidebarMenu { - position: relative; - width: 20%; - right: 0; - background-color: #f2f2f2; -} - -.biomass, -.grafieken{ - margin-top: 10px; - padding: 20px; -} -.widget { - margin: 0 auto; - width: 100%; - margin-top: 10px; - border-radius: 5px; - display: flex; - justify-content: center; -} - -.sidebarMenu ul { - width: 100%; - margin-top: 20px; - display: flex; - justify-content: space-between; -} - -.sidebarMenu li { - position: relative; - list-style-type: none; - display: grid; - margin-right: 20px; -} - -.sidebarMenu li::after { - content: ""; - position: absolute; - background-color: red; - width: 1px; - height: 100%; - left: -5px; -} -.sidebarMenu li:nth-child(1)::after { - background-color: #1ECAD3; -} -.sidebarMenu li:nth-child(2)::after { - background-color: #00A0C6; -} -.sidebarMenu li:nth-child(3)::after { - background-color: #F7A92E; -} - -.sidebarMenu .grafieken li::after{ - display: none; -} - -.grafieken ul { - display: flex; - flex-wrap: wrap; - position: relative; -} - -.grafieken ul li { - display: flex; - align-items: center; - width: 100%; - height: 40px; - margin-right: 0; - padding: 10px; - color: var(--toggle-color); - margin-top: 10px; - border-radius: 6px; - background-color: #fff; - color: var(--text-color-2); -} - -.grafieken ul li:hover { - cursor: pointer; - background-color: var(--sidebar-color); - color: var(--toggle-color); -} - -.grafieken ul li:nth-child(3) { - background-color: var(--sidebar-color); - color: var(--toggle-color); -} - -.grafieken ul li i { - margin-right: 10px; -} \ No newline at end of file diff --git a/static/js/charts-map.js b/static/js/charts-map.js index f878251..873345a 100644 --- a/static/js/charts-map.js +++ b/static/js/charts-map.js @@ -82,3 +82,68 @@ svg.append('g') svg.append('g') .attr('transform', 'translate(0,' + svg.attr('height') + ')') // .call(d3.axisBottom(xScale)); + + + // set the dimensions and margins of the graph +const margin = {top: 10, right: 10, bottom: 10, left: 10}, +width = 420 - margin.left - margin.right, +height = 200 - margin.top - margin.bottom; + +// append the svg object to the body of the page +const svgTree = d3.select("#treemap") +.append("svg") +.attr("width", width + margin.left + margin.right) +.attr("height", height + margin.top + margin.bottom) +.append("g") +.attr("transform", `translate(${margin.left}, ${margin.top})`); + +// Define data in JavaScript +const dataTree = [ +{ name: "Root", parent: "", value: 200 }, +{ name: "A", parent: "Root", value: 200 }, +{ name: "1", parent: "A", value: 50 }, +{ name: "2", parent: "A", value: 20 }, +{ name: "3", parent: "A", value: 35 }, +{ name: "4", parent: "A", value: 65 }, +{ name: "5", parent: "A", value: 30 }, +]; + +// stratify the data: reformatting for d3.js +const root = d3.stratify() +.id(function(d) { return d.name; }) +.parentId(function(d) { return d.parent; }) +(dataTree); + +root.sum(function(d) { return +d.value }); + +d3.treemap() +.size([width, height]) +.padding(2) +(root); + +// use this information to add rectangles: +svgTree.selectAll("rect") +.data(root.leaves()) +.join("rect") + .attr('x', function (d) { return d.x0; }) + .attr('y', function (d) { return d.y0; }) + .attr('width', function (d) { return d.x1 - d.x0; }) + .attr('height', function (d) { return d.y1 - d.y0; }) + .style("fill", "#008c38") + .on("mouseover", function (event, d) { + tooltip.attr("class", "active") + tooltip.html(`

Top ${d.data.name}:

${d.data.value}x gemeten`) +}) +.on("mouseout", function (d) { + tooltip.attr("class" , "") +}); + +// and to add the text labels +svgTree.selectAll("text") +.data(root.leaves()) +.join("text") + .attr("x", function(d){ return d.x0+10}) + .attr("y", function(d){ return d.y0+20}) + .text(function(d){ return d.data.name}) + .attr("font-size", "15px") + .attr("fill", "white"); diff --git a/static/js/filtering.js b/static/js/filtering.js new file mode 100644 index 0000000..1574d4d --- /dev/null +++ b/static/js/filtering.js @@ -0,0 +1,144 @@ +document.addEventListener("DOMContentLoaded", function () { + // Fetch JSON data on the client side + fetch("data/data.json") // Assuming this endpoint returns the necessary data + .then((response) => response.json()) + .then((data) => { + // Do something with the data + console.log("Fetched data:", data); + + // Get unique province names + var uniqueProvinces = [...new Set(data.map((item) => item.province))]; + var uniqueLocations = [ + ...new Set(data.map((item) => item.location_name)), + ]; + var uniqueIdentifications = [ + ...new Set(data.map((item) => item.identification_nl)), + ]; + + // Update the province dropdown + var provinceDropdown = document.getElementById("provinceDropdown"); + uniqueProvinces.forEach((provinceName) => { + var option = document.createElement("option"); + option.value = provinceName; + option.textContent = provinceName; + provinceDropdown.appendChild(option); + }); + + // Update the location dropdown initially with all locations + var locationDropdown = document.getElementById("locationDropdown"); + populateDropdown(locationDropdown, uniqueLocations); + + // Update the identification dropdown initially with all identifications + var identificationDropdown = document.getElementById( + "identificationDropdown" + ); + populateDropdown(identificationDropdown, uniqueIdentifications); + + // Add event listener to the province dropdown + provinceDropdown.addEventListener("change", function () { + var selectedProvince = provinceDropdown.value; + // Filter locations and identifications based on the selected province + var filteredData = data.filter( + (item) => item.province === selectedProvince + ); + var uniqueLocationsForProvince = [ + ...new Set(filteredData.map((item) => item.location_name)), + ]; + var uniqueIdentificationsForProvince = [ + ...new Set(filteredData.map((item) => item.identification_nl)), + ]; + + // Update the location dropdown with filtered locations + updateDropdown(locationDropdown, uniqueLocationsForProvince); + + // Update the identification dropdown with filtered identifications + updateDropdown( + identificationDropdown, + uniqueIdentificationsForProvince + ); + }); + + // Add event listener to the button for applying filters + var updateBtn = document.getElementById("updateBtn"); + updateBtn.addEventListener("click", function () { + // Get the selected values + var selectedProvince = provinceDropdown.value; + var selectedLocation = locationDropdown.value; + var selectedIdentification = identificationDropdown.value; + + // Update the filter summary + updateFilterSummary( + selectedProvince, + selectedLocation, + selectedIdentification + ); + }); + }) + .catch((error) => { + console.error("Error fetching JSON:", error); + }); + + // Function to populate a dropdown with options + function populateDropdown(dropdown, options) { + options.forEach((value) => { + var option = document.createElement("option"); + option.value = value; + option.textContent = value; + dropdown.appendChild(option); + }); + } + + // Function to update a dropdown with new options + function updateDropdown(dropdown, options) { + // Clear existing options + dropdown.innerHTML = ""; + // Populate with new options + populateDropdown(dropdown, options); + } + + // Function to update the filter summary + function updateFilterSummary(province, location, identification) { + var summaryDiv = document.getElementById("filterSummary"); + summaryDiv.style.opacity = "1"; + summaryDiv.innerHTML = `

Geselecteerde filters:

`; + + if (province !== "All") { + summaryDiv.innerHTML += `

Provincie: ${province}

`; + } + + if (location !== "alle locaties") { + summaryDiv.innerHTML += `

Locatie: ${location}

`; + } + + if (identification !== "All") { + summaryDiv.innerHTML += `

Soort: ${identification}

`; + } + } +}); + + +function togglePage(page) { + setTimeout(function () { + window.location.href = page; + }, 500); // 500 milliseconds delay +} + +function toggleAccordion(accordion) { + accordion.classList.toggle('open'); + var panel = accordion.nextElementSibling; + if (panel.style.display === 'flex') { + panel.style.display = 'none'; + } else { + panel.style.display = 'flex'; + } +} + +function addFilterItem() { + var formField = document.querySelector('.form-field'); + var newItem = formField.cloneNode(true); + var itemCount = document.querySelectorAll('.form-field').length + 1; + var newId = 'item' + itemCount; + newItem.id = newId; + newItem.querySelector('.accordion').textContent = 'Item ' + itemCount; + formField.parentNode.insertBefore(newItem, formField.nextSibling); +} \ No newline at end of file diff --git a/static/js/script-d3.js b/static/js/script-d3.js index 48f1544..84ad2eb 100644 --- a/static/js/script-d3.js +++ b/static/js/script-d3.js @@ -3,7 +3,6 @@ import * as d3 from "https://unpkg.com/d3?module"; // Declare variables for chart setup const body = document.querySelector("body"); -const modeSwitch = body.querySelector(".toggle-switch"); // Function to aggregate data based on date function aggregateData(data) { @@ -37,9 +36,9 @@ function aggregateData(data) { } // Function to update the chart based on selected values -function updateChart(selectedLocation, selectedIdentification) { +function updateChart(selectedLocation, selectedIdentification, selectedProvince) { fetch( - `/filteredData?location=${selectedLocation}&identification=${selectedIdentification}` + `/filteredData?location=${selectedLocation}&identification=${selectedIdentification}&province=${selectedProvince}` ) .then((response) => response.json()) .then((filteredData) => { @@ -54,29 +53,32 @@ function updateChart(selectedLocation, selectedIdentification) { // Add an event listener to the "Update Chart" button document.getElementById("updateBtn").addEventListener("click", function () { const selectedLocation = document.getElementById("locationDropdown").value; - const selectedIdentification = document.getElementById( - "identificationDropdown" - ).value; + const selectedIdentification = document.getElementById("identificationDropdown").value; + const selectedProvince = document.getElementById("provinceDropdown").value - updateChart(selectedLocation, selectedIdentification); + updateChart(selectedLocation, selectedIdentification, selectedProvince); }); // Function to create the initial D3.js chart function createChart(data) { + + // Check if data is empty + if (data.length === 0) { + document.getElementById("chart").innerHTML = `

Geen data gevonden?

Selecteer eerst uw vergelijkings wensen voordat u de data kunt bekijken

`; + return; + } + data.forEach((d) => { try { if (d.date && typeof d.date === "string") { const [day, month, year] = d.date.split("-"); d.date = new Date(`${year}-${month}-${day}`); - console.log("Parsed date:", d.date); } } catch (error) { console.error("Error processing data:", d, "Error:", error); } }); - console.log("received data:", data); - // Specify the chart’s dimensions. const width = 1800; const height = 700; @@ -91,8 +93,6 @@ function createChart(data) { .domain(d3.extent(data, (d) => d.date)) .range([marginLeft, width - marginRight]); - console.log("X Scale Domain:", x.domain()); - const y = d3 .scaleLinear() .domain([0, d3.max(data, (d) => d.count)]) @@ -115,17 +115,13 @@ function createChart(data) { .x((d) => x(d.date)) .y((d) => y(d.count)); - console.log("Line generator:", line); - // Create the SVG container. const svg = d3 .create("svg") .attr("viewBox", [0, 0, width, height]) .attr("width", width) .attr("height", height) - .attr("style", "max-width: 100%; height: auto;"); - - console.log("Created SVG container:", svg); + .attr("style", "max-width: 90%; height: auto;"); // Create a clip-path with a unique ID. const clip = svg @@ -143,12 +139,10 @@ function createChart(data) { .datum(data) .attr("clip-path", "url(#clip)") .attr("fill", "none") - .attr("stroke", "steelblue") + .attr("stroke", "#008c38") .attr("stroke-width", 1.5) .attr("d", line); - console.log("Created line:", path); - // Append circles for each data point with tooltips svg.selectAll("circle") .data(data) @@ -157,8 +151,11 @@ svg.selectAll("circle") .attr("cx", (d) => x(d.date)) .attr("cy", (d) => y(d.count)) .attr("r", 4) // Radius of the circle -.attr("fill", "steelblue") -.on("mouseover", (event, d) => showTooltip(event, d)) +.attr("fill", "#008c38") +.on("mouseover", (event, d) => { + showTooltip(event, d); + d3.select(event.currentTarget).style("cursor", "pointer"); +}) .on("mouseout", hideTooltip); @@ -200,27 +197,20 @@ fetch("/filteredData") }) .catch((error) => console.error("Error fetching data:", error)); -modeSwitch.addEventListener("click", () => { - body.classList.toggle("dark"); -}); - // Function to show tooltip function showTooltip(event, data) { const tooltip = d3.select("#tooltip"); - tooltip.transition().duration(200).style("opacity", 0.9); - - // Check if data.probability exists before using it - const probabilityText = data.object_order_probability !== undefined ? `
Probability: ${data.object_order_probability.toFixed(3)}` : ''; + tooltip.transition().duration(200).style("opacity", 1); - tooltip.html(`Date: ${data.date.toDateString()}
${probabilityText}`) - .style("left", (event.pageX) + "px") - .style("top", (event.pageY - 28) + "px"); + tooltip.html(` +

Wanneer is deze insect gemeten:

+ Datum: ${data.date ? data.date.toLocaleDateString('nl-NL') : 'N/A'}`); } // Function to hide tooltip function hideTooltip() { - d3.select("#tooltip").transition().duration(500).style("opacity", 0); + d3.select("#tooltip").transition().duration(200).style("opacity", 0); } diff --git a/views/pages/compare.ejs b/views/pages/compare.ejs index 114327a..8df70e8 100644 --- a/views/pages/compare.ejs +++ b/views/pages/compare.ejs @@ -4,8 +4,8 @@ - Insect Dashboard - Home - + Insect Dashboard - Vergelijken + - + +
-
-
- - - - - -
- -
- - - - - - -
- -
- -
-
- -
+ + +
+
+ +
+ diff --git a/views/pages/index.ejs b/views/pages/index.ejs index 92ddf93..520e223 100644 --- a/views/pages/index.ejs +++ b/views/pages/index.ejs @@ -16,7 +16,7 @@ href="https://unpkg.com/boxicons@2.1.1/css/boxicons.min.css" rel="stylesheet" /> - Insecten-Dashboard + Insecten-Dashboard - Home @@ -75,14 +75,14 @@

Top 3 meest voorkomende insecten families

-
charts icon
-

Top 10 Meest voorkomende soorten

+

Top 5 Meest voorkomende soorten

+