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
|
||||
|
||||
@ -6,4 +6,14 @@ Using vuejs and d3
|
||||
|
||||
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
|
52
src/App.vue
52
src/App.vue
@ -34,13 +34,13 @@
|
||||
<v-row align="center" justify="center" id="maincontent" ref="maincontent">
|
||||
<v-col class="shrink">
|
||||
|
||||
|
||||
<MultiCountryChart
|
||||
:countries="countriesData"
|
||||
:scaleType="chartConfig.scaleSelected"
|
||||
:dataType="chartConfig.typeSelected"
|
||||
:width="chartWidth"
|
||||
:height="chartWidth * (2/3)"
|
||||
:since="syncSinceInt"
|
||||
/>
|
||||
|
||||
<MultiBarChartSingle
|
||||
@ -75,6 +75,8 @@
|
||||
import DataLoader from './components/DataLoader'
|
||||
|
||||
|
||||
MultiBarChartSingle
|
||||
|
||||
let limit = (value, min, max) => {
|
||||
return value > max ? max : value < min ? min : value
|
||||
}
|
||||
@ -102,6 +104,7 @@
|
||||
dayStart: 30,
|
||||
dayEnd: 0,
|
||||
ymax: 0,
|
||||
syncSince: 0,
|
||||
resetConfig: false,
|
||||
},
|
||||
countriesConfig: {
|
||||
@ -133,6 +136,7 @@
|
||||
// Todo list of all countries using localstorage
|
||||
//all: Object.keys(AllData).sort(),
|
||||
},
|
||||
debug: false,
|
||||
}),
|
||||
watch: {
|
||||
drawer() {
|
||||
@ -163,27 +167,36 @@
|
||||
},
|
||||
methods: {
|
||||
loadConfig() {
|
||||
|
||||
let uc = localStorage.getItem('userConfig')
|
||||
if (uc && uc !== 'null') {
|
||||
uc = JSON.parse(uc)
|
||||
this.countriesConfig = uc.countriesConfig
|
||||
this.chartConfig = uc.chartConfig
|
||||
this.ccc("loadConfig storage")
|
||||
} else {
|
||||
this.countriesConfig = defaultUserConfig.countriesConfig
|
||||
this.chartConfig = defaultUserConfig.chartConfig
|
||||
this.ccc("loadConfig default")
|
||||
}
|
||||
},
|
||||
saveConfig() {
|
||||
this.ccc("saveConfig")
|
||||
let userConfig = {countriesConfig: this.countriesConfig, chartConfig: this.chartConfig}
|
||||
localStorage.setItem('userConfig', JSON.stringify(userConfig))
|
||||
},
|
||||
resetConfig() {
|
||||
localStorage.setItem('userConfig', 'null')
|
||||
this.ccc("resetConfig")
|
||||
this.loadConfig()
|
||||
},
|
||||
ccc(a, b) {
|
||||
if (this.debug) console.log(a, b)
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
syncSinceInt() {
|
||||
return parseInt(this.chartConfig.syncSince)
|
||||
},
|
||||
countriesData() {
|
||||
|
||||
if (!('France' in this.allData)) {
|
||||
@ -195,11 +208,29 @@
|
||||
|
||||
this.countriesConfig.selected.map((country) => {
|
||||
|
||||
let cdata = JSON.parse(JSON.stringify(this.allData[country.name]))
|
||||
let itemsCount = cdata.length
|
||||
let filtered = cdata.filter((e, i) =>
|
||||
let filtered = JSON.parse(JSON.stringify(this.allData[country.name]))
|
||||
|
||||
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 < (itemsCount - this.chartConfig.dayEnd))
|
||||
i < (filtered.length - this.chartConfig.dayEnd)
|
||||
)
|
||||
|
||||
if (addSomeDays) {
|
||||
// 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
|
||||
// filtered.map(d => {
|
||||
// ymax = d[this.dataSelected] > ymax ? d[this.dataSelected] : ymax
|
||||
@ -231,7 +254,6 @@
|
||||
country.list = filtered
|
||||
formatedData.push(country)
|
||||
})
|
||||
|
||||
return formatedData
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
<v-list dense >
|
||||
|
||||
<v-list-item>
|
||||
<v-list-item-action> Scale</v-list-item-action>
|
||||
<v-list-item-action>Scale</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<v-select
|
||||
class="vselect"
|
||||
@ -17,8 +17,9 @@
|
||||
</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-select
|
||||
class="vselect"
|
||||
@ -32,7 +33,7 @@
|
||||
|
||||
|
||||
<v-list-item>
|
||||
<v-list-item-action> DayStart</v-list-item-action>
|
||||
<v-list-item-action>Day Start</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<Counter
|
||||
v-model="configs.dayStart"
|
||||
@ -44,7 +45,7 @@
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item>
|
||||
<v-list-item-action> DayEnd</v-list-item-action>
|
||||
<v-list-item-action>Day End</v-list-item-action>
|
||||
<v-list-item-content>
|
||||
<Counter
|
||||
v-model="configs.dayEnd"
|
||||
@ -56,12 +57,12 @@
|
||||
</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-text-field
|
||||
class="ymax"
|
||||
v-model="configs.ymax"
|
||||
label="ymaxx"
|
||||
class="numField"
|
||||
v-model="configs.syncSince"
|
||||
label="syncSince"
|
||||
solo
|
||||
hide-details
|
||||
single-line
|
||||
@ -70,6 +71,22 @@
|
||||
</v-list-item-content>
|
||||
</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-action></v-list-item-action>
|
||||
<v-list-item-content>
|
||||
@ -95,6 +112,13 @@
|
||||
return {
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
'configs.syncSince': function(value){
|
||||
if ( ! (value > 0 ) ){
|
||||
this.configs.syncSince = 0
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetConfig() {
|
||||
this.configs.resetConfig = true
|
||||
@ -111,7 +135,7 @@
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.ymax {
|
||||
.numField {
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
:x="scales.x(new Date(day.date))"
|
||||
:y="scales.y(day.value)"
|
||||
:width="colsize"
|
||||
:height="height-scales.y(day.value)"
|
||||
:height="calcHeight(day.value)"
|
||||
class="barChartRect"
|
||||
></rect>
|
||||
<!-- <path-->
|
||||
@ -69,6 +69,7 @@
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
|
||||
colsize() {
|
||||
return this.scales.x(new Date(this.dataChart[1].date)) - 1
|
||||
},
|
||||
@ -113,6 +114,10 @@
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
calcHeight(value){
|
||||
let v = this.height-this.scales.y(value)
|
||||
return v > 0 ? v: 0
|
||||
},
|
||||
calculatePath(data) {
|
||||
|
||||
//console.log('datata',data)
|
||||
|
@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
|
||||
<svg :class="svgClass" :width="width" :height="height">
|
||||
|
||||
<g style="transform: translate(2px, 2px)">
|
||||
@ -17,7 +18,7 @@
|
||||
<circle v-for="(point,i) in (cinfo.list)"
|
||||
:key="'c'+i"
|
||||
:style="{fill:cinfo.color}"
|
||||
:cx="scales.x(new Date(point.date))"
|
||||
:cx="scales.x(xconvert(point,i))"
|
||||
:cy="scales.y(point[dataType])"
|
||||
r="2"
|
||||
/>
|
||||
@ -25,14 +26,14 @@
|
||||
<circle v-for="(point,i) in (cinfo.list)"
|
||||
opacity="0"
|
||||
:key="'cinv'+i"
|
||||
:cx="scales.x(new Date(point.date))"
|
||||
:cx="scales.x(xconvert(point,i))"
|
||||
:cy="scales.y(point[dataType])"
|
||||
r="7"
|
||||
@mouseover="overPoint(point,cinfo.color)"
|
||||
@mouseover="overPoint(point,i,cinfo.color)"
|
||||
/>
|
||||
|
||||
<rect :x="overpoint.x"
|
||||
:y="overpoint.y-10"
|
||||
:y="overpoint.y"
|
||||
:width=" (6+overpoint.text.toString().length) * 4"
|
||||
:height="18"
|
||||
/>
|
||||
@ -73,6 +74,7 @@
|
||||
dataType: String,
|
||||
width: Number,
|
||||
height: Number,
|
||||
since: Number,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -91,6 +93,7 @@
|
||||
},
|
||||
mounted() {
|
||||
this.drawAxis()
|
||||
|
||||
},
|
||||
watch: {
|
||||
scaleType() {
|
||||
@ -104,16 +107,18 @@
|
||||
minmax() {
|
||||
|
||||
let max = {x: 0, y: 0}
|
||||
let min = {x: new Date(), y: undefined}
|
||||
let min = {x: undefined, y: undefined}
|
||||
|
||||
this.countries.forEach(country => {
|
||||
country.list.forEach((item) => {
|
||||
country.list.forEach((item, i) => {
|
||||
|
||||
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.y = max.y < value ? value : max.y
|
||||
|
||||
if (typeof min.x === "undefined") min.x = dayvalue
|
||||
min.x = min.x > dayvalue ? dayvalue : min.x
|
||||
if (typeof min.y === "undefined") min.y = value
|
||||
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}
|
||||
},
|
||||
|
||||
scales() {
|
||||
|
||||
let scales = {}
|
||||
scales.x = scaleTime()
|
||||
.domain([this.minmax.min.x, this.minmax.max.x])
|
||||
scales.x = parseInt(this.since) > 0 ? scaleLinear() : scaleTime()
|
||||
scales.x = scales.x.domain([this.minmax.min.x, this.minmax.max.x])
|
||||
.range([0, this.width - 2 * this.margin.x])
|
||||
|
||||
// !!scaleLog need to start at 1 not 0!!
|
||||
@ -143,8 +147,13 @@
|
||||
|
||||
},
|
||||
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') {
|
||||
this.overpoint = {
|
||||
x: 0, y: 0, text: ""
|
||||
@ -153,17 +162,16 @@
|
||||
}
|
||||
|
||||
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]),
|
||||
text: point.date.slice(5,10)+": "+point [this.dataType],
|
||||
text: point.date.slice(5, 10) + ": " + point [this.dataType],
|
||||
color: color,
|
||||
}
|
||||
},
|
||||
calculatePath(data) {
|
||||
|
||||
//console.log('datata',data)
|
||||
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]))
|
||||
|
||||
return path(data)
|
||||
@ -181,7 +189,7 @@
|
||||
|
||||
axis.append("g")
|
||||
.attr("class", "axis axis--x")
|
||||
.attr("transform", "translate( 0," + (this.height - this.margin.y/2) + ")")
|
||||
.attr("transform", "translate( 0," + (this.height - this.margin.y / 2) + ")")
|
||||
.call(axisTop(this.scales.x));
|
||||
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user