基于SpringBoot和Leaflet的行政区划地图掩膜效果实战

news2025/1/6 19:08:51

目录

前言

一、掩膜小知识

1、GIS掩膜的实现原理

2、图层掩膜流程 

二、使用插件

1、leaflet-mask介绍

2、核心代码解释 

三、完整实例实现

1、后台逻辑实现

2、省级行政区划查询实现

3、行政区划定位及掩膜实现 

4、成果展示

总结


前言

        在之前的博客提过按空间矢量范围下载遥感,有兴趣的同学可以参考已下的博文地址:基于QGIS的研究区域遥感影像裁切下载方法-以岳麓区为例。在这篇博客中采用的是Qgis软件,这是 一款桌面端的GIS软件。在这里,首先简单解释一下gis中掩膜的相关概念。掩膜在制图中是一种遮盖工具,用于处理要素在显示上的冲突。掩膜可以理解为一种看不见的面要素,它遮挡住了不需要显示的图形。用这个工具可以将影像按面要素周长裁剪。

        存在两种类型的掩膜策略:一是:图层掩膜 - 是指在地图或场景中,某一要素图层或掩膜图层可以掩盖另一图层中的任何重叠要素。二是要素级掩膜 - 是指按照两图层间关系类所指定的方式对各相关要素进行的掩膜。无论采用哪种掩膜方式,掩膜要素的几何均会掩盖被掩膜要素的符号系统。 即使掩膜要素的符号化形状与几何不同,(例如,如果应用了符号缓冲),要素几何的形状会进行掩膜。 然而,被掩膜要素的符号系统会受到影响。 被掩膜要素看起来可能存在孔洞,但它们只是可见符号中的孔洞。 要素几何并未更改。

        本文讲解的是一种图层级的掩膜,即使用行政区划图层来进行掩膜。使用场景为,用户只需要在地图页面中展示目标行政区划内的影像信息,对于行政边界外的影像,则不展示。这就是WebGIS中掩膜的一种表达方式。本文重点讲解在Webgis中如何进行行政区划掩膜实现,通过代码实战的方式对功能进行详细的实现,采用网友编写一个掩膜组件,不仅避免了自己的区域绘制太小,也避免了不同行政区划切换时,有部分缝隙的问题。如果您目前也有WebGIS掩膜可视化需求,不妨来看看博客。

一、掩膜小知识

        在讲解地图之前,如果了解前端的朋友一定知道,在HTML5的应用中,可能会存在两个DIV,可能由于其内容和位置的设置存在空间重叠,有一部分区域会被另一个DIV进行遮盖。这种效果就是掩膜。(以上不是官网的定义,只是翻译成了大白话,易于大家理解)。通过上面的解释可以看出,在这个场景中涉及的图层起码有两个,而且存在空间折叠的关系。而实现效果就是通过叠加,使用遮罩这种方式来进行。

1、GIS掩膜的实现原理

        与上述Html的实现原始一致的,在这个场景当中。首先我们会使用栅格底图(一般是遥感影像)。然后在展示行政区划时,自动将行政区划外的地图遮住。比如在展示湖南省的行政区划时,只展示湖南省区域内的影像,对于湖南省外的区域则不展示,以空白的方式展现出来。先来看一下实际的效果。

2、图层掩膜流程 

        对于图层掩膜的流程,使用流程图描述如下:

         第一步是在地图上加载原始的遥感影像,可以是WMS或者XYZ瓦片。第二步是输入要叠加的升级行政区划范围,这里一般是采用GeoJSON的方式进行获取。第三步是从GeoJSON中解析出空间面信息,构建出遮罩范围,通过绘制遮罩面,设置遮罩面的透明度。同时将无需遮罩的范围留空,这样就能实现空间掩膜的效果。

二、使用插件

        在实现这个需求时,可以完全不用外部的插件,通过Leaflet自己构建Polygon面来实现遮罩掩膜的效果即可。但是使用自己绘制的面时,进行地图缩放时,会有一些不顺畅的原因。因此在开源社区找了一款开源的组件。通过组件来实现地图遮罩,方便又美观。

1、leaflet-mask介绍

        leaflet-mask是一个简单的地图遮罩层控件,继承自L.polygon。我们很多时候希望只显示某块区域内的内容,隐藏或者模糊区域外内容。此插件可以实现传入polygon的latlngs创建对应的遮罩图层。其gitee地址是leaflet-mask。大家可以将代码下载到本地,然后运行其官方的实例即可。

2、核心代码解释 

        对于这个插件来说,核心代码其实非常少,也是很容易看懂的。在下载的源代码中,可以直接打开来看,在src目录下有leaflet-Mask.js。使用文本编辑器或者javascript脚本编辑器打开这个脚本。

/**
 * 遮罩
 */
L.Mask = L.Polygon.extend({
    options: {
        isRect: true,    //是否为矩形遮罩,如果为是,则使用northWest,northEast,sourthEast,sourthWest创建矩形遮罩层外边界,如果为false,则使用传入的坐标数组作为遮罩层外边界
        northWest: { lat: 180.0, lng: -180.0 },  //遮罩层西北角坐标
        northEast: { lat: 180.0, lng: 180.0 },  //遮罩层东北角坐标
        sourthEast: { lat: -180.0, lng: 180.0 }, //遮罩层东南角
        sourthWest: { lat: -180.0, lng: -180.0 }, //遮罩层西南角
        maskBoundary: null,    //遮罩层边界坐标
        showPolygons: []    //显示区域
    },
    initialize(options) {
        L.Util.setOptions(this, options);
        let latlngs = this.getMaskLatLngs();
        this._setLatLngs(latlngs);
    },
    /**
     * 画遮蔽层的相关方法
            *思路: 创建一个矩形作为遮罩层,构造函数传入的坐标作为内环
     * @see https://blog.csdn.net/mapmonster/article/details/104455516
     * 
     * @param {*} latlngs 
     */
    getMaskLatLngs() {
        let latlngs = [];
        //是矩形遮罩,则使用northWest,northEast,sourthEast,sourthWest创建矩形遮罩层外边界
        if (this.options.isRect) {
            this.options.maskBoundary = [];
            this.options.maskBoundary.push(this.options.northWest);
            this.options.maskBoundary.push(this.options.sourthWest);
            this.options.maskBoundary.push(this.options.sourthEast);
            this.options.maskBoundary.push(this.options.northEast);
            this.options.maskBoundary.push(this.options.northWest);
        }
        latlngs.push(this.options.maskBoundary);
        for (let i = 0; i < this.options.showPolygons.length; i++) {
            latlngs = latlngs.concat(this.options.showPolygons[i].getLatLngs());
        }
        return latlngs;
    }
});

/**
 * 合乎leaflet语法
 * @param {*} options 
 * @returns 
 */
L.mask = function (latlngs, options) {
    return new L.Mask(latlngs, options);
};

        所有代码加起来,包括注释仅仅有52行,而且采用符合Leaflet的语法方式进行展示。可以看到这里的遮罩层是一个扩展自Polygon类的子类。这里设置了其默认的范围,即四个边界点。

三、完整实例实现

        本节重点将对实例进行完整的介绍,首先我们将遥感影像完整的展示出来。同时在界面右边展示行政区划信息,支持按省级行政区划名称进行检索。点击所在省份,将查询后台的接口返回GeoJSON格式的行政区划边界数据,然后调用leaflet-mask的掩膜对象,实现行政区域的遮罩。

1、后台逻辑实现

        这里介绍省级行政区划列表和查询省级行政区划GeoJson边界信息接口。包括控制层代码和数据库访问层代码。核心代码如下:

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.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.earthquake.domain.EarthQuakeProvinceStatVO;
import com.yelang.project.extend.earthquake.domain.EarthquakeInfo;
import com.yelang.project.extend.earthquake.domain.Province;
import com.yelang.project.extend.earthquake.service.IEarthquakeInfoService;
import com.yelang.project.extend.earthquake.service.IProvinceService;

@Controller
@RequestMapping("/eq/province")
public class ProvinceController extends BaseController{

	private String prefix = "earthquake/province";
	
	@Autowired
	private IProvinceService provinceService;
	
	@Autowired
	private IEarthquakeInfoService earthQuakeInfoService;
	
	@RequiresPermissions("eq:province:view")
    @GetMapping()
    public String map(){
        return prefix + "/map";
    }
	
    @RequiresPermissions("eq:province:list")
    @PostMapping("/list")
    @ResponseBody
    public TableDataInfo list(Province province){
        startPage();
        List<Province> list = provinceService.selectList(province);
        return getDataTable(list);
    }
    
    @RequiresPermissions("eq:province:geom")
    @GetMapping("/geojson/{id}")
    @ResponseBody
    public AjaxResult getGeojson(@PathVariable("id") Long id){
    	Province province = provinceService.findGeoJsonById(id, null);
        return AjaxResult.success().put("data", province.getGeomJson());
    }
    
}

        根据省份id查询省份行政区划边界GeoJSON的数据库访问层核心代码如下:

package com.yelang.project.extend.earthquake.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.earthquake.domain.Province;

/**
 * 省级行政区划数据接口
 * @author wuzuhu
 *
 */
public interface ProvinceMapper extends BaseMapper<Province>{

	static final String FIND_GEOJSON_SQL="<script>"
			+ "select st_asgeojson(geom) as geomJson from biz_province "
			+ "where id = #{id} "
			+ "<if test='null != name'>and name like concat('%', #{name}, '%')</if>"
			+ "</script>";
	@Select(FIND_GEOJSON_SQL)
	Province findGeoJsonById(@Param("id")Long id,@Param("name")String name);
	
}

2、省级行政区划查询实现

        在地图上我们需要首先展示行政区划列表,这里采用sidebar的组件进行展示。同时在列表中支持按照省级行政区划名称进行模糊查询。

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
	       },
	       {
	            field: 'id',
	            title: '',
	            visible: false
	       },
	       {
	           field: 'name',
	            title: '省份'
	       },
	       {
	          field: 'type',
	          title: '类别'
	       },
	       {
	           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.id + '\',\''+row.name+'\')"><i class="fa fa-paper-plane"></i>定位</a>');
	                 return actions.join('');
	            }
	          }]
	       };
	   $.table.init(options);
}

3、行政区划定位及掩膜实现 

        点击行政区划列表操作栏中的“定位”按钮,可以实现行政区划定位,以及进行区域掩膜。点击定位的时候,会通过后台的查询接口获取当前点击的行政区划的GeoJSON数据信息。关键方法如下:

function previewTown(gid,name){
	var myStyle = {color:"white",weight:5,"opacity":1};
	$.ajax({  
		  type:"get",  
		  url:prefix + "/geojson/" + gid,  
		  data:{},  
		  dataType:"json",  
		  cache:false,
		  processData:false,
		  success:function(result){
		        if(result.code == web_status.SUCCESS){
		        	var geojson = JSON.parse(result.data);
		        	var areaLayer = L.geoJSON(geojson,{style:myStyle}).addTo(mymap);
		        	showLayerGroup.clearLayers();
		        	showLayerGroup.addLayer(areaLayer);
		        	mymap.setView(areaLayer.getBounds().getCenter(),8);
		        	showMask(geojson);
		        }
		   },
		  error:function(){
		      $.modal.alertWarning("获取空间信息失败");
		  }
	});
}

        通过获取GeoJson的接口获取行政区划的空间位置之后,再调用leaflet-mask的构造方法将遮罩面渲染出来。首先来看一下获取的行政区划GeoJSON数据信息:

function showMask(geojson){
	 var showPolygons = [];
     var pArray = [];
     for (var i = 0; i < geojson.coordinates.length; i++) {
    	   var points = [];
    	   $.each(geojson.coordinates[i],function(k,v){
    	          points.push({lat:v[1],lng:v[0]});
    	   });
    	   //将闭合区域加到遮蔽层上,每次添加完后要再加一次西北角作为下次添加的起点和最后一次的终点
    	    pArray = pArray.concat(points);
    	    pArray.push(pArray[0]);
    	 }
    	 var polygon = L.polygon(pArray, { color: 'green' });
    	 showPolygons.push(polygon);
    	    
	     var mask = L.mask({
	         showPolygons: showPolygons,
	         color: '#C0C0C0',
	         fillOpacity: 1,
	         renderer: L.canvas({ padding: 1 })  //解决遮罩层拖拽与缩放显示不全的Bug
	     });
	     showLayerGroup.addLayer(mask);
	}

        通过以上的代码即可完成按照行政区划进行掩膜可视化的效果。

4、成果展示

        最后我们来看一下最终生成的省级行政区划掩膜可视化效果。通过点击分析按钮,进行当前省份信息的掩膜可视化。闲言少叙,上图为证。

天津市掩膜效果图 

湖北省掩膜效果图

云南省掩膜效果图 

 贵州省掩膜效果图 

总结

        以上就是本文的主要内容,本文讲解的是一种图层级的掩膜,即使用行政区划图层来进行掩膜。使用场景为,用户只需要在地图页面中展示目标行政区划内的影像信息,对于行政边界外的影像,则不展示。这就是WebGIS中掩膜的一种表达方式。行文仓促,难免有误,欢迎各位专家朋友批评指正,不甚感谢。

本文写作过程中参考以下博客,站在巨人的肩膀上,才能看得更高。

1、Leaflet实现地图按照行政区划遮罩。

2、Leaflet添加掩膜。

3、leaflet实现地图遮罩。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1547003.html

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

相关文章

喜报!数维杯数学建模成功入围安徽工业大学学科竞赛推荐名单

喜报&#xff01;数维杯数学建模挑战赛成功入围安徽工业大学学科竞赛参赛目录。 安徽工业大学创新创业学院发布了《2023年度安徽工业大学大学生学科竞赛参赛目录》。 其中&#xff0c;数维杯大学生数学建模挑战赛成功入围竞赛参赛目录&#xff0c;感谢全国各高校对数维杯的高…

CUMT linux操作系统课程设计 任务2

先说题目: 调试Linux内核的启动过程&#xff0c;并在Linux 0.11内核进入保护模式之前添加提示信息 //这里吐槽一下&#xff0c;学校发的文档让你用断点去查看运行根本无法操作&#xff0c;报错如下&#xff1a; 所以别管这个报错&#xff0c;先跟着我来 第一题&#xff0c;调试…

Filter,Listener

1&#xff0c;Filter 概念:Filter表示过滤器&#xff0c;是JavaWeb三大组件(Servlet、Filter、Listener)之一 过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些特殊的功能 过滤器一般完成一些通用的操作&#xff0c;比如:权限控制、统一编码处理、敏感字符处理等等…

关于ChatGPT辅助论文写作的重大风险预警

ChatGPT已经发布一年多了&#xff0c;我说说使用它之后最大的变化。 我在工作上变得更懒了&#xff01; 现在与工作有关的&#xff0c;做啥都想着先用ChatGPT来搞。 比如&#xff0c;拍领导马屁&#xff1a; 领导说&#xff0c;“996是福报&#xff0c;混日子不是兄弟。” 我想…

javaWeb校园二手平台项目

一、系统分析 1.1开发背景 随着全世界互联网技术的不断发展&#xff0c;各种基于互联网技术的网络应用不断涌现,网络技术正在不断的深入人们的生活。人们从Internet上获取信息、享受生活、交流感情、网上工作等。Internet正在迅速改变着人们的生活方式。 经过我国改革开放多年…

Docker数据卷与网络模式

华子目录 数据卷注意数据卷操作查看镜像&#xff0c;容器&#xff0c;数据卷所占空间 Docker的网络模式查看指定容器的网络模式bridge模式none模式host模式container模式 数据卷 数据卷是一个可供一个或多个容器使用的特殊目录&#xff0c;它绕过UFS&#xff0c;可以提供很多有…

【Qt】使用Qt实现Web服务器(八):SSE ( Server-sent Events )

1、简述 SSE ( Server-sent Events )是 WebSocket 的一种轻量代替方案,使用 HTTP 协议。 SSE 是单向通道,只能服务器向客户端发送消息,如果客户端需要向服务器发送消息,则需要一个新的 HTTP 请求。 WebSocket 是全双工通道,可以双向通信。 2、效果 在界面上不停的刷…

环境影响与碳排放生命周期评估应用及案例分析

生命周期分析 (Life Cycle Analysis, LCA) 是评价一个产品系统生命周期整个阶段——从原材料的提取和加工&#xff0c;到产品生产、包装、市场营销、使用、再使用和产品维护&#xff0c;直至再循环和最终废物处置——的环境影响的工具。这种方法被认为是一种“从摇篮到坟墓”的…

【Kubernetes】在 Mac 上搭建 Kubernetes

安装 Docker Desktop 前往 Install Docker Desktop on Mac | Docker Docs 下载 Docker Desktop 并完成安装。 配置镜像加速服务 在国内通过官方镜像源的下载速度很慢&#xff0c;因此需要配置镜像加速服务。 国内常见的镜像加速服务有&#xff1a; mirror.baidubce.comhub-…

基于springboot+vue+Mysql的网上图书商城

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

24/03/26总结

面向对象练习题&#xff1a;&#xff08;封装&#xff0c;继承&#xff0c;多态) 封装&#xff1a;对象代表什么&#xff0c;就得封装对应的数据&#xff0c;并提供数据对应的行为,(把零散的数据和行为封装成一个整体&#xff1a;也就是我们说的对象&#xff09; 继承:当封装…

Java编程练习之类的多态

类的多态可以从两方面体现&#xff1a;一是方法的重载&#xff0c;二是类的上下转型。 1&#xff09;方法的重载 方法的重载就是在同一个类中允许同时存在多个同名方法&#xff0c;只要这些方法的参数个数或类型不同即可。 练习1&#xff1a; 使用方法的重载描述所有的超市…

分治——快速排序算法

例题一 解法&#xff08;快排思想 - 三指针法使数组分三块&#xff09;&#xff1a; 算法思路&#xff1a; 类⽐数组分两块的算法思想&#xff0c;这⾥是将数组分成三块&#xff0c;那么我们可以再添加⼀个指针&#xff0c;实现数组分 三块。 设数组⼤⼩为 n &#xff0c…

快速了解LED开关电源的常见故障及如何解决

LED开关电源应用广泛&#xff0c;在我们的生活和工作中无处不在。然而在LED开关电源的实际使用中会出现一些问题和故障&#xff0c;可能会造成电源和LED灯受损。本文纳米软件将为大家介绍常见的LED开关电源的故障以及解决方法。 1. 输出电流不稳定 LED开关电源输出电流不稳定通…

Nuxt2:node-sass替换为sass,避免因依赖python导致Can‘t find Python executable “python“错误

一、问题描述 由于node-sass依赖于python&#xff0c;在缺少python环境时&#xff0c;会报以下错误&#xff1a; npm ERR! gyp ERR! stack Error: Cant find Python executable "python", you can set the PYTHON env variable.当服务器环境不想安装python或者不方…

Vitis报错:fatal error: xxx.h: No such file or directory.

在跑vitis工程文件时出现fatal error: xxx.h: No such file or directory.的错误&#xff0c;出现这种情况的主要是出现在大家用了自定义IP。 记住如果时出现Makefile出现错误&#xff0c;并不是你的代码问题而是软件bug&#xff0c;我们需要更改一些文件才能正常跑。 讲解一下…

如何快速下载GEO数据并获取其表达矩阵与临床信息 | 附完整代码 + 注释

GEO数据库可以说是大家使用频率贼高的数据库啦&#xff01;那它里面的数据怎么下载大家知道嘛&#xff01;今天给大家展示一种快速获取它的表达矩阵和临床信息的方法&#xff01; 话不多说&#xff01;咱们直接开始&#xff01; GEO编号获取 在GEO数据库中&#xff0c;你找到…

水牛社:宝妈副业,不仅赚钱更成长:一段充实之旅

大家好&#xff01;作为一名90后的全职宝妈&#xff0c;今天非常荣幸能够与大家分享我的互联网赚钱经验。趁着宝宝午睡的宝贵时光&#xff0c;我抓紧写下了这篇文章&#xff0c;虽时间紧凑&#xff0c;但我会力求内容清晰明了。 大约从2022年4月开始&#xff0c;我踏上了互联网…

【Node.js】定时任务

Cron表达式 Cron表达式是用于表示定时任务的一种语法。它由6个字段组成&#xff0c;按顺序分别表示分、时、日、月、周几和年份。 * * * * * * ┬ ┬ ┬ ┬ ┬ ┬ │ │ │ │ │ │ │ │ │ │ │ └── 星期…

【智能算法】蛇优化算法(SO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2022年&#xff0c;FA Hashim等人受到蛇特殊交配行为启发&#xff0c;提出了蛇优化算法&#xff08;Snake Optimizer, SO&#xff09;。 2.算法原理 2.1算法思想 SO算法模拟蛇类的战斗与交配模式…