Java+Leaflet:湖南省道路长度WebGIS的构建与实践

Java+Leaflet:湖南省道路长度WebGIS的构建与实践

目录

前言

一、基础空间数据简介

1、涉及相关表

2、省域道路长度检索

二、Java后台实现

1、道路视图对象

2、Mapper空间检索查询

3、控制API实现

三、WebGIS界面实现

1、里程图例及初始化

2、各地市信息展示

四、成果展示

1、总体展示

2、分区域说明

五、总结


前言

        在当今数字化时代,地理信息系统(GIS)技术在各个领域都发挥着至关重要的作用。它不仅为城市规划、交通管理、环境保护等提供了强大的技术支持,也为公众获取地理信息提供了便捷的途径。湖南省作为中国中部地区的重要省份,拥有复杂的地理环境和庞大的交通网络。如何高效地管理和展示湖南省的道路长度信息,对于交通规划、物流运输以及公众出行都具有极其重要的意义。因此,我们开展了基于Java和Leaflet的湖南省道路长度WebGIS系统的构建与实践研究。

        湖南省地处中国中部,交通网络密集且复杂。随着经济的快速发展和城市化进程的加快,湖南省的道路建设不断推进,道路长度不断增加。然而,传统的道路信息管理方式存在诸多弊端,如数据更新不及时、信息展示不直观、查询效率低下等。为了克服这些问题,开发一个高效、直观且易于操作的WebGIS系统显得尤为重要。通过WebGIS技术,可以将湖南省的道路长度信息以地图的形式直观展示出来,方便用户查询和分析,提高交通管理效率,优化物流运输路线,同时也为公众出行提供便利。

        通过本实践,我们期望能够成功构建一个功能完善、性能稳定的湖南省道路长度WebGIS系统。通过将Java和Leaflet的结合为湖南省道路长度WebGIS系统的构建提供了一个良好的技术平台。文章首先介绍相关的基础空间数据,然后介绍Java后台应用程序构建,最后介绍Leaflet进行WebGIS构建。相信,通过本研究的努力,将为湖南省的交通发展和社会进步做出积极贡献。

一、基础空间数据简介

        在上一篇博客中,我们详细介绍了如何把OSM数据转成公路里程信息表:基于SpringBoot和PostGIS的城市道路里程信息转换-以湖南省OSM数据为例。为了实现地级市空间范围的展示,同时还要标注各地级市的行政驻地,这里我们将对涉及的基础空间数据进行简单介绍。让大家了解相关的空间及业务表信息。

1、涉及相关表

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

        我们需要在地图上展示湖南省各地市的行政区划范围,在各地市的政府驻地标注信息,信息展示当前行政区名称和道路里程。实现申明,这份数据来源于OSM,且时间时效性不是最新的,因此城市的道路里程与实际情况肯定有所不一样,如需权威数据,可从官网网站获取,代码和SQL查询逻辑是一样的,可供参考。

2、省域道路长度检索

        为了实现上述需求,这里我们采用空间数据库表关联业务表来进行关联查询实现,这三张表的查询SQL如下:

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之后,在Navicat客户端中可以看到以下结果:

        在上述的SQL中,我们默认使用湖南省(代号430000)作为查询条件,实际情况下需要动态替换为别的省份信息即可。

二、Java后台实现

        有了上述的基础空间数据基础知识后,接下来将深入介绍如何使用Java进行后台程序的实现。以Java经典MVC三层架构为架构讲解。

1、道路视图对象

        在之前的博客内容中,我们定义了城市道路信息实体。在本例中,根据业务需要定义地市的行政驻地位置,因此我们选择扩展一个子类,包装成一个视图对象。后台将根据业务获得查询结果后,将数据按视图对象进行返回。该视图对象核心代码如下:

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; @Data @ToString(callSuper=true)//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 String provinceCode; @TableField(exist = false,value= "province_name") private String provinceName; @TableField(exist = false) private String geomJson; private String lat; private String lon; }

2、Mapper空间检索查询

        在Java中,我们使用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) /** * - 根据省份code查询对应的地市路网长度 * @param provinceCode 省份code * @return */ 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>"; /** * - 根据指定省份各地市的道路里程信息 * @param provinceCode 省级行政区划代码 * @return */ @Select(GET_ROADMILEAGELIST_BY_PROVINCECODE) List<UrbanRoadMileageInfoVO> getRoadMileageList(@Param("provinceCode") String provinceCode); }

        通过以上代码就能实现地市道路长度的求解。

3、控制API实现

        业务层比较简单,主要是作为控制层和数据库层的桥梁,这里没有太多太复杂的业务,因此这里不进行赘述,控制层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); } //预留跳转echarts统计的报表 } 

三、WebGIS界面实现

        本节将重点介绍WebGIS界面如何实现,我们在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:"红色"} ];

        为了方便在展示的时候将不同的里程长度与颜色值进行对应,这里我们没有采用colormap的方式,这里我们使用简单的值域判断,颜色识别转换的方法如下:

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、各地市信息展示

        为了便于在界面上进行直观的展示,我们在数据库保存的长度单位为米,但是在页面上展示的比较多,因此需要对数据进行特别的处理,处理逻辑比较简单,就是进行除法计算,然后对小数点进行保留展示。核心代码如下:

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){ var legendData = new Array(); 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); }

        这个扩展函数可以根据我们的业务需要进行扩展,这里仅展示打印信息,我们可以打开新的展示页面。

四、成果展示

        经过空间数据表的查询检索实践,后台Java的接口实现以及WebGIS页面实践。下面我们来看看最终的一个效果,结合地图和数据来进行介绍。

1、总体展示

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

430100 长沙市 17753221.56551351 430400 衡阳市 14434666.394707818 430600 岳阳市 14222249.927994445 430900 益阳市 13073704.1462314 430700 常德市 11931665.375579692 431100 永州市 10818152.494340602 431000 郴州市 10666092.158735342 431200 怀化市 10115713.30456733 430500 邵阳市 9892107.19298748 430200 株洲市 7374936.096146714 433100 湘西土家族苗族自治州 6795835.819083372 431300 娄底市 5727464.899432551 430300 湘潭市 4861742.381742725 430800 张家界市 3836270.2965029962

        通过表格可以很明显的看到,省会长沙市、衡阳市、岳阳市位列前三。从空间聚集来看,基本是分布在想东部和东南部。

2、分区域说明

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

        湘西北的道路里程确实比较弱,可能与山区的地质环境有关系,不太适合修路。要致富,先修路,可见把交通基础条件打造好,也是十分必要的。

        南部地区除衡阳外,邵阳、永州、郴州等地市的道路里程是排名中等,还是有较大的发展空间,但随着如今人口的流出,未来的交通发展更需要好好的规划规划。

五、总结

        以上就是本文的主要内容,通过本实践构建一个功能完善、性能稳定的湖南省道路长度WebGIS展示系统。通过将Java和Leaflet的结合为湖南省道路长度WebGIS系统的构建提供了一个良好的技术平台。在未来的展望中,我们希望能够进一步完善系统的功能,如增加道路拥堵情况的实时监测与分析、与其他交通信息系统的集成等。同时,随着技术的不断发展,我们还将探索新的技术应用,如人工智能、大数据等,为湖南省的道路管理提供更加智能化、精准化的解决方案。行文仓促,定有不足之处,欢迎各位朋友在评论区批评指正,不胜感激。

Read more

FPGA小白学习日志一:LED的点亮

1.工程准备 首先建立一个名为led的工程文件夹,文件夹下包含了doc、quartus_prj、rtl、sim四个子文件夹: 那么我们来分析各个文件夹包含了什么: doc:该文件夹主要包含了文档资料、数据手册、Visio波形等,相当于档案库; quartus_prj:该文件夹主要包括了使用Quartus II软件新建的工程,相当于操作台; rtl:该文件夹主要放置生成硬件电路的代码,相当于原材料; Sim:该文件夹放置对生成硬件电路代码的仿真文件,相当于质检室;     这四个文件夹各自完成不同的分工,但是它们之间有什么联系呢?答案是:他们之间通过路径关联和文件引用,形成一个完美的FPGA开发闭环。quartus_prj作为工程中枢,向上访问doc读取说明,向下访问rtl获取硬件代码,向外访问sim获取仿真脚本;sim向上访问rtl在逻辑上验证硬件代码的正确性。 2.设计过程    无论我们使用FPGA做什么类型的项目时,我们都要参照一个具体的流程,这里就介绍我自己的开发流程: 1.看手册和原理图,搞清楚我们需要实现什么功能,就像做饭时我们需要看食谱,要知道自己吃什么。

Vivado完整license文件获取与配置指南

本文还有配套的精品资源,点击获取 简介:Vivado是由Xilinx开发的FPGA和SoC设计综合工具,支持Verilog、VHDL等硬件描述语言,提供高级综合、仿真、IP集成等功能。本资源包“Vivado_的license文件.zip”包含用于解锁Vivado完整功能的许可证文件。介绍了许可证服务器配置、.lic文件管理、浮动与固定许可证区别、激活流程、更新与诊断等核心内容。适用于FPGA开发者、嵌入式系统工程师及学习者,帮助其合法配置Vivado环境,提升开发效率和项目执行能力。 1. Vivado工具与FPGA开发环境概述 Xilinx Vivado设计套件是面向FPGA和SoC开发的集成化软件平台,广泛应用于通信、工业控制、人工智能、嵌入式视觉等多个高科技领域。其核心功能包括项目创建、综合、实现、仿真、调试及系统级集成,支持从设计输入到硬件验证的全流程开发。 Vivado不仅提供了图形化界面(GUI)便于初学者快速上手,还支持Tcl脚本自动化操作,满足高级用户的大规模工程管理需求。其模块化架构设计使得开发者可以灵活选择所需功能组件,如HLS(高层次综合)、IP In

区块链|WEB3:时间长河共识算法(Time River Consensus Algorithm)

区块链|WEB3:时间长河共识算法(Time River Consensus Algorithm)

区块链|WEB3:时间长河共识算法(Time River Consensus Algorithm)(原命名为时间证明公式算法(TCC)) 本共识算法以「时间长河」为核心设计理念,通过时间节点服务器按固定最小时间间隔打包区块,构建不可篡改的历史数据链,兼顾区块链的金融属性与信用属性,所有优化机制形成完整闭环,无核心逻辑漏洞,具体总结如下: 一、核心机制(闭环无漏洞) 1. 节点准入与初始化:候选时间节点需先完成全链质押,首个时间节点由所有质押节点投票选举产生,彻底杜绝系统指定带来的初始中心化问题,实现去中心化初始化。 2. 时间节点推导与防作弊:下一任时间节点通过共同随机数算法从上一区块推导(输入参数:上一区块哈希、时间戳、固定数据顺序),推导规则公开可验证;时间节点需对数据顺序签名,任一节点发现作弊(篡改签名、操控随机数等),该节点立即失去时间节点资格并扣除全部质押。质押的核心目的是防止节点为持续获取区块打包奖励作弊,作弊损失远大于收益,确保共同随机数推导百分百不可作弊。 3. 节点容错机制:每个时间节点均配置一组合规质押节点构成的左侧顺邻节点队列(队列长度可随全网节点规

机器人集群协同与人机协同:科技浪潮下的协同进化之路

在人工智能技术蓬勃发展的当下,机器人系统正经历着从单一功能向群体智能的深刻转变。这种转变不仅体现在机器人集群协同作业能力的提升,更催生了人机协同这一新型交互模式。两种协同形态的并行发展,正在重塑工业生产、社会服务乃至日常生活的基本范式。 机器人集群:从简单协作到群体智能 现代机器人集群系统已突破早期简单协作的局限,形成了具有自主决策能力的分布式网络。在物流仓储领域,数百台AGV(自动导引车)通过实时通信网络构成动态调度系统,能够根据订单变化自动调整运输路径。这种集群运作模式使仓库空间利用率提升40%以上,同时将分拣效率提升至人工操作的5倍。 农业领域的无人机集群作业展现了另一种协同范式。在东北平原的春耕时节,多架植保无人机通过厘米级定位系统保持编队飞行,根据地形数据自动调整喷洒参数。这种集群作业模式使单日作业面积突破2000亩,较单机作业效率提升3倍,同时农药使用量减少15%。 城市地下管网检测中,微型机器人集群展现出独特的优势。由不同功能模块组成的机器人团队可分工完成管道探测、障碍清除和修复作业。在深圳某区的排水系统改造中,12台微型机器人组成的集群仅用72小时就完成了传统