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

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

基于 Java 与 Leaflet 技术栈,构建湖南省道路长度 WebGIS 系统。通过 PostGIS 存储空间数据,利用 MyBatis 实现复杂的空间 SQL 检索,后端返回 GeoJSON 格式数据。前端采用 Leaflet 渲染地图,根据道路里程分级着色展示各地市路网分布。系统解决了传统管理方式数据更新慢、展示不直观的问题,实现了省域道路信息的可视化查询与分析。

星辰大海发布于 2026/4/9更新于 2026/10/2598 浏览
基于 Java 和 Leaflet 的湖南省道路长度 WebGIS 系统构建

地理信息系统(GIS)在交通管理中至关重要。针对湖南省复杂的道路网络,我们利用 Java 后端结合 Leaflet 前端,构建了一套道路长度可视化系统。整体流程上,我们先梳理基础空间数据,接着搭建 Java 后台服务,最后用 Leaflet 完成前端可视化。

一、基础空间数据简介

为了实现地级市空间范围的展示及行政驻地标注,我们需要明确涉及的基础空间数据。数据来源主要基于 OSM 转换后的公路里程信息表,虽然时效性可能不如官方最新数据,但查询逻辑通用,可供参考。

1、涉及相关表

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

2、省域道路长度检索

通过空间数据库表关联业务表进行查询。默认使用湖南省(代号 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 );

运行这条 SQL,在数据库客户端里能看到这样的结果:

文章配图

二、Java 后台实现

采用经典的 MVC 三层架构,重点在于视图对象的设计与 MyBatis 的空间检索。

1、道路视图对象

为了返回包含行政驻地位置的数据,我们在原有实体基础上扩展了一个视图对象(VO)。核心代码如下:

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

2、Mapper 空间检索查询

使用 MyBatisPlus 封装 SQL 查询。这里将复杂的地理空间计算逻辑直接写入 Mapper,方便业务层调用。

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);
}

3、控制 API 实现

控制层主要负责页面跳转和数据接口暴露,逻辑相对简单。

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);
    }
}

三、WebGIS 界面实现

前端主要处理图例配置、地图渲染及交互逻辑。

1、里程图例及初始化

根据道路里程的不同区间设置颜色区分,这里采用简单的值域判断来映射颜色。

// 里程颜色配置
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"; }
}

2、各地市信息展示

数据库中存储的单位是米,前端展示时需转换为万公里并保留两位小数。同时需要处理 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("获取空间信息失败");
        }
    });
}

点击标注可以触发 showDetails 函数,后续可扩展为展示更详细的道路分级统计信息。

四、成果展示

系统最终效果结合了地图底图与统计数据,直观展示了各地市的道路分布情况。

1、总体展示

从湖南省的总体情况来看,按照道路里程降序排序如下所示:

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

可以看到,省会长沙市、衡阳市、岳阳市位列前三。从空间聚集来看,基本分布在东部和东南部。

文章配图

2、分区域说明

从区域分布来看,道路里程较长的集中在东北部的岳阳、益阳、常德等地区和南部的衡阳市。

文章配图

湘西北的道路里程相对较弱,可能与山区地质环境有关。要致富先修路,打造好交通基础条件十分必要。

文章配图

南部地区除衡阳外,邵阳、永州、郴州等地市的道路里程排名中等,仍有较大发展空间。随着人口流动的变化,未来的交通规划需更加精准。

文章配图

五、总结

本实践构建了一个功能完善、性能稳定的湖南省道路长度 WebGIS 展示系统。通过将 Java 后端与 Leaflet 前端结合,实现了复杂空间数据的可视化查询与分析。

未来展望中,计划进一步完善系统功能,例如增加道路拥堵情况的实时监测与分析、与其他交通信息系统的集成等。同时,探索人工智能、大数据等新技术的应用,为道路管理提供更加智能化、精准化的解决方案。

目录

  1. 一、基础空间数据简介
  2. 1、涉及相关表
  3. 2、省域道路长度检索
  4. 二、Java 后台实现
  5. 1、道路视图对象
  6. 2、Mapper 空间检索查询
  7. 3、控制 API 实现
  8. 三、WebGIS 界面实现
  9. 1、里程图例及初始化
  10. 2、各地市信息展示
  11. 四、成果展示
  12. 1、总体展示
  13. 2、分区域说明
  14. 五、总结

更多推荐文章

查看全部
  • Huobao Drama 开源短剧生成平台:从剧本到视频
  • JDK 17 新特性整理
  • 使用 cpolar 内网穿透实现 OpenClaw 远程访问
  • Docker Compose 部署 OpenClaw 并接入飞书机器人
  • 从 GetDiagnostics 到 C++ 全栈诊断:排障与调试工具集
  • C++11 核心特性详解:Lambda、可变参数与函数包装器
  • 网络安全入门指南:职业方向、学习路线与方法论
  • RTX 4090 实测:圣光艺苑 AI 绘画工具古典风格生成效果
  • 递归与回溯算法专题:汉诺塔、链表操作及快速幂
  • Web 安全实战:XSS 漏洞闯关技巧解析
  • Clawdbot 镜像免配部署 Qwen3-32B:Web 网关直连方案
  • Windows 系统下 VSCode 配置 C/C++ 开发环境实战
  • 宇树 Unitree 机器人 ROS 2 Humble 环境部署指南 (Go2/B2/H1)
  • 前端 API 设计最佳实践:构建优雅的接口规范
  • 美以伊战中 AI 全场景落地细节与成效实战解析
  • OpenClaw 多机器人多 Agent 模式:构建 AI 助手团队
  • 小米 Miloco 大模型智能家居本地化部署实战
  • Axum Rust Web 框架核心特性与实战指南
  • 5 款免费 AIGC 检测工具推荐及降重技巧
  • 智能车竞赛惯导与视觉避障思路分享

相关免费在线工具

  • 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