使用 GeoJSON 时间轴将文本框添加到 Python Folium 地图

问题描述 投票:0回答:1

我使用 python Folium 和 GeoJSON 制作了一个动画时间轴地图。

地图使用线条显示一些人从 A 点旅行到 B 点。这些线条随着时间的推移而显示,并通过将线条和点添加为 GeoJSON 中的特征而制作动画时间线。

虽然这些旅行线路和点随着时间的推移出现在地图上,但我还需要显示一些文本(可能是文本框或一些标题或标签),其中的文本随着时间的推移而变化,例如

*"Events of 2017: blablabla", "Events of 2018: blablabla"*
等。 这些文本框、标签或更改的标题 (??) 将随着时间的推移而出现,而其余元素则沿着时间线出现。即使弹出窗口保持打开状态且可见,无需单击制作者,我也可以接受。

我查看了其他 stackoverflow 问题,但它们只解决了 GeoJSON 弹出窗口,我知道如何创建这些弹出窗口,但它们不是我需要的,除非有一种方法可以让弹出窗口永久显示而无需单击它。

尝试:

  1. 我尝试使用 DivIcon
     在地图上添加一个文本框作为 
    点标记,以显示 HTML 文本而不是图标,这效果很好,只是它只是一个不会随时间变化的文本,所以它是静态且不更新文本。
from folium.features import DivIcon
folium.Marker([47.717414, -20.897951], icon=DivIcon(html=('<textarea  style= "font-family:Helvetica, Avenir, Helvetica neue, Sans-serif;font-size:12pt;color:Black; outline:2px black; background-color:transparent;"> Sometext.</textarea>'))).add_to(m)
  1. 我尝试在 DivIcon 图标中添加多点标记及其文本,但这只会造成难以辨认的文本重叠。我可以让标记出现在地图的非重叠位置,但这无论如何都会一次显示所有文本,而我需要它们随着时间的推移逐渐出现。

  2. 我尝试将文本显示为 GeoJSOn 点功能之一的 popup,但我实现的只是将文本放在地图中某个点的弹出窗口或工具提示中,所以我实际上无法除非我每次“怀疑”可能存在新文本时停止时间线并手动单击或将鼠标悬停在该图标上,否则都会看到它。我没有找到一种方法可以让弹出窗口保持打开状态而无需单击它。

  3. 我尝试询问 Chat GPT 和 Google Bard,他们都提出了涉及不存在的代码或不存在的库的解决方案。

有没有办法让某种永久可见的文本随着时间的推移而显示和变化?也许根本不使用 GeoJSON,但是使用一些可以与地图的 HTML 配合使用的 JavaScript 东西?我不是这方面的专家。

这是我用来制作地图的代码中的示例代码:

import folium
from folium import plugins
from folium.plugins import TimestampedGeoJson

#Create map
m = folium.Map(location=[latitude, longitude], zoom_start=4)

# Create points
points=[
{
        "times": ["2017-06-02T00:10:00", "2017-06-02T00:40:00"],
        "popup": "<h1>Some text 1</h1>",
        "coordinates": [10.000654, 53.550341], # Longitude first
    },
    # more points
]

lines = [
    {
        "coordinates": [
            [28.9662187, 41.0091982], # Longitude first
            [10.000654, 53.550341],
        ],
        "times": ["2017-06-02T00:00:00", "2017-06-02T00:10:00"],
    },
    # more lines
]

# Create "features" list to be filled for GeoJSON
features=[]

# Fill features with points
for point in points:
    features.append(
    {
        "type": "Feature",
        "geometry": {
            "type": "Point",
            "coordinates": point["coordinates"],
        },
        "properties": {
            "times": point["times"],
            "popup": point["popup"],
        },
    }
    )

# Fill features with lines
for line in lines:
    features.append(
    {
        "type": "Feature",
        "geometry": {
            "type": "LineString",
            "coordinates": line["coordinates"],
        },
        "properties": {
            "times": line["dates"],
            "popup": "Some Line Text",
        },
    }
)

# Create GeoJSON layer and add to map
timestamped_geojson = plugins.TimestampedGeoJson(
    {
        "type": "FeatureCollection",
        "features": features,
    },
    period="PT1M",
    add_last_point=True,
)
timestamped_geojson.add_to(m)
python textbox geojson folium timeline
1个回答
0
投票

我的英语不太好所以抱歉。

当我在 Pyqt 6 上编写软件时,我遇到了完全相同的问题。我想到的唯一更真实的拐杖是稍微更改 TimestampedGeoJson 中的 _template ,以便将当前日期打印到 javascript 控制台,然后拦截来自控制台的消息。

首先,我创建了一个继承自 TimestampedGeoJson 的 Customtimestamp 类:

class CustomTimestamped(TimestampedGeoJson):
    _template = Template(
        """
        {% macro script(this, kwargs) %}
            L.Control.TimeDimensionCustom = L.Control.TimeDimension.extend({
                _getDisplayDateFormat: function(date){
                    var newdate = new moment(date);
                    console.log(newdate.format("{{this.date_options}}"))
                    return newdate.format("{{this.date_options}}");
                }
            });
            {{this._parent.get_name()}}.timeDimension = L.timeDimension(
                {
                    period: {{ this.period|tojson }},
                }
            );
            var timeDimensionControl = new L.Control.TimeDimensionCustom(
                {{ this.options|tojson }}
            );
            {{this._parent.get_name()}}.addControl(this.timeDimensionControl);

            var geoJsonLayer = L.geoJson({{this.data}}, {
                    pointToLayer: function (feature, latLng) {
                        if (feature.properties.icon == 'marker') {
                            if(feature.properties.iconstyle){
                                return new L.Marker(latLng, {
                                    icon: L.icon(feature.properties.iconstyle)});
                            }
                            //else
                            return new L.Marker(latLng);
                        }
                        if (feature.properties.icon == 'circle') {
                            if (feature.properties.iconstyle) {
                                return new L.circleMarker(latLng, feature.properties.iconstyle)
                                };
                            //else
                            return new L.circleMarker(latLng);
                        }
                        //else

                        return new L.Marker(latLng);
                    },
                    style: function (feature) {
                        return feature.properties.style;
                    },
                    onEachFeature: function(feature, layer) {
                        if (feature.properties.popup) {
                        layer.bindPopup(feature.properties.popup);
                        }
                        if (feature.properties.tooltip) {
                        layer.bindTooltip(feature.properties.tooltip);
                        }
                    }
                })

            var {{this.get_name()}} = L.timeDimension.layer.geoJson(
                geoJsonLayer,
                {
                    updateTimeDimension: true,
                    addlastPoint: {{ this.add_last_point|tojson }},
                    duration: {{ this.duration }},
                }
            ).addTo({{this._parent.get_name()}});
        {% endmacro %}
        """
    )

    def __init__(
        self,
        data,
        transition_time=200,
        loop=True,
        auto_play=True,
        add_last_point=True,
        period="P1D",
        min_speed=0.1,
        max_speed=10,
        loop_button=False,
        date_options="YYYY-MM-DD HH:mm:ss",
        time_slider_drag_update=False,
        duration=None,
        speed_slider=True,
    ):
        super().__init__(data,
                         transition_time=transition_time,
                         loop=loop,
                         auto_play=auto_play,
                         add_last_point=add_last_point,
                         period=period,
                         min_speed=min_speed,
                         max_speed=max_speed,
                         loop_button=loop_button,
                         date_options=date_options,
                         time_slider_drag_update=time_slider_drag_update,
                         duration=duration,
                         speed_slider=speed_slider,
                         )

在这个课程中,在_template中,我改变了

console.log(newdate.format(this.date_options)

console.log(new date.format("{{this.date_options}}"))

现在 TimestampedGeoJson 在动画期间显示的日期格式将被打印到 javascript 控制台

接下来在从

QWebEngineView
继承的基类中,我重新定义了
javaScriptConsoleMessage
类的函数
QWebEnginePage
,如下所示:

page = QWebEnginePage(self)
page.javaScriptConsoleMessage = lambda _1, message, _2, _3: self.change_params(message)
self.setPage(page)

现在,每次出现动画时刻时,都会调用

self.change_params
函数。它接受来自 javascript 控制台的消息作为参数

在此功能中您可以做您需要的事情。

© www.soinside.com 2019 - 2024. All rights reserved.