2. First processing tool
Introduction
An R script for Processing R Provider is no different than a normal R script. The small differences between the two are simply logical structures so that the QGIS plug-in can interpret them correctly. The following is a list of the peculiarities of R scripts for QGIS:
- These files have the extension
.rsx
and may be accompanied by a help file with the extension.rsx.help
.. - These have at least two major sections, a header and a body, which are discussed in a later section.
- Both
.rsx
and.rsx.help
must be in an rscripts directory predefined in the plug-in configuration.
All R instructions written in the script will be internally rewritten in a temporary Rscript (.r
|.R
) that will be executed in the R console.
Structure of a script
An R script for processing has two main parts. A header containing the tool configuration; and a body containing the R code that will execute the procedure. The example we will study below is taken from the built-in scripts in the installation of the Processing R provider plug-in.
1##Example scripts=group
2##Scatterplot=name
3##Layer=vector
4##X=Field Layer
5##Y=Field Layer
6##output_plots_to_html
7
8# simple scatterplot
9plot(Layer[[X]], Layer[[Y]])
Lines starting with a double hash sign (##
) become the header of the script. As example: ##Layer=vector
. This double hash sign should only be used for header lines (##
). Comment lines should begin with a single hash sign (#
). Considering this difference can avoid confusion to the Processing R Prvider algorithm. An example of a comment is the line # simple scatterplot
.
The header lines will be internally converted into different types of instructions, in order to work properly as a tool. These lines will determine the structure of the tool interface and the behavior of the tool.
A header line consists of two parts: Identifier and Type.
... from the above script we can take any of the first five lines as an example, and we will observe that it has the above mentioned structure. However, some header parameters may simply consist of a tag (for example ##output_plots_to_html
) that are used internally to indicate specific behaviors of the tool. More details will be given in a later section.
Comments and other R instructions are not part of the header, so they will be considered as script body. These lines will be transcribed to a temporary Rscript, which will be executed line by line in the R console, through a stdin/stdout subprocess.
Parameter types
The header can contain different types of parameters. And according to them the tool will behave in one way or another:
Metadata
These are parameters that allow you to organize the script in the processing index. Among them we have:
name
Allows to define a short name for the script.display_name
Allows to define a long name for the script. This will be displayed in the index and in the title bar of the tool.- group` name of the group the script belongs to. Allows you to organize the new tool as part of a specific group of Processing index tools.
Behavioral parameters
They are used to define the general behavior of the script. These parameters are configured in the header, but the user does not interact with them. Among them we have:
load_raster_using_rgdal
load_vector_using_rgdal
pass_filenames
dont_load_any_packages
github_install
expression
output_plots_to_html
Input parameters
These parameter lines specify the appearance of the script interface. From the basic structure ##parameter_name=type [ default value/from variable ]
we can note that parameter_name
will be the name of the object containing that variable in the R session. While type
will be the input data type, from the possible input types (vector, raster, point, number, string, boolean, ...).
Parameter | Default value | From variable |
---|---|---|
vector |
Yes | No |
raster |
Yes | No |
point |
Yes | No |
number |
Yes | No |
string |
Yes | No |
boolean |
Yes | No |
Field |
No | defined in vector |
color |
Yes | No |
range |
Yes | No |
datetime |
Yes | No |
Band |
No | defined in raster |
extent |
No | No |
crs |
No | No |
enum |
No | No |
enum literal |
No | No |
Output parameters
Those are lines that allow to define the type of result that the script will have. These parameters generate graphical interfaces to save files, while internally they are translated as instructions to write R objects as files. The specification of these parameters is as follows:
Among the possible outputs we have the following:
- Layer outputs:
vector
,raster
,table
. - Value outputs:
string
number
. - Directories and files:
folder
andfile
. In the case of file, you can specify an extension for the output file. For examplecsv
.
As an option, you can use the noprompt
keyword at the end of each output parameter, to specify not to generate the widget in the tool interface.
Exercise: Spatial centrallity tool
Assume you have a layer of points from which you need to calculate different measures of spatial central tendency. Your data is loaded into the QGIS session, but QGIS does not have a tool that allows you to calculate center points from your data.
The exercise is to create a tool for QGIS that does the calculation of the center point, with different statistics. For example "Mean center", "Median center", "Central feature" or "Weighted mean center" (see more details).
Before you try it, take 3 minutes to analyze the following R code. The portions of code between the <
and >
signs correspond to the variable names of the input parameters. The <
and >
signs are not necessary in a valid script, they have only been used to highlight the example. Your turn!
1# Functions and objects for calculating central point ----
2
3 # < Here will be the code for 4 helper functions for calculation. >
4 # < These are not important for now. >
5
6# Calculate central point ----
7 # extract coordinate matrix of the points ----
8 xy <- st_coordinates(< LAYER >)
9
10 # Control for weights field ----
11 < WEGIHTS FIELD > <- if(!is.null(< WEGIHTS FIELD >)) < LAYER >[[< WEGIHTS FIELD >]]
12
13 # get the central point ----
14 mc <- switch(< TYPE CENTER >,
15 mean.center = mean_mc(),
16 weighted.mean.center = mean_mc(< WEGIHTS FIELD >),
17 median.center = median_mc(),
18 central.feature = central_feature(),
19 all = all_features()
20 )
21
22 # Convert to Simple Feature y asign CRS ----
23 < CENTRAL POINT > <- st_as_sf(st_geometry(mc), crs = st_crs(< LAYER >))
24
25 # Name central point attributes ----
26 < CENTRAL POINT >$Name <- if(< TYPE CENTER > == "all") nms else nms[< TYPE CENTER >]
27
When you are ready:
-
Create a new script for QGIS Processing from
/Create New R Script
. -
Write the header of your QGIS Processing script, based on the R code that you have reviewed
- Start by assigning a name to the script with the parameter
name
. - Add a group name to the script with the parameter
group
. - Optionally, you can also add the
display_name
parameter to give a more explanatory title for the tool. - Add a parameter for a vector layer of type point using the
vector point
parameter. - Adds a drop-down list (
enum literal
) with the selection optionsall;mean.center;weighted.mean.center;median.center;central.feature
. Note that each item in the list is separated by a semicolon (;). - Add a parameter for an optional weight field to be displayed from the vector layer, using the
Field
parameter followed by the variable name you assigned in the input layer parameter. - Adds the name of the output layer using the
output vector
parameter.
- Start by assigning a name to the script with the parameter
-
Copy and paste the R code shown below into the body of your new script:
Click here to see the fully functional code of this exercise.
1# Funciones y objetos para calcular punto medio ---- 2 nms <- c(mean.center = "Mean center", 3 median.center = "Median center", 4 central.feature = "Central feature", 5 weighted.mean.center = "Weighted mean center") 6 7 mean_mc <- function(w = NULL){ 8 if(is.null(w) && Centro_espacial == "weighted.mean.center") 9 warning("Weights field is null. Mean center instead!") 10 11 if(!is.null(w)) { 12 m <- apply(xy, 2, weighted.mean, w = w) 13 } else {m <- apply(xy, 2, mean)} 14 st_point(m) 15 } 16 17 median_mc <- function() st_point(apply(xy, 2, median)) 18 19 central_feature <- function(){ 20 d <- st_distance(Capa) 21 d <- apply(d, 1, sum) 22 st_point(xy[which.min(d),]) 23 } 24 25 all_features <- function(){ 26 if(!is.null(Campo_de_pesos)) 27 st_sfc(mean_mc(), median_mc(), central_feature(), mean_mc(Campo_de_pesos)) 28 else 29 st_sfc(mean_mc(), median_mc(), central_feature()) 30 } 31 32# Calcular punto medio ---- 33 # extraer matriz de coordenadas de los puntos ---- 34 xy <- st_coordinates(Capa) 35 # Control para el campo de pesos ---- 36 Campo_de_pesos <- if(!is.null(Campo_de_pesos)) Capa[[Campo_de_pesos]] 37 # obtener el punto medio ---- 38 mc <- switch(Centro_espacial, 39 mean.center = mean_mc(), 40 weighted.mean.center = mean_mc(Campo_de_pesos), 41 median.center = median_mc(), 42 central.feature = central_feature(), 43 all = all_features() 44 ) 45 # Convertir a Simple Feature y asignar CRS ---- 46 Punto_central <- st_as_sf(st_geometry(mc), crs = st_crs(Capa)) 47 # Asignar nombres como atributos del punto medio ---- 48 Punto_central$Name <- if(Centro_espacial == "all") nms else nms[Centro_espacial]
r -
Finally save your script in the script directory defined in the plug-in configuration.
The content below has been intentionally hidden. Unfold it only if you feel you cannot perform the exercise on your own.
Click to display the help content.
1##Taller UseR!2022=group
2##centrality=name
3##Centralidad espacial=display_name
4##Capa=vector point
5##Centro_espacial=enum literal all
6##Campo_de_pesos=optional field Capa
7##Punto_central=output vector
8
9# FUNCIONES Y OBJETOS AUXILIARES PARA CALCULAR PUNTO MEDIO ----
10nms <- c(mean.center = "Mean center",
11 median.center = "Median center",
12 central.feature = "Central feature",
13 weighted.mean.center = "Weighted mean center")
14
15mean_mc <- function(w = NULL){
16 if(is.null(w) && Centro_espacial == "weighted.mean.center")
17 warning("Weights field is null. Mean center instead!")
18
19 if(!is.null(w)) {
20 m <- apply(xy, 2, weighted.mean, w = w)
21 } else {m <- apply(xy, 2, mean)}
22 st_point(m)
23}
24
25median_mc <- function() st_point(apply(xy, 2, median))
26
27central_feature <- function(){
28 d <- st_distance(Capa)
29 d <- apply(d, 1, sum)
30 st_point(xy[which.min(d),])
31}
32
33all_features <- function(){
34 if(!is.null(Campo_de_pesos))
35 st_sfc(mean_mc(), median_mc(), central_feature(), mean_mc(Campo_de_pesos))
36 else
37 st_sfc(mean_mc(), median_mc(), central_feature())
38}
39
40# CALCULAR PUNTO MEDIO ----
41# extraer matriz de coordenadas de los puntos ----
42xy <- st_coordinates(Capa)
43
44# Control para el campo de pesos ----
45Campo_de_pesos <- if(!is.null(Campo_de_pesos)) Capa[[Campo_de_pesos]]
46
47# obtener el punto medio ----
48mc <- switch(Centro_espacial,
49 mean.center = mean_mc(),
50 weighted.mean.center = mean_mc(Campo_de_pesos),
51 median.center = median_mc(),
52 central.feature = central_feature(),
53 all = all_features()
54)
55
56# Convertir a Simple Feature y asignar CRS ----
57Punto_central <- st_as_sf(st_geometry(mc), crs = st_crs(Capa))
58
59# Asignar nombres como atributos del punto medio ----
60Punto_central$Name <- if(Centro_espacial == "all") nms else nms[Centro_espacial]
61