【问题标题】:How to copy a marker's location on folium Map by clicking on it?如何通过单击复制标记在folium Map 上的位置?
【发布时间】:2021-08-10 04:01:40
【问题描述】:

我可以使用 folium.plugins.MousePosition 在地图上打印给定标记的位置。
class GeoMap:
        
    def update(self, location_center:np.array, locations: np.array):
        self.map = folium.Map(location_center, zoom_start=10)
        for i in range(locations.shape[0]):
            location = tuple(locations[i, j] for j in range(locations.shape[1]))
            folium.Marker(
                location=location,
            ).add_to(self.map)
        formatter = "function(num) {return L.Util.formatNum(num, 3) + ' º ';};"
        plugins.MousePosition(
            position="topright",
            separator=" | ",
            empty_string="NaN",
            lng_first=True,
            num_digits=20,
            prefix="Coordinates:",
            lat_formatter=formatter,
            lng_formatter=formatter,
        ).add_to(self.map)
        
    def display(self):
        display(self.map)

但是,我希望用户能够通过单击它来复制叶地图上标记的位置。我想可能有一种方法可以使用 on_click 事件(在 Python 中)获取标记的位置。但是,我在网上没有找到任何示例。

我正在使用 Python,但如果您有一个使用 Python 和一些 Javascript 运行良好的解决方案,那也可以。

任何帮助将不胜感激!

谢谢,

【问题讨论】:

    标签: javascript python folium


    【解决方案1】:

    用按钮复制

    由于您使用的是标记,因此您可以为每个标记添加popup。单击标记图标时将打开弹出窗口。 popup 可以将 html 字符串作为输入,因此您可以使用它来插入复制按钮或类似的东西。

    随后,您需要将javascript 复制功能添加到folium html 输出。这可以通过MacroElement 完成。

    实现this 将产生以下基本示例:

    import folium
    import jinja2
    
    location_center = [45.5236, -122.6750]
    locations = [[45.5012, -122.6655],[45.5132, -122.6708],[45.5275, -122.6692],[45.5318, -122.6745]]
    
    m = folium.Map(location_center, zoom_start=13)
    for location in locations:
        folium.Marker(
            location=location,
            popup = f'<input type="text" value="{location[0]}, {location[1]}" id="myInput"><button onclick="myFunction()">Copy location</button>'
        ).add_to(m)
        
    el = folium.MacroElement().add_to(m)
    el._template = jinja2.Template("""
        {% macro script(this, kwargs) %}
        function myFunction() {
          /* Get the text field */
          var copyText = document.getElementById("myInput");
    
          /* Select the text field */
          copyText.select();
          copyText.setSelectionRange(0, 99999); /* For mobile devices */
    
          /* Copy the text inside the text field */
          document.execCommand("copy");
        }
        {% endmacro %}
    """)
    
    display(m)
    

    点击事件复制

    如果您希望在单击标记时直接复制经纬度:这也是可以的,但需要 monkey patchingMarkerjinja template 添加点击事件。需要进行猴子修补,因为标记的模板在 folium 中硬编码。

    另外,点击触发的函数可以用MacroElement定义:

    import folium
    import jinja2
    from jinja2 import Template
    from folium.map import Marker
    
    tmpldata = """<!-- monkey patched Marker template -->
    {% macro script(this, kwargs) %}
        var {{ this.get_name() }} = L.marker(
            {{ this.location|tojson }},
            {{ this.options|tojson }}
        ).addTo({{ this._parent.get_name() }}).on('click', onClick);
    {% endmacro %}
    """
    
    Marker._mytemplate = Template(tmpldata)
    def myMarkerInit(self, *args, **kwargs):
        self.__init_orig__(*args, **kwargs)
        self._template = self._mytemplate
    Marker.__init_orig__ = Marker.__init__
    Marker.__init__ = myMarkerInit
    
    location_center = [45.5236, -122.6750]
    locations = [[45.5012, -122.6655],[45.5132, -122.6708],[45.5275, -122.6692],[45.5318, -122.6745]]
    
    m = folium.Map(location_center, zoom_start=13)
    
    for location in locations: #range(locations.shape[0]):
        folium.Marker(
            location=location,
            popup = f'<p id="latlon">{location[0]}, {location[1]}</p>'
        ).add_to(m)
    
    el = folium.MacroElement().add_to(m)
    el._template = jinja2.Template("""
        {% macro script(this, kwargs) %}
        function copy(text) {
            var input = document.createElement('textarea');
            input.innerHTML = text;
            document.body.appendChild(input);
            input.select();
            var result = document.execCommand('copy');
            document.body.removeChild(input);
            return result;
        };
        
        function getInnerText( sel ) {
            var txt = '';
            $( sel ).contents().each(function() {
                var children = $(this).children();
                txt += ' ' + this.nodeType === 3 ? this.nodeValue : children.length ? getInnerText( this ) : $(this).text();
            });
            return txt;
        };
        
        function onClick(e) {
           var popup = e.target.getPopup();
           var content = popup.getContent();
           text = getInnerText(content);
           copy(text);
        };
        {% endmacro %}
    """)
    
    display(m)
    

    从可拖动标记复制

    如果通过在 Marker 对象中设置 draggable=True 来使用可拖动标记,则从弹出窗口中复制硬编码坐标是没有意义的。在这种情况下,您最好从 Marker 对象中检索最新坐标并相应地更新弹出窗口:

    import folium
    import jinja2
    from jinja2 import Template
    from folium.map import Marker
    
    tmpldata = """<!-- monkey patched Marker template -->
    {% macro script(this, kwargs) %}
        var {{ this.get_name() }} = L.marker(
            {{ this.location|tojson }},
            {{ this.options|tojson }}
        ).addTo({{ this._parent.get_name() }}).on('click', onClick);
    {% endmacro %}
    """
    
    Marker._mytemplate = Template(tmpldata)
    def myMarkerInit(self, *args, **kwargs):
        self.__init_orig__(*args, **kwargs)
        self._template = self._mytemplate
    Marker.__init_orig__ = Marker.__init__
    Marker.__init__ = myMarkerInit
    
    location_center = [45.5236, -122.6750]
    locations = [[45.5012, -122.6655],[45.5132, -122.6708],[45.5275, -122.6692],[45.5318, -122.6745]]
    
    m = folium.Map(location_center, zoom_start=13)
    
    for location in locations: #range(locations.shape[0]):
        folium.Marker(
            location=location,
            popup = f'<p id="latlon">{location[0]}, {location[1]}</p>',
            draggable=True
        ).add_to(m)
    
    el = folium.MacroElement().add_to(m)
    el._template = jinja2.Template("""
        {% macro script(this, kwargs) %}
        function copy(text) {
            var input = document.createElement('textarea');
            input.innerHTML = text;
            document.body.appendChild(input);
            input.select();
            var result = document.execCommand('copy');
            document.body.removeChild(input);
            return result;
        };
        
        function onClick(e) {
           var lat = e.latlng.lat; 
           var lng = e.latlng.lng;
           var newContent = '<p id="latlon">' + lat + ', ' + lng + '</p>';
           e.target.setPopupContent(newContent);
           copy(lat + ', ' + lng);
        };
        {% endmacro %}
    """)
    
    display(m)
    

    【讨论】:

    • 当你有draggable=True时你知道如何更新坐标吗?然后拖动它,我的坐标相同。如果我将我的从 A 拖到 B。A 仍然是我可以复制的坐标,但我想复制 B。
    • @AnonymousUser 当然,我已经用一个例子更新了答案
    • 是否可以在 python 中使用 Javascript 中的那些变量。例如,我想用var latvar lng 的值创建一个python 变量。所以我想要pythonLat = lat,但是lat在JS里面所以我不能在python中使用它。
    • @AnonymousUser 当然,但这超出了这个问题的范围。您可以将post request 添加到onClick 中的flask api 端点,或者使用其他解决方案,如dash_leaflet,如本线程中的其他答案所示。
    • 我收到您的代码时出现此错误RecursionError at / maximum recursion depth exceeded in comparison 我在重新加载页面时收到此错误,并添加了sys.setrecursionlimit(50000),但它没有收到错误,而是停止运行服务器。
    【解决方案2】:

    如果你不拘泥于使用 folium,使用 dash-leaflet 可以轻松实现这种交互性,

    import json
    import dash
    import dash_leaflet as dl
    import dash_html_components as html
    from dash.dependencies import Input, Output, ALL
    
    # Setup markers.
    locations = [[45.5012, -122.6655], [45.5132, -122.6708], [45.5275, -122.6692], [45.5318, -122.6745]]
    markers = [dl.Marker(position=l, id=dict(id=i)) for i, l in enumerate(locations)]
    # Create small example dash app showing a map and a div (for logging).
    app = dash.Dash(prevent_initial_callbacks=True)
    app.layout = html.Div([
        dl.Map([dl.TileLayer()] + markers, center=[45.5236, -122.6750], zoom=14,
               style={'width': '1000px', 'height': '500px'}),
        html.Div(id="log")
    ])
    
    # Callback for interactivity.
    @app.callback(Output("log", "children"), Input(dict(id=ALL), "n_clicks"))
    def log_position(_):
        idx = json.loads(dash.callback_context.triggered[0]['prop_id'].split(".")[0])["id"]
        location = locations[idx]
        print(location)  # print location to console
        return location  # print location to ui
    
    if __name__ == '__main__':
        app.run_server()
    

    我不确定您所说的“复制”是什么意思,因此我在示例中演示了如何在 Python 层(打印到控制台)和 ui 层(写入 div)中访问该位置.

    【讨论】:

      猜你喜欢
      • 2021-07-08
      • 2022-12-17
      • 1970-01-01
      • 1970-01-01
      • 2013-04-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-19
      • 1970-01-01
      相关资源
      最近更新 更多