【发布时间】:2018-09-09 09:50:05
【问题描述】:
我正在尝试破解用于解析 SVG 路径的 R 工作流,在 this webpage 上使用 this file。我在定位生成的多边形时遇到了伪影:
一些国家不与邻国结盟 - 例如。美国/加拿大、美国/墨西哥、俄罗斯/亚洲邻国。由于效果会影响具有更复杂多边形的国家/地区,因此累积求和似乎可能是一个问题,但我不清楚问题出在我的工作流程中,即:
- 将原始 SVG 解析为 XML,并提取所有 SVG 路径字符串
- 使用
nodejs's svg-path-parser module 解析单个路径字符串 - 将生成的 data.frames(结合了绝对坐标和相对坐标)处理成所有绝对坐标
我在这里使用 R(针对美国/加拿大)复制了完整的工作流程,并通过外部调用 nodejs:
require(dplyr)
require(purrr)
require(stringr)
require(tidyr)
require(ggplot2)
require(rvest)
require(xml2)
require(jsonlite)
# Get and parse the SVG
doc = read_xml('https://visionscarto.net/public/fonds-de-cartes-en/visionscarto-bertin1953.svg')
countries = doc %>% html_nodes('.country')
names(countries) = html_attr(countries, 'id')
cdi = str_which(names(countries), 'CIV') # unicode in Cote d'Ivoire breaks the code
countries = countries[-cdi]
# Extract SVG paths and parse with node's svg-path-parser module.
# If you don't have node you can use this instead (note this step might be the problem):
# d = read_csv('https://gist.githubusercontent.com/geotheory/b7353a7a8a480209b31418c806cb1c9e/raw/6d3ba2a62f6e8667eef15e29a5893d9d795e8bb1/bertin_svg.csv')
d = imap_dfr(countries, ~{
message(.y)
svg_path = xml_find_all(.x, paste0("//*[@id='", .y, "']/d1:path")) %>% html_attr('d')
node_call = paste0("node -e \"var parseSVG = require('svg-path-parser'); var d='", svg_path,
"'; console.log(JSON.stringify(parseSVG(d)));\"")
system(node_call, intern = T) %>% fromJSON %>% mutate(country = .y)
}) %>% as_data_frame()
# some initial processing
d1 = d %>% filter(country %in% c('USA United States','CAN Canada')) %>%
mutate(x = replace_na(x, 0), y = replace_na(y, 0), # NAs need replacing
relative = replace_na(relative, FALSE),
grp = (command == 'closepath') %>% cumsum) # polygon grouping variable
# new object to loop through
d2 = d1 %>% mutate(x_adj = x, y_adj = y) %>% filter(command != 'closepath')
# loop through and change relative coords to absolute
for(i in 2:nrow(d2)){
if(d2$relative[i]){ # cumulative sum where coords are relative
d2$x_adj[i] = d2$x_adj[i-1] + d2$x_adj[i]
d2$y_adj[i] = d2$y_adj[i-1] + d2$y_adj[i]
} else{ # code M/L require no alteration
if(d2$code[i] == 'V') d2$x_adj[i] = d2$x_adj[i-1] # absolute vertical transform inherits previous x
if(d2$code[i] == 'H') d2$y_adj[i] = d2$y_adj[i-1] # absolute holrizontal transform etc
}
}
# plot result
d2 %>% ggplot(aes(x_adj, -y_adj, group = paste(country, grp))) +
geom_polygon(fill='white', col='black', size=.3) +
coord_equal() + guides(fill=F)
任何帮助表示赞赏。 SVG 路径语法在w3 中指定,并且更简洁地总结为here。
编辑(回复@ccprog)
这是从svg-path-parser 返回的H 命令序列的数据:
code command x y relative country
<chr> <chr> <dbl> <dbl> <lgl> <chr>
1 l lineto -0.91 -0.6 TRUE CAN Canada
2 l lineto -0.92 -0.59 TRUE CAN Canada
3 H horizontal lineto 189. NA NA CAN Canada
4 l lineto -1.03 0.02 TRUE CAN Canada
5 l lineto -0.74 -0.07 TRUE CAN Canada
这是d2 在循环之后的相同序列的样子:
code command x y relative country grp x_adj y_adj
<chr> <chr> <dbl> <dbl> <lgl> <chr> <int> <dbl> <dbl>
1 l lineto -0.91 -0.6 TRUE CAN Canada 20 199. 143.
2 l lineto -0.92 -0.59 TRUE CAN Canada 20 198. 143.
3 H horizontal lineto 189. 0 FALSE CAN Canada 20 189. 143.
4 l lineto -1.03 0.02 TRUE CAN Canada 20 188. 143.
5 l lineto -0.74 -0.07 TRUE CAN Canada 20 187. 143.
这看起来不好吗?当我查看 y_adj 的 H 和前几行的原始值时,它们是相同的 142.56。
编辑 2:工作解决方案,感谢@ccprog
d = imap_dfr(countries, ~{
message(.y)
svg_path = xml_find_all(.x, paste0("//*[@id='", .y, "']/d1:path")) %>% html_attr('d')
node_call = paste0("node -e \"var parseSVG = require('svg-path-parser'); var d='", svg_path,
"'; console.log(JSON.stringify(parseSVG.makeAbsolute(parseSVG(d))));\"")
system(node_call, intern = T) %>% fromJSON %>% mutate(country = .y)
}) %>% as_data_frame() %>%
mutate(grp = (command == 'moveto') %>% cumsum)
d %>% ggplot(aes(x, -y, group = grp, fill=country)) +
geom_polygon(col='black', size=.3, alpha=.5) +
coord_equal() + guides(fill=F)
【问题讨论】:
-
我也把这个提交到了github上的svg-path-parser模块
-
我对 R 不熟悉,但对我来说,您似乎通过查找
closepath命令将路径分组,然后将每个组中的第一个moveto作为起点累积转换为绝对位置的位置。错误的两个来源是:1。moveto命令,除了第一个,也可以是相对的(相对于前一组的最后一个坐标)。 2. 不能使用closepath命令关闭组。搜索开头moveto会更可靠。 -
嗨@ccprog。我确实使用
closepath创建变量grp(标识唯一多边形),但它在解析实际坐标时没有任何作用。事实上,我只是使用 SVGrelative字段,据我所知,它指定坐标是相对还是绝对。对于绝对代码,您还必须考虑H/V命令,这些命令继承了前一点的非活动坐标。