-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Allow users to add new Control classes without implementing a plugin #2122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,18 @@ | |
import json | ||
import operator | ||
import warnings | ||
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple, Union | ||
from typing import ( | ||
Any, | ||
Callable, | ||
Dict, | ||
Iterable, | ||
List, | ||
Optional, | ||
Sequence, | ||
Tuple, | ||
Union, | ||
get_args, | ||
) | ||
|
||
import numpy as np | ||
import requests | ||
|
@@ -33,6 +44,7 @@ | |
TypeJsonValue, | ||
TypeLine, | ||
TypePathOptions, | ||
TypePosition, | ||
_parse_size, | ||
escape_backticks, | ||
get_bounds, | ||
|
@@ -1813,7 +1825,7 @@ def __init__(self, popup: Union[IFrame, Html, str, None] = None): | |
if isinstance(popup, Element): | ||
popup = popup.render() | ||
if popup: | ||
self.popup = "`" + escape_backticks(popup) + "`" | ||
self.popup = "`" + escape_backticks(popup) + "`" # type: ignore | ||
else: | ||
self.popup = '"Latitude: " + lat + "<br>Longitude: " + lng ' | ||
|
||
|
@@ -1992,3 +2004,61 @@ def __init__( | |
out.setdefault(cm(color), []).append([[lat1, lng1], [lat2, lng2]]) | ||
for key, val in out.items(): | ||
self.add_child(PolyLine(val, color=key, weight=weight, opacity=opacity)) | ||
|
||
|
||
class Control(JSCSSMixin, MacroElement): | ||
""" | ||
Add a Leaflet Control object to the map | ||
|
||
Parameters | ||
---------- | ||
control: str | ||
The javascript class name of the control to be rendered. | ||
position: str | ||
One of "bottomright", "bottomleft", "topright", "topleft" | ||
|
||
Examples | ||
-------- | ||
|
||
>>> import folium | ||
>>> from folium.features import Control, Marker | ||
>>> from folium.plugins import Geocoder | ||
|
||
>>> m = folium.Map( | ||
... location=[46.603354, 1.8883335], attr=None, zoom_control=False, zoom_start=5 | ||
... ) | ||
>>> Control("Zoom", position="topleft").add_to(m) | ||
""" | ||
|
||
_template = Template( | ||
""" | ||
{% macro script(this, kwargs) %} | ||
var {{ this.get_name() }} = new L.Control.{{this._name}}( | ||
{% for arg in this.args %} | ||
{{ arg | tojavascript }}, | ||
{% endfor %} | ||
{{ this.options|tojavascript }} | ||
).addTo({{ this._parent.get_name() }}); | ||
{% endmacro %} | ||
""" | ||
) | ||
|
||
def __init__( | ||
self, | ||
control: Optional[str] = None, | ||
*args, | ||
position: Optional[TypePosition] = None, | ||
**kwargs, | ||
): | ||
super().__init__() | ||
if control: | ||
self._name = control | ||
|
||
if position is not None: | ||
position = position.lower() # type: ignore | ||
if position not in (args := get_args(TypePosition)): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While I was a Fortran programmer in my early days, type checking is not my forte. (Heck, I moved to Python to get away from this ;-p) My crude review tells me that this is equivalent to what I described in my review comment, right? If so, merge away! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wrote my first program for money in RPG :-) which is a fairly obscure language almost as old as Fortran. Whitespace is significant to a fault.
I added one more test. |
||
raise TypeError(f"position must be one of {args}") | ||
kwargs["position"] = position | ||
|
||
self.args = args | ||
self.options = remove_empty(**kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One problem that we have in folium templates are the silent erros when passing keywords to it. For example, if someone types
bottonright
instead ofbottomright
, it will take user a while to figure out what is wrong.Maybe we should check for this 4 valid kw? I know this doesn't solve it for all templates, but in this case it may be small enough that it is worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean as a static typing check or as an assert?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should avoid
assert
use outside of tests.I'm old school but here is what I would do:
position.lower()
to validate names copied from leaflet JS docs;ValueError
if not.What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see we have been inconsistent in banning
assert
:-)