基于Leaflet的乡镇行政区划在WebGIS中的可视化工具实践

news/2024/11/24 12:16:13/

前言

        在构建WebGIS的应用系统中,通常会遇到以下的建设需求。功能点如下:

  • 实现影像地图的展示,可以放大、缩小和浏览地图。
  • 地图的拖拽范围需要控制在合理的经纬度范围内。
  • 在影像地图侧边实现某乡镇级行政区的信息展示,包括名称,级别;以及支持在影像地图上进行矢量数据的展示,同时在显示的边界内展示乡镇信息。

        通过以上的信息分析,大致的技术点和关键技术其实在个人博客中已经有一定的涉及。比如关于Leafletjs的二维WebGIS系统开发、如何在LeafLet上叠加影像地图、Leaflet如何限制地图的拖动范围、空间矢量数据如何导入PostGIS数据库、MybatisPlus中操作Geometry字段信息、LeafLet中展示GeoJSON数据。

        系列文章地址如下表所示:

序号博客地址
1gis信息可视化之一Leaflet组件介绍
2layerGroup在LeafLet中的实战
3postgis空间数据导入及可视化
4基于Mybatis-Plus实现Geometry字段在PostGis空间数据库中的使用
5基于Leaflet的leaflet-sidebar侧边栏组件集成
6Leaflet中如何限制地图的拖动范围
7玩转Leaflet-带你吃透Control知识
8LeafLet实战-扩展工具栏指南
           本文将采用Leafletjs地图开发组件,围绕GeoJSON的可视化展示,以湖南省乡镇行政区划数据的查询,空间定位作为实践案例,完整讲述一个基础的WebGIS小功能,最后形成一个GeoJSON的可视化工具。

一、WebGIS可视化工具系统分析及设计

1、需求分析

        在上面的前言部分,简单的对功能需求进行了分析。其实功能很简单,是最常见的WebGIS功能点。主要包括影像地图的展示、地图的放大和缩小,平移;影像底图图层和标签图层的叠加展示、湖南省乡镇行政区划数据的展示和空间定位,对GeoJSON数据进行地图定位,叠加自定义Marker对象,显示乡镇名称和自定义样式等功能。

 2、业务架构

         业务架构比较简单,针对简单的业务需求设计简单的架构。主要包含以下三层:

        数据层:数据层主要包含业务数据库,用于存储用户信息、权限数据等;矢量数据库主要包含湖南省乡镇行政区划矢量信息;影像底图就是瓦片和标签瓦片信息等;

        服务层:服务层在数据层的基础上,主要提供相应的数据查询服务能力,包含空间数据查询服务、空间分析服务、用户管理服务、地图展示服务;通过服务层,将相关底层的调用封装起来,供上层的应用层进行调用。

        应用层:应用层主要面向具体的使用用户。将直接调用服务层的服务组装成用户需要的功能。主要包含行政区划展示、空间数据定位展示、项目管理及用户管理功能。

3、技术架构

         同样基于简单的业务需求,以及简单的应用架构,这里采用简单的单体架构模式。在此基础上可以进行架构的迭代和升级,保证业务的扩展性。

        前端技术栈:这里的前端依然采用最简单的es5架构,使用Jquery+Html5+css等经典原生开发模式。主要采用的技术如下:

序号技术点说明
1Leaflet.jsWebGIS 地图展示组件
2leaflet-sidebar.js基于Leaflet的侧边栏展示组件
3thymeleaf前端模板引擎
4bootstrap前端bootstrap组件库
5bootstrap-table基于bootstrap的表格组件库
6jqueryDom操作和Ajax的操作库

         后端技术栈:后端是经过改造的Ruoyi单体框架,主要改造点是完全的兼容PostgreSQL,很多原来MySQL的语法在迁移到PG后有很多的不兼容问题,在此基础上进行了升级和改造。

序号技术点说明
1Springboot基础技术框架
2Mybatis-plus操作数据库的ORM框架
3flywaydb自动管理数据组件
4postgis-jdbcpostgis数据库支持驱动
5spring-boot-adminspringboot监控组件
6shiroshiro开源安全认证框架

         空间数据库:空间数据库中不仅仅包含空间数据的存储,还有普通业务数据的存储。这里完全采用PostgreSQL和扩展PostGIS进行空间数据存储和分析需求。

二、WebGIS可视化工具空间数据处理

1、基础数据准备

        在进行系统研发之前,需要提前准备湖南省的乡镇行政区划shp数据。这里使用的实例数据从互联网下载而成,仅供学习使用。

 2、shp数据导入空间数据库

        由于系统要实现将湖南省的所有乡镇信息全部导入到空间数据库PostGIS中,这里我们采用PostGIS自带的客户端工具进行导入的方式。具体操作方式略,有兴趣的朋友可以看之前写得博文或者自行查询搜索引擎查阅相关资料。

        将数据导入空间数据库后,会在数据库中形成一张biz_hn_town的表,其中,biz_hn_town是在导入的时候自动把shp的文件名当成了表名。

可以查看一下这张表的物理结构,如下sql所示:

-- ----------------------------
-- Table structure for biz_hn_town
-- ----------------------------
DROP TABLE IF EXISTS "public"."biz_hn_town";
CREATE TABLE "public"."biz_hn_town" ("gid" int4 NOT NULL DEFAULT nextval('biz_hn_town_gid_seq'::regclass),"gml_id" varchar(80) COLLATE "pg_catalog"."default","name" varchar(80) COLLATE "pg_catalog"."default","layer" varchar(80) COLLATE "pg_catalog"."default","code" varchar(80) COLLATE "pg_catalog"."default","grade" int4,"geom" "public"."geometry"
);-- ----------------------------
-- Indexes structure for table biz_hn_town
-- ----------------------------
CREATE INDEX "biz_hn_town_geom_idx" ON "public"."biz_hn_town" USING gist ("geom" "public"."gist_geometry_ops_2d"
);-- ----------------------------
-- Primary Key structure for table biz_hn_town
-- ----------------------------
ALTER TABLE "public"."biz_hn_town" ADD CONSTRAINT "biz_hn_town_pkey" PRIMARY KEY ("gid");

3、乡镇行政区划数据查询 

通过查询语句,我们可以看到

select * from biz_hn_town;

三、WebGIS可视化工具后端开发实现

1、相关类图设计

 2、控制器类关键代码

package com.yelang.project.extend.map.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.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
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.framework.web.page.TableDataInfo;
import com.yelang.project.extend.map.domain.HnTown;
import com.yelang.project.extend.map.service.IHnTownService;/***地图Controller* @author yelangking* @date 2023-5-7*/
@Controller
@RequestMapping("/extend/map")
public class MapController extends BaseController{private String prefix = "extend/map";@Autowiredprivate IHnTownService hnTownService;@RequiresPermissions("extend:map:view")@GetMapping()public String map(){return prefix + "/map";}@RequiresPermissions("extend:map:list")@PostMapping("/list")@ResponseBodypublic TableDataInfo list(HnTown hnTown){startPage();List<HnTown> list = hnTownService.selectList(hnTown);return getDataTable(list);}@RequiresPermissions("extend:map:geom")@GetMapping("/geojson/{id}")@ResponseBodypublic AjaxResult editSave(@PathVariable("id") Long id){HnTown hnTown = hnTownService.findGeoJsonById(id, null);return AjaxResult.success().put("data", hnTown.getGeomJson());}
}

 3、Mapper关键代码

        mapper这里采用了自定义的sql脚本,并有mybatis执行引擎来进行执行。将空间字段geom采用st_asgeojson方法转换成geojson字符串,并由前端leaflet.js进行渲染。

package com.yelang.project.extend.map.mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yelang.project.extend.map.domain.HnTown;public interface HnTownMapper extends BaseMapper<HnTown>{static final String FIND_GEOJSON_SQL="<script>"+ "select st_asgeojson(geom) as geomJson from biz_hn_town "+ "where gid = #{gid} "+ "<if test='null != name'>and name like concat('%', #{name}, '%')</if>"+ "</script>";@Select(FIND_GEOJSON_SQL)HnTown findGeoJsonById(@Param("gid")Long gid,@Param("name")String name);
}
package com.yelang.project.extend.map.domain;import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.yelang.framework.handler.PgGeometryTypeHandler;import lombok.*;@TableName(value ="biz_hn_town",autoResultMap = true)
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class HnTown {@TableId(value="gid")private Long gId;@TableField(value="gml_id")private String gmlId;private String name;private String layer;private String code;private Integer grade;@TableField(typeHandler = PgGeometryTypeHandler.class)private String geom;@TableField(exist=false)private String geomJson;
}

四、WebGIS可视化工具前端开发实现

1、地图定义

$("#mapid").height($(window).height());//动态设置高度L.CRS.CustomEPSG4326 = L.extend({}, L.CRS.Earth, {code: 'EPSG:4326',projection: L.Projection.LonLat,transformation: new L.Transformation(1 / 180, 1, -1 / 180, 0.5),scale: function (zoom) {return 256 * Math.pow(2, zoom - 1);}
});//限制地图的拖动范围是正负90到正负180,这样才合理。
var maxBounds = L.latLngBounds(L.latLng(-90, -180), L.latLng(90, 180)); //构建视图限制范围 第一个参数是左上角经纬度 第二个参数是右下点经纬度
var mymap = L.map('mapid',{crs:L.CRS.CustomEPSG4326,maxBounds:maxBounds,attributionControl:false}).setView([29.052934, 104.0625], 5);
var showLayerGroup =L.featureGroup().addTo(mymap);L.tileLayer('http://localhost:8086/data/basemap_nowater/1_10_tms/{z}/{x}/{y}.jpg', {minZoom:1,
maxZoom: 16,
id: 'baseMap-nowater',
tileSize: 256,
zoomOffset: -1
}).addTo(mymap);//标签
L.tileLayer('http://localhost:8086/data/basemap_nowater/1-10label/{z}/{x}/{y}.png', {maxZoom: 10,minZoom:1,id: 'mapbox/label',tileSize: 256,zoomOffset: -1
}).addTo(mymap);var popup = L.popup();function onMapClick(e) {popup.setLatLng(e.latlng).setContent("当前坐标为:" + e.latlng.toString()).openOn(mymap);
}mymap.on('click', onMapClick);

 2、初始化侧边栏

function initSidebar(){//初始化sidebar页面var sidebar = L.control.sidebar('sidebar', {position: 'right'}).addTo(mymap);//默认sidebar打开,并展示一个tab页sidebar.open();$("#xz_info").addClass("active");$("#home").addClass("active");//初始化行政区划表格initHnTownTable();}function initHnTownTable(){var options = {url: prefix + "/list",createUrl: prefix + "/add",updateUrl: prefix + "/edit/{id}",modalName: "乡镇行政区划",columns: [{checkbox: true},,{title: '操作',align: 'center',formatter: function(value, row, index) {var actions = [];actions.push('<a class="btn btn-success btn-xs ' + removeFlag + '" href="javascript:void(0)" onclick="previewTown(\'' + row.gid + '\',\''+row.name+'\')"><i class="fa fa-paper-plane"></i>定位</a>');return actions.join('');}}]};$.table.init(options);
}

 3、空间预览定位

function previewTown(gid,name){var myStyle = {color:"red",weight:8,"opacity":0.6};$.ajax({  type:"get",  url:prefix + "/geojson/" + gid,  data:{},  dataType:"json",  cache:false,processData:false,success:function(result){if(result.code == web_status.SUCCESS){var areaLayer = L.geoJSON(JSON.parse(result.data),{style:myStyle}).addTo(mymap);var content = "<div>名称:"+name+"</div>" +"<div>时间:2020-05-04</div>" +"<div>面积:8.52 K㎡</div>";var myIcon = L.divIcon({html: "<div style='color:#fff;'>"+content+"</div>",className: 'my-div-icon',iconSize: 100});showLayerGroup.clearLayers();showLayerGroup.addLayer(areaLayer);mymap.fitBounds(areaLayer.getBounds());//中心点位L.marker(areaLayer.getBounds().getCenter(), { icon: myIcon}).addTo(showLayerGroup);}},error:function(){$.modal.alertWarning("获取空间信息失败");}});}

五、WebGIS可视化工具效果及使用体会

1、行政区划列表展示

        在实际工 具的展示中可以看到,底图实现了自定义影像地图的加载,以及侧边栏表格的数据展示。在表格中展示了乡镇名称、级别以及定位操作按钮。点击定位按钮可以进行乡镇区划定位。

 2、行政区划搜索及定位

         点击列表中的定位按钮,可以实现当前乡镇空间的自动定位,并在地图中进行高亮展示定位。同时在列表中实现按照乡镇名字进行检索的功能,具体如下图所示(以中寨镇为例):

 3、使用体会

        通过这个简单的程序,集中演示了WebGIS乡镇行政区划数据可视化工具的基本功能,重点围绕WebGIS的相关技术,讲解如何开发可视化工具。基于该工具,基本满足我们的预期业务需求,实现了乡镇行政区划数据的展示及空间定位。对于掌握WebGIS的开发和实现空间数据可视化提供了良好的基础知识讲解。

总结

        以上就是本文的主要内容,本文将重点讲解如何采用Leafletjs地图开发组件,围绕GeoJSON的可视化展示,以湖南省乡镇行政区划数据的查询,空间定位作为实践案例,完整讲述一个基础的WebGIS小功能,最后形成一个GeoJSON的可视化工具。希望这个可视化工具实现的技术路径可以作为您开发WebGIS程序的一些参考。行文仓促,如有问题,欢迎批评指针,更加环境在文章的最后留下您的宝贵意见和评论。


http://www.ppmy.cn/news/97507.html

相关文章

关于GeoServer发布服务时数据源设置的避坑指南

题外话 时光任然&#xff0c;一年一度的五一劳动节已然来到。作为疫情之后迎来的第一个五一&#xff0c;不知道各位小伙伴们怎么度过这个劳动节呢&#xff1f;是决定去另一个城市&#xff0c;观察体验一下不一样的风景&#xff0c;或者去旅游&#xff0c;给自己放假。昨天被123…

跨域问题及解决方案

跨域问题是指在前端Web开发中&#xff0c;不同源的网页之间无法相互访问或通信的问题。产生跨域问题的原因是由于浏览器的同源策略&#xff08;Same Origin Policy&#xff09;所导致的。 同源策略是浏览器的一种安全机制&#xff0c;限制了来自不同源&#xff08;域名、端口、…

开启php8的JIT及时编译,超级详细 照抄即可

JIT时php8的重要功能之一&#xff0c;可以极大的提高性能&#xff1b; JIT编译器集成在了Opcache插件中&#xff0c;仅在启动Opcache插件才有效 Opcache将 PHP 脚本编译后的字节码存储到内存中&#xff0c;以避免每次执行脚本时重新解析和编译&#xff0c;从而提高 PHP 应用程…

JavaScript表单事件(下篇)

目录 八、keydown: 当用户按下键盘上的任意键时触发。 九、keyup: 当用户释放键盘上的键时触发。 十、keypress: 当用户按下键盘上的字符键时触发。 十一、focusin: 当表单元素或其子元素获得焦点时触发。 十二、focusout: 当表单元素或其子元素失去焦点时触发。 十三、c…

(转载)基于遗传算法的多目标优化算法(matlab实现)

1 理论基础 1.1 多目标优化及Pareto最优解 多目标优化问题可以描述如下&#xff1a; 其中&#xff0c;f(x)为待优化的目标函数&#xff1b;x为待优化的变量&#xff1b;Ib和ub分别为变量x的下限和上限约束&#xff1b;Aeq*xbeq为变量x的线性等式约束&#xff1b;A*x≤b为变…

小端、大端字节序

小端字节序&#xff1a;将低序字节存储在内存低地址 大端字节序&#xff1a;将高序字节存储在内存低地址 16进制可以直观反映内存存储数据的情况 …

RabbitMQ基础

文章目录 前言一、&#x1f367; MQ 相关的概念1、什么是MQ2、为什么要用MQ流量消峰应用解耦异步处理 二、&#x1f9c3; RabbitMQ1、RabbitMQ 的概念2、四大核心概念生产者交换机队列消费者 3、RMQ的六大核心模式4、工作原理与各个名词介绍5、安装 RMQ 三、&#x1f964; Hell…

5.开源非对称加密算法RSA实现

5.开源非对称加密算法RSA实现 前期内容导读&#xff1a; 开源加解密RSA/AES/SHA1/PGP/SM2/SM3/SM4介绍开源AES/SM4/3DES对称加密算法介绍及其实现开源AES/SM4/3DES对称加密算法的验证实现开源非对称加密算法RSA/SM2实现及其应用 1. 开源组件 非对称秘钥加密介绍 加密组件引入方…