Создание и визуализация пользовательских диаграмм и графиков в QGIS при помощи R: различия между версиями
(вроде завершил, осталась вычитка) |
Нет описания правки |
||
Строка 1: | Строка 1: | ||
{{Статья|Черновик}} | {{Статья|Черновик}} | ||
{{Аннотация|В QGIS есть встроенные инструменты построения графиков. С их помощью вы можете построить гистограммы, круговые диаграммы и текстовые диаграммы. К сожалению, встроенные инструменты обладают рядом недостатков, таких как множественные артифакты при рендеринге, громоздскость при отображении большого количества данных, непривлекательный вид. Создание собственных графиков может решить | {{Аннотация|В QGIS есть встроенные инструменты построения графиков. С их помощью вы можете построить гистограммы, круговые диаграммы и текстовые диаграммы. К сожалению, встроенные инструменты обладают рядом недостатков, таких как множественные артифакты при рендеринге, громоздскость при отображении большого количества данных, непривлекательный вид. Создание собственных графиков и диаграмм может решить часть этих проблем. В данной статье показано, как используя R можно создавать пользовательские диаграммы и графики для QGIS. }} | ||
==Идея== | ==Идея== |
Версия от 14:53, 29 марта 2015
В QGIS есть встроенные инструменты построения графиков. С их помощью вы можете построить гистограммы, круговые диаграммы и текстовые диаграммы. К сожалению, встроенные инструменты обладают рядом недостатков, таких как множественные артифакты при рендеринге, громоздскость при отображении большого количества данных, непривлекательный вид. Создание собственных графиков и диаграмм может решить часть этих проблем. В данной статье показано, как используя R можно создавать пользовательские диаграммы и графики для QGIS.
Идея
Мы можем создать необходимые графики в формате SVG сторонними инструментами и отображать их в качетсве [части] условного знака необходимого объекта. Здесь будет показано применение программной среды R, в частности, пакета "ggplot2".
Смоделируем ситуацию. Предположим, нам надо отобразить на диаграмме некоторые величины, находящиеся в атрибутах векторного слоя. Пусть у нас будет 12 атрибутов с необходимыми значениями, но мы знаем, что в будущем их количество увеличится до 20.
Если мы попытаемся использовать встроенные инструменты, то полученные диаграммы загромоздят значительную область экрана (гистограммы), либо будут практически нечитаемы (текстовые диаграммы). В нашем случае лучше всего подойдут радиальные диаграммы.
Вот так будет выглядеть конечный результат (два варианта):
Итак, нам нужно решить следующие задачи:
- Создать диаграммы в формате SVG.
- Вывести их на карту в качестве элемента условного знака объекта.
Так как вторая задача проще и, возможно, вы предпочитаете использовать другое ПО для создания диаграмм, то сначала объясним как визуализировать уже готовые диаграммы.
Визуализация диаграмм в QGIS
Здесь всё довольно просто. Поместите все диаграммы в одну папку и дайте им имена, соответствующие уникальным идентификаторам объектов в таблице атрибутов к которым они относятся. Например, если у векторного слоя есть атрибут "ID", содержащий значения типа 0, 1, 2..., то назовите соответствующие им диаграммы 0.svg, 1.svg, 2.svg...
Теперь перейдём к редактированию векторного слоя. Создайте текстовый атрибут в котором будет храниться путь к диаграммам (убедитесь, что поля будут способны вместить достаточное количество символов). Для заполнения полей атрибута воспользуемся калькулятором полей. Вот это простое выражение позволит справится с задачей:
'путь/к/папке/с/диаграммами/' || "ID" || '.svg'
Приведённое выражение создаёт строку текста путём присоединения к пути к папке значения атрибута "ID" и расширения файла. Таким образом, для каждого объекта слоя будет записан путь к файлу с его диаграммой. Обратите внимание, что статические (неизменные) значения заключены в одинарные кавычки ('), а динамические (извлекаемые из соответствующего атрибута слоя) - в двойные ("); кроме того, не забывайте, что последним символом в пути к папке должен быть слеш, в противном случае, полученный путь будет неправильным (например, 'путь0.svg', вместо 'путь/0.svg'). Создание атрибута для сохрания пути и его заполнение можно произвести одновременно - выберите опцию "создать новое поле" в калькуляторе полей.
Для того, чтобы отобразить диаграммы в качестве условного знака надо зайти в свойства точечного слоя и во вкладке Style задать тип символа слоя: SVG-маркер (если у вас полигональный слой, то Centroid Fill -> Marker -> SVG-marker). Справа от адресной строки задания пути к файлу нажмите кнопку Data defined override и выберите Field type: sting и задайте нужный атрибут, см. скриншот:
Обязательно задайте размеры маркера побольше - от 15 мм, а то диаграмма будет неразличима.
Создание скрипта R для построения диаграмм
Это - самая сложная часть. Неискушённому пользователю придётся порядочно покопаться в документации к "ggplot2" и соответствующих примерах, чтобы разобраться, как контролировать различные аспекты визуализации. Не будем вдаваться во все тонкости (читайте мануалы), отметим только основные моменты.
Вот какие задачи небходимо решить на этапе создания диаграмм:
- Подобрать палитру
Для этого как нельзя лучше подходит инструмент для подбора палитры Color Brewer. К сожалению, обеспечить отчётливое восприятие отдельных цветов и оттенков возможно только при довольно ограниченном наборе объектов для раскрашивания. Естественное ограничение - примерно 10 цветов, нам же нужно составить палитру для 20(!). Но выход есть - можно объединить две "количественные" палитры по 10 цветов:
- Добавить несуществующие данные
Это необходимо для того, чтобы внешний облик диаграмм не претерпевал значительных изменений при добавлении новых значений (см. код скрипта).
- Сделать фон дигаммы прозрачным
(Здесь и далее приведены параметры для построения графиков в "ggplot2")
plot.background = element_blank()
- Сделать фон области значений диаграммы прозрачным
panel.background = element_blank()
- Удалить сетку области значений диаграммы (опционально)
panel.grid = element_blank()
- Удалить легенду
legend.position = 'none'
- Удалить подписи к осям и объектам
axis.text.x = element_blank(), axis.text.y = element_blank()
- Обрезать поля диаграммы
plot.margin = unit(c(0,0,-0.3,-0.3), 'cm')
- Сохранить диаграммы на диск с сохранением прозрачности фона
ggsave(file = 'имя_файла_и_путь', plot = p, width = 5, height = 5, units = 'cm', bg = "transparent")
- Создать полноценную диаграмму-легенду для расшифровки созданных диаграмм
Для этого нужно построить диаграмму такого же типа, как и для объектов слоя, но с легендой, подписями и максимальными значениями данных.
Скрипт на R для Processing Toolbox
Теперь приведём пример скрипта для QGIS. Можете запустить окно создания R-скрипта в Processing Toolbox, вставить туда этот скрипт и сохранить (необходимо предварительно установить R и подключить его в QGIS). Он появится в R-scripts -> Plotting. Вот так выглядит окно настраиваемых параметров скрипта при его запуске из Processing Toolbox.
Список параметров:
- layer - Векторный слой с данными для диаграммы.
- ID - Атрибут, содержащий уникальные значения.
- start_field - Атрибут с которого начинается серия атрибутов, содержащих значения для построения диаграмм (атрибуты должны идти подряд).
- end_field - Атрибут на котором заканчивается серия атрибутов, содержащих значения для построения диаграмм (атрибуты должны идти подряд).
- add_ghost_data - Требуется ли добавлять несуществующие атрибуты? Ставьте галочку, если планируете в будущем добавить дополнительные атрибуты и переделывать диаграммы.
- total_bins_including_ghosts - если требуется добавить несуществующие атрибуты - указать общее количество атрибутов по которым будут строиться диаграммы в будущем.
- ghost_value - Если требуется добавить несуществующие значения - укажите значение которое будет присвоено дополнительным атрибутам на данный момент.
- plot_width - Ширина SVG-диаграмм в сантиметрах.
- plot_height - Высота SVG-диаграмм в сантиметрах.
- palette - Палитра для раскраски значений атрибутов диаграммы в виде HEX-кодов (наличие решётки "#" необязательно), разделённых запятой. Если количество элементов палитры будет меньше количества атрибутов, то часть атрибутов не будет отображена на диаграмме. Количество элементов палитры может превышать количество атрибутов.
- out_folder - Папка в которую будут записаны диаграммы. Несмотря на приписку "[optional]" - этот параметр является обязательным.
- R plots - Путь по которому будет сохранена диаграмма-легенда
После окончания работы скрипта в указанной папке появятся SVG-диаграммы, а пользователю будет показана диаграмма-легенда:
Ниже приведён код скрипта для запуска из Processing Toolbox, снабжённый необходимыми комментариями. Обратите внимание, что элементы графического интерфейса (входные параметры скрипта) задаются двойным знаком "#":
##Переменная = параметр дефолтное_значение
##Plotting = group
##layer=vector
##ID=field layer
##start_field= field layer
##end_field= field layer
##add_ghost_data=boolean False
##total_bins_including_ghosts=number 20
##ghost_value=number 0
##plot_width=number 5
##plot_height=number 5
##palette=string #a6cee3,#1f78b4,#b2df8a,#33a02c,#fb9a99,#e31a1c,#fdbf6f,#ff7f00,#cab2d6,#6a3d9a,#8dd3c7,#ffffb3,#bebada,#fb8072,#80b1d3,#fdb462,#b3de69,#fccde5,#d9d9d9,#bc80bd
##out_folder=folder
##showplots
library(ggplot2)
library(reshape2)
library(grid)
# R не допускает пробелов в именах атрибутов, а аткже начала имени атрибута с цифры.
# Необходимо привести загруженные в R имена атрибутов в соответсвие с их именами в векторном слое
fixFieldName <- function(field_name){
field_name <- gsub(' ', '.', field_name)
if( is.na(as.integer(substr(field_name,1,1))) == F ) {
field_name <- paste('X', field_name, sep = '')
}
field_name
}
palette <- strsplit(palette, ',')
palette_1 <- c()
# Processing удаляет знак '#' из строки параметров - надо это исправить
for (i in palette) {
char = substr(i,1,1)
print(char)
if ( char != '#') {
colour <- paste('#', i, sep = '')
palette_1 <- c(palette_1, colour)
}
}
palette <- palette_1
# Processing не добавляет слеш в конец адреса папки. Надо это исправить.
slash <- substr(out_folder, nchar(out_folder), nchar(out_folder))
if (slash != '/' | slash != '\\' ) {
out_folder <- paste(out_folder, '/', sep = '')
}
ID <- fixFieldName(ID)
start_field <- fixFieldName(start_field)
end_field <- fixFieldName(end_field)
dFrame <- as.data.frame(layer)
ID <- match(ID, names(dFrame))
start_field <- match(start_field, names(dFrame))
end_field <- match(end_field, names(dFrame))
print(start_field)
print(end_field)
max_value <- max(dFrame[,start_field:end_field])
addGhostData <- function(temp_df, d_value = 0, n_bins = 20){
# Эта функция добавляет несуществующие данные
temp_df$variable <- as.character(temp_df$variable) # необходимо для добавления новых строк
# создаём необходимое количество записей
if (total_columns < n_bins) {
for (col in (total_columns + 1):n_bins) {
row <- c(paste('X', 'ZZ99', col, sep = ''), d_value) # так в 90% случаев добавленные записи будут в конце легенды на диаграмме ;-)
temp_df <- rbind(temp_df, row)
}
}
# возвращаем датафрейм в нормальное состояние
temp_df$variable <- as.factor(temp_df$variable)
temp_df$value <- as.integer(temp_df$value)
temp_df
}
# создаём и записываем диаграммы
for (i in c( 1:nrow(dFrame) ) ) {
# подготовка данных
total_columns <- end_field - start_field +1
temp_df <- dFrame[,start_field:end_field][i,]
temp_df <- melt(temp_df)
# добавляем несуществующие атрибуты, если требуется
if (add_ghost_data) {
temp_df <- addGhostData(temp_df, ghost_value, total_bins_including_ghosts)
}
# рисуем диаграмму
p <- ggplot(temp_df, aes(x = variable, y = value)) +
geom_bar(aes(fill = variable), colour = 'white', stat="identity")+
coord_polar()+
ylim(0, max_value)+ # set y limits so all charts will be comparable
theme(axis.text.x = element_blank(),
axis.text.y = element_blank(),
axis.title = element_blank(),
axis.ticks = element_blank(),
title = element_text(face = 'bold', size = 16),
legend.position = 'none',
legend.title = element_blank(),
legend.text = element_text(size = 10),
plot.margin = unit(c(0,0,-0.3,-0.3), 'cm'), # удаляем поля!!!
plot.background = element_blank(), # прозрачный фон диаграммы
panel.background = element_blank(), # прозрачный фон области значений диаграммы
panel.grid = element_blank() # удаляем сетку области диаграммы (закомментируйте эту строку и запятую на предыдущей строке, чтобы оставить сетку)
)+
labs(x=NULL, y=NULL, title = NULL)+ # ещё раз удалим все подписи ;-)
scale_fill_manual(values = palette) # раскрасим диаграмму в нужные цвета
# получаем имя файла
fid <- dFrame[,ID][i]
file_name <- paste(out_folder, fid, '.svg', sep = '')
# сохраняем диаграмму
ggsave(file = file_name, plot = p, width = plot_width, height = plot_height, units = 'cm', bg = "transparent")
}
# Создаёс диаграмму-легенду
temp_df <- dFrame[,start_field:end_field]
l <- list(NULL)
for ( i in (1:ncol(temp_df)) ){l[[i]] = max_value}
temp_df <- rbind(temp_df, l)
row <- nrow(temp_df)
new_df <- temp_df[row,]
new_df <- melt(new_df)
# добавляем несуществующие атрибуты, если надо
if (add_ghost_data) {
new_df <- addGhostData(new_df, ghost_value, total_bins_including_ghosts)
}
# удаляем нежелательные знаки из имён атрибутов, а в 5% случаев - нужные (но ничего не поделать!)
new_df$variable <- as.character(new_df$variable)
for (i in (1:nrow(new_df))) {
variable <- new_df[,1][i]
print(variable)
new_df[,1][i] <- substr(variable, 2, nchar(variable))
}
new_df$variable <- as.factor(new_df$variable)
# Строим диаграмму-легенду
p <- ggplot(new_df, aes(x = variable, y = value)) +
geom_bar(aes(fill = variable), colour = 'white', stat="identity")+
coord_polar()+
ylim(0, max_value)+ # set y limits so all charts will be comparable
labs(title = 'Reference chart',
legend = 'Attribute') +
theme(axis.text.x = element_text(angle=0, hjust = NULL, size = 10, color = 'black'),
axis.text.y = element_text(angle=0, hjust = NULL, size = 10, color = 'black'),
axis.title = element_blank(),#element_text(face = 'bold', size = 16),
title = element_text(face = 'bold', size = 16),
legend.position = 'right',
legend.title = element_blank(),#element_text(angle=0, hjust = NULL, size = 14, color = 'black'),
legend.text = element_text(size = 10)
)+
scale_fill_manual(values = palette)
plot(p)
Заключение
В данной статье мы продемонстрировали как создавать пользовательские диаграммы на основе значений атрибутов векторных данных и отображать их в качестве условных знаков для объектов слоя. Приведённый скрипт