Мой первый пост будет посвящен работе с пакетом lingtypology
, который я недавно закоммитил в CRAN. Скоро пройдет месяц и я смогу закоммитить пакет снова с исправлениями и новыми возможностями.
В пакете lingtypology
мне захотелось нарисовать карту. Однако на этот раз я хочу приблизиться к формату, который можно публиковать в статье. Так что я выбираю черно-белую подложку.
library(lingtypology)
# закачаем данные
df <- read.csv("http://web-corpora.net/~agricolamz/maps/2016%20Kuban%20article/data.csv")
# нарисуем при помощи пакета lingtypology
map.feature(df$language,
features = df$languoid,
latitude = df$latitude,
longitude = df$longitude,
radius = 8,
color = c("white", "black","grey"),
stroke.features = df$languoid,
stroke.color = "black",
stroke.legend = F,
tile = "OpenStreetMap.BlackAndWhite")
В целом, конечно, отлично, но мы же хотим знать что это за точки, ведь если мы опубликуем эту карту в статье никто не сможет тыкать в них. К тому же в январе я добавил в пакет lingtypology
аргументы, позволяющие добавлять изображения. Все необходимые названия селений уже записаны в файле:
head(df$village)
## [1] Вако-Жиле Бесленей Хабез Новохумаринский
## [5] Абазакт Псауче-Дахэ
## 22 Levels: Абазакт Адыгэ-Хабль Али-Бердуковский Баралки ... Эрсакон
Нужно получить список картинок (например, .png, желательно, прозрачных), содержащих названия всех селений и ничего другого. После этого можно будет выложить их в интернет и дать ссылки для пакета lingtypology
.
У меня было несколько вариантов:
base
, и в ggplot
, хотя идея делать графики, а потом их обрезать меня пугала. Как я автоматически пойму насколько нужно обрезать? Тем более, что буквы имеют подстрочные элементы (например, у или р), которые не надо обрезать, в то время, как это пространство у слов, не содержащих подстрочные элементы следует обрезать.standalone
, который делает .pdf, содержащий исключительно написанное или нарисованное, без всяких рамок и прочего. Правда потом все равно придется делать из .pdf .png, но это-то R умеет.Наверняка, есть способ более быстрый (совершенно точно), элегантный и прочее и прочее… Однако я решил использовать LaTeX, точнее его разновидность XeLaTeX. В результате я могу контролировать даже шрифт получивших надписей.
Для начала я решил создать векторы, содержащие в качестве строки XeLaTeX-код (все бэкслэши приходится экранировать):
latex <- paste("
\\documentclass{standalone}
\\usepackage[english,russian]{babel}
\\usepackage{fontspec}
\\defaultfontfeatures{Ligatures={TeX},Renderer=Basic}
\\setmainfont[Ligatures={TeX,Historic},
SmallCapsFont={Brill},
SmallCapsFeatures={Letters=SmallCaps}]{Brill}
\\setsansfont{Brill}
\\begin{document}",
df$village,
"\\end{document}")
latex[1]
## [1] "\n\\documentclass{standalone}\n\\usepackage[english,russian]{babel}\n\\usepackage{fontspec}\n\\defaultfontfeatures{Ligatures={TeX},Renderer=Basic}\n\\setmainfont[Ligatures={TeX,Historic},\n SmallCapsFont={Brill},\n\tSmallCapsFeatures={Letters=SmallCaps}]{Brill}\n\\setsansfont{Brill}\n\\begin{document} Вако-Жиле \\end{document}"
Теперь нужно создать все эти XeLaTeX файлы
setwd("/home/agricolamz/_DATA/OneDrive1/_Work/Articles/2017 II Kuban phonology/map/names")
mapply(function(x, y){write(x,file = paste0(y, ".tex"))},
latex, # аргумент x
df$village) # аргумент y
cd "/home/agricolamz/_DATA/OneDrive1/_Work/Articles/2017 II Kuban phonology/map/names"
ls
## How_to_create_village_names.html
## How_to_create_village_names.Rmd
## Абазакт.tex
## Адыгэ-Хабль.tex
## Али-Бердуковский.tex
## Баралки.tex
## Бесленей.tex
## Блечепсин.tex
## Вако-Жиле.tex
## Жако.tex
## Зеюко.tex
## Инжичишхо.tex
## Коноково.tex
## Кошехабль.tex
## Кош-Хабль.tex
## Кургоковское.tex
## Малый-Зеленчук.tex
## Новохумаринский.tex
## Псауче-Дахэ.tex
## Уляп.tex
## Хабез.tex
## Ходзь.tex
## Хумара.tex
## Эрсакон.tex
Скомпелируем все эти файлы .tex (если в каком-то из названий есть пробел, то у меня все умирает и не работает):
cd "/home/agricolamz/_DATA/OneDrive1/_Work/Articles/2017 II Kuban phonology/map/names"; for i in *.tex; do xelatex $i;done
# И приберемся за собой...
rm *.aux
rm *.log
rm *.tex
cd "/home/agricolamz/_DATA/OneDrive1/_Work/Articles/2017 II Kuban phonology/map/names"
ls
## How_to_create_village_names.html
## How_to_create_village_names.Rmd
## Абазакт.pdf
## Адыгэ-Хабль.pdf
## Али-Бердуковский.pdf
## Баралки.pdf
## Бесленей.pdf
## Блечепсин.pdf
## Вако-Жиле.pdf
## Жако.pdf
## Зеюко.pdf
## Инжичишхо.pdf
## Коноково.pdf
## Кошехабль.pdf
## Кош-Хабль.pdf
## Кургоковское.pdf
## Малый-Зеленчук.pdf
## Новохумаринский.pdf
## Псауче-Дахэ.pdf
## Уляп.pdf
## Хабез.pdf
## Ходзь.pdf
## Хумара.pdf
## Эрсакон.pdf
Последний шаг: сконвертируем получившиеся .pdf в .png:
setwd("/home/agricolamz/_DATA/OneDrive1/_Work/Articles/2017 II Kuban phonology/map/names")
library("animation")
sapply(df$village, function(x){
im.convert(paste0(x, ".pdf"),
paste0(x, ".png"),
extra.opts="-density 400")})
Теперь я залью файлы в интернет и построю карту:
# Вектор с адресами файлов в интернете
df$url <- paste0(
"http://web-corpora.net/~agricolamz/maps/2016%20Kuban%20article/",
df$village,
".png")
# карта
library(leaflet); library(lingtypology)
map.feature(df$language,
features = df$languoid,
latitude = df$latitude,
longitude = df$longitude,
radius = 8,
color = c("white", "black","grey"),
stroke.features = df$languoid,
stroke.color = "black",
stroke.legend = F,
popup = df$village,
image.url = df$url,
image.height = 15,
image.width = 10*nchar(as.character(df$village)),
tile = "OpenStreetMap.BlackAndWhite")
Дальше осталось вручную подвести каждый сдвиг названия относительно точки:
df$x.shift[6] <- 20
map.feature(df$language,
features = df$languoid,
latitude = df$latitude,
longitude = df$longitude,
radius = 8,
color = c("white", "black","grey"),
stroke.features = df$languoid,
stroke.color = "black",
stroke.legend = F,
image.url = df$url,
image.height = 11*df$font.size,
# Ширина изображения должна зависеть от количества символов
image.width = 10*nchar(as.character(df$village)),
image.X.shift = df$x.shift, # подобранные значения
image.Y.shift = df$y.shift, # подобранные значения
tile = "OpenStreetMap.BlackAndWhite")
Конечно, местами все не так красиво, все основные проблемы сейчас в аргументе image.height
однако цель, в целом, достигнута.