Jump to content

Module: Leaflet Map


Mats

Recommended Posts

Hi to everybody and special thanks to @Mats for sharing this big porting, to @gebeer @netcarver @dab for the hard implementations and  to @Ivan Gretsky to keep it alive again, but also to all the contributors and maintainers of this awesome module. 

I'm testing it and it works really fine and easy.

I would try to add an extra class to the markers and I followed this linked tip to Leaflet.AwesomeMarker properties for the main page with all the markers:

   $options = array(
        'useMarkerSettings' => true,
        'markerFormatter' => function($page, $marker_options) {
            $marker_options['icon'] = $page->icon; // Override the default icon for this marker.
            $marker_options['markerColor'] = $page->color; // Override the default color for this marker.
			// Adding extra classes to this marker ??
            $marker_options['extraClasses'] = 'music-stage'; // Or dynamically from an option field: $page->stage_kind->value
            return $marker_options;
        }, 
...
);

But as you can see from the screenshot below, no classes are added to the icon (there is a blank space instead between the classes into <i></i> tag; actually it's always present, with or without the 'extraClasses' index into $marker_options).

150269848_Schermata2023-06-29alle07_09_13.thumb.jpg.51c63adf3cb80514fa345c485f911983.jpg

Am I missing something?

P.S. The JS correctly loads  the object properties:

mleafletmap1.addMarkerIcon(L.AwesomeMarkers.icon({prefix: 'fa', icon: 'music', markerColor: 'red', iconColor: 'white', extraClasses: 'music-stage'})

Basically it would be nice to add extra classes to the whole marker (not only to the icon) to manipulate it with js (eg. filtering by type/class on a triggered event such as a button click or select change, or hiding from page if an event date is passed, like for exibitions/music lives and so on).

Does anybody have any suggestions to do this?

Thanks in advance.

Edited by Cybermano
typo fixed
Link to comment
Share on other sites

  • 2 weeks later...

Hi, is there anybody that knows how to easly disable "scrollWheelZoom" on admin page editing?

Or is there a "module/field" settings development plan for map options in admin?
(an overrideble setting in the field options could be great).


I find a little annoying the zooming in/out of the map on page scroll.

At the moment I put manually a js line of code in InputfieldLeafletMapMarker.js after line 24

// line 24
var map = L.map(mapElement).setView([lat, lng], options.zoom);

// new line
map.scrollWheelZoom.disable();

But I'm not so happy to modify original modules... (on updates the modifications could be lose).

The second approach that I have succesfully tested is to load a js file in admin, but it needs to completely initialize a new map, only with the add of an option in the declaration of the map init:

var map = L.map('_Inputfield_fm_mappa_map', {center: [lat, lng], zoom: parseInt($('#_Inputfield_fm_mappa_zoom').val()), scrollWheelZoom: 0,} );
  Reveal hidden contents

Any suggestions?

 

 

Link to comment
Share on other sites

  On 7/11/2023 at 10:47 AM, Cybermano said:

Hi, is there anybody that knows how to easly disable "scrollWheelZoom" on admin page editing?

Or is there a "module/field" settings development plan for map options in admin?
(an overrideble setting in the field options could be great).


I find a little annoying the zooming in/out of the map on page scroll.

At the moment I put manually a js line of code in InputfieldLeafletMapMarker.js after line 24

// line 24
var map = L.map(mapElement).setView([lat, lng], options.zoom);

// new line
map.scrollWheelZoom.disable();

But I'm not so happy to modify original modules... (on updates the modifications could be lose).

The second approach that I have succesfully tested is to load a js file in admin, but it needs to completely initialize a new map, only with the add of an option in the declaration of the map init:

var map = L.map('_Inputfield_fm_mappa_map', {center: [lat, lng], zoom: parseInt($('#_Inputfield_fm_mappa_zoom').val()), scrollWheelZoom: 0,} );
  Reveal hidden contents

Any suggestions?

 

 

Expand  

Let me encourage you to create a PR) I promise to work with it.

  • Thanks 2
Link to comment
Share on other sites

OHHH, SORRY! I've replyed from my co-worker account: I'm Cybermano.

---

Hi @Ivan Gretsky, as promised I made a PR on GitHub: hope it's ok.

Basically I added a new InputfieldCheckbox in the InputfieldLeafletMapMarker.module fields configuration.

Then I setted it in the constructor and also assigned to a variable into the ___render(): if the checkbox is checked, this variable will inoculate an html class ("scrollwheel-disabled") into the div.InputfieldLeafletMapMarkerMap.

At the end, into the InputfieldLeafletMapMarker.js, if the div has this class assigned, map.scrollWheelZoom.disable() will do the work.

 

Sorry, I didn't found a cleaner way ?: hope this could be helpful or of inspiration.?

I attach the two file here, too.

InputfieldLeafletMapMarker.jsFetching info... InputfieldLeafletMapMarker.moduleFetching info...

Edited by Mike-it
Typos fixed
  • Like 3
Link to comment
Share on other sites

  • 2 weeks later...

I had a problem with PHP 8.1.8 an saving pages containing the field. I got this error:

  Quote

Fatal Error: Uncaught TypeError: round(): Argument #1 ($num) must be of type int|float, string given in site/modules/FieldtypeMapMarker/InputfieldLeafletMapMarker.module:193

Expand  

I changed lines 193 and 194 to make sure all values that get rounded are floats:

 if( ((string) round(floatval($lat), $precision)) != ((string) round(floatval($this->defaultLat), $precision)) ||
     ((string) round(floatval($lng), $precision)) != ((string) round(floatval($this->defaultLng), $precision))) {

Maybe this could be included in a future version?

Cheers,
Flo

  • Like 2
Link to comment
Share on other sites

Hi there,

I'm testing a static call with JS to set a new view of the map (I need it on event change without refreshing the page), as found on Github and Stackoverflow:

// frontend
var mymap = L.map($('#mleafletmap1')).setView([45.53, 10.21], 2);

// backend
var mymap = L.map($('#_Inputfield_fm_mappa_map')).setView([45.53, 10.21], 2);

This produces a TypeError: t.className is undefined.

  Reveal hidden contents

I looking for a solution, but didn't found it (...and I'm not a skilled js coder).

Does anybody have an idea?

Link to comment
Share on other sites

I would advice all you that the geocoder API is no longer accessible in the format of the current module.

I have an error geocoding address and the return message from nominatim.openstreetmap is: 
Using the URL /search/ and /reverse/ (with slashes) is no longer supported.
Change url from /search/?q=Berlin in /search?q=Berlin

Also complete addresses are changed, see below:

File not found: API no longer accessible via this URL

Using the URL /search/ and /reverse/ (with slashes) is no longer supported. Please use URLs as given in the documentation.

Examples how to change the URL:

You use: https://nominatim.openstreetmap.org/search/?q=Berlin
Change to: https://nominatim.openstreetmap.org/search?q=Berlin

You use: https://nominatim.openstreetmap.org/search/US/Texas/Huston
Change to: https://nominatim.openstreetmap.org/search?q=Huston, Texas, US

See github issue #3134 for more details.

See github issue #3134 for more details.

 

Maybe editing ControlGeocoder.js fixes partially the problem?

:343 L.Control.Geocoder.jsonp(this.options.serviceUrl + 'search/', L.extend({
change in
L.Control.Geocoder.jsonp(this.options.serviceUrl + 'search', L.extend({

:370 L.Control.Geocoder.jsonp(this.options.serviceUrl + 'reverse/', L.extend({
change in
L.Control.Geocoder.jsonp(this.options.serviceUrl + 'reverse', L.extend({

 

I have tested with simple addresses (italian street, number and city) ad it works fine again, both search and reverse.

Posted a commit on Github:
https://github.com/FriendsOfProcessWire/FieldtypeLeafletMapMarker/commit/63254dffea9bd504eb45b124d64fd5832e38f013

  • Like 4
  • Thanks 4
Link to comment
Share on other sites

  • 1 year later...

Hi,

does anybody know how to use the module with a repeater? I need to have multiple mappoints in one map at my frontend, and due to the nature of the site I cannot put them in pages. So I thought of using a repeater for those points (as it is not possible to add multiple markers to one map). In the backend it works fine, but I cannot figure out how to collect those points and display them in one map.

 

<?php
$map = $modules->get('MarkupLeafletMap');
echo $map->getLeafletMapHeaderLines();
$mappoints = [];
foreach ($page->map_repeater as $point) {
        array_push($mappoints, $point->map); //or 
		array_push($mappoints, [$point->map]); //or
		array_push($mappoints, $point->map->LeafletMapMarker);
		array_push($mappoints, [$point->map->lat, $point->map->lng]);
	}
echo $map->render($page, $mappoints, array('height' => '270px'));
?>

All I tried gave me different mistakes. Does anybody know the correct syntax?

Link to comment
Share on other sites

Hi @torf

Never used Leaflet Map with repeaters, but in past I had to collect lot of points into a single map.
 

I simply used a PageArray with wire('pages')-find('template=MY_TEMPLATE, and so on...') and then I passed the PageArray to the $map->render($MY_PAGE_ARRAY, 'MY_MAP_FIELD', $options); it works.

So I suggest to collect your points in a PageArray with proper selectors. Don't forget that your repeater has a template, and if useful you can get the parents of them.

 

Let me know if this solution can fit your needs.

Edited by Cybermano
Link to comment
Share on other sites

  On 11/29/2024 at 5:22 PM, torf said:

Thanks @Cybermano I already did it with page arrays and it works great, but this time I'd prefer a repeater. Using page arrays would make the backend to complicated.

Expand  

Hi @torf, maybe I didn't get it at all or maybe I didn't explain well my idea.
So I'll try again... 😉

Surely I could be wrong, but for me a PageArray is not only for admin side, but all the pw pages collected into a php variable created with pw selectors in a script file.

For shortness I have created 2 pages with two repeaters each (repeater name is "multipoints"), with a title and a leaflet map field (named 'fm_mappa') into.
This is the simply code for testing. Please note that I didn't get the repeater with a foreach statement, but with a pw search with a selector for all the templates with name equals to "repeater_NAMEOFTHEREPEATER", as pw builds. Obviously into your selector you can build your search to the desired points, e.g with sorting or something other.

echo $pagesWithMultipointsRepeater = wire('pages')->find('template=repeater_multipoints');
        if (count($pagesWithMultipointsRepeater)){
            $map = $modules->get('MarkupLeafletMap');       
            $options = array(
                'height' => 800,
                'markerColour' => 'red',
            );
            echo '<div class="g-mb-25">';
            echo $map->render($pagesWithMultipointsRepeater,'fm_mappa',$options);
            echo '</div>';
        }

The numbers before the map are the repeater ids (note the "echo" on the first line of the code).

1796299206_Thefrontendmap.thumb.png.93c6289220481a0d9d893e5fc000cac1.png

And these are the admin pages:

298966150_Page1.thumb.png.84c204957b92a3efd15b1b0abe261288.png 918678329_Page2.thumb.png.3552138362ac88df8af0689c9ed318d9.png


I hope that's clearer.

  • Thanks 1
Link to comment
Share on other sites

Or maybe this edited code of your first post works?


$map = $modules->get('MarkupLeafletMap');
echo $map->getLeafletMapHeaderLines();
$mappoints = new PageArray();
foreach ($page->map_repeater as $point) {
	$mappoints->add($point) // the repater is a page
	}
echo $map->render($mappoints, 'your_map_field_name', array('height' => '270px'));
?>

 

Link to comment
Share on other sites

For the records: I found a way to use multiple points or areas directly from a repeater, but it's not the most beautiful method. It works by simply omitting the frontend part of the module.

I just load the leaflet js and css manually, extract the lat and lng from the repeater, and paste them in the site. Which has the advantage that I can use additional fields in my repeater (in my example I use a field named "radius" to draw multiple circles:

<div id="map" style="height: 200px"></div>
<script>
    var map = L.map('map').setView([51.505, -0.09], 13);
    L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
    let boundaries = [];
    <?php
    foreach($page->my_repeater as $repeaterdata) {
        $diameter = $repeaterdata->radius * 1000;
        echo "
        var circle = L.circle([{$repeaterdata->map->lat}, {$repeaterdata->map->lng}], {
        color: 'red',
        fillColor: '#f03',
        fillOpacity: 0.5,
        radius: {$diameter}
    }).addTo(map);
        ";
    }

    ?>
</script>

Please note that this is just a proof of concept. I haven't played around with markerclusters, automatic boundaries or any other fancy stuff at this point.

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...