diff --git a/src/app/modules/soundscape/components/perception-chart/perception-chart.component.html b/src/app/modules/soundscape/components/perception-chart/perception-chart.component.html new file mode 100644 index 00000000..eac970ba --- /dev/null +++ b/src/app/modules/soundscape/components/perception-chart/perception-chart.component.html @@ -0,0 +1,15 @@ +

+ {{ 'soundscape.perception.title' | translate }} +

+
+ + +
+
diff --git a/src/app/modules/soundscape/components/perception-chart/perception-chart.component.scss b/src/app/modules/soundscape/components/perception-chart/perception-chart.component.scss new file mode 100644 index 00000000..052677cd --- /dev/null +++ b/src/app/modules/soundscape/components/perception-chart/perception-chart.component.scss @@ -0,0 +1,4 @@ +#perceptionChart{ + min-height: 600px; + width: 100%; +} diff --git a/src/app/modules/soundscape/components/perception-chart/perception-chart.component.ts b/src/app/modules/soundscape/components/perception-chart/perception-chart.component.ts new file mode 100644 index 00000000..136883ca --- /dev/null +++ b/src/app/modules/soundscape/components/perception-chart/perception-chart.component.ts @@ -0,0 +1,113 @@ +import { AfterViewInit, Component, HostListener, Input } from '@angular/core'; +import { Observations } from '../../../../models/observations'; +import * as echarts from 'echarts/core'; +import { BarChart, PieChart } from 'echarts/charts'; +import { CanvasRenderer } from 'echarts/renderers'; +import { GridComponent, LegendComponent } from 'echarts/components'; + +echarts.use([GridComponent, LegendComponent, BarChart, CanvasRenderer,PieChart]); + +@Component({ + selector: 'app-perception-chart', + templateUrl: './perception-chart.component.html', + styleUrl: './perception-chart.component.scss' +}) +export class PerceptionChartComponent implements AfterViewInit{ + + @Input() observations: Observations[]; + @HostListener('window:resize', ['$event']) + onResize(event: any) { + this.chart.resize(); + } + private chart: echarts.ECharts; + private option! : echarts.EChartsCoreOption; + private data:number[][] = []; + public pie: number = 0; + + + public pieOptions: {value:number, label:string}[] = [ + { value: 0, label: 'Tranquilitat' }, + { value: 1, label: 'Neteja i manteniment' }, + { value: 2, label: 'Accessibilitat' }, + { value: 3, label: 'Seguretat' } + ]; + + private legendsLabels: string[][] = [ + ['Gens tranquil', 'Poc tranquil', 'Moderament tranquil', 'Prou tranquil', 'Molt tranquil'], + ['Poc net', 'Net', 'Molt net'], + ['Poc accessible', 'Accessible', 'Molt accessible'], + ['Poc segur', 'Segur', 'Molt segur'], + ] + + ngAfterViewInit(): void { + let chartDom = document.getElementById('perceptionChart')!; + this.chart = echarts.init(chartDom); + this.data = this.getDataFromObservations(); + this.updateChart(); + } + public updateChart(): void { + this.option = { + title: { + text: this.pieOptions[this.pie].label, + left: 'center' + }, + tooltip: { + trigger: 'item' + }, + legend: { + orient: 'vertical', + left: 'left' + }, + series: { + name: this.pieOptions[this.pie].label, + type: 'pie', + radius: '50%', + data: this.data[this.pie].map((val, index) => ({value: val , name: this.legendsLabels[this.pie][index]})), + emphasis: { + itemStyle: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)' + } + } + } + }; + this.chart.setOption(this.option); + } + + + private getDataFromObservations(): number[][] { + let data: number[][] = [] + + let quiet: number[] = Array.from({length: this.legendsLabels[0].length}, () => 0); + let cleaning: number[] = Array.from({length: this.legendsLabels[1].length}, () => 0); + let accessibility: number[] = Array.from({length: this.legendsLabels[2].length}, () => 0); + let security: number[] = Array.from({length: this.legendsLabels[3].length}, () => 0); + + this.observations.forEach(obs => { + if(Number(obs.attributes.quiet)){ + quiet[Number(obs.attributes.quiet) - 1] ++; + } + + if(Number(obs.attributes.quiet) && Number(obs.attributes.quiet) <= 3){ + cleaning[Number(obs.attributes.quiet) - 1] = cleaning[Number(obs.attributes.quiet) - 1] + 10; + } + + if(Number(obs.attributes.quiet) && Number(obs.attributes.quiet) <= 3){ + accessibility[Number(obs.attributes.quiet) - 1] = accessibility[Number(obs.attributes.quiet) - 1] + 5; + } + + if(Number(obs.attributes.quiet) && Number(obs.attributes.quiet) <= 3){ + security[Number(obs.attributes.quiet) - 1] = security[Number(obs.attributes.quiet) - 1] + 2; + } + }); + + data.push(quiet); + data.push(cleaning); + data.push(accessibility); + data.push(security); + + return data; + } + +} diff --git a/src/app/modules/soundscape/components/quas-chart/quas-chart.component.html b/src/app/modules/soundscape/components/quas-chart/quas-chart.component.html index b360b12f..e8258419 100644 --- a/src/app/modules/soundscape/components/quas-chart/quas-chart.component.html +++ b/src/app/modules/soundscape/components/quas-chart/quas-chart.component.html @@ -1,2 +1,2 @@ -

{{'soundscape.soundLevels.title' | translate}}

-
+

{{'soundscape.quas.title' | translate}}

+
diff --git a/src/app/modules/soundscape/components/quas-chart/quas-chart.component.ts b/src/app/modules/soundscape/components/quas-chart/quas-chart.component.ts index 78dea705..443dcd41 100644 --- a/src/app/modules/soundscape/components/quas-chart/quas-chart.component.ts +++ b/src/app/modules/soundscape/components/quas-chart/quas-chart.component.ts @@ -18,7 +18,6 @@ export class QuasChartComponent implements AfterViewInit{ private chart: echarts.ECharts; private option! : echarts.EChartsCoreOption; public totalObservationTypes:number = 0 - //private quietTypesLabel = ['Silent', 'Quiet', 'Moderate', 'Loud', 'Very Loud']; private quietTypesLabel = ['Moderament tranquil', 'Bastant tranquil', 'Molt tranquil']; private dBLevels = ['>35', '35-40', '40-45', '45-50', '50-55', '55-60', '60-65', '65-70', '70-75', '75-80', '>80']; @@ -73,7 +72,7 @@ export class QuasChartComponent implements AfterViewInit{ top:40 }, xAxis: { - name: 'dB(A)', + name: 'dBA', nameLocation: 'middle', nameGap: 35, type: 'category', @@ -93,7 +92,6 @@ export class QuasChartComponent implements AfterViewInit{ } private getDataFromObservations(): number[][] { - let quietTypes:string[] = []; let dBLevels:number[][] = []; for (let i = 0; i < this.quietTypesLabel.length; ++i) { diff --git a/src/app/modules/soundscape/components/sound-levels-chart/sound-levels-chart.component.html b/src/app/modules/soundscape/components/sound-levels-chart/sound-levels-chart.component.html index f46e2fdc..5c76305a 100644 --- a/src/app/modules/soundscape/components/sound-levels-chart/sound-levels-chart.component.html +++ b/src/app/modules/soundscape/components/sound-levels-chart/sound-levels-chart.component.html @@ -1,2 +1,2 @@

{{'soundscape.soundLevels.title' | translate}}

-
+
diff --git a/src/app/modules/soundscape/components/sound-levels-chart/sound-levels-chart.component.ts b/src/app/modules/soundscape/components/sound-levels-chart/sound-levels-chart.component.ts index 6d54708a..5cc9b397 100644 --- a/src/app/modules/soundscape/components/sound-levels-chart/sound-levels-chart.component.ts +++ b/src/app/modules/soundscape/components/sound-levels-chart/sound-levels-chart.component.ts @@ -21,29 +21,32 @@ echarts.use([ styleUrl: './sound-levels-chart.component.scss' }) export class SoundLevelsChartComponent implements AfterViewInit{ - private chart: echarts.ECharts; + @Input() observations: Observations[]; @HostListener('window:resize', ['$event']) onResize(event: any) { this.chart.resize(); } - @Input() observations: Observations[]; + private chart: echarts.ECharts; private max: Number = 0; ngAfterViewInit(): void { - let data = this.getDataFromObservations() + let data = this.getDataFromObservations(); let chartDom = document.getElementById('levelsChart')!; this.chart = echarts.init(chartDom); let option: EChartsOption; + let legendData: string[] = ['< 35 dBA', '35 - 40 dBA', '40 - 45 dBA', '45 - 50 dBA', '50 - 55 dBA', '55 - 60 dBA', '60 - 65 dBA', '65 - 70 dBA', '70 - 75 dBA', '75 - 80 dBA', '> 80 dBA']; option = { legend: { - data: ['< 35 dB(A)', '35 - 40 dB(A)', '40 - 45 dB(A)', '45 - 50 dB(A)', '50 - 55 dB(A)', '55 - 60 dB(A)', '60 - 65 dB(A)', '65 - 70 dB(A)', '70 - 75 dB(A)', '75 - 80 dB(A)', '> 80 dB(A)'], + data: legendData, + orient: 'vertical', + left: 'left' }, polar: { - radius: [10, '80%'] + radius: [10, '88%'] }, radiusAxis: { max: this.max.toFixed(2), - z: 5, + z: 1, axisLine: { show: true, lineStyle: { @@ -62,12 +65,14 @@ export class SoundLevelsChartComponent implements AfterViewInit{ }, axisLabel: { show: true, - formatter: '{value} dB(A)', + formatter: '{value} dBA', textStyle: { color: '#333', fontSize: 12, - //backgroundColor: '#FFF' }, + label: { + backgroundColor: '#6a7985' + } } }, @@ -75,7 +80,7 @@ export class SoundLevelsChartComponent implements AfterViewInit{ type: 'category', //data: Array.from({length: 24}, (_, i) => `${i}:01 h - ${i == 23 ? 0 : i+1}:00 h`), data: Array.from({length: 24}, (_, i) => `${i}:00`), - z: 6, + z: 10, startAngle: 90, axisLine: { show: true, @@ -99,35 +104,44 @@ export class SoundLevelsChartComponent implements AfterViewInit{ axisPointer: { type: 'cross', label: { - backgroundColor: '#6a7985' + backgroundColor: '#6a7985', + formatter: (params:any) => { + if(params.axisDimension === 'angle') { + let hour = params.value.split(':')[0]; + return `${hour}:01 h - ${hour == 23 ? 0 : Number(hour)+1}:00 h` + } else { + return `${Math.round(params.value)} dBA` + } + } } }, formatter: (params:any) => { - return `${params[0].value} dB(A)`; - } - }, - series: { - type: 'bar', - data: data.map((value) => { - return { - value: value, - name: this.getLabel(Number(value)), - itemStyle: { - color: this.getColor(Number(value)) - } - }; - }), - coordinateSystem: 'polar', - barGap: '0', - barCategoryGap: '0', - emphasis: { - itemStyle: { - shadowBlur: 10, - shadowOffsetX: 0, - shadowColor: 'rgba(0, 0, 0, 0.5)' - } + let dB = 0; + params.forEach((param:any) => { + dB = param.value > dB ? param.value : dB; + }); + return dB ? `${dB} dBA` : 'Sense observacions'; }, }, + series: Array.from({length: 24}, () => {}).map((_, sid) => { + return { + type: 'bar', + data: Array.from({length: 24}, (_, i) => { + if(i === sid) return data[i]; + return 0; + }), + coordinateSystem: 'polar', + name: this.getLabel(Number(data[sid])), + stack: 'a', + emphasis: { + focus: 'series' + }, + itemStyle: { + color: this.getColor(Number(data[sid])) + }, + }; + + }), animation: true }; this.chart.setOption(option); @@ -135,7 +149,6 @@ export class SoundLevelsChartComponent implements AfterViewInit{ private getDataFromObservations(): Number[] { let data: Number[][] = Array.from({length: 24}, () => []); - let max = 0; this.observations.forEach(observation => { let hour = new Date(observation.attributes.created_at).getHours(); if(!data[hour]) data[hour] = []; @@ -185,27 +198,27 @@ export class SoundLevelsChartComponent implements AfterViewInit{ private getLabel(value: number): string{ switch (true) { case value <= 35: - return '< 35 dB(A)'; + return '< 35 dBA'; case value > 35 && value <= 40: - return '35 - 40 dB(A)'; + return '35 - 40 dBA'; case value > 40 && value <= 45: - return '40 - 45 dB(A)'; + return '40 - 45 dBA'; case value > 45 && value <= 50: - return '45 - 50 dB(A)'; + return '45 - 50 dBA'; case value > 50 && value <= 55: - return '50 - 55 dB(A)'; + return '50 - 55 dBA'; case value > 55 && value <= 60: - return '55 - 60 dB(A)'; + return '55 - 60 dBA'; case value > 60 && value <= 65: - return '60 - 65 dB(A)'; + return '60 - 65 dBA'; case value > 65 && value <= 70: - return '65 - 70 dB(A)'; + return '65 - 70 dBA'; case value > 70 && value <= 75: - return '70 - 75 dB(A)'; + return '70 - 75 dBA'; case value > 75 && value <= 80: - return '75 - 80 dB(A)'; + return '75 - 80 dBA'; case value > 80: - return '> 80 dB(A)'; + return '> 80 dBA'; default: return 'Unknown'; } diff --git a/src/app/modules/soundscape/components/sound-types-chart/sound-types-chart.component.html b/src/app/modules/soundscape/components/sound-types-chart/sound-types-chart.component.html index 6675d32e..c330a12a 100644 --- a/src/app/modules/soundscape/components/sound-types-chart/sound-types-chart.component.html +++ b/src/app/modules/soundscape/components/sound-types-chart/sound-types-chart.component.html @@ -1,2 +1,2 @@

{{'soundscape.soundTypes.title' | translate}}

-
+
diff --git a/src/app/modules/soundscape/page/soundscape.component.html b/src/app/modules/soundscape/page/soundscape.component.html index 0e5dccaf..17605a70 100644 --- a/src/app/modules/soundscape/page/soundscape.component.html +++ b/src/app/modules/soundscape/page/soundscape.component.html @@ -34,6 +34,9 @@
+
+ +
} diff --git a/src/app/modules/soundscape/soundscape.module.ts b/src/app/modules/soundscape/soundscape.module.ts index 7bd19430..5c36c2a3 100644 --- a/src/app/modules/soundscape/soundscape.module.ts +++ b/src/app/modules/soundscape/soundscape.module.ts @@ -4,6 +4,7 @@ import { CommonModule } from '@angular/common'; import { ButtonModule } from 'primeng/button'; import { CheckboxModule } from 'primeng/checkbox'; import { RadioButtonModule } from 'primeng/radiobutton'; +import { SelectButtonModule } from 'primeng/selectbutton'; import { SoundscapeComponent } from './page/soundscape.component'; import { SharedComponentsModule } from '../../shared/shared.module'; @@ -11,6 +12,7 @@ import { FormsModule } from '@angular/forms'; import { SoundLevelsChartComponent } from './components/sound-levels-chart/sound-levels-chart.component'; import { SoundTypesChartComponent } from './components/sound-types-chart/sound-types-chart.component'; import { QuasChartComponent } from './components/quas-chart/quas-chart.component'; +import { PerceptionChartComponent } from './components/perception-chart/perception-chart.component'; @@ -20,6 +22,7 @@ import { QuasChartComponent } from './components/quas-chart/quas-chart.component SoundLevelsChartComponent, SoundTypesChartComponent, QuasChartComponent, + PerceptionChartComponent, ], imports: [ CommonModule, @@ -27,6 +30,7 @@ import { QuasChartComponent } from './components/quas-chart/quas-chart.component CheckboxModule, RadioButtonModule, SharedComponentsModule, + SelectButtonModule, FormsModule ] }) diff --git a/src/assets/i18n/ca.json b/src/assets/i18n/ca.json index 2e89dba4..49c6c697 100644 --- a/src/assets/i18n/ca.json +++ b/src/assets/i18n/ca.json @@ -39,11 +39,17 @@ "wholeDay": "Tot el dia" }, "soundLevels":{ - "title": "Nivells de so" + "title": "Visualització gradual dels nivells de so per hores" }, "soundTypes":{ - "title": "Tipus de so", - "typesAverage": "Mitjana tipus de so enregistrat per observació" + "title": "Tipus de so" + }, + "quas":{ + "title": "Distribució de nivells de pressió sonora a l'àrea tranquil·la" + }, + "perception":{ + "title": "Descripció de la percepció del espai" + } } } diff --git a/src/assets/layout/styles/theme/bootstrap4-light-blue/theme.css b/src/assets/layout/styles/theme/bootstrap4-light-blue/theme.css index 5a1bde22..6a5d608c 100644 --- a/src/assets/layout/styles/theme/bootstrap4-light-blue/theme.css +++ b/src/assets/layout/styles/theme/bootstrap4-light-blue/theme.css @@ -1824,8 +1824,8 @@ } .p-selectbutton .p-button { - background: #6c757d; - border: 1px solid #6c757d; + background: var(--orange-light); + border: 1px solid var(--orange-light); color: #ffffff; transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s; } @@ -1834,8 +1834,8 @@ color: #ffffff; } .p-selectbutton .p-button:not(.p-disabled):not(.p-highlight):hover { - background: #5a6268; - border-color: #545b62; + background: var(--orange); + border-color: var(--orange); color: #ffffff; } .p-selectbutton @@ -1847,8 +1847,8 @@ color: #ffffff; } .p-selectbutton .p-button.p-highlight { - background: #545b62; - border-color: #4e555b; + background: var(--orange); + border-color: var(--orange); color: #ffffff; } .p-selectbutton .p-button.p-highlight .p-button-icon-left, @@ -1856,8 +1856,8 @@ color: #ffffff; } .p-selectbutton .p-button.p-highlight:hover { - background: #545b62; - border-color: #4e555b; + background: var(--orange-light); + border-color: var(--orange-light); color: #ffffff; } .p-selectbutton .p-button.p-highlight:hover .p-button-icon-left,