Joomla 4 Tips and Tricks: Using Google Geocode
From Joomla! Documentation
Introduction[edit]
This tip comes from a component that displays events such as festivals on a world map. Each event needs latitude and longitude data but only to the nearest town. This tip is about data entry for an individual event and does not cover the display of all events on a world map. The data entry form has umpteen fields spread amongst seven tabs - just three of the fields related to geocoding are mentioned here. You can look up latitude and longitude values in the what3words api but that involves finding the location to get the words.
The Google Map API Key[edit]
A key is obtained from Google - you may need different keys for your production and development environments. Provision is made for the key in the component configuration form:
<fieldset name="apikey"
label="COM_MYCOMPONENT_CONFIG_GOOGLE_MAPS_API_LABEL"
description="COM_MYCOMPONENT_CONFIG_GOOGLE_MAPS_API_DESC">
<field
id="apikey"
name="apikey"
type="text"
default=""
size="50"
label="MYCOMPONENT_CONFIG_GOOGLE_MAPS_API_KEY_LABEL"
description="COM_MYCOMPONENT_CONFIG_GOOGLE_MAPS_API_KEY_DESC"
/>
</fieldset>
This will create a form tab with one field. Navigate to your component configuration page and enter your API Key.
XML form file[edit]
The data entry form needs a custom field in its xml form file:
<field
name="geocode_address"
type="geocode"
label="COM_MYCOMPONENT_CAMP_GEOCODE_ADDRESS_LABEL"
description="COM_MYCOMPONENT_CAMP_GEOCODE_ADDRESS_DESC"
class=""
/>
<field
name="latitude"
type="text"
label="COM_COMPONENT_CAMP_LATITUDE_LABEL"
description="COM_COMPONENT_CAMP_LATITUDE_DESC"
class="w-auto validate-numeric"
size="32"
/>
<field
name="longitude"
type="text"
label="COM_COMPONENT_CAMP_LONGITUDE_LABEL"
description="COM_COMPONENT_CAMP_LONGITUDE_DESC"
class="w-auto validate-numeric"
size="32"
/>
HTML[edit]
Form field file[edit]
And a custom form field file in src/Field/GeocodeField to generate the code, which is rendered like this:
<div class="input-group">
<input id="jform_geocode_address" value="" name="jform[geocode_address]" class="form-control w-auto me-2">
<input class="form-control w-auto me-2 readonly" id="country_name" type="text" readonly="readonly" value="Iceland">
<button class="btn btn-sm btn-info me-2" id="setLatLon" onclick="return false;">Go!</button>
<button id="previewmap" class="btn btn-sm btn-info"
data-bs-toggle="modal"
data-bs-target="#modal-box"
data-bs-title="Location Map"
data-bs-action="showLocationMap"
onclick="return false;">Preview</button>
</div>
Edit file[edit]
The API key needs to be passed to Javascript in the edit file:
// near the top, before ?>
$config = ComponentHelper::getParams('com_mycomponent');
$key = $config->get('apikey');
$uri = JURI::getInstance();
$scheme = $uri->getScheme();
// Register and attach a custom item in one run
$wa->registerAndUseScript('gmap', $scheme . '://maps.googleapis.com/maps/api/js?key=' . $key . '&sensor=false', [], ['defer' => true]);
$wa->addInlineScript("
var mapapikey = '{$key}';
");
...
<!-- near the bottom -->
<?php include_once JPATH_COMPONENT . '/layouts/modalbox.php'; ?>
The data entry form, click the Go button to generate latitude and longitude values, then the Preview button to check the results:
Javascript[edit]
There are two part to the Javascript. The first part inserts latitude and longitude values:
// ===== geocode =====
var mygeocode = document.getElementById('setLatLon');
mygeocode && mygeocode.addEventListener('click', function (event) {
var country = document.getElementById('country_name').value;
var address = document.getElementById('jform_geocode_address').value + ',' + country;
var latitude = document.getElementById('jform_latitude');
var longitude = document.getElementById('jform_longitude');
var preview = document.getElementById('modal-box');
geocoder = new google.maps.Geocoder();
geocoder.geocode( { 'address': address }, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
latitude.value = results[0].geometry.location.lat();
longitude.value = results[0].geometry.location.lng();
} else {
alert('Geocode was not successful for the following reason: ' + status);
}
});
});
And the second part shows a location map for the user to check there has bot been a horrendous error with entry of the postal address:
// ===== Show a modal box and decide wht to do ===
var modalbox = document.getElementById("modal-box");
var modalSave = document.getElementById("modal-save");
if (modalbox) {
var modalTitle = modalbox.querySelector('.modal-title');
var modalBody = modalbox.querySelector('.modal-body');
}
modalbox && modalbox.addEventListener('show.bs.modal', function (event) {
// Button that triggered the modal
let button = event.relatedTarget;
// Extract info from data-bs-* attributes
let title = button.getAttribute('data-bs-title');
let action = button.getAttribute('data-bs-action');
let item_id = button.getAttribute('data-bs-id');
modalTitle.textContent = title;
// Set the modal content empty.
modalBody.textContent = 'Fetching content - Please Wait!';
...
if (action == 'showLocationMap') {
modalSave.classList.add('hidden');
let latitude = document.getElementById('jform_latitude');
let longitude = document.getElementById('jform_longitude');
let lat = latitude.value;
let lon = longitude.value;
if (lat == '0' && lon == '0') {
alert('Please set latitude and longitude and then select the Preview buttton!');
var description = document.querySelector(".modal-body");
description.innerHTML = 'Map display aborted - please set latitude and longitude';
return;
}
var url = `http://maps.googleapis.com/maps/api/staticmap?center=${lat},${lon}&zoom=5&size=200x200&maptype=hybrid&markers=color:green%7Clabel:C%7C${lat},${lon}&sensor=false&key=${mapapikey}`;
var description = document.querySelector(".modal-body");
description.innerHTML = '<div class="text-center"><img src="'+url+'"></div>';
}
...
}