This topic is locked

Guide 6 –PHPRunner - Indicate direction through a map

12/8/2020 2:39:22 PM
PHPRunner Tips and Tricks
fhumanes author


This example solves a problem that occurs in sparsely populated urban centers and is the power to georeference an address through its latitude and longitude using OpenStreetMap.
But also, and no less important, what is explained is how to integrate JavaScript libraries in the developments made with PHPRunner, without having to develop a "plugin", quite simply and I think, accessible to almost everyone.
Requirements to be resolved
Using OpenStreetMap (you do not want to use Google Map due to its costs), it is desired that a user who is using the application on a mobile phone can report the location (latitude and longitude) where he lives or where an event or event has occurred. It is for users in rural areas, so there is no standard postal address that can inform us of that location
DEMO: https://fhumanes.com/address


Technical Solution
As I always do, the first thing is to see what exists on the internet and mainly, if there is a JavaScript library in GITHUB that provides the functionality that is required.
After several attempts and failures (it worked on PC, but not on mobile) I observed that this library was used on many websites https://leafletjs.com/
I verified that it worked on PC and mobile and that it gave me the required functionality. Now I just had to decide how to integrate it.
The solution, which I am going to explain to you, is to understand the simplest way to do it in PHPRunner.
First, create a table to do a proof-of-concept project of the functionality.

CREATE TABLE `address_user` (

`idaddress_user` int(11) NOT NULL AUTO_INCREMENT,

`Name` varchar(100) NOT NULL,

`Latitude` varchar(50) DEFAULT NULL,

`Longitude` varchar(50) DEFAULT NULL,

PRIMARY KEY (`idaddress_user`) )

ENGINE=InnoDB

AUTO_INCREMENT=0

DEFAULT CHARSET=utf8


Second, start a PHPRunner project (version 10.4, but the solution works for any version) that has these characteristics.

  • I modified the default Query to include the field that would represent the map

SELECT idaddress_user, Name, '' map, Latitude, Longitude FROM address_user


I use the "map" field to present the map on the screens.

  • To load the JavaScript libraries and definition of extra CSS styles that the solution requires, I used the "After Application Initialized" event. I do this so that these objects are also loaded for the "popup" pages. In this case the solution has a problem, which I have not solved yet, and it is not fully functional in "popup".
    In this event I create a variable with the HTML code that must be added to the page and in the definition part of the "head" header, I put it.
    In order not to load it on all pages, I check which "Table" or "View" is running and if it is adequate, I load the libraries.



customjscript ='';

global $pinfo;

$temp1 = explode('_',$pinfo['filename']);

$tableName = '';

for ($i = 0; $i < count($temp1)-1 ; $i++) {

$tableName .= $temp1[$i].'_';

}

$tableName = substr($tableName,0,-1); // delete last '_'

if ($tableName == 'address_user') { // Only load the libraries in the tables that require it

$customjscript = <<<EOT

<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/>

<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>

<style>

#map {

width: 600px;

height: 400px;

}

</style>

EOT;

}


  • The "map" field, in Designer, I indicate that it is a field of type "custom" and there I indicate the "DIV" of the map and the JavaScript that must be executed. The JavaScript has 3 variations:
    1/ When we are in "Add", we must locate ourselves on the map close to where we are and that is why the GPS is used to locate ourselves on the map.
    2/ When we are in "Edit" the possibility that there is no information is contemplated and then it acts as if it were "Add". If you have information, then it is used to locate yourself on the map.

    If the location is changed, then the record is updated and if no new address is reported, the Latitude and Longitude fields are not changed.
    3/ When we are in "View" the map is shown with the registered location information, without the possibility of changing this.



if ($data['Latitude'] == '') { // Has not fixed address

$value = <<<EOT

<div id='map'></div>

<script>

var lat = 0; // To share

var lon = 0; // To share



var map = L.map('map').fitWorld();



L.tileLayer('https://asprunner.com/forums/file.php?topicimage=1&fieldname=question&id=27955&image=3&table=forumtopics', {

attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'

}).addTo(map);

function onLocationFound(e) {

var radius = e.accuracy / 2;

L.marker(e.latlng).addTo(map)

.bindPopup("You are " + radius + " meters from this point").openPopup();

L.circle(e.latlng, radius).addTo(map);

}

function onLocationError(e) {

alert(e.message);

}

map.on('locationfound', onLocationFound);

map.on('locationerror', onLocationError);

map.locate({setView: true, maxZoom: 16});



var popup = L.popup();

function onMapClick(e) {

popup

.setLatLng(e.latlng)

.setContent("You have set the location to:
" + 'Latitude: '+ e.latlng.lat.toString() +'
'+'Longitude: '+e.latlng.lng.toString())

.openOn(map);

// console.log('Todo: '+e.latlng.toString());

// console.log('Separado: '+ e.latlng.lat.toString()+' más: '+ e.latlng.lng.toString());

lat = e.latlng.lat.toString();

lon = e.latlng.lng.toString();

}

map.on('click', onMapClick);

</script>

EOT;

} else {

$Latitude = $data['Latitude'];

$Longitude = $data['Longitude'];

$js_update = '';

global $params;

$pageType = $params[pageType];

if ($pageType == 'edit') { // Is page Edit

$js_update = <<<EOT

var popup = L.popup();

function onMapClick(e) {

popup

.setLatLng(e.latlng)

.setContent("You have set the location to:
" + 'Latitude: '+ e.latlng.lat.toString() +'
'+'Longitude: '+e.latlng.lng.toString())

.openOn(map);

// console.log('Todo: '+e.latlng.toString());

// console.log('Separado: '+ e.latlng.lat.toString()+' más: '+ e.latlng.lng.toString());

lat = e.latlng.lat.toString();

lon = e.latlng.lng.toString();

}

map.on('click', onMapClick);

EOT;

}

$value = <<<EOT

<div id='map'></div>

<script>

var lat = 0; // To share

var lon = 0; // To share



var map = L.map('map').setView([$Latitude, $Longitude], 16);



L.tileLayer('https://asprunner.com/forums/file.php?topicimage=1&fieldname=question&id=27955&image=4&table=forumtopics', {

attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'

}).addTo(map);

L.marker([$Latitude, $Longitude]).addTo(map)

.bindPopup('This is the address/place reported')

.openPopup();

$js_update

</script>

EOT;

};


  • The “Javascript OnLoad event” is used to control the interaction carried out on the map and load the data saved in the variables “lat” and “lon”, in the form fields. A very IMPORTANT aspect is the use of the "on ('beforeSave')" event that gives us the possibility of applying control logic to set whether it is mandatory or not and to pass the data from the maps' JavaScript variables to the PHPRunner fields.



var ctrlLatitude = Runner.getControl(pageid, 'Latitude');

ctrlLatitude.makeReadonly();

var ctrlLongintude = Runner.getControl(pageid, 'Longitude');

ctrlLongintude.makeReadonly();

this.on('beforeSave', function(formObj, fieldControlsArr, pageObj){

if ( lat == 0 ) {

alert("Es necesario informar en el mapa la dirección/lugar");

Runner.delDisabledClass(pageObj.saveButton ); // Activar nuevamente, botón SAVE

return false;

} else {

ctrlLatitude.setValue(lat);

ctrlLongintude.setValue(lon);

return true;

}

});


  • As I have explained in other examples, if we add "pseudo-fields" to the tables, these must be destroyed "unset (field)" in the "Before Record Add / Update" event so that they do not act on the inserts or updates.
    The most important part of the example is this way of integrating JavaScript libraries, which will allow you to make your applications have functionalities that would otherwise be impossible with the standard PHPRunner.
    As I always indicate, any questions or problems you have, you can send them to my email account [email="fernandohumanes@gmail.com"]fernandohumanes@gmail.com[/email]
    As always, I leave the sources of the example on my portal so that you can install it on your computers.

fhumanes author 12/18/2020

The functionality of the example has been extended by adding that once the coordinates of the address location have been obtained, the postal address of said location is obtained.
The OpenStreetMap Nomatim service is used to obtain the data.
The example page https://fhumanes.com/address/ has been updated adding this new functionality, which is also, like the use of maps, free.