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

news2024/10/7 8:26:58

前言

        在构建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";
	@Autowired
	private IHnTownService hnTownService;
	@RequiresPermissions("extend:map:view")
    @GetMapping()
    public String map(){
        return prefix + "/map";
    }
	
    @RequiresPermissions("extend:map:list")
    @PostMapping("/list")
    @ResponseBody
    public TableDataInfo list(HnTown hnTown){
        startPage();
        List<HnTown> list = hnTownService.selectList(hnTown);
        return getDataTable(list);
    }
    
    @RequiresPermissions("extend:map:geom")
    @GetMapping("/geojson/{id}")
    @ResponseBody
    public 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.coloradmin.cn/o/508001.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java中的深拷贝和浅拷贝

目录 &#x1f34e;引出拷贝 &#x1f34e;浅拷贝 &#x1f34e;深拷贝 &#x1f34e;总结 引出拷贝 现在有一个学生类和书包类&#xff0c;在学生类中有引用类型的书包变量&#xff1a; class SchoolBag {private String brand; //书包的品牌private int size; //书…

使用Vue+vue-router+路由守卫实现路由鉴权功能实战

目录 一、本节介绍和上节回顾 1. 上节介绍 2. Vue + SpringBoot前后端分离项目实战的目录

探秘C语言:字符分类与转换函数,让你的程序更加精准和优美

本篇博客会讲解C语言ctype.h这个头文件中的2类好用的库函数&#xff0c;分别是字符分类函数和字符转换函数。 字符分类函数 字符分类函数&#xff0c;指的是判断一个字符是不是属于某个类别&#xff0c;如果属于这个类别&#xff0c;返回非0数&#xff1b;如果不属于这个类别…

MGA元宇宙创世大会 中国2022

MGA元宇宙创世大会 中国2022 主办方:MGA元宇宙创世联盟 协办方&#xff1a;增强现实核心技术产业联盟 元宇宙创世大会中国2022将包含两场主题峰会&#xff0c;一个是虚拟现实与增强现实峰会&#xff0c;一个是NFT与区块链峰会。涵盖元宇宙最重要的两大支撑技术&#xff08;VR/…

BrightID与Poap使用注册说明

把这两个app一起介绍&#xff0c;主要是因为这两个app是获取gitcoin及其他一些平台空投的前提条件&#xff0c;而且这两个app本身也会有一些诸如token、NFT之类的奖励。 BrightID BrightID是一个web3的身份证&#xff0c;用来证明当前操作的行为是你本人。由于验证流程的唯一…

chanmama响应数据解析

0x00目标url aHR0cHM6Ly93d3cuY2hhbm1hbWEuY29tL2F1dGhvckRldGFpbC85OTI0MjExODcxOC9wcm9tb3Rpb24 0x01接口分析 简单的get 但是返回数据被加密了 这里我们就来想想怎么解密这些数据。首先后端发来的数据是加密的&#xff0c;但是我们在前端看到的可不是加密后的数据。前端…

Rust + WASM 入门

一、参考资料 参考官方技术文档 https://rustwasm.github.io/ 二、安装脚手架 cargo-generate # cargo-generate 用于快速生成 WASM 项目的脚手架&#xff08;类似 create-react-app&#xff09; cargo install cargo-generate 三、下载安装 wasm-pack.exe 打包工具 双击安装…

大数据湖体系规划与建设方案(ppt可编辑)

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除。 业界主流公司对于数据湖的规划 — IBM IBM 公司提出的数据湖架构&#xff0c;包括六大关键部件&#xff1a;数据湖资源库按照数据特点进行原始格式的分类存储库企业IT交互统…

【新星计划-2023】详解交换机的工作原理、功能与作用

交换机有多个端口&#xff0c;每个端口都具有桥接功能&#xff0c;可以连接一个局域网或一台高性能服务器或工作站&#xff0c;实际上&#xff0c;交换机有时被称为多端口网桥。那么&#xff0c;对于交换机的工作原理这块你是否有了解呢&#xff1f;接下来我们就来为大家详细介…

Android程序员如何面临被优化(亲身经历与看法)

前言 相信大家都有过这样一个经历&#xff0c;就是在一家公司工作久了&#xff0c;能轻松的完成每天的工作内容&#xff0c;无论是在大公司还是其他小公司&#xff0c;这样的状态时间长了之后&#xff0c;公司领导就会认为你每天不做事&#xff0c;总暗中招一些工资低的人代替…

java基础知识梳理

虽然已经在实际工作中与java打交道5年之多&#xff0c;但是一直没系统地对java这门语言进行梳理和总结&#xff0c;掌握的知识也比较零散。恰好利用这段时间重新认识下java&#xff0c;并对一些常见的语法和知识点做个总结与回顾&#xff0c;一方面为了加深印象&#xff0c;方便…

OtterCTF---Memory Forensics内存取证(1-13)

一.OtterCTF 内存取证 CTF地址&#xff1a; OtterCTF 国产化一下&#xff1a; 注册一下 登录就可以 &#xff08;注&#xff1a;因为邮箱不验证&#xff0c;随意搞个就可以&#xff09;&#xff1a; 1 - What the password? 第一题&#xff1a; 国产化&#xff1a; 下载…

DEJA_VU3D - Cesium功能集 之 104-攻击箭头(标绘+编辑)

前言 编写这个专栏主要目的是对工作之中基于Cesium实现过的功能进行整合,有自己琢磨实现的,也有参考其他大神后整理实现的,初步算了算现在有差不多实现小140个左右的功能,后续也会不断的追加,所以暂时打算一周2-3更的样子来更新本专栏(每篇博文都会奉上完整demo的源代码,…

FreeRTOS:中断配置

目录 一、Cortex-M 中断1.1中断简介1.2中断管理简介1.3优先级分组定义1.4优先级设置1.5用于中断屏蔽的特殊寄存器1.5.1PRIMASK 和 FAULTMASK 寄存器1.5.2BASEPRI 寄存器 二、FreeRTOS 中断配置宏2.1configPRIO_BITS2.2configLIBRARY_LOWEST_INTERRUPT_PRIORITY2.3configKERNEL_…

Class 01 - R语言介绍及部分功能演示

Class 01 - R语言介绍及部分功能演示 教程介绍功能演示数据导入 R语言&Python对比在数据科学中的不同优势 教程介绍 在本章中&#xff0c;将学习到编程的新概念&#xff0c;以及如何使用R语言来做数据的分析。 我们先谈谈计算机编程吧&#xff0c;计算机编程是指向计算机…

计算机网络学习笔记-数据链路层

目录 概述 三个功能 封装成帧 透明传输 差错检测 点对点协议PPP 三个组成部分 帧格式 透明传输 工作状态 使用广播信道的局域网 以太网 两个主要标准 高速以太网&#xff08;速率 > 100Mb/s&#xff09; MAC层&#xff1a; 集线器 交换机 自学习和转发帧 生…

tkinter+re实现百度网盘分享链接自动跳转

目录 一、背景 二、需求分析 三、实现 三、打包 四、测试 一、背景 相信大家使用百度网盘的时候都会去一些链接网站吧&#xff0c;比如下面这个网站 这个网站具体实现的功能是&#xff1a;管理员上传百度网盘链接&#xff0c;用户点击复制链接按钮可以将百度网盘的分享链…

线性结构-数组

数组(Array)是最简单的数据结构&#xff0c;是由有限个相同类型的变量或对象组成的有序集合。因为数组中各元素之间是按顺序线性排列的&#xff0c;所以数组是一种线性数据结构。 数组是一类物理空间和逻辑形式都连续的线性数据结构&#xff1a; 数组用唯一的名字标识&#xf…

输出数字的位数(C语言)以及逆序输出

#include <stdio.h>int main() {int N;int i 0;scanf("%d",&N);int a[5];int j;while(N > 0){a[i] N%10;i;N N/10;}printf("这个数字是%d位数\n",i); for(j 0;j < i;j){printf("%d",a[j]);} } 原题如下&#xff1a;

并发编程08:原子操作类

文章目录 8.1 基本类型原子类8.1.1 常用API简介8.1.2 Case 8.2 数组类型原子类8.2.1 常用API简介8.2.2 Case 8.3 引用类型原子类8.4 对象的属性修改原子类8.4.1 使用目的8.4.2 使用要求8.4.3 Case 8.5 原子操作增强类原理深度解析8.5.1 常用API8.5.2 面试题8.5.3 点赞计数器8.5…