highcharter包:R+HighCharts(二)

highcharter包:R+HighCharts(二)

接上一篇博客。本文介绍了highcharts包更细致的用法。主要是三部分:Highcharts、Highcharts Stock和Highcharts Map。

Highcharts

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
library(dplyr)
library(stringr)
library(purrr)
library(highcharter)
n <- 5
set.seed(123)
colors <- c("#d35400", "#2980b9", "#2ecc71", "#f1c40f", "#2c3e50", "#7f8c8d")
colors2 <- c("#000004", "#3B0F70", "#8C2981", "#DE4968", "#FE9F6D", "#FCFDBF")

df <- tibble(x = seq_len(n) - 1) %>%
mutate(
y = 10 + x + 10 * sin(x),
y = round(y, 1),
z = (x * y) - median(x * y),
e = 10 * abs(rnorm(length(x))) + 2,
e = round(e, 1),
low = y - e,
high = y + e,
value = y,
name = sample(fruit[str_length(fruit) <= 5], size = n),
color = rep(colors, length.out = n),
segmentColor = rep(colors2, length.out = n)
) %>%
print()

create_hc <- function(t){
dont_rm_high_and_low <- c("areaarrange",
"areasplinerange",
"columnrange",
"errorbar")
is_polar <- str_detect(t, "polar")
t <- str_replace(t, "polar", "")
if(!t %in% dont_rm_high_and_low){
df <- df %>% select(-e, -low, -high)
}
highchart() %>%
hc_title(text = paste(
ifelse(is_polar, "polar", ""),
t
),
style = list(
fontSize = "15px"
)) %>%
hc_chart(type = t,
polar = is_polar) %>%
hc_xAxis(categories = df$name) %>%
hc_add_series(df, name = "Fruit Consumption",
showInLegend = F)
}

hcs <- c("line", "spline", "area", "areaspline",
"column", "bar", "waterfall", "funnel",
"pyramid", "pie", "treemap", "scatter",
"bubble", "arearange", "areasplinerange",
"columnrange", "errorbar", "polygon",
"polarline", "polarcolumn",
"polarcolumnrange", "coloredarea",
"coloredline") %>%
map(create_hc)

上面的代码创建了一个list对象:hcs,里面存储了上面指定类型的图表:

R
1
2
# 例如:
hcs[[9]]

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
data("favorite_bars")
data("favorite_pies")

highchart() %>%
hc_add_series(favorite_pies, "column",
hcaes(x = "pie", y = "percent"),
name = "Pie") %>%
hc_add_series(favorite_bars, "pie",
hcaes(x = "bar", y = "percent"),
name = "Bars") %>%
hc_plotOptions(
series = list(
showInLegend = F,
pointFormat = "{point.y}"
),
column = list(
colorByPoint = T
),
pie = list(
colorByPoint = T, center = c('30%', '10%'),
size = 120, dataLabels = list(enabled = F)
)
) %>%
hc_yAxis(
title = list(text = "Percent of tastiness"),
labels = list(format = "{value}%"),
max = 100
) %>%
hc_xAxis(
categoroies = favorite_pies$pie
) %>%
hc_title(
text = "This is a bar graph describing my favorite pies including a pie chart describing my favorite bars"
) %>%
hc_subtitle(
text = "In percent of tastiness and awesomeness"
) %>%
hc_credits(
enabled = T, text = "Source: HIMYM",
href = "https://www.youtube.com/watch?v=f_J8QU1m0Ng",
style = list(fontSize = "12px")
) %>%
hc_add_theme(hc_theme_google())

Chart color gradient it’s on fire:

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
highchart() %>% 
hc_chart(backgroundColor = "#") %>%
hc_title(text = "Chart color gradient it's on fire",
style = list(color = "#CCC")) %>%
hc_yAxis(labels = list(
style = list(color = "#CCC")),
gridLineColor = "#111111"
) %>%
hc_series(
list(
data = abs(rnorm(100)) + 1,
type = "areaspline",
marker = list(enabled = F),
color = list(
linearGradient = list(
x1 = 0, y1 = 1, x2 = 0, y2 = 0
),
stops = list(
list(0, "transparent"),
list(0.33, "yellow"),
list(0.66, "red"),
list(1, "#ccc")
)
),
fillColor = list(
linearGradient = list(x1 = 0, y1 = 1, x2 = 0, y2 = 0),
stops = list(
list(0, "transparent"),
list(0.1, "yellow"),
list(0.5, "red"),
list(1, "black")
)
)
)
) %>%
hc_add_theme(hc_theme_null())

Highcharts Stock

基础图

R
1
2
3
library(quantmod)
x <- getSymbols("AAPL", auto.assign = F)
hchart(x)

蜡烛图和OHLC图

R
1
2
3
4
y <- getSymbols("AMZN", auto.assign = F)
highchart(type = "stock") %>%
hc_add_series(x) %>%
hc_add_series(y, type = "ohlc")

时间序列

R
1
2
3
4
5
6
7
8
9
usdcny <- getSymbols("USD/CNY", src = "oanda", auto.assign = F)
eurcny <- getSymbols("EUR/CNY", src = "oanda", auto.assign = F)

hc <- highchart(type = "stock") %>%
hc_title(text = "Charting some Symbols") %>%
hc_subtitle(text = "Data extracted using quantmod package") %>%
hc_add_series(usdcny, id = "usdcny") %>%
hc_add_series(eurcny, id = "eurcny") %>%
print()

添加标记

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
library(dplyr)
set.seed(123)
data_flags <- tibble(
date = sample(time(usdcny), size = 5),
title = sprintf("E #%s", seq_along(date)),
text = sprintf("An interesting eveent #%s in %s",
seq_along(date), date)
) %>%
glimpse()

hc %>%
hc_add_series(data_flags,
hcaes(x = date),
type = "flags",
onSeries = "usdcny")

多个序列

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
`600519.SS` <- getSymbols("600519.SS", from = Sys.Date() - lubridate::years(1), auto.assign = F)
GZMT <- adjustOHLC(`600519.SS`)
GZMT.SMA.10 <- SMA(Cl(GZMT), n = 5)
GZMT.SMA.200 <- SMA(Cl(GZMT), n = 100)
GZMT.RSI.14 <- RSI(Cl(GZMT))
GZMT.RSI.SellLevel <- xts(rep(70, NROW(GZMT)), index(GZMT))
GZMT.RSI.BuyLevel <- xts(rep(30, NROW(GZMT)), index(GZMT))

highchart(type = "stock") %>%
hc_yAxis_multiples(
create_yaxis(3, heights = c(2, 1, 1),
turnopposite = T)
) %>%
hc_add_series(GZMT, yAxis = 0, name = "GZMT") %>%
hc_add_series(GZMT.SMA.10, yAxis = 0, name = "Fast MA") %>%
hc_add_series(GZMT.SMA.200, yAxis = 0, name = "Slow MA") %>%
hc_add_series(GZMT$`600519.SS.Volume`, color = "grey", yAxis = 1, name = "Volume", type = "column") %>%
hc_add_series(GZMT.RSI.14, yAxis = 2, name = "Osciallator") %>%
hc_add_series(GZMT.RSI.SellLevel, color = hex_to_rgba("red", 0.7), yAxis = 2, name = "Sell level") %>%
hc_add_series(GZMT.RSI.BuyLevel, color = hex_to_rgba("blue", 0.7), yAxis = 2, name = "Buy level")

Highcharts Map

R
1
2
# 新西兰
hcmap("countries/nz/nz-all")

R
1
2
# 中国
hcmap("countries/cn/custom/cn-all-sar-taiwan")

R
1
2
# 多个国家
hcmap("custom/usa-and-canada", showInLegend = F)

R
1
2
3
# 加利福尼亚
hcmap("countries/us/us-ca-all") %>%
hc_title(text = "California")

把数据映射到地图上

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cn <- download_map_data("countries/cn/custom/cn-all-sar-taiwan")
cndf <- get_data_from_map(cn)
set.seed(1234)
data_fake <- cndf %>%
select(code = `hc-a2`) %>%
mutate(value = 1e5 * abs(rt(nrow(.), df = 10))) %>%
glimpse()

hcmap("countries/cn/custom/cn-all-sar-taiwan",
data = data_fake, value = "value",
joinBy = c("hc-a2", "code"),
name = "Fake data",
dataLabels = list(
enabled = T,
format = '{point.name}'
),
borderColor = "#FAFAFA",
borderWidth = 0.1,
tooltip = list(
valueDecimal = 2,
valuePrefix = "RMB",
valueSuffix = " CNY"
))

添加更多的数据

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cities <- tibble(
name = c("London", "Birmingham",
"Glasgow", "Liverpool"),
lat = c(51.507222, 52.483056, 55.858, 53.4),
lon = c(-0.1275, -1.893611, -4.259, -3),
z = c(1, 2, 3, 2)
)

hcmap("countries/gb/gb-all",
showInLegend = F) %>%
hc_add_series(data = cities,
type = "mapbubble",
name = "Cities",
maxSize = '10%') %>%
hc_mapNavigation(enabled = T)

从geojson绘制地图

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
getContent <- function(url){
library(httr)
content(GET(url))
}

world <- getContent("https://raw.githubusercontent.com/johan/world.geo.json/master/countries.geo.json")

world <- jsonlite::fromJSON(
world, simplifyVector = F
)

# 洋流数据
marine <- getContent("http://cedeusdata.geosteiniger.cl/geoserver/wfs?srsName=EPSG%3A4326&typename=geonode%3Amundo_corrientes_maritimas&outputFormat=json&version=1.0.0&service=WFS&request=GetFeature")

# 板块数据
plates <- getContent("http://cedeusdata.geosteiniger.cl/geoserver/wfs?srsName=EPSG%3A4326&typename=geonode%3Amundo_limites_placas&outputFormat=json&version=1.0.0&service=WFS&request=GetFeature")

# 火山数据
volcano <- getContent("http://cedeusdata.geosteiniger.cl/geoserver/wfs?srsName=EPSG%3A4326&typename=geonode%3Amundo_volcanes&outputFormat=json&version=1.0.0&service=WFS&request=GetFeature")

# 绘制地图
highchart(type = "map") %>%
hc_chart(backgroundColor = "#161C20") %>%
hc_add_series(mapData = world,
showInLegend = F,
nullColor = "#424242",
borderWidth = 0) %>%
hc_add_series(data = marine,
type = "mapline",
geojson = T,
color = "#2980b9",
name = "Marine currents") %>%
hc_add_series(data = plates,
type = "mapline",
lineWidth = 2,
zIndex = -1,
geojson = T,
color = "#d35400",
name = "Plates",
tooltip = list(
pointFormat = "{point.properties.TIPO}"
)) %>%
hc_add_series(data = volcano,
type = "mappoint",
color = hex_to_rgba("#f1c40f", 0.4),
geojson = T,
name = "Volcanos",
tootltip = list(
pointFormat = "{point.properties.NOMBRE}"
),
marker = list(
lineWidth = 0, radius = 2
))

插件

motion插件

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
library(highcharter)
library(dplyr)
library(purrr)
set.seed(1234)
n <- 20
z <- sample(1:n)
sequences <- map2(
1:n,
z,
function(x, y){
ifelse(x == 1:n, y, 0)
})

df <- tibble(
lat = runif(n, -180, 180),
lon = runif(n, -180, 180),
z = z,
color = colorize(z),
sequence = sequences
)

hcmap() %>%
hc_add_series(data = df,
type = "mapbubble",
minSize = 0,
maxSize = 30) %>%
hc_motion(enabled = T, series = 1,
labels = 1:n, loop = T,
autoPlay = T,
updateInterval = 1000,
magnet = list(step = 1)) %>%
hc_plotOptions(series = list(
showInLegend = F
))

geojsonio包

中国机场的分布图:

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# install.packages("geojsonio")
library(geojsonio)
cngeojson <- getContent("https://raw.githubusercontent.com/longwosion/geojson-map-china/master/china.json")

cngeojson <- jsonlite::fromJSON(cngeojson, simplifyVector = F)

cngeojson <- geojsonio::as.json(cngeojson)
airports <- read.csv("https://raw.githubusercontent.com/aswan-zz/proship/master/libhttps://czxb.github.io/br/global_airports.csv")
airports <- filter(airports,
country == "China")
airp_geojson <- geojson_json(airports,
lat = "latitude", lon = "longitude")
class(airp_geojson)

highchart(type = "map") %>%
hc_add_series(mapData = cngeojson,
showInLegend = F) %>%
hc_add_series(data = airp_geojson,
type = "mappoint",
dataLabels = list(
enabled = F
),
name = "机场",
tootltip = list(
pointFormat = "{point.name}"
),
colorByPoint = T)

人口金字塔

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# idbr:获取世界人口数据
library(idbr)
library(purrr)
library(dplyr)
idb_api_key("35f116582d5a89d11a47c7ffbfc2ba309133f09d")
yrs <- seq(1990, 2030, by = 5)
df <- map_df(c("male", "female"),
function(sex){
mutate(idb1("China", yrs, sex = sex), sex_label = sex)
})
names(df) <- tolower(names(df))

series <- df %>%
as_tibble() %>%
mutate(population = pop*ifelse(sex_label == "male", -1, 1)) %>%
group_by(sex_label, age) %>%
do(data = list(sequence = .$population)) %>%
ungroup() %>%
group_by(sex_label) %>%
do(data = .$data) %>%
mutate(name = sex_label) %>%
list_parse()

maxpop <- max(abs(df$pop))
xaxis <- list(
categories = sort(unique(df$age)),
reversed = F, tickInterval = 5,
labels = list(step = 5)
)

highchart() %>%
hc_chart(type = "bar") %>%
hc_motion(enabled = T,
labels = yrs,
series = c(0, 1),
autoplay = T,
updateInterval = 1) %>%
hc_add_series_list(series) %>%
hc_plotOptions(
series = list(stacking = "normal"),
bar = list(groupPadding = 0,
pointPadding = 0,
borderWidth = 0)
) %>%
hc_tooltip(shared = T) %>%
hc_yAxis(
labels = list(
formatter = JS(
"function(){
return Math.abs(this.value)/1000000 + 'M';
}"
)
),
tickInterval = 0.5e6,
min = -maxpop,
max = maxpop
) %>%
hc_xAxis(
xaxis,
rlist::list.merge(xaxis,
list(opposite = T,
linedTo = 0))
) %>%
hc_tooltip(shared = F,
formatter = JS(
"function(){
return '<b>' + this.series.name + ', age ' + this.point.category + '</b><br/>' + 'Population: ' + Highcharts.numberFormat(Math.abs(this.point.y), 0);
}"
)) %>%
hc_add_theme(hc_theme_ft())# idbr:获取世界人口数据
library(idbr)
library(purrr)
library(dplyr)
idb_api_key("35f116582d5a89d11a47c7ffbfc2ba309133f09d")
yrs <- seq(1990, 2030, by = 5)
df <- map_df(c("male", "female"),
function(sex){
mutate(idb1("China", yrs, sex = sex), sex_label = sex)
})
names(df) <- tolower(names(df))

series <- df %>%
as_tibble() %>%
mutate(population = pop*ifelse(sex_label == "male", -1, 1)) %>%
group_by(sex_label, age) %>%
do(data = list(sequence = .$population)) %>%
ungroup() %>%
group_by(sex_label) %>%
do(data = .$data) %>%
mutate(name = sex_label) %>%
list_parse()

maxpop <- max(abs(df$pop))
xaxis <- list(
categories = sort(unique(df$age)),
reversed = F, tickInterval = 5,
labels = list(step = 5)
)

highchart() %>%
hc_chart(type = "bar") %>%
hc_motion(enabled = T,
labels = yrs,
series = c(0, 1),
autoplay = T,
updateInterval = 1) %>%
hc_add_series_list(series) %>%
hc_plotOptions(
series = list(stacking = "normal"),
bar = list(groupPadding = 0,
pointPadding = 0,
borderWidth = 0)
) %>%
hc_tooltip(shared = T) %>%
hc_yAxis(
labels = list(
formatter = JS(
"function(){
return Math.abs(this.value)/1000000 + 'M';
}"
)
),
tickInterval = 0.5e6,
min = -maxpop,
max = maxpop
) %>%
hc_xAxis(
xaxis,
rlist::list.merge(xaxis,
list(opposite = T,
linedTo = 0))
) %>%
hc_tooltip(shared = F,
formatter = JS(
"function(){
return '<b>' + this.series.name + ', age ' + this.point.category + '</b><br/>' + 'Population: ' + Highcharts.numberFormat(Math.abs(this.point.y), 0);
}"
)) %>%
hc_add_theme(hc_theme_ft())

motion示例:

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
highchart() %>% 
hc_chart(type = "column") %>%
hc_yAxis(max = 6, min = 0) %>%
hc_add_series(name = 'A',
data = c(2, 3, 4),
zIndex = -10) %>%
hc_add_series(name = 'B',
data = list(
list(sequence = c(1, 2, 3, 4)),
list(sequence = c(3, 2, 1, 3)),
list(sequence = c(2, 5, 4, 3))
)) %>%
hc_add_series(name = 'C',
data = list(
list(sequence = c(3, 2, 1, 3)),
list(sequence = c(2, 5, 4, 3)),
list(sequence = c(1, 2, 3, 4))
)) %>%
hc_motion(enabled = T,
labels = 2000:2003,
series = c(1, 2))

多颜色的序列

R
1
2
3
4
5
6
7
8
9
n <- 10
colors <- sample(colorize(1:n))
df <- tibble(
x = 1:n,
y = runif(n, 1, 5),
color = colorize(y)
)
hchart(df, "coloredarea",
hcaes(x, y, segmentColor = color))

R
1
2
hchart(df, "coloredline",
hcaes(x, y, segmentColor = color))

fontawsome

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
library(MASS)
dscars <- round(mvrnorm(n = 20, mu = c(1, 1), Sigma = matrix(c(1, 0, 0, 1), 2)), 2)
dsplan <- round(mvrnorm(n = 10, mu = c(3, 4), Sigma = matrix(c(2,.5,2,2),2)), 2)
dstrck <- round(mvrnorm(n = 15, mu = c(5, 1), Sigma = matrix(c(1,.5,.5,1),2)), 2)

highchart() %>%
hc_chart(type = "scatter",
zoomType = "xy") %>%
hc_tooltip(
useHTML = T,
pointFormat = paste0(
"<span style=\"color:{series.color};\">{series.options.icon}</span>",
"{series.name}: <b>[{point.x}, {point.y}]</b><br/>"
)
) %>%
hc_add_series(data = list_parse2(
as.data.frame(dscars)
),
marker = list(
symbol = fa_icon_mark("calendar")
),
icon = fa_icon("calendar"), name = "calendar") %>%
hc_add_series(data = list_parse2(
as.data.frame(dsplan)
),
marker = list(
symbol = fa_icon_mark("plane")
),
icon = fa_icon("plane"), name = "plane") %>%
hc_add_series(data = list_parse2(
as.data.frame(dstrck)
),
marker = list(
symbol = fa_icon_mark("truck")
),
icon = fa_icon("truck"), name = "truck")

grouped categroies

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
library(purrr)
library(dplyr)
mpgg <- mpg %>%
filter(class %in% c("suv", "compact", "midsize")) %>%
group_by(class, manufacturer) %>%
summarise(count = n())

cate_grouped <- mpgg %>%
group_by(name = class) %>%
do(categories = .$manufacturer) %>%
list_parse()

highchart() %>%
hc_xAxis(categories = cate_grouped) %>%
hc_add_series(data = mpgg,
type = "bar",
hcaes(y = count,
color = manufacturer)) %>%
hc_add_theme(hc_theme_538())

导出csv

R
1
2
3
4
highcharts_demo() %>% 
hc_exporting(
enabled = T
)

拖拽散点

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
data("citytemp")
highchart() %>%
hc_chart(animation = F) %>%
hc_title(text = "draggable points demo") %>%
hc_xAxis(categories = month.abb) %>%
hc_plotOptions(
series = list(
point = list(
events = list(
drop = JS(
"function(){
alert(this.series.name + ' ' + this.category + ' ' + Highcharts.numberFormat(this.y, 2))
}"
)
)
),
stickyTracking = F
),
column = list(
stacking = "normal"
),
line = list(
cursor = "ns-resize"
)
) %>%
hc_tooltip(yDecimals = 2) %>%
hc_add_series(
data = citytemp$tokyo,
draggableY = T,
dragMinY = 0,
type = "column",
minPointLength = 2,
name = "东京"
) %>%
hc_add_series(
data = citytemp$new_york,
draggableY = T,
dragMinY = 0,
type = "column",
minPointLength = 2,
name = "纽约"
) %>%
hc_add_series(
data = citytemp$berlin,
draggableY = T,
name = "柏林"
)

更多的散点样式

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
dss <- map(c("square", "triangle", 
"cross", "plus"),
function(s){
x <- rnorm(20, runif(1)*2)
y <- (rnorm(1) + 1)*x + runif(20)
list(
name = s,
data = list_parse(
tibble(x, y)
),
marker = list(
symbol = s,
enabled = T
),
lineColor = NULL
)
})
highchart() %>%
hc_chart(type = "scatter") %>%
hc_add_series_list(dss)

R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
highchart() %>% 
hc_title(text = "I'm a pirate looking chart") %>%
hc_xAxis(categories = month.abb) %>%
hc_defs(patterns = list(
list(id = 'custom-pattern',
path = list(d = 'M 0 0 L 10 10 M 9 -1 L 11 1 M -1 9 L 1 11',
stroke = "black",
strokeWidth = 1
)
)
)) %>%
hc_add_series(data = c(7.0, 6.9, 9.5, 14.5, 18.2, 21.5, 25.2,
26.5, 23.3, 18.3, 13.9, 9.6),
type = "area",
fillColor = 'url(#custom-pattern)') %>%
hc_add_theme(hc_theme_handdrawn())

# R

评论

程振兴

程振兴 @czxa.top
截止今天,我已经在本博客上写了659.4k个字了!

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×