2. First processing tool

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.

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]])
r

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.

1##variable_name=type [default_value/from_variable]
r

... 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.

The header can contain different types of parameters. And according to them the tool will behave in one way or another:

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.
1##Example scripts=group
2##Scatterplot=name
3##Scatterplot from selected fields=display_name
r

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

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

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:

1##output_variable =output <type>
r

Among the possible outputs we have the following:

  • Layer outputs: vector, raster, table.
  • Value outputs: string number.
  • Directories and files: folder and file. In the case of file, you can specify an extension for the output file. For example csv.
👌 Tip!

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.

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
...
r

When you are ready:

  • Create a new script for QGIS Processing from :inline/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 options all;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.
  • 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.

🤞 Hint

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
...
r

Translations: