如何在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. 运行步骤
- 安装依赖:
pip install flask geopandas folium pulp openpyxl - 修改代码中的文件路径为本地实际路径
- 运行Flask应用,访问
http://localhost:5000 - 点击任意行政区,在弹窗输入参数后点击计算即可查看结果
内容的提问来源于stack exchange,提问作者Marcelo Endurance Albuquerque
相关产品推荐
相关产品推荐

