python-visualization / folium

Python Data. Leaflet.js Maps.

Home Page:https://python-visualization.github.io/folium/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add example pattern for pulling JsCode snippets from external javascript files

thomasegriffith opened this issue · comments

Is your feature request related to a problem? Please describe.
I have found myself embedding a lot of javascript code via JsCode when using the Realtime plugin. This gets messy for a few reasons:

  • unit testing the javascript code is not possible
  • adding leaflet plugins can result in a huge amount of javascript code embedded inside python
  • and the code is just not as modular as it could be

I'm wondering if there's a recommended way to solve this.

Describe the solution you'd like
A solution would allow one to reference specific functions from external javascript files within a JsCode object.

It's not clear to me what would be better: whether to do this from within folium (using the API) or to use some external library to populate the string of javascript code that's passed to JsCode based on some javascript file and function. In either case, having an example in the examples folder would be great.

Describe alternatives you've considered
If nothing is changed about the folium API, this could just be done external to folium. As in, another library interprets a javascript file, and given a function name, the function's definition is returned as a string. Is this preferable to building the functionality into folium? If so, does anybody know of an existing library that can already do this?

Additional context
n/a

Implementation
n/a

Very interesting idea. I have noticed the same drawbacks of having javascript as strings inside python. (And I would add there is no editor support or linting of javascript from inside python).

I don't know of a library that does exactly this, but there are javascript parsers available in python. E.g. https://github.com/PiotrDabkowski/pyjsparser. And there is pythonmonkey. This serves a totally different purpose, but also does javascript parsing.

I think with either we could hack up a PoC that does what you want. Do you have an example of how you would want to use this library to be?

Agreed: lack of editor and linting support are also painful.

Do you have an example of how you would want to use this library to be?

Maybe something like:

pointToLayer.js

function pointToLayer(f, latlng) {
    ...
}

plugin.js

function somePlugin() {
    ...
}

folium.py

point_to_layer_function = ExternalJsFunction(file="/path/to/foo.js", function="pointToLayer")
some_plugin_function = ExternalJsFunction(file="/path/to/plugin.js", function="somePlugin")

# A list of functions is passed to `point_to_layer`
realtime_plugin = folium.plugins.Realtime(..., point_to_layer=[point_to_layer_function, some_plugin_function])

Would a preprocessor be acceptable? Something that takes as input a javascript module and generates the required python code?

Maybe. What would the usage look like?

I created a small PoC in javascript that creates JsCode definitions for each function inside a javascript module.

It creates a file example.py like this:

from folium.utilities import JsCode
hello = JsCode("""() => { console.log('hello, world') }""")
toString = JsCode("""(fn) => { return fn.toString() }""")

From python you would be able to use this as follows:

import example

Realtime(
    url,
    on_each_feature=example.hello,
).add_to(m)

That's pretty cool! Seems useful to me.

Can you post the javascript module too? It's defining non-lambda functions hello and toString that later get mapped to lambdas in the generated python?

This is the code I used for the PoC. It needs to be tweaked to accept arguments.

module = require("./bye.js")
console.log("from folium.utilities import JsCode")
Object.entries(module).forEach(([k,v]) => {
   console.log(`${k} = JsCode("""${v.toString()}""")`);
});

This is the code for bye.js. I based it on a PythonMonkey example. I think the crucial part is that the function names need to be exported from javascript somehow. The PythonMonkey example used direct assignments to exports, which does not feel natural to me.

exports.hello = () => { console.log('hello, world') };
exports.toString = (fn) => { return fn.toString() };

@thomasegriffith Is this sufficient for you? If you are okay with it, I'd like to close the issue.

I don't think the example I wrote is sufficiently mature to be included in the Folium core library (even as a documentation.) I checked with the PythonMonkey development team. They have plans to make javascript source code directly accessible. We can revisit this issue when that happens.

Sounds good @hansthen. Thanks for the discussion and example!!