v0.02 sync x axis

This commit is contained in:
T__o 2020-04-26 23:04:17 +02:00
parent 54b47af0dc
commit aae0790c27
6 changed files with 113 additions and 43 deletions

View File

@ -1,4 +1,4 @@
# covcharts # CovCharts
Charts on COVID 19 Charts on COVID 19
@ -6,4 +6,14 @@ Using vuejs and d3
Data source from : https://github.com/pomber/covid19 Data source from : https://github.com/pomber/covid19
**Numbers are not reality**
## Features :
- sync X axis at a value
- reset (broken, need reload page too)
- set a limit for y axis
- select first and last day
- select data source (confirmed, deaths, recovered)
- select scale (log/linear)
- select countries

1
doc.md
View File

@ -3,6 +3,7 @@
# Changelog # Changelog
v0.02 sync x axis
v0.01 repack, data auto loading v0.01 repack, data auto loading

View File

@ -34,13 +34,13 @@
<v-row align="center" justify="center" id="maincontent" ref="maincontent"> <v-row align="center" justify="center" id="maincontent" ref="maincontent">
<v-col class="shrink"> <v-col class="shrink">
<MultiCountryChart <MultiCountryChart
:countries="countriesData" :countries="countriesData"
:scaleType="chartConfig.scaleSelected" :scaleType="chartConfig.scaleSelected"
:dataType="chartConfig.typeSelected" :dataType="chartConfig.typeSelected"
:width="chartWidth" :width="chartWidth"
:height="chartWidth * (2/3)" :height="chartWidth * (2/3)"
:since="syncSinceInt"
/> />
<MultiBarChartSingle <MultiBarChartSingle
@ -75,6 +75,8 @@
import DataLoader from './components/DataLoader' import DataLoader from './components/DataLoader'
MultiBarChartSingle
let limit = (value, min, max) => { let limit = (value, min, max) => {
return value > max ? max : value < min ? min : value return value > max ? max : value < min ? min : value
} }
@ -102,6 +104,7 @@
dayStart: 30, dayStart: 30,
dayEnd: 0, dayEnd: 0,
ymax: 0, ymax: 0,
syncSince: 0,
resetConfig: false, resetConfig: false,
}, },
countriesConfig: { countriesConfig: {
@ -133,6 +136,7 @@
// Todo list of all countries using localstorage // Todo list of all countries using localstorage
//all: Object.keys(AllData).sort(), //all: Object.keys(AllData).sort(),
}, },
debug: false,
}), }),
watch: { watch: {
drawer() { drawer() {
@ -163,27 +167,36 @@
}, },
methods: { methods: {
loadConfig() { loadConfig() {
let uc = localStorage.getItem('userConfig') let uc = localStorage.getItem('userConfig')
if (uc && uc !== 'null') { if (uc && uc !== 'null') {
uc = JSON.parse(uc) uc = JSON.parse(uc)
this.countriesConfig = uc.countriesConfig this.countriesConfig = uc.countriesConfig
this.chartConfig = uc.chartConfig this.chartConfig = uc.chartConfig
this.ccc("loadConfig storage")
} else { } else {
this.countriesConfig = defaultUserConfig.countriesConfig this.countriesConfig = defaultUserConfig.countriesConfig
this.chartConfig = defaultUserConfig.chartConfig this.chartConfig = defaultUserConfig.chartConfig
this.ccc("loadConfig default")
} }
}, },
saveConfig() { saveConfig() {
this.ccc("saveConfig")
let userConfig = {countriesConfig: this.countriesConfig, chartConfig: this.chartConfig} let userConfig = {countriesConfig: this.countriesConfig, chartConfig: this.chartConfig}
localStorage.setItem('userConfig', JSON.stringify(userConfig)) localStorage.setItem('userConfig', JSON.stringify(userConfig))
}, },
resetConfig() { resetConfig() {
localStorage.setItem('userConfig', 'null') localStorage.setItem('userConfig', 'null')
this.ccc("resetConfig")
this.loadConfig() this.loadConfig()
}, },
ccc(a, b) {
if (this.debug) console.log(a, b)
},
}, },
computed: { computed: {
syncSinceInt() {
return parseInt(this.chartConfig.syncSince)
},
countriesData() { countriesData() {
if (!('France' in this.allData)) { if (!('France' in this.allData)) {
@ -195,11 +208,29 @@
this.countriesConfig.selected.map((country) => { this.countriesConfig.selected.map((country) => {
let cdata = JSON.parse(JSON.stringify(this.allData[country.name])) let filtered = JSON.parse(JSON.stringify(this.allData[country.name]))
let itemsCount = cdata.length
let filtered = cdata.filter((e, i) => if (this.syncSinceInt !== 0) {
filtered = filtered.filter((e) => {
//console.log(' e', e[this.chartConfig.typeSelected], syncSince, typeof this.syncSince)
return e[this.chartConfig.typeSelected] >= this.syncSinceInt
}
)
}
if (this.chartConfig.ymax > 0) {
filtered = filtered.map((d) => {
this.configLists.typesList.map(e => {
d[e] = d[e] > this.chartConfig.ymax ? NaN : d[e]
})
return d
})
}
filtered = filtered.filter((e, i) =>
i >= (this.chartConfig.dayStart - 1) && i >= (this.chartConfig.dayStart - 1) &&
i < (itemsCount - this.chartConfig.dayEnd)) i < (filtered.length - this.chartConfig.dayEnd)
)
if (addSomeDays) { if (addSomeDays) {
// Adding addDays data points // Adding addDays data points
@ -215,14 +246,6 @@
} }
} }
if (this.chartConfig.ymax > 0) {
filtered = filtered.map((d) => {
this.configLists.typesList.map(e => {
d[e] = d[e] > this.chartConfig.ymax ? this.chartConfig.ymax : d[e]
})
return d
})
}
// // Get ymax // // Get ymax
// filtered.map(d => { // filtered.map(d => {
// ymax = d[this.dataSelected] > ymax ? d[this.dataSelected] : ymax // ymax = d[this.dataSelected] > ymax ? d[this.dataSelected] : ymax
@ -231,7 +254,6 @@
country.list = filtered country.list = filtered
formatedData.push(country) formatedData.push(country)
}) })
return formatedData return formatedData
} }
} }

View File

@ -17,6 +17,7 @@
</v-list-item> </v-list-item>
<v-list-item> <v-list-item>
<v-list-item-action>Type</v-list-item-action> <v-list-item-action>Type</v-list-item-action>
<v-list-item-content> <v-list-item-content>
@ -56,12 +57,12 @@
</v-list-item> </v-list-item>
<v-list-item> <v-list-item>
<v-list-item-action> y Max</v-list-item-action> <v-list-item-action>Sync X </v-list-item-action>
<v-list-item-content> <v-list-item-content>
<v-text-field <v-text-field
class="ymax" class="numField"
v-model="configs.ymax" v-model="configs.syncSince"
label="ymaxx" label="syncSince"
solo solo
hide-details hide-details
single-line single-line
@ -70,6 +71,22 @@
</v-list-item-content> </v-list-item-content>
</v-list-item> </v-list-item>
<v-list-item>
<v-list-item-action>Limit Y </v-list-item-action>
<v-list-item-content>
<v-text-field
class="numField"
v-model="configs.ymax"
label="ymaxx"
solo
hide-details
single-line
/>
</v-list-item-content>
</v-list-item>
<v-list-item> <v-list-item>
<v-list-item-action></v-list-item-action> <v-list-item-action></v-list-item-action>
<v-list-item-content> <v-list-item-content>
@ -95,6 +112,13 @@
return { return {
} }
}, },
watch:{
'configs.syncSince': function(value){
if ( ! (value > 0 ) ){
this.configs.syncSince = 0
}
}
},
methods: { methods: {
resetConfig() { resetConfig() {
this.configs.resetConfig = true this.configs.resetConfig = true
@ -111,7 +135,7 @@
margin: 0px; margin: 0px;
} }
.ymax { .numField {
max-width: 50%; max-width: 50%;
} }

View File

@ -7,7 +7,7 @@
:x="scales.x(new Date(day.date))" :x="scales.x(new Date(day.date))"
:y="scales.y(day.value)" :y="scales.y(day.value)"
:width="colsize" :width="colsize"
:height="height-scales.y(day.value)" :height="calcHeight(day.value)"
class="barChartRect" class="barChartRect"
></rect> ></rect>
<!-- <path--> <!-- <path-->
@ -69,6 +69,7 @@
}, },
}, },
computed: { computed: {
colsize() { colsize() {
return this.scales.x(new Date(this.dataChart[1].date)) - 1 return this.scales.x(new Date(this.dataChart[1].date)) - 1
}, },
@ -113,6 +114,10 @@
}, },
}, },
methods: { methods: {
calcHeight(value){
let v = this.height-this.scales.y(value)
return v > 0 ? v: 0
},
calculatePath(data) { calculatePath(data) {
//console.log('datata',data) //console.log('datata',data)

View File

@ -1,6 +1,7 @@
<template> <template>
<div> <div>
<svg :class="svgClass" :width="width" :height="height"> <svg :class="svgClass" :width="width" :height="height">
<g style="transform: translate(2px, 2px)"> <g style="transform: translate(2px, 2px)">
@ -17,7 +18,7 @@
<circle v-for="(point,i) in (cinfo.list)" <circle v-for="(point,i) in (cinfo.list)"
:key="'c'+i" :key="'c'+i"
:style="{fill:cinfo.color}" :style="{fill:cinfo.color}"
:cx="scales.x(new Date(point.date))" :cx="scales.x(xconvert(point,i))"
:cy="scales.y(point[dataType])" :cy="scales.y(point[dataType])"
r="2" r="2"
/> />
@ -25,14 +26,14 @@
<circle v-for="(point,i) in (cinfo.list)" <circle v-for="(point,i) in (cinfo.list)"
opacity="0" opacity="0"
:key="'cinv'+i" :key="'cinv'+i"
:cx="scales.x(new Date(point.date))" :cx="scales.x(xconvert(point,i))"
:cy="scales.y(point[dataType])" :cy="scales.y(point[dataType])"
r="7" r="7"
@mouseover="overPoint(point,cinfo.color)" @mouseover="overPoint(point,i,cinfo.color)"
/> />
<rect :x="overpoint.x" <rect :x="overpoint.x"
:y="overpoint.y-10" :y="overpoint.y"
:width=" (6+overpoint.text.toString().length) * 4" :width=" (6+overpoint.text.toString().length) * 4"
:height="18" :height="18"
/> />
@ -73,6 +74,7 @@
dataType: String, dataType: String,
width: Number, width: Number,
height: Number, height: Number,
since: Number,
}, },
data() { data() {
return { return {
@ -91,6 +93,7 @@
}, },
mounted() { mounted() {
this.drawAxis() this.drawAxis()
}, },
watch: { watch: {
scaleType() { scaleType() {
@ -104,16 +107,18 @@
minmax() { minmax() {
let max = {x: 0, y: 0} let max = {x: 0, y: 0}
let min = {x: new Date(), y: undefined} let min = {x: undefined, y: undefined}
this.countries.forEach(country => { this.countries.forEach(country => {
country.list.forEach((item) => { country.list.forEach((item, i) => {
let value = item[this.dataType] let value = item[this.dataType]
let dayvalue = new Date(item.date) let dayvalue = this.xconvert(item, i)
max.x = max.x < dayvalue ? dayvalue : max.x max.x = max.x < dayvalue ? dayvalue : max.x
max.y = max.y < value ? value : max.y max.y = max.y < value ? value : max.y
if (typeof min.x === "undefined") min.x = dayvalue
min.x = min.x > dayvalue ? dayvalue : min.x min.x = min.x > dayvalue ? dayvalue : min.x
if (typeof min.y === "undefined") min.y = value if (typeof min.y === "undefined") min.y = value
min.y = min.y > value ? value : min.y min.y = min.y > value ? value : min.y
@ -121,15 +126,14 @@
}) })
}) })
//console.log(' {min: min, max: max} ', min.y, max, this.scaleType)
return {min: min, max: max} return {min: min, max: max}
}, },
scales() { scales() {
let scales = {} let scales = {}
scales.x = scaleTime() scales.x = parseInt(this.since) > 0 ? scaleLinear() : scaleTime()
.domain([this.minmax.min.x, this.minmax.max.x]) scales.x = scales.x.domain([this.minmax.min.x, this.minmax.max.x])
.range([0, this.width - 2 * this.margin.x]) .range([0, this.width - 2 * this.margin.x])
// !!scaleLog need to start at 1 not 0!! // !!scaleLog need to start at 1 not 0!!
@ -143,8 +147,13 @@
}, },
methods: { methods: {
overPoint(point, color) { xconvert(d, i) {
if (parseInt(this.since) > 0) {
return i
}
return new Date(d.date)
},
overPoint(point,i, color) {
if (point === 'reset') { if (point === 'reset') {
this.overpoint = { this.overpoint = {
x: 0, y: 0, text: "" x: 0, y: 0, text: ""
@ -153,17 +162,16 @@
} }
this.overpoint = { this.overpoint = {
x: this.scales.x(new Date(point.date)), x: this.scales.x(this.xconvert(point,i)),
y: this.scales.y(point [this.dataType]), y: this.scales.y(point [this.dataType]),
text: point.date.slice(5, 10) + ": " + point [this.dataType], text: point.date.slice(5, 10) + ": " + point [this.dataType],
color: color, color: color,
} }
}, },
calculatePath(data) { calculatePath(data) {
//console.log('datata',data) //console.log('datata',data)
const path = line() const path = line()
.x((d) => this.scales.x(new Date(d.date))) .x((d, i) => this.scales.x(this.xconvert(d, i)))
.y(d => this.scales.y(d[this.dataType] === 0 ? 1 : d[this.dataType])) .y(d => this.scales.y(d[this.dataType] === 0 ? 1 : d[this.dataType]))
return path(data) return path(data)