news 2026/9/30 7:29:47

MongoDB+Spark Streaming实时交通预测课程设计实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MongoDB+Spark Streaming实时交通预测课程设计实战

简介:本资源是一份面向大数据初学者与课程设计实践者的非关系型数据库综合实训项目,聚焦交通拥堵预测场景,适用于本科课程设计、工程实训或毕业设计选题。项目采用Kafka模拟实时交通数据流,经预处理后存入Redis等NoSQL存储,再基于Spark建模并将模型持久化至HDFS,最终实现预测推理闭环,完整覆盖大数据采集、存储、计算与应用全流程。压缩包共31个文件,含9个核心Scala程序文件(实现Kafka生产/消费、Redis读写、Spark建模与预测)、4个XML配置文件(Hadoop/Spark环境适配)、3个IML与Properties工程配置文件,以及README说明文档,整体仅60KB,轻量易部署。目前已有177人学习下载,资源结构清晰,模块分离明确(tf_producer/tf_consumer/tf_modeling/tf_prediction),附带完整Maven工程配置与版本兼容说明,便于快速复现与二次开发。

1. 用 MongoDB + Spark Streaming 实时接住路口摄像头数据:这不是 demo,是能跑通的交通拥堵预测课程设计闭环

你手头有一份「大数据非关系型数据库课程设计-交通拥堵预测」的文档或源码包,但打开后发现:只有几张架构图、一段模糊的“使用 NoSQL 存储车辆轨迹”的描述,以及一个叫traffic_prediction.py却跑不起来的脚本——这太常见了。我去年帮三个学院改毕设,90% 的学生卡在「数据进不去、模型训不动、结果画不出」三连翻车点。这份课程设计真正的价值,不是教你写 Hadoop 配置,而是让你用MongoDB 做实时写入缓冲、用 Spark Streaming 拉流做滑动窗口聚合、用 LightGBM 做分钟级拥堵概率输出,最后把预测结果回写进 MongoDB 并用 Flask 拉出来画热力图。它不追求吞吐百万 QPS,但要求你从 Kafka 模拟器发一条 JSON 车辆记录开始,到浏览器看到「西二环辅路未来15分钟拥堵概率 73%」为止,全程可调试、可断点、可查日志。适合大数据专业大三以上、已学完《NoSQL 原理》《Spark 编程基础》、正为毕业设计/课程设计找真实落地方案的同学。别被标题里的“交通”吓住——核心是时序数据建模 + 非关系型存储选型 + 流批一体落地,换成交警卡口、共享单车调度、甚至工厂设备振动监测,骨架完全复用。


2. 为什么选 MongoDB 而不是 Cassandra 或 Redis?从写入吞吐、查询模式到索引策略的真实取舍

2.1 交通场景下 NoSQL 选型的四个硬约束:写入频次、查询维度、更新频率、扩展成本

课程设计里常写“选用 NoSQL 数据库”,但没说清为什么是 MongoDB。我们来拆解真实交通数据的写入特征:

  • 写入频次:单个路口摄像头每秒产生 3~5 条车辆过车记录(车牌、时间戳、车道号、速度),按 100 个路口算,峰值写入约 400 条/秒;
  • 查询维度:需要按「路口ID+时间范围」查历史轨迹,按「时间段+区域」查拥堵统计,偶尔按「车牌号」查单辆车全路径;
  • 更新频率:车辆记录写入即固化,极少更新;但拥堵预测结果需每分钟覆盖写入一次(如predict_result_{road_id}_{minute});
  • 扩展成本:课程设计环境通常只有 3 台虚拟机(8C16G),无法部署 6 节点 Cassandra 集群,也不愿为 Redis Cluster 配置哨兵+分片逻辑。

MongoDB 在这四点上形成平衡:WiredTiger 引擎单节点写入 3000+ ops/s 足够覆盖;支持复合索引({road_id: 1, timestamp: -1})高效支撑时间范围查询;upsert操作天然适配预测结果覆盖写入;副本集模式下 3 节点即可满足高可用,且部署命令比 Cassandra 的cassandra.yaml配置少 80% 行。

提示:别被“MongoDB 不适合时序数据”的玄学说法带偏。只要做好分片键设计(见 2.3 节),它比 InfluxDB 更易与 Spark 生态对接——Spark DataFrame 可直连 MongoDB,无需额外开发 Sink。

2.2 MongoDB 集群部署:3 节点副本集 + 分片键设计实操(含完整配置文件)

课程设计环境通常用 VirtualBox 或 VMware 搭 3 台 CentOS 7 虚拟机(IP:192.168.56.10/11/12)。不要用单节点——副本集是理解高可用的最小闭环。以下是精简版部署步骤(跳过 yum 源配置等通用环节):

# 所有节点执行:安装 MongoDB 6.0(官方 repo) sudo tee /etc/yum.repos.d/mongodb-org-6.0.repo << 'EOF' [mongodb-org-6.0] name=MongoDB Repository baseurl=https://repo.mongodb.org/yum/redhat/7/mongodb-org/6.0/x86_64/ gpgcheck=1 enabled=1 gpgkey=https://www.mongodb.org/static/pgp/server-6.0.asc EOF sudo yum install -y mongodb-org # 创建数据目录(注意 SELinux 上下文) sudo mkdir -p /data/db/{rs1,rs2,rs3} sudo chown -R mongod:mongod /data/db sudo semanage fcontext -a -t mongod_var_lib_t "/data/db(/.*)?" sudo restorecon -Rv /data/db # 编辑 /etc/mongod.conf(以 192.168.56.10 节点为例) storage: dbPath: /data/db/rs1 journal: enabled: true systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log net: port: 27017 bindIp: 192.168.56.10,127.0.0.1 replication: replSetName: "trafficRS" sharding: clusterRole: shardsvr

关键点说明:

  • bindIp必须显式列出内网 IP,不能只写0.0.0.0(课程设计环境防火墙常默认拒绝);
  • shardsvr角色是为后续可能的分片预留,当前副本集模式下该参数不影响运行;
  • dbPath按节点区分(rs1/rs2/rs3),避免启动冲突;
  • 日志路径需提前创建并授权:sudo mkdir -p /var/log/mongodb && sudo chown mongod:mongod /var/log/mongodb。

启动后,在任意节点执行初始化:

// 连接主节点(假设 192.168.56.10 为初始主) mongo --host 192.168.56.10:27017 // 初始化副本集 rs.initiate({ _id: "trafficRS", members: [ { _id: 0, host: "192.168.56.10:27017" }, { _id: 1, host: "192.168.56.11:27017" }, { _id: 2, host: "192.168.56.12:27017" } ] })

验证:rs.status()查看stateStr是否为PRIMARY/SECONDARY,且ok: 1。

2.3 分片键设计:用road_id哈希分片,而非时间戳范围分片的血泪经验

很多同学第一反应是按timestamp分片——毕竟交通数据天然有时序性。但这是典型踩坑点。原因:

  • 时间戳范围分片会导致新写入全部打到同一个 chunk(热点问题),而课程设计环境无自动均衡能力;
  • 查询常需跨时间范围拉取多路口数据(如“早高峰所有主干道”),范围分片会触发广播查询,拖慢响应。

正确做法:对road_id做哈希分片,保证数据均匀分布,且road_id是绝大多数查询的必备条件(如查某路口历史、查某区域拥堵)。

// 启用分片(在 mongos 节点执行,课程设计可省略 mongos,直接在副本集上操作) sh.enableSharding("traffic_db") sh.shardCollection("traffic_db.vehicle_records", { "road_id": "hashed" }) // 创建复合索引支撑高频查询 db.vehicle_records.createIndex({ "road_id": 1, "timestamp": -1 }) db.vehicle_records.createIndex({ "plate_number": 1 })

参数说明:

  • { "road_id": "hashed" }:MongoDB 自动将road_id哈希成 64 位整数,均匀映射到分片;
  • 索引{"road_id":1,"timestamp":-1}支持「某路口最近 N 条记录」查询,排序方向-1保证时间倒序;
  • plate_number单字段索引用于车辆轨迹追溯,虽查询频次低,但缺失会导致全表扫描。

注意:哈希分片后,road_id必须出现在所有查询条件中,否则查询将广播到所有分片。课程设计中所有 Spark 读写逻辑必须强制带上road_id过滤。


3. Spark Streaming 接 Kafka 模拟车流:从 JSON 解析、窗口聚合到 MongoDB 写入的端到端链路

3.1 Kafka 模拟器搭建:用 Python 脚本生成符合交通标准的 JSON 数据流

课程设计无需部署真实 Kafka 集群。用kafka-python库写一个轻量模拟器,生成符合 GA/T 497-2016《公路车辆智能监测记录系统技术规范》的 JSON:

# producer_simulator.py from kafka import KafkaProducer import json import time import random producer = KafkaProducer( bootstrap_servers=['192.168.56.10:9092'], value_serializer=lambda v: json.dumps(v).encode('utf-8') ) # 模拟 100 个路口,每个路口 3 条车道 roads = [f"road_{i:03d}" for i in range(1, 101)] lanes = ["L1", "L2", "L3"] speeds = [30, 40, 50, 60, 70] # km/h while True: record = { "road_id": random.choice(roads), "lane_id": random.choice(lanes), "plate_number": f"京A{random.randint(1000,9999)}", "speed": random.choice(speeds), "timestamp": int(time.time() * 1000), # 毫秒级时间戳 "direction": random.choice(["N", "S", "E", "W"]) } producer.send('traffic_topic', value=record) time.sleep(0.2) # 控制发送节奏,模拟 5 条/秒

关键点说明:

  • timestamp用毫秒级 Unix 时间戳,与 Spark Structured Streaming 的current_timestamp()对齐;
  • plate_number格式简化,实际项目需校验合法性;
  • speed设定合理区间(城市主干道 30~70km/h),避免模型学习到异常值。

启动命令:python producer_simulator.py &。验证:kafka-console-consumer.sh --bootstrap-server 192.168.56.10:9092 --topic traffic_topic --from-beginning应看到持续 JSON 流。

3.2 Spark Streaming 作业:滑动窗口聚合 + 特征工程代码详解

核心逻辑:每 30 秒计算过去 5 分钟内各路口的「平均车速」「车流量」「速度标准差」,作为拥堵预测特征。使用 Structured Streaming(非 DStream)因其 API 更稳定、与 DataFrame 集成更紧密。

# spark_streaming_job.py from pyspark.sql import SparkSession from pyspark.sql.functions import * from pyspark.sql.types import * # 定义 Schema(严格匹配 Kafka JSON 结构) schema = StructType([ StructField("road_id", StringType(), False), StructField("lane_id", StringType(), False), StructField("plate_number", StringType(), False), StructField("speed", IntegerType(), False), StructField("timestamp", LongType(), False), StructField("direction", StringType(), False) ]) spark = SparkSession.builder \ .appName("TrafficStreaming") \ .config("spark.mongodb.input.uri", "mongodb://192.168.56.10:27017/traffic_db.vehicle_records") \ .config("spark.mongodb.output.uri", "mongodb://192.168.56.10:27017/traffic_db.feature_windows") \ .getOrCreate() # 从 Kafka 读取流 df = spark \ .readStream \ .format("kafka") \ .option("kafka.bootstrap.servers", "192.168.56.10:9092") \ .option("subscribe", "traffic_topic") \ .option("startingOffsets", "latest") \ .load() \ .select(from_json(col("value").cast("string"), schema).alias("data")) \ .select("data.*") # 关键:添加事件时间列(Kafka 消息自带 timestamp,非处理时间) df_with_event_time = df.withColumn("event_time", from_unixtime(col("timestamp")/1000).cast("timestamp")) # 滑动窗口聚合:5分钟窗口,30秒滑动步长 windowed_df = df_with_event_time \ .withWatermark("event_time", "10 minutes") \ # 允许 10 分钟乱序 .groupBy( window(col("event_time"), "5 minutes", "30 seconds"), col("road_id") ) \ .agg( avg("speed").alias("avg_speed"), count("*").alias("vehicle_count"), stddev("speed").alias("speed_stddev"), max("speed").alias("max_speed"), min("speed").alias("min_speed") ) \ .withColumn("window_start", col("window.start")) \ .withColumn("window_end", col("window.end")) \ .drop("window") # 写入 MongoDB(注意:outputMode 必须为 Append,因窗口聚合结果不可更新) query = windowed_df \ .writeStream \ .format("com.mongodb.spark.sql.DefaultSource") \ .option("database", "traffic_db") \ .option("collection", "feature_windows") \ .option("checkpointLocation", "/tmp/spark-checkpoint-traffic") \ .outputMode("Append") \ .start() query.awaitTermination()

参数说明:

  • withWatermark("event_time", "10 minutes"):容忍 10 分钟内到达的乱序数据,避免窗口关闭后迟到数据丢失;
  • window(col("event_time"), "5 minutes", "30 seconds"):定义滑动窗口,5 分钟计算周期,每 30 秒触发一次计算;
  • outputMode("Append"):窗口聚合结果为追加写入,MongoDB 中每条记录代表一个窗口的统计值;
  • checkpointLocation:必须指定本地路径(非 HDFS),用于故障恢复,课程设计环境/tmp即可。

3.3 MongoDB 写入优化:禁用_id自动生成 + 批量插入配置

默认情况下,Spark 写入 MongoDB 会为每条记录生成 ObjectId,带来额外 CPU 开销。课程设计中应显式关闭:

# 在 spark_streaming_job.py 的 writeStream 配置中追加: .option("forceInsert", "true") \ .option("replaceDocument", "false") \

同时,在 MongoDB 配置中调优写入性能:

// 进入 mongo shell,执行 db.getSiblingDB("traffic_db").getCollection("feature_windows").createIndex( { "road_id": 1, "window_start": 1 }, { background: true } )

提示:background: true避免建索引阻塞写入,课程设计数据量小时可忽略,但养成习惯。


4. 拥堵预测模型训练与部署:LightGBM 特征工程、离线训练、在线推理服务封装

4.1 特征工程闭环:从 MongoDB 抽取窗口特征 + 构造标签(拥堵与否)

拥堵预测本质是二分类问题。标签定义需符合交管业务逻辑:当某路口 5 分钟内平均车速 < 20km/h 且车流量 > 50 辆,则标记为“拥堵”(阈值可根据本地路网调整)。

# feature_extraction.py from pymongo import MongoClient import pandas as pd from datetime import datetime, timedelta client = MongoClient("mongodb://192.168.56.10:27017/") db = client["traffic_db"] collection = db["feature_windows"] # 抽取最近 24 小时特征数据(课程设计用 1 小时足够) end_time = datetime.now() start_time = end_time - timedelta(hours=1) cursor = collection.find({ "window_start": {"$gte": start_time, "$lt": end_time} }, { "_id": 0, "road_id": 1, "avg_speed": 1, "vehicle_count": 1, "speed_stddev": 1, "max_speed": 1, "min_speed": 1, "window_start": 1 }) df = pd.DataFrame(list(cursor)) df["is_congested"] = ((df["avg_speed"] < 20) & (df["vehicle_count"] > 50)).astype(int) # 保存为 CSV 供模型训练 df.to_csv("traffic_features.csv", index=False) print(f"Extracted {len(df)} samples, congested ratio: {df['is_congested'].mean():.2%}")

关键点说明:

  • 标签构造必须基于业务规则,而非简单聚类——课程设计要体现“懂业务”;
  • window_start保留时间戳,便于后续做时间序列交叉验证;
  • 输出 CSV 时去掉_id,避免 LightGBM 解析失败。

4.2 LightGBM 训练脚本:处理类别不平衡 + 特征重要性可视化

交通数据中“畅通”样本远多于“拥堵”,需用scale_pos_weight缓解:

# train_model.py import pandas as pd import lightgbm as lgb import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report, confusion_matrix df = pd.read_csv("traffic_features.csv") X = df[["avg_speed", "vehicle_count", "speed_stddev", "max_speed", "min_speed"]] y = df["is_congested"] # 处理缺失值(课程设计中极少出现,但必须写) X = X.fillna(X.mean()) # 划分训练测试集(按时间切分,避免未来信息泄露) split_idx = int(len(X) * 0.8) X_train, X_test = X.iloc[:split_idx], X.iloc[split_idx:] y_train, y_test = y.iloc[:split_idx], y.iloc[split_idx:] # 计算 scale_pos_weight:负样本数/正样本数 scale_pos_weight = len(y_train[y_train==0]) / len(y_train[y_train==1]) lgb_model = lgb.LGBMClassifier( objective='binary', scale_pos_weight=scale_pos_weight, n_estimators=100, learning_rate=0.1, max_depth=6, random_state=42 ) lgb_model.fit(X_train, y_train) # 评估 y_pred = lgb_model.predict(X_test) print(classification_report(y_test, y_pred)) # 特征重要性 plt.figure(figsize=(10, 6)) lgb.plot_importance(lgb_model, max_num_features=10) plt.title("LightGBM Feature Importance") plt.tight_layout() plt.savefig("feature_importance.png")

参数说明:

  • scale_pos_weight动态计算,避免硬编码;
  • max_depth=6防止过拟合,课程设计数据量小,深度过大易翻车;
  • plot_importance直观展示哪些特征驱动预测,答辩时可重点讲。

4.3 Flask 在线推理服务:接收路口ID+时间,返回拥堵概率

模型训练完需封装为 HTTP 服务,供前端调用。关键:加载模型时缓存,避免每次请求反序列化:

# app.py from flask import Flask, request, jsonify import joblib import numpy as np from pymongo import MongoClient app = Flask(__name__) model = joblib.load("lgb_model.pkl") # 预先保存的模型 client = MongoClient("mongodb://192.168.56.10:27017/") db = client["traffic_db"] @app.route("/predict", methods=["POST"]) def predict_congestion(): data = request.json road_id = data.get("road_id") window_end = data.get("window_end") # ISO 格式时间字符串 # 从 MongoDB 查询最近一个窗口特征 collection = db["feature_windows"] record = collection.find_one({ "road_id": road_id, "window_end": {"$lte": window_end} }, sort=[("window_end", -1)]) if not record: return jsonify({"error": "No feature found"}), 404 # 构造特征向量(顺序必须与训练一致) features = np.array([[ record.get("avg_speed", 0), record.get("vehicle_count", 0), record.get("speed_stddev", 0), record.get("max_speed", 0), record.get("min_speed", 0) ]]) prob = model.predict_proba(features)[0][1] # 拥堵概率 return jsonify({ "road_id": road_id, "congestion_probability": float(prob), "status": "congested" if prob > 0.5 else "smooth" }) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True)

启动命令:python app.py。测试:

curl -X POST http://localhost:5000/predict \ -H "Content-Type: application/json" \ -d '{"road_id":"road_001","window_end":"2023-10-01T12:00:00Z"}'

注意:window_end传入的是字符串,MongoDB 查询时需确保时区一致(建议全部用 UTC)。


5. 避坑指南:课程设计中最常翻车的 5 个问题及现场急救方案

5.1 现象:Spark Streaming 作业启动后无任何输出,query.status显示ACTIVE但query.recentProgress为空

原因:Kafka topic 无数据,或 Spark 消费者组 offset 位置错误(如设置earliest但 topic 为空)。
解决:

  1. 先用kafka-console-consumer.sh确认 topic 有数据;
  2. 删除消费者组:kafka-consumer-groups.sh --bootstrap-server 192.168.56.10:9092 --group spark-streaming-group --delete;
  3. 在 Spark 代码中显式设置option("startingOffsets", "latest"),避免从头消费空数据。

5.2 现象:MongoDB 写入报错Command failed with error 13 (Unauthorized)

原因:课程设计环境常忽略权限配置,默认mongod启动无认证,但 Spark 连接串若含?authSource=admin会触发鉴权。
解决:

  1. 检查/etc/mongod.conf中security.authorization是否为disabled(课程设计建议关闭);
  2. Spark 连接 URI 去掉?authSource=admin,直接用mongodb://192.168.56.10:27017/...;
  3. 若必须开启认证,需创建用户:db.createUser({user:"spark",pwd:"123456",roles:["readWrite"]})。

5.3 现象:LightGBM 训练时报错ValueError: Input contains NaN, infinity or a value too large for dtype('float32')

原因:特征数据中存在None或inf,常见于speed_stddev在单车道单辆车时计算为nan。
解决:

  1. 在特征抽取阶段增加清洗:df = df.replace([np.inf, -np.inf], np.nan).fillna(0);
  2. 或在训练前用X = X.clip(lower=-1e6, upper=1e6)截断异常值。

5.4 现象:Flask 服务返回500 Internal Server Error,日志显示pymongo.errors.ServerSelectionTimeoutError

原因:Flask 进程与 MongoDB 连接未复用,每次请求新建连接,耗尽 socket。
解决:

  1. 将MongoClient实例化移到全局作用域(如app.py顶层),而非predict_congestion函数内;
  2. 设置连接池大小:MongoClient(..., maxPoolSize=10, minPoolSize=3)。

5.5 现象:浏览器访问 Flask 接口超时,curl却正常

原因:前端 JavaScript 发送请求时未设置Content-Type: application/json,Flask 默认解析 form-data,导致request.json为None。
解决:

  1. 前端代码明确设置 header:fetch("/predict", {method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify(data)});
  2. Flask 中增加健壮性检查:if not data or "road_id" not in data: return jsonify({"error":"Missing road_id"}), 400。

6. 从 MongoDB 归档到实时热力图:用 Flask+ECharts 实现最后一公里交付

6.1 MongoDB 聚合管道:动态计算区域拥堵指数(非简单平均)

课程设计答辩时,光有单路口预测不够,需展示宏观态势。这里用 MongoDB 原生聚合实现「区域拥堵指数」:对某行政区划内所有路口,按congestion_probability加权平均,权重为该路口日均车流量(来自历史统计)。

// 在 mongo shell 中执行(或封装为 Python 脚本定时调用) db.feature_windows.aggregate([ { $match: { "window_end": { $gte: ISODate("2023-10-01T12:00:00Z"), $lt: ISODate("2023-10-01T12:05:00Z") } } }, { $lookup: { from: "road_traffic_stats", // 预先存好的各路口日均车流量 localField: "road_id", foreignField: "road_id", as: "stats" } }, { $unwind: "$stats" }, { $group: { _id: "$stats.district", // 按行政区划分组 weighted_sum: { $sum: { $multiply: ["$congestion_probability", "$stats.daily_volume"] } }, total_volume: { $sum: "$stats.daily_volume" } } }, { $project: { district: "$_id", congestion_index: { $divide: ["$weighted_sum", "$total_volume"] }, _id: 0 } } ])

关键点说明:

  • $lookup关联静态路网统计表,避免在应用层 join;
  • $unwind展开数组,使每条记录对应一个路口;
  • $group计算加权平均,比简单avg(congestion_probability)更反映真实压力。

6.2 Flask API 封装聚合结果:支持时间范围与行政区划筛选

# app.py 新增路由 @app.route("/district_congestion", methods=["GET"]) def get_district_congestion(): district = request.args.get("district") start_time = request.args.get("start_time") # ISO 格式 end_time = request.args.get("end_time") pipeline = [ {"$match": { "window_end": {"$gte": datetime.fromisoformat(start_time), "$lt": datetime.fromisoformat(end_time)} }}, {"$lookup": {"from": "road_traffic_stats", "localField": "road_id", "foreignField": "road_id", "as": "stats"}}, {"$unwind": "$stats"}, {"$group": { "_id": "$stats.district", "weighted_sum": {"$sum": {"$multiply": ["$congestion_probability", "$stats.daily_volume"]}}, "total_volume": {"$sum": "$stats.daily_volume"} }}, {"$project": { "district": "$_id", "congestion_index": {"$divide": ["$weighted_sum", "$total_volume"]}, "_id": 0 }} ] if district: pipeline.insert(0, {"$match": {"stats.district": district}}) result = list(db.feature_windows.aggregate(pipeline)) return jsonify(result)

6.3 ECharts 热力图渲染:用 GeoJSON 区划数据 + 动态颜色映射

前端 HTML 中引入 ECharts,关键代码:

<div id="map" style="width: 100%; height: 600px;"></div> <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script> <script> const chart = echarts.init(document.getElementById('map')); let geoJsonData; // 预加载的 GeoJSON 行政区划 // 获取拥堵数据并渲染 async function renderHeatmap() { const res = await fetch('/district_congestion?start_time=2023-10-01T12:00:00&end_time=2023-10-01T12:05:00'); const data = await res.json(); // 构建 series 数据:district -> congestion_index const seriesData = data.map(item => ({ name: item.district, value: item.congestion_index * 100 // 转为百分比 })); chart.setOption({ tooltip: { trigger: 'item' }, geo: { map: 'beijing', // 自定义地图名,需注册 GeoJSON roam: true, label: { show: true } }, series: [{ type: 'map', map: 'beijing', data: seriesData, visualMap: { min: 0, max: 100, text: ['高拥堵', '低拥堵'], calculable: true, inRange: { color: ['#FF4C4C', '#FFCC00', '#66FF66'] // 红-黄-绿 } } }] }); } // 注册 GeoJSON 地图(此处省略具体 GeoJSON 加载逻辑) echarts.registerMap('beijing', geoJsonData); renderHeatmap(); </script>

提示:GeoJSON 数据可从国家地理信息公共服务平台下载,课程设计用北京市朝阳区简化版即可(<100KB)。

从那以后我每次做课程设计,都强制走一遍「Kafka 模拟器 → Spark Streaming → MongoDB 写入 → 特征抽取 → LightGBM 训练 → Flask 封装 → ECharts 渲染」全链路,哪怕只跑通 5 条数据。因为答辩时老师问“这个模块怎么验证”,你掏出浏览器里正在跳动的热力图,比讲一百页 PPT 都管用。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 7:29:47

用扣子COZE搭建企业官网智能客服:知识库、多轮对话与工作流实践指南

简介&#xff1a;面向企业官网需要自动应答客户咨询的落地需求&#xff0c;这份基于扣子COZE平台的智能客服开发资料&#xff0c;完整呈现了多轮对话助手的设计思路与应用配置方式&#xff0c;适合有一定编程基础、希望低成本构建客服系统的开发者或企业技术人员参考。资源共1个…

作者头像 李华
网站建设 2026/9/30 7:28:50

基于JSP的服装商城交易管理系统设计与实现

简介&#xff1a;一份围绕基于JSP的服装商城交易管理系统设计与实现的答辩演示文稿&#xff0c;面向计算机相关专业毕业生及需要进行系统类课程设计或毕业答辩的学生。资源从选题意义、课题背景、系统功能目标到技术方案逐步展开&#xff0c;重点覆盖JSP与Servlet在业务逻辑处理…

作者头像 李华
网站建设 2026/9/30 7:27:48

KonopkaControls WinForm 高 DPI 圆角与布局优化方案

简介&#xff1a;本资源是面向Delphi中高级开发者的一套完整可视化控件源码库&#xff0c;专为适配Delphi 12.3环境优化设计&#xff0c;继承自经典Raize Components体系&#xff0c;由Konopka公司持续维护升级。它提供高度可定制的VCL界面组件&#xff08;如增强型按钮、网格、…

作者头像 李华
网站建设 2026/9/30 7:27:14

四平信誉好的考研寒假特训营机构用户力荐

2026四平信誉好的考研寒假特训营机构用户力荐考研寒假集训是基础夯实的关键窗口&#xff0c;但不少考生会陷入自学效率低、择校无方向、缺乏系统规划的困境。所谓考研寒假特训营&#xff0c;是指利用寒假7-10天的连续假期&#xff0c;通过封闭式面授、闭环督学、针对性基础训练…

作者头像 李华
网站建设 2026/9/30 7:25:53

Hot 100 --- 下一个排列

本文概览&#xff1a;本文讲解下一个排列&#xff1a;要让排列"只变大一点点"&#xff0c;就得找从右往左第一个升序相邻对&#xff08;它右边的降序段已经顶到头了&#xff09;&#xff0c;用降序段里比它大的最小数替换&#xff0c;再把右边那段反转成升序&#xf…

作者头像 李华
网站建设 2026/9/30 7:24:38

HarmonyOS 7 + User Authentication Kit + Asset Store Kit 技术干货:生物认证、敏感操作授权与关键资产安全闭环【鸿蒙心迹】

认证成功并不等于“敏感数据就安全了”。真正完整的安全链路&#xff0c;应该把“谁在操作、这次操作有没有被授权、授权之后能访问什么、敏感数据存在哪里”几件事连起来。这篇文章用一个安全资产中心 Demo&#xff0c;把 User Authentication Kit 与 Asset Store Kit 的职责边…

作者头像 李华