Javascript dynamism
This tutorial should follow the first one on Javascript. We will continue the leaflet app by adding new features which requires JS to communicate with R, namely:
- A button to reset the view of leaflet to its default ;
- A label that will display where the user clicked on the map, by using R.
Note
Please note that the button resetting the view should be implemented directly in Javascript as R is not required for that. But for this tutorial, we will go through R to learn how to communicate between the two languages.
Resetting the view
In this section, we will learn how to send things from R to JS. First, create a new R script and call it main.R. Add it before the GUI in the sequence file main.pseq:

Now open the main.R file. There is two ways to communicate with Javascript :
- Using
rpgm.executeInJavascript(code): this allows to directly executes JS code. It is not recommended, as this limits how much data you can send to JS and does not make a clear distinction between your R and JS code. - Using
rpgm.sendToJavascript(message, data): this sends a "packet" to Javascript with some data that is automatically translated to JS. This is the recommended way.
Create a setDefaultView() function and call rpgm.sendToJavascript.
setDefaultView <- function(){
rpgm.sendToJavascript('resetView', list()); # resetView doesn't need data, so we use an empty list
}
resetView is just a name we chose so that we will know what to do when
receiving a resetView message in Javascript.
Go into main.pgui and add a button calling the R function:

Now we have to receive the message in Javascript by using the didReceivedJavascriptMessage event.
Edit the main.js file and add a function to reset the default view:
function setDefaultView(){
// Check the map exists
if(window.mapInstance){
window.mapInstance.setView([51.505, -0.09], 13);
window.mapInstance.invalidateSize(); // This fixes some grey area sometimes showing in leaflet
}
}
The function simply checks if the map exists and set the view to its default location.
Now we have to call this setDefaultView function when JS receives the resetView message:
RPGM.on('didReceiveJavascriptMessage', (message, data)=>{
if(message === 'resetView'){
setDefaultView();
}
});
You can test! Execute your app, drag the map and click on the button: the map will center itself on the initial location.
Detecting clicks in R
We will now learn how to send data from JS to R by detecting clicks on the map
and tell R where the click happened. Just like the didReceiveJavascriptMessage,
we will create a special function in R that will be called when data is sent from
Javascript.
In main.pgui, create a new label with id lastClick and a default value:

In the main.js file, add this code at the end of the initializeMap function
to listen to the click on the map:
// Detect clicks
window.mapInstance.on('click', (e)=>{
RPGM.sendToLanguage('r', 'mapClick', {latlng: e.latlng.toString()});
});
This code call the function rpgm.sendToLanguage(language, message, data):
- The first argument is the language to send the message to. Either
"r"or"python"; - The second argument is the message name that we can choose. Here we chose
"mapClick"; - The third and last argument is some data, here it's a string representation of the longitude and latitude of the click.
Info
Why is there no rpgm.executeInLanguage(language, code) function in Javascript?
Because this would open a huge security vulnerability: users could execute any
R or Python code on your computer or in RPGM Server, where even a beginner
computer science student could have a complete control of the server.
We now have to catch this message in R. For that, RPGM will call a function named
onRPGMJavascript(message, data) in R when it receives a Javascript message.
So, let's create a function in main.R:
onRPGMJavascript <- function(message, data){
if(message == 'mapClick'){
gui.setValue('this', 'lastClick', data$latlng);
}
}
The function check if the message is called lastClick, which is what we have
set in the main.js file. If so, set the value of the label to the latlng
data from JS which is a string representation of where the user clicked.
Now test the app:

It works!
Final files
Your R code should look like this:
setDefaultView <- function(){
rpgm.sendToJavascript('resetView', list()); # resetView doesn't not need data, so we use an empty list
}
onRPGMJavascript <- function(message, data){
if(message == 'mapClick'){
gui.setValue('this', 'lastClick', data$latlng);
}
}
And the Javascript code:
window.mapInstance = null;
function initializeMap(){
// Stop if the user is not in the correct GUI
if(RPGM.getCurrentStepId() !== 'leaflet'){
return;
}
// Stop if already initialized
if(window.mapInstance !== null){
return;
}
// Initialize map
window.mapInstance = L.map('map');
window.mapInstance.setView([51.505, -0.09], 13);
const tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(window.mapInstance);
// Detect clicks
window.mapInstance.on('click', (e)=>{
RPGM.sendToLanguage('r', 'mapClick', {latlng: e.latlng.toString()});
});
}
function setDefaultView(){
// Check the map exists
if(window.mapInstance){
window.mapInstance.setView([51.505, -0.09], 13);
window.mapInstance.invalidateSize(); // This fixes some grey area sometimes showing in leaflet
}
}
RPGM.on('didEnterStep', initializeMap); // Function called when entered a new step
RPGM.on('didReceiveJavascriptMessage', (message, data)=>{
if(message === 'resetView'){
setDefaultView();
}
});
initializeMap();
Advanced topics
You can find advanced topics about the Javascript integration in the next tutorial.