folium icon indicating copy to clipboard operation
folium copied to clipboard

Generated HTML could be much smaller

Open mpickering opened this issue 5 years ago • 10 comments

The HTML generated by folium is quite verbose which leads to big files when you have 2000 markers for example.

  • The javascript is not separate from the HTML which makes it harder to minimise. Running the javascript through the closure compiler reduced it to about 70% of the original size.
  • Variables names are very long which adds a significant length to the generated file.

I'm sure there are more savings but these two seemed like the most obvious.

mpickering avatar Oct 07 '18 14:10 mpickering

You're right that the generated HTML/JS could be more efficient. Right now it is necessary for everything to render into one HTML file, so they can be embedded in Jupyter notebooks.

  • Interesting idea to compile/minimize the JavaScript before embedding. Would this be possible while still embedding it?
  • Names could indeed be shorter. Though they have to be long enough to guarantee unique names, so I don't know how much can be saved here.

Conengmo avatar Oct 12 '18 07:10 Conengmo

Consider this generated code, which adds a circle with a popup to a layer:

var circle_f4cd3b21ebb84d1897bee067cc1de213 = L.circle(
                [49.87654, 12.34567],
                {
  "bubblingMouseEvents": true,
  "color": "#0033ff",
  "dashArray": null,
  "dashOffset": null,
  "fill": true,
  "fillColor": "#0033ff",
  "fillOpacity": 0.2,
  "fillRule": "evenodd",
  "lineCap": "round",
  "lineJoin": "round",
  "opacity": 1.0,
  "radius": 1234.0,
  "stroke": true,
  "weight": 3
}
                )
                .addTo(feature_group_bdeeee916f2b4309b25004b362ad675b);
            
    
            var popup_f103219425994b68951c51e0e03459e9 = L.popup({maxWidth: '300'
            
            });

            
                var html_912b7a603e5b4194bca0d327f238d88e = $('<div id="html_912b7a603e5b4194bca0d327f238d88e"
 style="width: 100.0%; height: 100.0%;">
t=0: 2029-12-15 09:35:28: id 12345678</div>')[0];
                popup_f103219425994b68951c51e0e03459e9.setContent(
html_912b7a603e5b4194bca0d327f238d88e);
            

            circle_f4cd3b21ebb84d1897bee067cc1de213.bindPopup(
popup_f103219425994b68951c51e0e03459e9)
            ;

So i have two questions:

  1. Do we really need to generate the values of all styling parameters of Leaflet's Path class, or we could simply omit them and Leaflet will use default values mentioned here ?

  2. Could you give an example of a use case where we need to access/modify popups and their html? In other words, where do we access html_....... , popup_........... variables?

I think there definetely should be a reason we do all this, instead of simply chaining Leaflet instructions like this, for example (this code displays exactly the same circle and popup):

L.circle([49.87654, 12.34567], {'radius': 1234.0, 'color': '#0033ff'}).bindPopup("t=0: 2029-12-15 09:35:28: id 12345678").addTo(feature_group_bdeeee916f2b4309b25004b362ad675b)

Thanks.

DenisFLASH avatar Jan 03 '19 15:01 DenisFLASH

I've been looking around to find something to contribute, and this seemed like an interesting issue (and something I definitely thought of before when using Folium!)

  • Compressing JS before embedding is possible, and I think this would have to be done in Branca. The template interpolation process is somewhat complicated, so I might have overlooked something, but it seems that HTML and JS are glued outside of Folium.
  • Ostensibly names can be unique without including UUIDs: for example, names in the form marker_0, marker_1, marker_2, etc., where the class keeps an ID, incremented on every object instantiation. Thread safety is an obvious question here.

I can confirm ~30% size reduction from minifying JS alone (I used slimit.minify).

Norrius avatar Jan 03 '19 16:01 Norrius

@Norrius that sounds like a good plan. There are many whitespaces to trim too.

ocefpaf avatar Jan 03 '19 21:01 ocefpaf

@ocefpaf @Norrius what about default values of styling parameters (cf. my question just above), omitting them could reduce the size by 50% or something (rough estimation), right?

DenisFLASH avatar Jan 04 '19 09:01 DenisFLASH

I think @DenisFLASH is right about the default parameters but I wonder if a good minimiser will eliminate the common expressions anyway?

mpickering avatar Jan 04 '19 09:01 mpickering

Generating shorter code in the first place (@DenisFLASH) will of course help a lot, but I don't know enough about Leaflet.js to do it and having compressed JS should be beneficial in any case. I tried out a couple of available minifiers:

  • slimit (MIT license) initially looked very promising, but it panics on the following code from examples, on the $(` combination:
var html_abc = $(`<div id="html_abc" style="width: 100.0%; height: 100.0%;"><i>Mt. Hood Meadows</i></div>`)[0];
popup_def.setContent(html_abc);

It also has a mangler, which (in theory) could have helped with long names as well (it renames things into a, b, c, ... Y, Z, ab, ac, ...), but in practice simply breaks the code.

  • jsmin (MIT license) claims not to support ES6, but works fine on what I tested, and I think backticks are a part of ES6?
  • rJSmin (Apache License 2.0) appears to be based on the same original project as jsmin and produces very similar (often identical) results. It is however much faster.

All of these only apply simple transformations such as removing whitespace, so no clever replacements (as suggested by @mpickering).

I also want to check if django-compressor would work here. Unlike the rest, it is not a drop-in compressor function, so it needs some more investigation.

Norrius avatar Jan 04 '19 19:01 Norrius

Hi @Norrius, thanks for taking on this project! Having smaller htmls is a good goal so I’ll be happy to review any PRs related to this.

As I see it there are three things we need to decide on:

  1. which minifier/ compressor to use
  2. where to hook it in
  3. how to expose the option to users

Other ideas in this thread are interesting as well, thanks for bringing them up @denisFLASH. But as @mpickering said a good JS processor might already solve some of these.

I’ll list the other ideas here, so someone can open another issue or PR for them if still relevant after the JS compression project.

  • using Leaflet defaults instead of explicitly defining them in folium
  • reusing object options for multiple similar objects
  • shorter variable names

Conengmo avatar Jan 07 '19 07:01 Conengmo

While we're on the topic, I was expecting that the save method wouldn't pull the geoJSON files and embed them inside the HTML files if they could just be used as a reference? Or would this break too many things?

Trollgeir avatar Mar 25 '19 01:03 Trollgeir

@mpickering is this issue resolved?

barat-sh avatar May 01 '23 06:05 barat-sh