Custom Fields ● OpenStreetMap
with plenty of features
present slides https://slides.woluweb.be
Basic Joomla Tutorials 2020.01.07 | @basicjoomla YouTube ## Goal of this presentation
The goal is to create and assign an OpenStreetMap Custom Field to one or several categories. So each article (being Members, Events, whatever) will be geolocalized.
And based on this we will create a global OpenStreetMap module showing all the locations.
We will start with a simple multiple markers map.
And then we will even end up with: - Multiple markers - Different markers for each category - Tooltip showing
1. Title with Link
2. Intro Image
3. Intro Text - Clustering - And even Layers with Filters
1 NEW FOR JOOMLA 4
Someone contacted me in June 2023 to let me know that the override would not work any more (at least in her case) in Joomla 4 while it was working on her Joomla 3 website.
That error was mentioning “FieldsHelper”.
So I just told her to add the following at the beginning of the file and this solved the issue:
use Joomla\Component\Fields\Administrator\Helper\FieldsHelper
2 What we will achieve
3 The online demo
4 OpenStreetMap - Leaflet
In order to create our OpenStreetMap, we will use the following open-source Javascript library: https://leafletjs.com/
See the following examples: https://leafletjs.com/examples.html
Note : don’t forget of course to add the link to the .js file and to the .css file
5 OSM Custom Fields
I have found 3 OpenStreetMap Custom Fields out there.
Each has its strengths :)
5.1 Advanced Custom Fields by Tassos Marinos
Free and Paid (€ 19)
Includes 25+ Custom Fields, among which OSM, Google Maps & Bing Maps
https://www.tassos.gr/joomla-extensions/advanced-custom-fields
https://www.tassos.gr/joomla-extensions/advanced-custom-fields/docs/the-openstreetmap-field
5.2 OpenStreetMap Custom Field by Nordmograph
Paid (€ 10)
There is also a Google Maps field. There are also extensions allowing to build maps linked to different sources (Articles but also third-party extensions. You can really build crazy things (see example during this live session)
5.3 OpenStreetMap by GMapFP
Free
There are also plenty of maps-related extensions by GMapFP.
https://creation-web.pro/extensions-joomla-francaises/46-field-osm
6 The chosen OSM Custom Field
For the sake of this presentation we will use the OSM Custom Field, being part of Advanced Custom Fields by Tassos Marinos.
The reason being that it will only write the Latitude/Longitude in the database (without extra informations like a personalized marker, a text, a zoom level or whatever).
So it will be easier for us to manipulate this Custom Field to create our multiple markers map.
7 Little change since version 1.1.0 of that OSM Custom Field (with overrides)
Since version 1.1.0 released in June 2020, this OSM Custom field by Tassos Marinos has new features and includes a.o. a Text field in order to display a Tooltip.
In practice this means that the value of the OSM field is now stored in the database as JSON, in both Free and Pro versions and regardless of whether you’ve any of the new options enabled.
In practise this means that I have adapted slightly the code in the overrides shown in the next Chapter.
Before
$latlon = $fields_by_name['open-street-map']->rawvalue;
and you would echo $latlon
Now
$value = json_decode($fields_by_name['open-street-map']->rawvalue);
$tooltip = $value->tooltip;
$latlon = $value->coordinates;
and you would echo $latlon
Note : the tooltip is a Pro feature. We don’t need it in our examples even if I mention it here for completeness
There are 3 scenarios (no matter whether you are using Free or Pro, activating the Tooltip or not) :
- if you have upgraded from a previous version and already had articles having an OSM custom field and are ready to edit&save each concerned article then you should simply use the code “after”
- if you have upgraded from a previous version and already had articles having an OSM custom field but are NOT ready to edit&save each concerned article (for example bc you have too many) then you have to add the fixValue() Method. See code on the next slide
- if you are still on some previous version and don’t want to update then you should use the code “now” or [better option] use the fixValue() Method to be future-proof
Note : actually if you have a mix of articles in the new and in the old format, the latter will simply not show on the map - unless you edit&save them (scenario 1) - or unless you add the fixValue() Method (scenario 2)
Code to be used if you keep articles having the old format:
// this should be put outside the loop
// the OSM CF rawvalue will be transformed in the new format for articles having the old format
function fixValue(&$value) {
// New format already
if (is_object(json_decode($value)))
{return;
}
// Convert old format
$value = json_encode(['coordinates' => $value]);
}
// this will be put inside the loop (like in the classical example)
$value = $fields_by_name['open-street-map']->rawvalue;
$value);
fixValue($value = json_decode($value);
$latlon = $value->coordinates;
$tooltip = $value->tooltip;
8 New since version 2.6.0 of Advanced Custom Fields
See the changelog of Advanced Custom Fields: https://www.tassos.gr/joomla-extensions/advanced-custom-fields/changelog :
- Jun 06, 2023 - version 2.6.0 : Implements the Map Field allowing you to create OpenStreetMap, Google Maps and Bing Maps with unlimited markers
- …
- Nov 06, 2023 - version 2.7.2 : Removes deprecated fields Google Maps, OpenStreetMap and Bing Maps in favor of ACF Map
So the code for the “good old Open Street Map Custom Field” is still
$value = json_decode($fields_by_name['open-street-map']->rawvalue);
$latlon = $value->coordinates;
but the code for the “new Map Custom Field” becomes
$value = json_decode($fields_by_name['open-street-map']->rawvalue,true);
$lat = $value[0]['latitude'];
$lon = $value[0]['longitude'];
$latlon = $lat.",".$lon;
9 Little change since version 1.1.0 of that OSM Custom Field (without overrides)
Also, because of this using Articles Anywhere as explained hereafter will be less straightforward since you will need to “json_decode” (so you will probably need to use Sourcerer inside Articles Anywhere… and of course order the plugins correctly if it does not trigger as expected).
Before:
-street-map output="value"] [open
After
{source}<?php
$value = json_decode('[open-street-map output="value"]');
$latlon = $value->coordinates;
echo $latlon;
?>
/source} {
10 Your own map-marker
Instead of PNG image, I chose SVG image for two reasons : 1. vector => image is always perfect whatever its size 2. easy to edit to change colors : you don’t need Photoshop… just edit it with your basic Text Editor
The present SVG was found on https://www.iconfinder.com/search/?q=map+marker&from=navbar
11 Preparing the Articles
In the present demo, we will create two Categories - JoomlaDays - Joomla User Groups
Then we install the OSM Custom Field.
Finally, we create a OSM Custom Field and assign it to the 2 above categories.
12 OSM without override
How could we possibibly loop through articles of different categories without playing with Overrides / Alternate Layouts ?
Actually, simply by using Articles Anywhere by RegularLabs
https://www.regularlabs.com/extensions/articlesanywhere
The only drawback compared to making overrides : we will need the Pro version (the Free version does not allow to loop through articles).
In many cases it would be enough, but in order to avoid end-users to “break” the code we would put in an Article or in a Module (and in this case also because our Editor would strip some part of our code), we will also use ReReplacer by RegularLabs
https://www.regularlabs.com/extensions/rereplacer
This allows us to replace a shortcode that we create like {osm} by our code (and we can even use regular expressions which can be handy).
13 Articles Anywhere - right configuration
Have the following order in the Plugins: 1. System - Regular Labs - ReReplacer 2. System - Regular Labs - Articles Anywhere
To avoid having the Comments START: Articles Anywhere in the HTML before/after the content -which would prevent the display of the map-markers- go to plugin Articles Anywhere > Advanced > set “Place HTML comments” to NO.
14 Articles Anywhere - syntax
Let’s assume we have a Category called JoomlaDays and that we created an OSM Custom Field with Name “open-street-name”
The following code
="JoomlaDays"}
{articles category-street-map output="value"]
[open/articles} {
will output
50.84717044999999,4.35198095255952
50.842995099999996,4.43528195676678
Interesting feature : the possibility to use syntax like category=“current”
14.1 OSM without override 1 - screenshot
just one Category | custom Marker (svg) | Tooltip with link to Article
14.2 OSM without override 1 - code
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script>
<div id="map" style="width: 100%; height: 400px;"> </div>
<script>
var map = L.map('map').setView([50.84717044999999,4.35198095255952], 12);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
attribution.addTo(map);
})
var MyOwnIcon = L.icon({
: '/images/map-marker.svg',
iconUrl: [38, 38],
iconSize;
})
="Recipes"}
{articles categoryL.marker([[open-street-map output="value"]], {icon: MyOwnIcon}).bindPopup('[link][title][/link]').addTo(map);
/articles}
{
</script>
14.3 OSM without override 1 - comments
Note :
"[link][title][/link]"
would not work because of double quotes in the Link… which are not “escaped”
2 solutions :
'[link][title][/link]'
"<a href=\"[sefurl]\">[title]</a>"
14.4 OSM without override 2 - screenshot
several Categories - different Marker for each Category
14.5 OSM without override 2 - code
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script>
<div id="map" style="width: 100%; height: 400px;"> </div>
<script>
var map = L.map('map').setView([50.84717044999999,4.35198095255952], 6);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
attribution.addTo(map);
})
var MyOwnIcon = L.Icon.extend({
: {
options: [40, 40],
iconSize: [20, 40],
iconAnchor: [0, -45]
popupAnchor
};
})
var jugsIcon = new MyOwnIcon({
: '/images/map-marker-blue.svg'
iconUrl,
})= new MyOwnIcon({
joomladaysIcon : '/images/map-marker-green.svg'
iconUrl;
})
="joomla-events" include_child_categories="true"}
{articles categoryL.marker([[open-street-map output="value"]], {icon: [category-alias]Icon}).bindPopup('[link][title][/link]').addTo(map);
/articles}
{
</script>
14.6 OSM without override 3 - screenshot
tooltip opens automatically
14.7 OSM without override 3 - code
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script>
<div id="map" style="width: 100%; height: 400px;"> </div>
<script>
var map = L.map('map').setView([50.84717044999999,4.35198095255952], 6);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
attribution.addTo(map);
})
var MyOwnIcon = L.Icon.extend({
: {
options: [40, 40],
iconSize: [20, 40],
iconAnchor: [0, -45]
popupAnchor
};
})
var jugsIcon = new MyOwnIcon({
: '/images/map-marker-blue.svg'
iconUrl,
})= new MyOwnIcon({
joomladaysIcon : '/images/map-marker-green.svg'
iconUrl;
})
L.control.scale().addTo(map);
="joomla-events" include_child_categories="true" order="title"}
{articles categoryL.marker([[open-street-map output="value"]], {icon: [category-alias]Icon}).bindPopup('[link][title][/link]').addTo(map).openPopup();
/articles}
{
</script>
="joomla-events" include_child_categories="true"}
{articles categorylink][title][/link]<br />
[/articles} {
14.8 OSM without override 3 - comment
.addTo(map).openPopup()
The openPopup() opens then the popup (so for the last article of the loop since only one is shown at the time)
15 OSM with override
15.1 Modules: Articles - Newsflash
As you remember from the previous presentations, in an override / alternate layout we can display the Custom Field having ID “X” with the following line of code :
<?php echo $this->item->jcfields[X]->value; ?>
This works automatically for many Views, but not for all of them.
For example, in Modules: Articles - Newsflash, if you set “Trigger Plugin Events” to YES, then the variable jcfields will be available. But if you set it to NO, the Custom Fields won’t be readily available.
And for this presentation I don’t want to use Modules: Articles - Newsflash because it has too many options (so one might get confused by the length of the override).
15.2 Modules: Articles - Latest News
Therefore, I prefer to use Modules: Articles - Latest because it has just the Options we need : - Category: selection of one or multiple Category - Count: set it to 0 to have no limit
The only drawback is that jcfields is not readily available. Nevermind, Alexandre ELISÉ shared a few line of codes allowing to use Custom Fields by ID and even by Name in such a case:
https://gist.github.com/alexandreelise/e04d417c9f911ce2ab2a3e931142e89b
15.3 Override print_r
if you want to see what is available for each Article
<?php
/**
* @package Joomla.Site
* @subpackage mod_articles_latest
*
* @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
?>
<?php
foreach ($list as $item) {
echo '<pre>' . print_r($item, true) . '</pre>';
}?>
15.4 adding CSS and JS in our override
Instead of adding the Leaflet CSS and JS directly like this
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script>
we should use Joomla’s API for stylesheets/scripts and have this
<?php
// https://docs.joomla.org/J3.x:Adding_JavaScript_and_CSS_to_the_page
use Joomla\CMS\Factory;
$document = Factory::getDocument();
$document->addStyleSheet("https://unpkg.com/leaflet@1.6.0/dist/leaflet.css", [], ['integrity' => 'sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==','crossorigin' => '']);
$document->addScript("https://unpkg.com/leaflet@1.6.0/dist/leaflet.js", [], ['integrity' => 'sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==','crossorigin' => '']);
?>
Txs to Dimitris Grammatikogiannis for drawing our attention to this
15.5 OSM with override - basic - screenshot
multiple Categories | tooltip with link to Article | standard Marker
15.6 OSM with override - basic - code
<?php
/**
* @package Joomla.Site
* @subpackage mod_articles_latest
*
* @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
?>
<?php
// https://docs.joomla.org/J3.x:Adding_JavaScript_and_CSS_to_the_page
use Joomla\CMS\Factory;
$document = Factory::getDocument();
$document->addStyleSheet("https://unpkg.com/leaflet@1.6.0/dist/leaflet.css", [], ['integrity' => 'sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==','crossorigin' => '']);
$document->addScript("https://unpkg.com/leaflet@1.6.0/dist/leaflet.js", [], ['integrity' => 'sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==','crossorigin' => '']);
?>
<h2>Override for OpenStreetMap</h2>
<div id="map" style="width: 100%; height: 400px;"> </div>
<script>
var map = L.map('map').setView([50.84717044999999,4.35198095255952], 6);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
attribution.addTo(map);
})
<?php foreach ($list as $item) : ?>
<?php
// example made after an exchange between Marc Dechèvre and Alexandre Elisé
// https://gist.github.com/alexandreelise/e04d417c9f911ce2ab2a3e931142e89b
$jcfields = FieldsHelper::getFields('com_content.article', $item, true);
// custom fields by name
// usage: $fields_by_name['name-of-field']->value
$fields_by_name = \Joomla\Utilities\ArrayHelper::pivot($jcfields, 'name');
?>
<?php
$value = json_decode($fields_by_name['open-street-map']->rawvalue);
$tooltip = $value->tooltip;
$latlon = $value->coordinates;
?>
<?php if (!empty($latlon)): // add a marker only if lat/long is not empty otherwise would get error ?>
<?php // use htmlspecialchars($item->title ,ENT_QUOTES) for the Title and not just $item->title otherwise the map would not display if the Title would for example contain a simple quote ?>
L.marker([<?php echo $latlon; ?>]).bindPopup('<a href="<?php echo $item->link; ?>"><?php echo htmlspecialchars($item->title, ENT_QUOTES); ?></a>').addTo(map);
<?php endif ?>
<?php endforeach; ?>
</script>
15.7 OSM with override - basic with clustering
When you have numerous locations then you will probably want to have clustering on your map so that map-markers “merge” when unzooming.
In order to do that, we need to integrate another javascript in our code.
Instead of adding the different points directly to the Map, we add them to a Layer. And once the Layer is complete we add it to the map. And all points on that Layer will cluster automatically when needed :)
15.8 OSM with override - basic with clustering - code
<?php
/**
* @package Joomla.Site
* @subpackage mod_articles_latest
*
* @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
?>
<?php
// https://docs.joomla.org/J3.x:Adding_JavaScript_and_CSS_to_the_page
use Joomla\CMS\Factory;
$document = Factory::getDocument();
// Leaflet library - SEE : https://leafletjs.com/
$document->addStyleSheet("https://unpkg.com/leaflet@1.6.0/dist/leaflet.css", [], ['integrity' => 'sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==','crossorigin' => '']);
$document->addScript("https://unpkg.com/leaflet@1.6.0/dist/leaflet.js", [], ['integrity' => 'sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==','crossorigin' => '']);
// Cluster plugin - SEE : https://github.com/Leaflet/Leaflet.markercluster
$document->addStyleSheet("https://leaflet.github.io/Leaflet.markercluster/dist/MarkerCluster.css", [], ['crossorigin' => '']);
$document->addStyleSheet("https://leaflet.github.io/Leaflet.markercluster/dist/MarkerCluster.Default.css", [], ['crossorigin' => '']);
$document->addScript("https://leaflet.github.io/Leaflet.markercluster/dist/leaflet.markercluster-src.js", [], ['crossorigin' => '']);
?>
<h2>Override for OpenStreetMap</h2>
<div id="map" style="width: 100%; height: 400px;"> </div>
<script>
var map = L.map('map').setView([50.84717044999999,4.35198095255952], 6);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
attribution.addTo(map);
})
var myClusterGroup = L.markerClusterGroup();
<?php foreach ($list as $item) : ?>
<?php
// example made after an exchange between Marc Dechèvre and Alexandre Elisé
// https://gist.github.com/alexandreelise/e04d417c9f911ce2ab2a3e931142e89b
$jcfields = FieldsHelper::getFields('com_content.article', $item, true);
// custom fields by name
// usage: $fields_by_name['name-of-field']->value
$fields_by_name = \Joomla\Utilities\ArrayHelper::pivot($jcfields, 'name');
?>
<?php
$value = json_decode($fields_by_name['open-street-map']->rawvalue);
$tooltip = $value->tooltip;
$latlon = $value->coordinates;
?>
<?php if (!empty($latlon)): // add a marker only if lat/long is not empty otherwise would get error ?>
<?php // use htmlspecialchars($item->title ,ENT_QUOTES) for the Title and not just $item->title otherwise the map would not display if the Title would for example contain a simple quote ?>
var marker = L.marker([<?php echo $latlon; ?>]).bindPopup('<a href="<?php echo $item->link; ?>"><?php echo htmlspecialchars($item->title, ENT_QUOTES); ?></a>');
.addLayer(marker);
myClusterGroup
<?php endif ?>
<?php endforeach; ?>
.addLayer(myClusterGroup);
map
</script>
15.9 OSM with override - intermediate - screenshot
diff. Marker for each Category | tooltip with extra Custom Fields
15.10 OSM with override - intermediate - code
<?php
/**
* @package Joomla.Site
* @subpackage mod_articles_latest
*
* @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
?>
<?php
// https://docs.joomla.org/J3.x:Adding_JavaScript_and_CSS_to_the_page
use Joomla\CMS\Factory;
$document = Factory::getDocument();
$document->addStyleSheet("https://unpkg.com/leaflet@1.6.0/dist/leaflet.css", [], ['integrity' => 'sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==','crossorigin' => '']);
$document->addScript("https://unpkg.com/leaflet@1.6.0/dist/leaflet.js", [], ['integrity' => 'sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==','crossorigin' => '']);
?>
<h2>Override for OpenStreetMap</h2>
<div id="map" style="width: 100%; height: 400px;"></div>
<script>
var map = L.map('map').setView([50.84717044999999,4.35198095255952], 6);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
attribution.addTo(map);
})
var MyOwnIcon = L.Icon.extend({
: {
options: [40, 40],
iconSize: [20, 40],
iconAnchor: [0, -45]
popupAnchor
};
})
var jugsIcon = new MyOwnIcon({
: '/images/map-marker-blue.svg'
iconUrl,
})= new MyOwnIcon({
joomladaysIcon : '/images/map-marker-green.svg'
iconUrl,
})= new MyOwnIcon({
enIcon : '/images/map-marker-red.svg'
iconUrl;
})
L.control.scale().addTo(map);
<?php foreach ($list as $item) : ?>
<?php
// example made after an exchange between Marc Dechèvre and Alexandre Elisé
// https://gist.github.com/alexandreelise/e04d417c9f911ce2ab2a3e931142e89b
$jcfields = FieldsHelper::getFields('com_content.article', $item, true);
// custom fields by name
// usage: $fields_by_name['name-of-field']->value
$fields_by_name = \Joomla\Utilities\ArrayHelper::pivot($jcfields, 'name');
?>
<?php
$value = json_decode($fields_by_name['open-street-map']->rawvalue);
$tooltip = $value->tooltip;
$latlon = $value->coordinates;
?>
<?php if (!empty($latlon)): // add a marker only if lat/long is not empty otherwise would get error ?>
<?php // use htmlspecialchars($item->title ,ENT_QUOTES) for the Title and not just $item->title otherwise the map would not display if the Title would for example contain a simple quote ?>
var customPopup = '<a href="<?php echo $item->link; ?>"><?php echo htmlspecialchars($item->title, ENT_QUOTES); ?></a><br /><?php echo $fields_by_name['country']->value; ?>';
L.marker([<?php echo $latlon; ?>], {icon: <?php echo $item->category_alias; ?>Icon}).bindPopup(customPopup).addTo(map);
<?php endif ?>
<?php endforeach; ?>
</script>
15.11 OSM with override - advanced - screenshot
Clustering | Layers with Filters
15.12 OSM with override - advanced - code
<?php
/**
* @package Joomla.Site
* @subpackage mod_articles_latest
*
* @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
?>
<?php
// https://docs.joomla.org/J3.x:Adding_JavaScript_and_CSS_to_the_page
use Joomla\CMS\Factory;
$document = Factory::getDocument();
// Leaflet library - SEE : https://leafletjs.com/
$document->addStyleSheet("https://unpkg.com/leaflet@1.6.0/dist/leaflet.css", [], ['integrity' => 'sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==','crossorigin' => '']);
$document->addScript("https://unpkg.com/leaflet@1.6.0/dist/leaflet.js", [], ['integrity' => 'sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==','crossorigin' => '']);
// Cluster plugin - SEE : https://github.com/Leaflet/Leaflet.markercluster
$document->addStyleSheet("https://leaflet.github.io/Leaflet.markercluster/dist/MarkerCluster.css", [], ['crossorigin' => '']);
$document->addStyleSheet("https://leaflet.github.io/Leaflet.markercluster/dist/MarkerCluster.Default.css", [], ['crossorigin' => '']);
$document->addScript("https://leaflet.github.io/Leaflet.markercluster/dist/leaflet.markercluster-src.js", [], ['crossorigin' => '']);
?>
<?php
use Joomla\CMS\Uri\Uri;
// A function used when we need to pass HTML code into a Javascript variable (article's title and introduction)
function convertHtmlToJsString($string)
{$string = str_replace("\r", "", $string);
$string = str_replace("\n", "", $string);
return addslashes($string);
}?>
<h2>Override for OpenStreetMap</h2>
<div id="map" style="width: 100%; height: 400px;"></div>
<script>
.addEventListener('DOMContentLoaded', function() {
document//
var icon_dirname = '<?php echo Uri::root();?>/images';
var map_center_latlon = [50.84717044999999,4.35198095255952];
var map_zoom_initial = 6;
var use_clustering = true; // true | false
// Declare icons
var MyOwnIcon = L.Icon.extend({
: {
options: [40, 40],
iconSize: [20, 40],
iconAnchor: [0, -45]
popupAnchor
};
})var jugsIcon = new MyOwnIcon({
: icon_dirname + '/map-marker-blue.svg'
iconUrl;
})var joomladaysIcon = new MyOwnIcon({
: icon_dirname + '/map-marker-green.svg'
iconUrl;
})//
var map = L.map('map').setView(map_center_latlon, map_zoom_initial);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
attribution.addTo(map);
})L.control.scale().addTo(map);
// Because we use "overlays" (checkbox markers selection) we need layergroups.
// See : https://leafletjs.com/reference-1.6.0.html#control-layers
var layerGroups = {};
var layergroup_name;
<?php foreach ($list as $item) : ?>
<?php
// example made after an exchange between Marc Dechèvre and Alexandre Elisé
// https://gist.github.com/alexandreelise/e04d417c9f911ce2ab2a3e931142e89b
$jcfields = FieldsHelper::getFields('com_content.article', $item, true);
// custom fields by name
// usage: $fields_by_name['name-of-field']->value
$fields_by_name = \Joomla\Utilities\ArrayHelper::pivot($jcfields, 'name');
$value = json_decode($fields_by_name['open-street-map']->rawvalue);
$tooltip = $value->tooltip;
$latlon = $value->coordinates;
// Check if custom field "open-street-map" is filled (should add some more checks)
if(isset($fields_by_name['open-street-map']) && trim($latlon) != ''){
?>
<?php $article_images = json_decode($item->images); ?>
= '<?php echo $item->category_alias;?>';
layergroup_name // Create a layergroup for the article's category (only once by category).
if(layerGroups[layergroup_name] == undefined){
if(use_clustering == true){
= L.markerClusterGroup();
layerGroups[layergroup_name] else{
}= L.layerGroup();
layerGroups[layergroup_name]
}
}// Add the marker into the layergroup
var marker = L.marker([<?php echo $latlon; ?>], {icon: <?php echo $item->category_alias; ?>Icon});
// Use htmlspecialchars($item->title ,ENT_QUOTES) for the Title and not just $item->title otherwise the map would not display if the Title would for example contain a simple quote.
var title = <?php echo "'" . convertHtmlToJsString($item->title) . "'" ?>;
var introduction = <?php echo "'" . convertHtmlToJsString($item->introtext) . "'" ?>;
var introimage='';
<?php if(!empty($article_images->image_intro)) echo "var introimage = '<img src=\"" . $article_images->image_intro ."\"style=\"width: 200px;\">'" ?>;
.bindPopup('<a href="<?php echo $item->link; ?>">' + title + '</a>'+ introduction + introimage);
marker.addLayer(marker);
layerGroups[layergroup_name]<?php
else{
}// If custom field is not filled, add some debug comments
echo "// " . $latlon . "\n";
echo "// " . $item->title . "\n";
}?>
<?php endforeach; ?>
// Add layergroups to the map
for(var layergroup_name in layerGroups){
.addTo(map);
layerGroups[layergroup_name]
}// Add "overlays" (checkboxes for each category) to the map
var baseLayers = {};
var overlays = layerGroups;
L.control.layers(baseLayers, overlays).addTo(map);
;
})</script>
15.13 OSM with different tiles
As you can see from the examples above, to have the “classic” tiles we just use the following
https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png
But we could use other sources of course.
For example if you want “black & white” tiles, just replace the above link by the following
https://tiles.wmflabs.org/bw-mapnik/{z}/{x}/{y}.png
You can find other source for example on https://wiki.openstreetmap.org/wiki/Tile_servers
16 Thank you
For giving me the idea of this presentation and his example
Philippe COMBET http://www.adosis.com
For the trick to easily integrate Custom Fields in Overrides where it is not foreseen
Alexandre ELISÉ https://alexandre-elise.fr
For the Clustering and the Layers
Philippe LAMBOTTE https://www.m4ucode.be
For their Custom Fields and the new versions following our exchanges
Tassos MARINOS https://www.tassos.gr
Adrien ROUSSEL https://www.nordmograph.com
Fabrice PELLETIER https://www.gmapfp.org
For being #jPositive
So many members of the Joomla Community ;)
17 Get in touch
Any suggestion about this presentation ?
Please feel free to contact me. I’ll be happy to keep improving it:)
Marc Dechèvre | woluweb
+32 474 37 13 12 | +32 2 772 58 69