v0.02 sync x axis
This commit is contained in:
parent
54b47af0dc
commit
aae0790c27
12
README.md
12
README.md
@ -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
1
doc.md
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
v0.02 sync x axis
|
||||||
v0.01 repack, data auto loading
|
v0.01 repack, data auto loading
|
||||||
|
|
||||||
|
|
||||||
|
52
src/App.vue
52
src/App.vue
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user