Hi,

Views is generating my leaflet map.
I can set all map options by creating mymap_leaflet_map_info() , but how can i subscribe to events and use Methods for Modifying Map State without modifying leaflet.drupal.js or leaflet_markercluster.drupal.js ?
I dont know how to access lMap object from my custom .js.
I want to panTo marker when hovering over coresponding node.

Thank you!

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

pvhee’s picture

Status: Active » Closed (works as designed)

Right now, the only way to do such things is to overwrite the javascript. Have a look at the Leaflet Markercluster module for an example. It's an ugly way, but we'd need to refactor the JS into different behaviors that can be overwritten separately.

nbchip’s picture

Ok, did that. Thank you.

Maybe u can link some example of how this refactored code would have to look like.
Leaflet.js is pretty small and simple so maybe i can try to refactor it :)

pvhee’s picture

Title: Access and manipulate leaflet map from js » Refactor leaflet.drupal.js to allow other modules to extend and hook into map creation

Great blog post from Lullabot on how Javascript could be made more extendable: www.lullabot.com/articles/your-javascript-should-expose-apis-too

pvhee’s picture

Status: Closed (works as designed) » Needs work
pvhee’s picture

Status: Needs work » Needs review

Commited http://drupalcode.org/project/leaflet.git/commit/782027b adding support for jQuery's trigger and bind methods. Initially, we trigger the "leaflet.map" event at the end of the map creation, allowing other modules to do more stuff with the map objects.

An example:

$(document).bind('leaflet.map', function(e, map, lMap) {
  // do something with your map, e.g. adding a support for LocateControl, https://github.com/domoritz/leaflet-locatecontrol
  L.control.locate().addTo(map);
}

Looking forward to explore possibilities with this patterns, allowing other modules to change settings now hardcoded in JS, and making it easier to integrate 3rd party plugins with the Leaflet module.

pvhee’s picture

Issue summary: View changes

little edit

gcb’s picture

Category: Support request » Feature request
Issue summary: View changes
Status: Needs review » Active
wernerglinka’s picture

I am trying the same thing, to access the map instance so I can inject a GeoJSON file into the leaflet map. I am using just leaflet and leaflet views to create a leaflet based view.

I do not see that the 'leafmap.map' event is available at all. There must be something I do wrong here, maybe I don't see the forest for the trees... Any pointers are welcome...

chriscalip’s picture

I've been immersing myself with the issues and potential approaches for this refactoring of leaflet.drupal.js
Might I ask for a roadmap?

chriscalip’s picture

Well here's my use case. I am currently implementing leaflet ctools content pane that is capable of the standard d.o. leaflet settings "map source, map settings" but I also want to include are

interaction widgets settings e.g.
vanilla-leaflet-theme-marker, custom-theme-marker, custom-popup-marker, leaflet-geosearch.control.widget, multiple-categories-overlay-control.widget, update-marker-driving-directions-based-on-user-location, etc..

datasources widgets settings e.g.
custom-jsonp-marker-sources..

chriscalip’s picture

Might I suggest the use of the wins by the openlayers module while making use of the lessons learned from www.lullabot.com/articles/your-javascript-should-expose-apis-too
https://www.drupal.org/node/1446166
and d.o leaflet module's leanings towards js events?

So openlayers module leverages ctools to create different types of plugins namely: behaviors and layer_types. Of particular relevance is openlayer's ctools plugin type behaviors which is kind of similar to what this module is going for : jsevents.

This way plugin types : leaflet jsevents extends map functionality like adding leaflet markercluster , leaflet_geosearch while helping with the form generation..

das-peter’s picture

Same problems here. It's quite a messy task to integrate other modules that extend leaflet.drupal.js.

Might I ask for a roadmap?

Let's do it :D I've spent to many hours fiddling with the current JS, working on a refactoring would be better.

robertwb’s picture

+1 for a road-map @das-peter - should we do it here in the issue queue or as a child to the main Leaflet module page?

robertwb’s picture

Added a thread in issue queue for roadmap brainstorming.

Anonymous’s picture

The solution from #5 worked for me but I had to modify the weight of my JavaScript file to be -5, which makes sense once you know.
Here is what it looks like:

  drupal_add_js(drupal_get_path('module', 'MODULENAME') .'/JSFILE.js',array('group' => JS_DEFAULT, 'weight' => -5));
robertwb’s picture

Thanks for this nugget of info @pferlito - my knowledge of jQuery & OO js (and Drupal integration of it) is very low. I was wondering last night how one made sure that modules like Leaflet GEOJSON insured that the map was already created before doing stuff to it - I see now "'weight' => -5" does the trick in this case. Of course I could have just gone to the docs for drupal_add_js :).

I added a piece on this in the Coding section of the roadmap thread.

das-peter’s picture

Status: Active » Needs review
FileSize
11.93 KB

I just took a shot at this and implemented new triggers.
The triggers should allow us to adjust pretty much all the things.
To ensure people know what triggers we provide there's now the file leaflet.drupal.api.js that provides a JS api doc - similar to what we use for Drupal hooks.

Pol’s picture

Very nice. I should do the same for OL 3.1.x

chriscalip’s picture

Would like to float the idea of controlling the list of binders like what d8 is planning to do:

Control the list of behaviors run by Drupal.attachBehaviors
https://www.drupal.org/node/2367655

das-peter’s picture

das-peter’s picture

Pol’s picture

Hi all,

I've successfully implemented the JS hooks in OpenLayers 3.1.x, thanks das-pater !

You can see it live here: http://dev.through-my-eyes.be/ (Hit F12 to see the console output)
I know it's not leaflet, but it's just to show you a working example.

Console output are generated using JS hooks, have a look at the source file.

BTW das-peter, there's a small typo on line 55:

+++ b/leaflet.drupal.api.js
@@ -0,0 +1,191 @@
+$(document).bind('leaflet.layers_perpare', function(event, data) {
alcroito’s picture

Attaching a re-roll of #16 against latest DEV (thanks das-peter!) with a couple of things modified:
1) Fixed typo from $(document).bind('leaflet.layers_perpare', function(event, data) { to $(document).bind('leaflet.layers_prepare', function(event, data) {both in code and in API documentation.
2) Extracted the leaflet_create_feature() internal method to be publicly accessible, so that developers can create new features, that will invoke the appropriates hooks / JS events.

Personally I've tested only a couple of hooks that I required and they all worked.
I think this is a pretty important patch to get in.

robertwb’s picture

Thanks @Placinta - not sure how much time you have but if you have a moment, it would be awesome to see a test case laid out, module combos or sample code if you did it programmatically? If not I will stumble through a testing of my own. Your #2 looks interesting, as I have been thinking recently that the js hooks are only one part of the puzzle, allowing some things to be updated inside Drupal php would be desirable.

alcroito’s picture

My sample code is not very complicated.
Using the hooks provided, I generate unique colors and set the style of polygon layers that are outputted by leaflet views, as well as create markers inside the polygons.

$(document).on('leaflet.features_prepare', function(event, params) {
    // Define a new color generator, and set the hue initial seed value
    // rather than it being random.
    var color = new Color;
    color.hue = 0; // Red.
    Drupal.settings.leafletColorGenerator = color;
  });
// Change features before they are created.
  $(document).on('leaflet.feature_create', function(event, params) {
    params.feature.options = params.feature.options || {};

    // Get already generated color or generate new color.
    var uniqueColor = params.feature.options.color || getUniqueColor();

    // Set color properties for the polygon.
    params.feature.options.color = uniqueColor;
    params.feature.options.weight = 2;
    params.feature.options.opacity = 0.6;
    params.feature.options.fillColor = uniqueColor;
    params.feature.options.fillOpacity = 0.3;

    // Save featureType as an option, so we can filter by it.
    // We cast to int, because the $.inArray check is strict in the type.
    params.feature.options.featureType = parseInt(params.feature.featureType);

    // Create a marker inside the polygon.
    createMarkerForPolygon(params);
  });
/**
   * Create marker inside polygon feature.
   */
  function createMarkerForBoundary(params) {
    // Some features might not have the geo location set, because
    // it is added to Drupal.settings via views and PHP code.
    if (typeof params.feature.markerGeoLocation !== 'undefined') {
      var markerGeoLocation = params.feature.markerGeoLocation;
      var markerObject = {
        lat: markerGeoLocation.lat,
        lon: markerGeoLocation.lng,
        options: {}
      };
      var point = Drupal.leaflet.create_point(markerObject, params.lMap);

      // Use the same popup as for the polygon.
      point.bindPopup(params.feature.popup);

      params.lMap.addLayer(point);
    }
  }
robertwb’s picture

Thanks @Placinta - so, essentially this code lives outside of any module, and does not have any PHP component? I just ask to understand how to duplicate.

This is also a very cool test case in general. Thanks for the info!

alcroito’s picture

The code lives in a JS file inside a custom module, which is added whenever a certain leaflet view is executed.

/**
 *  Implements hook_views_pre_view().
 */
function custom_module_views_pre_view(&$view, &$display_id, &$args) {
  if ($view->name == 'custom_leaflet_map' && $display_id == 'custom_leaflet_map') {
    // Load underscore.
    libraries_load('underscore');

    // Load leaflet search.
    libraries_load('leaflet.search');

    // Load JS file when the view is about to be rendered.
    drupal_add_js(drupal_get_path('module', 'custom_module') . '/js/custom_leaflet_map.js');
  }
}

And there is some PHP code involved to pass additional fields added into the view, to the JS settings object that is sent by the leaflet module, to the JS implementation.

/**
 *  Implements hook_leaflet_views_alter_points_data_alter().
 */
function cusotm_module_leaflet_views_alter_points_data_alter($result, &$points) {
  // Add geo location field to create a marker using the geo coordinates.
  if (
    isset($result->field_field_coordinates[0]['raw']['latitude'])
    && !empty($result->field_field_coordinates[0]['raw']['latitude'])
    && isset($result->field_field_coordinates[0]['raw']['longitude'])
    && !empty($result->field_field_coordinates[0]['raw']['longitude'])
    && isset($points[0])
  ) {
    $markerGeoLocation = array(
      'lat' => $result->field_field_coordinates[0]['raw']['latitude'],
      'lng' => $result->field_field_coordinates[0]['raw']['longitude'],
    );
    $points[0]['markerGeoLocation'] = $markerGeoLocation;
  }
}
robertwb’s picture

@Placinta Awesome - thanks!

eigentor’s picture

What is the status here?
Are the examples @Placinta in #24 and #26 on the patch from #22 that did not get committed?

Or is this possible with the method given in #5?

If I get it right, there was a lot of action a year ago https://www.drupal.org/node/2350121#comment-9469567 in the development roadmap, but the stuff never really made it into the leaflet module, right?

robertwb’s picture

@eigentor - in terms of how to get this working, I think that hte patches thus far provided look super-promising, and the API documentation portion of them should make things more accessible. The use case put up here in #24 & #26 needs to be tested, and preferably, in the form of a fully functioning module file with a hook to create a map that demonstrates its use.

As for the roadmap process - you are pretty much right, the roadmap process produced a lot of good ideas, but the initial planning has not resulted in any decision making. But this set of patches should absolutely go in -- I think that @pvhee has moved on to OL? But if we can get some testing on this @rdboer would likely commit.

robertwb’s picture

@Placinta - I know it's been a while but... I noticed your implementation uses "on" instead of "bind". I expected vbased on the API docs to see "bind" ( "$(document).on('leaflet.features_prepare'" instead of "$(document).bind('leaflet.features_prepare'" ).

Are these interchangeable or am I misunderstanding the hook usage? Thanks!

robertwb’s picture

Ahh - I see that the "in()" method is a multiple approach to "bind", so (in case anyone else is wondering):

Leaflet advertises a "hook" with "trigger()" in "Drupal.behaviors.leaflet":

        // allow other modules to get access to the map object using jQuery's trigger method
        $(document).trigger('leaflet.map', [this.map, lMap]);

and sub-modules implement it with "bind()", or if they intend to handle multiple triggers with a single method they use "in()". For example, the Leaflet GeoJSON module implements (binds) the trigger "leaflet_map", calling the function Drupal.leafletBBox.onMapLoad() (though LeafletGeoJSON module does NOT yet implement a hook to load it's javascript for other modules to use -- patch forthcoming):

  $(document).bind('leaflet.map', function(e, map, lMap) {
    Drupal.leafletBBox.onMapLoad(lMap);
  });

References:

robertwb’s picture

Patch #22 from @placinta no longer applies to the latest dev version. I believe the sticking point is that this implementation moves "leaflet_create_feature()" from "Drupal.behaviors.leaflet" to "Drupal.leaflet".

Does anyone know if this is an important move because it enables outside access to the leaflet_create_feature method? I am assuming so, and thus am proposing we split this patch into 2:

  1. Move leaflet_create_feature() from "Drupal.behaviors.leaflet" to "Drupal.leaflet"
  2. Create js triggers for leaflet map, layer and feature events

I think that #1 would be important because it could be committed more rapidly without waiting for full community review of the structure and function of the 2nd part. Then, modifications to the 2nd part would be more insulated against future patch fails as other commits come into the model.

Thoughts? Adding a related issue and patch for #1, and if someone can disabuse me of the notion that moving leaflet_create_feature() to Drupal.leaflet is not essential for plugin support, I will close it.

robertwb’s picture

Status: Needs review » Needs work

From what I can tell, moving leaflet_create_feature() to Drupal.leaflet is making this patch really hard to test and maintain because it breaks compatibility with other patches and/or is gets broken itself as commits are made. I am going to re-roll this patch with the following modification:

  • Keep the function Drupal.behaviors.leaflet.leaflet_create_feature() and mark as deprecated
  • Remove additional code changes such as if() statement around "map_layer = Drupal.leaflet.create_layer(layer, key)"
  • Add support for ctools plugins of type "leaflet_layers" to load necessary js when indicated in the map array

This should preserve the ability to test and reduce conflicts with other patches by appending a code block onto the base, but not chopping a huge chunk out of it.