You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python Folium地图行政区弹窗中集成Demand与Investment输入功能?

问题描述

我有两段Python代码:一段用GeoPandas和Folium生成带行政区的莫桑比克地图,点击行政区会弹出信息窗口;另一段是基于PuLP的线性规划计算器,需要用户输入Demand(需求)和Investment(投资),结合对应行政区的参数完成计算。我想实现点击地图上的行政区时,在弹窗里让用户输入这两个值,触发计算逻辑。尝试用Flask和JavaScript实现,但JS代码没正常运行,求解决办法。

代码1:GeoPandas + Folium生成莫桑比克地图

def create_map():
    folder_path = r'D:\Trabalho de Licenciatura\New folder\Python\Moz Geo Data\Distritos'
    shapefile_path = folder_path + "\\Distritos.shp"
    districts = gpd.read_file(shapefile_path)
    
    districts_json = districts.to_crs(epsg='4326').to_json()
    
    districts_data = json.loads(districts_json)

    map_mozambique = folium.Map(location=[-18.665695, 35.529562], zoom_start=6)

    def generate_popup_content(properties):
        html = f"<h3>{properties['ADM2_PT']}</h3>"
        html += f"<p>Additional information: {properties['ADM1_PT']}</p>"
        return folium.Popup(html)

    for feature in districts_data['features']:
        properties = feature['properties']
        geometry = feature['geometry']
        popup_content = generate_popup_content(properties)
        folium.GeoJson(
            data=geometry,
            style_function=lambda x: {
                'fillColor': '#ffff00',
                'color': '#000000',
                'weight': 1,
                'fillOpacity': 0.4
            },
            highlight_function=lambda x: {'weight': 3},
            tooltip=properties['ADM2_PT'],
            popup=popup_content
        ).add_to(map_mozambique)

    map_mozambique.save('mapa.html')
    webbrowser.open('mapa.html')

create_map()

代码2:PuLP线性规划计算器

def lp(district_name):
    import pulp
    
    model = pulp.LpProblem(name="Lp", sense=pulp.const.LpMinimize)
    A=float(consts(district_name).get("A"))
    B=float(consts(district_name).get("B"))
    C=float(consts(district_name).get("C"))
    x_1 = pulp.LpVariable(name="x_1", lowBound=0)
    x_2 = pulp.LpVariable(name="x_2", lowBound=0)
    x_3 = pulp.LpVariable(name="x_3", lowBound=0)
    
    obj_func = 41*x_1 + 11*x_2 + 24*x_3
    
    model += obj_func
    
    
    model += (x_1 + x_2 + x_3 >= float(input("Demand?")))
    model += (A*x_1 + B*x_2 + C*x_3 <= float(input("Investment?")) )
    
    
    status = model.solve()
    for var in model.variables():
        
        print(f"{var.name}:{var.value()}")

def consts(district):
    import openpyxl
    values={}
    book= openpyxl.load_workbook(r"D:\Trabalho de Licenciatura\New folder\Python\WebApp\Dados_py.xlsx")
    worksheet=book["Dados"]
    
    for row in worksheet.iter_rows(min_row=1, values_only=True):
        if row[0]==district:
            value_A=str(row[1])
            value_B=str(row[2])
            value_C=str(row[3])
            values={"A": value_A, "B": value_B, "C": value_C}
    
    return values

lp()
解决方案

要实现点击地图行政区弹窗输入参数并触发线性规划计算,需将Flask作为后端提供API,前端用JavaScript处理弹窗交互和请求,具体实现如下:

1. 完整Flask整合代码

from flask import Flask, request, jsonify
import pulp
import openpyxl
import geopandas as gpd
import folium
from folium import Popup

app = Flask(__name__)

# 提前加载行政区参数,避免重复读取Excel
def load_district_constants():
    constants = {}
    book = openpyxl.load_workbook(r"D:\Trabalho de Licenciatura\New folder\Python\WebApp\Dados_py.xlsx")
    worksheet = book["Dados"]
    for row in worksheet.iter_rows(min_row=1, values_only=True):
        if row[0]:
            constants[row[0]] = {
                "A": float(row[1]),
                "B": float(row[2]),
                "C": float(row[3])
            }
    return constants

district_constants = load_district_constants()

# 线性规划计算API
@app.route('/calculate', methods=['POST'])
def calculate_lp():
    data = request.get_json()
    district_name = data.get('district_name')
    demand = data.get('demand')
    investment = data.get('investment')
    
    # 参数校验
    if not all([district_name, demand, investment]):
        return jsonify({"error": "请填写完整参数"}), 400
    
    consts = district_constants.get(district_name)
    if not consts:
        return jsonify({"error": "未找到该行政区的参数"}), 404
    
    try:
        demand = float(demand)
        investment = float(investment)
    except ValueError:
        return jsonify({"error": "参数需为数字"}), 400
    
    # 构建线性规划模型
    model = pulp.LpProblem(name="Lp", sense=pulp.const.LpMinimize)
    A, B, C = consts["A"], consts["B"], consts["C"]
    
    x_1 = pulp.LpVariable(name="x_1", lowBound=0)
    x_2 = pulp.LpVariable(name="x_2", lowBound=0)
    x_3 = pulp.LpVariable(name="x_3", lowBound=0)
    
    obj_func = 41*x_1 + 11*x_2 + 24*x_3
    model += obj_func
    
    model += (x_1 + x_2 + x_3 >= demand)
    model += (A*x_1 + B*x_2 + C*x_3 <= investment)
    
    status = model.solve()
    
    # 整理返回结果
    result = {
        "status": pulp.LpStatus[status],
        "variables": {
            "x_1": round(x_1.value(), 2) if x_1.value() else None,
            "x_2": round(x_2.value(), 2) if x_2.value() else None,
            "x_3": round(x_3.value(), 2) if x_3.value() else None
        },
        "objective_value": round(pulp.value(obj_func), 2) if pulp.value(obj_func) else None
    }
    
    return jsonify(result)

# 主页,生成带交互弹窗的地图
@app.route('/')
def index():
    folder_path = r'D:\Trabalho de Licenciatura\New folder\Python\Moz Geo Data\Distritos'
    shapefile_path = folder_path + "\\Distritos.shp"
    districts = gpd.read_file(shapefile_path)
    districts_json = districts.to_crs(epsg='4326').to_json()
    
    map_mozambique = folium.Map(location=[-18.665695, 35.529562], zoom_start=6)
    
    # 生成带输入表单的弹窗内容
    def generate_interactive_popup(properties):
        district_name = properties['ADM2_PT']
        html = f"""
        <h3>{district_name}</h3>
        <p>所属省份: {properties['ADM1_PT']}</p>
        <div style="margin:8px 0;">
            <label>需求(Demand):</label>
            <input type="number" id="demand_{district_name}" step="0.1" min="0" style="margin-left:8px;">
        </div>
        <div style="margin:8px 0;">
            <label>投资(Investment):</label>
            <input type="number" id="investment_{district_name}" step="0.1" min="0" style="margin-left:8px;">
        </div>
        <button onclick="calculateLP('{district_name}')" style="padding:4px 12px;">计算</button>
        <div id="result_{district_name}" style="margin-top:12px;"></div>
        """
        return Popup(html, max_width=320)
    
    # 添加GeoJson图层
    folium.GeoJson(
        districts_json,
        style_function=lambda x: {
            'fillColor': '#ffff00',
            'color': '#000000',
            'weight': 1,
            'fillOpacity': 0.4
        },
        highlight_function=lambda x: {'weight': 3},
        tooltip=lambda x: x['properties']['ADM2_PT'],
        popup=lambda x: generate_interactive_popup(x['properties'])
    ).add_to(map_mozambique)
    
    # 嵌入地图和前端交互逻辑
    map_html = map_mozambique._repr_html_()
    return f"""
    <!DOCTYPE html>
    <html>
    <head>
        <title>莫桑比克行政区规划计算</title>
        {map_html.split('<head>')[1].split('</head>')[0]}
    </head>
    <body>
        {map_html.split('<body>')[1].split('</body>')[0]}
        <script>
            async function calculateLP(districtName) {{
                const demandInput = document.getElementById(`demand_\${districtName}`);
                const investmentInput = document.getElementById(`investment_\${districtName}`);
                const resultDiv = document.getElementById(`result_\${districtName}`);
                
                const demand = demandInput.value;
                const investment = investmentInput.value;
                
                if (!demand || !investment) {{
                    resultDiv.innerHTML = '<p style="color:red;">请填写所有参数</p>';
                    return;
                }}
                
                try {{
                    const response = await fetch('/calculate', {{
                        method: 'POST',
                        headers: {{ 'Content-Type': 'application/json' }},
                        body: JSON.stringify({{
                            district_name: districtName,
                            demand: demand,
                            investment: investment
                        }})
                    }});
                    
                    if (!response.ok) {{
                        const error = await response.json();
                        throw new Error(error.error);
                    }}
                    
                    const result = await response.json();
                    resultDiv.innerHTML = `
                        <p>计算状态: \${result.status}</p>
                        <p>x₁: \${result.variables.x_1 ?? '无有效解'}</p>
                        <p>x₂: \${result.variables.x_2 ?? '无有效解'}</p>
                        <p>x₃: \${result.variables.x_3 ?? '无有效解'}</p>
                        <p>目标函数值: \${result.objective_value ?? '无有效解'}</p>
                    `;
                }} catch (err) {{
                    resultDiv.innerHTML = `<p style="color:red;">\${err.message}</p>`;
                }}
            }}
        </script>
    </body>
    </html>
    """

if __name__ == '__main__':
    app.run(debug=True)

2. 关键修正点

  • 后端预加载Excel:避免每次请求重复读取文件,提升性能
  • 弹窗嵌入交互表单:修改Folium的Popup内容,添加输入框和计算按钮
  • 异步请求处理:用JavaScript的fetch发送POST请求,异步获取计算结果并展示
  • 全链路错误处理:前端校验输入完整性,后端校验参数格式和行政区存在性

3. 运行步骤

  1. 安装依赖:pip install flask geopandas folium pulp openpyxl
  2. 修改代码中的文件路径为本地实际路径
  3. 运行Flask应用,访问http://localhost:5000
  4. 点击任意行政区,在弹窗输入参数后点击计算即可查看结果

内容的提问来源于stack exchange,提问作者Marcelo Endurance Albuquerque

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 02:57:01