跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Java大前端java

基于 Java 和 Leaflet 的湖南省道路长度 WebGIS 系统构建

基于 Java 和 Leaflet 构建湖南省道路长度 WebGIS 系统,利用 PostGIS 存储空间数据,MyBatis Plus 处理 Mapper 查询。后端采用 MVC 三层架构封装地市路网长度接口,前端通过 Leaflet 渲染地图,依据里程阈值动态着色并标注行政驻地。系统实现了省域道路检索、分级统计及可视化展示,有效提升了交通数据的查询效率与管理直观性,为区域交通规划提供技术参考。

念念不忘发布于 2026/3/24更新于 2026/8/2438 浏览
基于 Java 和 Leaflet 的湖南省道路长度 WebGIS 系统构建

背景与目标

在交通规划与物流管理中,如何高效展示和分析区域道路长度信息至关重要。针对湖南省复杂的地理环境与密集路网,我们构建了一套基于 Java 后端与 Leaflet 前端的 WebGIS 系统,旨在实现道路里程数据的直观查询、分级统计及可视化展示。

基础空间数据

系统核心依赖 PostGIS 空间数据库存储道路网络及行政区划信息。主要涉及三张表:

序号表名说明
1biz_urban_road_mileage_info城市道路里程信息表(业务表)
2biz_geographic_name城市名称信息表(点状空间数据)
3biz_city市级行政区划信息表(面状空间数据)

注:数据来源为 OSM,时效性仅供参考,实际生产环境建议接入权威官方数据,SQL 逻辑通用。

省域道路检索 SQL

通过空间关联查询获取各地市行政驻地坐标及道路总长,默认以湖南省代码 430000 为例:

SELECT t1.*, T3.province_code, t3.province_name, st_asgeojson ( t3.geom ) geomJson, st_x ( t2.geom ) lon, st_y ( t2.geom ) lat 
FROM biz_urban_road_mileage_info t1, biz_geographic_name t2, biz_city t3 
WHERE t1.parent_code = '430000' 
AND t1.city_code = t3.city_code 
AND T1.city_name = t2.NAME 
AND st_contains ( t3.geom, t2.geom );

执行后可见包含 GeoJSON 几何信息及驻地的结果集,支持动态替换省份代码。

后端实现

采用经典 MVC 三层架构,利用 MyBatis Plus 简化数据库操作。

视图对象设计

为了在前端展示时区分地市行政驻地位置,我们在原有实体基础上扩展了视图对象(VO),包装省份代码、名称及 GeoJSON 数据:

package com.yelang.project.extend.earthquake.domain;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;




       {
          ;
    
     String provinceCode;
    
     String provinceName;
    
     String geomJson;
     String lat;
     String lon;
}
@Data
@ToString(callSuper=true)
@EqualsAndHashCode(callSuper=false)
public
class
UrbanRoadMileageInfoVO
extends
UrbanRoadMileageInfo
implements
Serializable
private
static
final
long
serialVersionUID
=
1101541707654186490L
@TableField(exist = false,value= "province_code")
private
@TableField(exist = false,value= "province_name")
private
@TableField(exist = false)
private
private
private

Mapper 空间查询

核心在于将 SQL 中的空间函数(如 ST_Length, ST_Contains)映射到 MyBatis XML 注解中,按道路等级分类统计长度:

package com.yelang.project.extend.earthquake.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yelang.project.extend.earthquake.domain.UrbanRoadMileageInfo;
import com.yelang.project.extend.earthquake.domain.UrbanRoadMileageInfoVO;

public interface UrbanRoadMileageInfoMapper extends BaseMapper<UrbanRoadMileageInfo> {
    /**
     * - 查询省级以下地市路网长度 SQL
     */
    static final String LIST_BYPROVINCE_SQL = "SELECT t1.city_code,MAX(t1.city_name) AS city_name," +
        " SUM(CASE WHEN r.fclass IN ('motorway', 'motorway_link') THEN ST_Length(r.geom::geography) ELSE 0 END) AS highway_length, " +
        " SUM(CASE WHEN r.fclass IN ('trunk', 'trunk_link') THEN ST_Length(r.geom::geography) ELSE 0 END) AS trunk_length, " +
        " SUM(CASE WHEN r.fclass IN ('primary', 'primary_link') THEN ST_Length(r.geom::geography) ELSE 0 END) AS primary_length, " +
        " SUM(CASE WHEN r.fclass IN ('secondary', 'secondary_link') THEN ST_Length(r.geom::geography) ELSE 0 END) AS secondary_length," +
        " SUM(CASE WHEN r.fclass IN ('tertiary', 'tertiary_link') THEN ST_Length(r.geom::geography) ELSE 0 END) AS tertiary_length, " +
        " SUM(CASE WHEN r.fclass IN ('residential', 'living_street') THEN ST_Length(r.geom::geography) ELSE 0 END) AS residential_length, " +
        " SUM(CASE WHEN r.fclass IN ('service', 'unclassified') THEN ST_Length(r.geom::geography) ELSE 0 END) AS service_length, " +
        " SUM(CASE WHEN r.fclass IN ('footway', 'pedestrian', 'path') THEN ST_Length(r.geom::geography) ELSE 0 END) AS pedestrian_length, " +
        " SUM(CASE WHEN r.fclass = 'cycleway' THEN ST_Length(r.geom::geography) ELSE 0 END) AS cycleway_length, " +
        " SUM(CASE WHEN r.fclass = 'track' THEN ST_Length(r.geom::geography) ELSE 0 END) AS track_length," +
        " SUM(CASE WHEN r.fclass in ('steps', 'footway') THEN ST_Length(r.geom::geography) ELSE 0 END) AS steps_length," +
        " SUM(ST_Length(r.geom::geography)) AS total_length " +
        " FROM biz_road_network r JOIN biz_city t1 ON ST_Contains(t1.geom, r.geom) WHERE t1.province_code = #{province_code} " +
        " GROUP BY t1.city_code ORDER BY total_length DESC ";

    @Select(LIST_BYPROVINCE_SQL)
    public List<UrbanRoadMileageInfo> getListByProvinceCode(@Param("province_code")String provinceCode);

    final static String GET_ROADMILEAGELIST_BY_PROVINCECODE = "<script>" +
        " SELECT t1.*,T3.province_code,t3.province_name,st_asgeojson ( t3.geom ) geomJson, " +
        " st_x ( t2.geom ) lon,st_y ( t2.geom ) lat " +
        " FROM biz_urban_road_mileage_info t1,biz_geographic_name t2,biz_city t3 " +
        " WHERE t1.parent_code = #{provinceCode} AND t1.city_code = t3.city_code " +
        " AND T1.city_name = t2.NAME AND st_contains ( t3.geom, t2.geom ) " +
        "</script>";

    @Select(GET_ROADMILEAGELIST_BY_PROVINCECODE)
    List<UrbanRoadMileageInfoVO> getRoadMileageList(@Param("provinceCode") String provinceCode);
}

控制层 API

Controller 负责页面跳转与数据接口暴露,使用 Shiro 进行权限控制:

package com.yelang.project.extend.earthquake.controller;
import java.util.List;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.yelang.framework.web.controller.BaseController;
import com.yelang.framework.web.domain.AjaxResult;
import com.yelang.project.extend.earthquake.domain.UrbanRoadMileageInfoVO;
import com.yelang.project.extend.earthquake.service.IUrbanRoadMileageInfoService;

@Controller
@RequestMapping("/eq/urbanroadmileageinfo")
public class UrbanRoadMileageInfoController extends BaseController{
    private String prefix = "earthquake/urbanroadmileageinfo";
    @Autowired
    private IUrbanRoadMileageInfoService urbanRoadMileageInfoService;

    @RequiresPermissions("eq:urbanroadmileageinfo:map")
    @GetMapping("/")
    public String main(ModelMap mmap){
        return prefix + "/main";
    }

    @RequiresPermissions("eq:urbanroadmileageinfo:list")
    @GetMapping("/data/{pcode}")
    @ResponseBody
    public AjaxResult ewsnProvinceList(@PathVariable("pcode") String pcode){
        List<UrbanRoadMileageInfoVO> dataList = urbanRoadMileageInfoService.getRoadMileageList(pcode);
        return AjaxResult.success().put("data", dataList);
    }
}

前端交互

前端基于 Leaflet 地图引擎,重点处理图例配置、数据渲染及交互事件。

里程图例与颜色映射

根据道路总长度设定不同色阶,便于直观识别路网密度:

// 里程颜色配置
var colorList = [
    {name:"5千公里以下",color:"#00FF00",rgb:new Color(0, 255, 0),colorDesc:"绿色"},
    {name:"5千 -8 千公里",color:"#FFFF00",rgb:new Color(255, 255, 0),colorDesc:"黄色"},
    {name:"8 千 -1.1 万公里",color:"#FFA500",rgb:new Color(255, 165, 0),colorDesc:"蓝色"},
    {name:"1.1 万 -1.4 万公里",color:"#113fc1",rgb:new Color(255, 0, 0),colorDesc:"橙色"},
    {name:"1.4 万 -1.6 万公里",color:"#800080",rgb:new Color(128, 0, 128),colorDesc:"紫色"},
    {name:"1.6 万以上",color:"#FF0000",rgb:new Color(153, 51, 102),colorDesc:"红色"}
];

function getColorByLength(length){
    if(length >= 0 && length <= 5000) { return "#00FF00"; }
    if(length >= 5001 && length <= 8000) { return "#FFFF00"; }
    if(length >= 8001 && length <= 11000) { return "#FFA500"; }
    if(length >= 11001 && length <= 14000) { return "#113fc1"; }
    if(length >= 14001 && length <= 16000) { return "#800080"; }
    if(length >= 16001) { return "#FF0000"; }
}

地图渲染与信息展示

后端返回的单位是米,前端需转换为万公里并保留两位小数。通过 AJAX 获取数据后,解析 GeoJSON 并添加到图层,同时标注行政驻地:

function buildShowInfo(index,color,data){
    var length = parseFloat(data.totalLength) / (1000.0 * 10000 );
    var result = "<div + color + '; animation-spaceInDown onclick='showDetails('"+data.cityCode+")'><div>" + data.cityName ;
    result += "<span>:" + length.toFixed(2) + "万公里</span></div>";
    result += "</div>";
    return result;
}

function previewRoadMap(pid,provinceCode,name){
    previewProvince(pid,name);
    $.ajax({
        type:"get",
        url:ctx + "eq/urbanroadmileageinfo/data/" + provinceCode,
        data:{},
        dataType:"json",
        cache:false,
        processData:false,
        success:function(result){
            if(result.code == web_status.SUCCESS){
                collisionLayer.clearLayers();
                var dataArray = result.data;
                if(dataArray != null && dataArray.length > 1){
                    for(var i = 0;i< dataArray.length;i++){
                        var areaData = dataArray[i];
                        var tempTotalLength = parseFloat(areaData.totalLength) / 1000.0;
                        var color = getColorByLength(tempTotalLength);
                        // 绘制多边形区域
                        var areaLayer = L.geoJSON(JSON.parse(areaData.geomJson),{
                            style: {color:color,fillColor:color,weight:3,"opacity":0.65, fillOpacity: 0.65 }
                        }).addTo(mymap);
                        // 创建自定义图标显示里程信息
                        var myIcon = L.divIcon({
                            iconSize: null,
                            className: '',
                            popupAnchor:[5,5],
                            shadowAnchor:[5,5],
                            html: buildShowInfo(i,color,areaData)
                        });
                        showLayerGroup.addLayer(areaLayer);
                        // 中心点位标记
                        L.marker([areaData.lat, areaData.lon], { icon: myIcon}).addTo(collisionLayer);
                    }
                    collisionLayer.addTo(showLayerGroup);
                }
            }
        },
        error:function(){
            $.modal.alertWarning("获取空间信息失败");
        }
    });
}

function showDetails(cityCode){
    console.log(cityCode);
    // 此处可扩展为打开新页面展示该地市详细分级统计
}

效果预览

系统运行后,可清晰看到湖南省各地市路网分布情况。按总里程降序排列,长沙、衡阳、岳阳位列前三,且空间聚集特征明显。

文章配图

全省排名示例:

430100 长沙市 17753221.56551351
430400 衡阳市 14434666.394707818
430600 岳阳市 14222249.927994445
430900 益阳市 13073704.1462314
430700 常德市 11931665.375579692
...

从区域分布来看,东北部(岳阳、益阳、常德)及南部(衡阳)道路里程较长;湘西北受地质环境影响相对较弱;南部邵阳、永州等地仍有较大发展空间。系统成功实现了数据可视化,为后续的交通规划提供了直观依据。

文章配图

文章配图

文章配图

结语

本实践通过 Java 后端与 Leaflet 前端结合,构建了一个功能完善的道路长度 WebGIS 系统。未来可进一步集成实时路况监测或引入 AI 算法优化路径规划,提升交通管理的智能化水平。

目录

  1. 背景与目标
  2. 基础空间数据
  3. 省域道路检索 SQL
  4. 后端实现
  5. 视图对象设计
  6. Mapper 空间查询
  7. 控制层 API
  8. 前端交互
  9. 里程图例与颜色映射
  10. 地图渲染与信息展示
  11. 效果预览
  12. 结语
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于 LLama-Factory 微调大模型生成合规隐私政策
  • 大模型技术入门与进阶学习指南
  • 通义万相 2.1 助力 AIGC 创作,多模态生成技术解析
  • Spec Kit:GitHub 官方推出的规范驱动开发工具包
  • Android 大厂面试核心考点与问题汇总
  • 前端流式输出:从原理到实战落地
  • OpenClaw 新手入门:环境搭建、模型配置与 WebUI 远程访问
  • 低空经济驱动下的无人机光伏巡检技术应用
  • iOS 蓝牙开发核心流程与注意事项
  • Python 桌面应用开发:CustomTkinter 入门与实战
  • 机器人正运动学与逆运动学基础
  • MixAIHub:主流 AI 模型镜像访问方案
  • 网络安全常用工具汇总:从漏洞扫描到渗透测试实战指南
  • HarmonyOS 6 Navigation 组件导航生命周期解析
  • Linux 进程控制
  • Openclaw 开源仿生爪如何革新机器人抓取?
  • Kafka 副本同步:理解 ISR 与 AR
  • Flutter for OpenHarmony 实战:Material Color Utilities 算法驱动动态换肤
  • Python 面向对象编程:软件对象基础与封装
  • 算法优选:位运算技巧

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online