基于SpringBoot和Leaflet的行政區(qū)劃地圖掩膜效果實戰(zhàn)教程
前言
在之前的博客提過按空間矢量范圍下載遙感,有興趣的同學可以參考已下的博文地址:基于QGIS的研究區(qū)域遙感影像裁切下載方法-以岳麓區(qū)為例。在這篇博客中采用的是Qgis軟件,這是 一款桌面端的GIS軟件。在這里,首先簡單解釋一下gis中掩膜的相關概念。掩膜在制圖中是一種遮蓋工具,用于處理要素在顯示上的沖突。掩膜可以理解為一種看不見的面要素,它遮擋住了不需要顯示的圖形。用這個工具可以將影像按面要素周長裁剪。
存在兩種類型的掩膜策略:一是:圖層掩膜 - 是指在地圖或場景中,某一要素圖層或掩膜圖層可以掩蓋另一圖層中的任何重疊要素。二是要素級掩膜 - 是指按照兩圖層間關系類所指定的方式對各相關要素進行的掩膜。無論采用哪種掩膜方式,掩膜要素的幾何均會掩蓋被掩膜要素的符號系統(tǒng)。 即使掩膜要素的符號化形狀與幾何不同,(例如,如果應用了符號緩沖),要素幾何的形狀會進行掩膜。 然而,被掩膜要素的符號系統(tǒng)會受到影響。 被掩膜要素看起來可能存在孔洞,但它們只是可見符號中的孔洞。 要素幾何并未更改。
本文講解的是一種圖層級的掩膜,即使用行政區(qū)劃圖層來進行掩膜。使用場景為,用戶只需要在地圖頁面中展示目標行政區(qū)劃內的影像信息,對于行政邊界外的影像,則不展示。這就是WebGIS中掩膜的一種表達方式。本文重點講解在Webgis中如何進行行政區(qū)劃掩膜實現(xiàn),通過代碼實戰(zhàn)的方式對功能進行詳細的實現(xiàn),采用網友編寫一個掩膜組件,不僅避免了自己的區(qū)域繪制太小,也避免了不同行政區(qū)劃切換時,有部分縫隙的問題。如果您目前也有WebGIS掩膜可視化需求,不妨來看看博客。
一、掩膜小知識
在講解地圖之前,如果了解前端的朋友一定知道,在HTML5的應用中,可能會存在兩個DIV,可能由于其內容和位置的設置存在空間重疊,有一部分區(qū)域會被另一個DIV進行遮蓋。這種效果就是掩膜。(以上不是官網的定義,只是翻譯成了大白話,易于大家理解)。通過上面的解釋可以看出,在這個場景中涉及的圖層起碼有兩個,而且存在空間折疊的關系。而實現(xiàn)效果就是通過疊加,使用遮罩這種方式來進行。
1、GIS掩膜的實現(xiàn)原理
與上述Html的實現(xiàn)原始一致的,在這個場景當中。首先我們會使用柵格底圖(一般是遙感影像)。然后在展示行政區(qū)劃時,自動將行政區(qū)劃外的地圖遮住。比如在展示湖南省的行政區(qū)劃時,只展示湖南省區(qū)域內的影像,對于湖南省外的區(qū)域則不展示,以空白的方式展現(xiàn)出來。先來看一下實際的效果。

2、圖層掩膜流程
對于圖層掩膜的流程,使用流程圖描述如下:

第一步是在地圖上加載原始的遙感影像,可以是WMS或者XYZ瓦片。第二步是輸入要疊加的升級行政區(qū)劃范圍,這里一般是采用GeoJSON的方式進行獲取。第三步是從GeoJSON中解析出空間面信息,構建出遮罩范圍,通過繪制遮罩面,設置遮罩面的透明度。同時將無需遮罩的范圍留空,這樣就能實現(xiàn)空間掩膜的效果。
二、使用插件
在實現(xiàn)這個需求時,可以完全不用外部的插件,通過Leaflet自己構建Polygon面來實現(xiàn)遮罩掩膜的效果即可。但是使用自己繪制的面時,進行地圖縮放時,會有一些不順暢的原因。因此在開源社區(qū)找了一款開源的組件。通過組件來實現(xiàn)地圖遮罩,方便又美觀。
1、leaflet-mask介紹
leaflet-mask是一個簡單的地圖遮罩層控件,繼承自L.polygon。我們很多時候希望只顯示某塊區(qū)域內的內容,隱藏或者模糊區(qū)域外內容。此插件可以實現(xiàn)傳入polygon的latlngs創(chuàng)建對應的遮罩圖層。其gitee地址是leaflet-mask。大家可以將代碼下載到本地,然后運行其官方的實例即可。

2、核心代碼解釋
對于這個插件來說,核心代碼其實非常少,也是很容易看懂的。在下載的源代碼中,可以直接打開來看,在src目錄下有l(wèi)eaflet-Mask.js。使用文本編輯器或者javascript腳本編輯器打開這個腳本。
/**
* 遮罩
*/
L.Mask = L.Polygon.extend({
options: {
isRect: true, //是否為矩形遮罩,如果為是,則使用northWest,northEast,sourthEast,sourthWest創(chuàng)建矩形遮罩層外邊界,如果為false,則使用傳入的坐標數(shù)組作為遮罩層外邊界
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: [] //顯示區(qū)域
},
initialize(options) {
L.Util.setOptions(this, options);
let latlngs = this.getMaskLatLngs();
this._setLatLngs(latlngs);
},
/**
* 畫遮蔽層的相關方法
*思路: 創(chuàng)建一個矩形作為遮罩層,構造函數(shù)傳入的坐標作為內環(huán)
* @see https://blog.csdn.net/mapmonster/article/details/104455516
*
* @param {*} latlngs
*/
getMaskLatLngs() {
let latlngs = [];
//是矩形遮罩,則使用northWest,northEast,sourthEast,sourthWest創(chuàng)建矩形遮罩層外邊界
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類的子類。這里設置了其默認的范圍,即四個邊界點。
三、完整實例實現(xiàn)
本節(jié)重點將對實例進行完整的介紹,首先我們將遙感影像完整的展示出來。同時在界面右邊展示行政區(qū)劃信息,支持按省級行政區(qū)劃名稱進行檢索。點擊所在省份,將查詢后臺的接口返回GeoJSON格式的行政區(qū)劃邊界數(shù)據(jù),然后調用leaflet-mask的掩膜對象,實現(xiàn)行政區(qū)域的遮罩。
1、后臺邏輯實現(xiàn)
這里介紹省級行政區(qū)劃列表和查詢省級行政區(qū)劃GeoJson邊界信息接口。包括控制層代碼和數(shù)據(jù)庫訪問層代碼。核心代碼如下:
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());
}
}根據(jù)省份id查詢省份行政區(qū)劃邊界GeoJSON的數(shù)據(jù)庫訪問層核心代碼如下:
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;
/**
* 省級行政區(qū)劃數(shù)據(jù)接口
* @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、省級行政區(qū)劃查詢實現(xiàn)
在地圖上我們需要首先展示行政區(qū)劃列表,這里采用sidebar的組件進行展示。同時在列表中支持按照省級行政區(qū)劃名稱進行模糊查詢。
function initSidebar(){//初始化sidebar頁面
var sidebar = L.control.sidebar('sidebar', {position: 'right'}).addTo(mymap);
//默認sidebar打開,并展示一個tab頁
sidebar.open();
$("#xz_info").addClass("active");
$("#home").addClass("active");
//初始化行政區(qū)劃表格
initHnTownTable();
}
function initHnTownTable(){
var options = {
url: prefix + "/list",
createUrl: prefix + "/add",
updateUrl: prefix + "/edit/{id}",
modalName: "鄉(xiāng)鎮(zhèn)行政區(qū)劃",
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)" rel="external nofollow" onclick="previewTown(\'' + row.id + '\',\''+row.name+'\')"><i class="fa fa-paper-plane"></i>定位</a>');
return actions.join('');
}
}]
};
$.table.init(options);
}
3、行政區(qū)劃定位及掩膜實現(xiàn)
點擊行政區(qū)劃列表操作欄中的“定位”按鈕,可以實現(xiàn)行政區(qū)劃定位,以及進行區(qū)域掩膜。點擊定位的時候,會通過后臺的查詢接口獲取當前點擊的行政區(qū)劃的GeoJSON數(shù)據(jù)信息。關鍵方法如下:
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的接口獲取行政區(qū)劃的空間位置之后,再調用leaflet-mask的構造方法將遮罩面渲染出來。首先來看一下獲取的行政區(qū)劃GeoJSON數(shù)據(jù)信息:

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]});
});
//將閉合區(qū)域加到遮蔽層上,每次添加完后要再加一次西北角作為下次添加的起點和最后一次的終點
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);
}通過以上的代碼即可完成按照行政區(qū)劃進行掩膜可視化的效果。
4、成果展示
最后我們來看一下最終生成的省級行政區(qū)劃掩膜可視化效果。通過點擊分析按鈕,進行當前省份信息的掩膜可視化。閑言少敘,上圖為證。

天津市掩膜效果圖

湖北省掩膜效果圖

云南省掩膜效果圖

貴州省掩膜效果圖
總結
以上就是本文的主要內容,本文講解的是一種圖層級的掩膜,即使用行政區(qū)劃圖層來進行掩膜。使用場景為,用戶只需要在地圖頁面中展示目標行政區(qū)劃內的影像信息,對于行政邊界外的影像,則不展示。這就是WebGIS中掩膜的一種表達方式。行文倉促,難免有誤,歡迎各位專家朋友批評指正,不甚感謝。
本文寫作過程中參考以下博客,站在巨人的肩膀上,才能看得更高。
1、Leaflet實現(xiàn)地圖按照行政區(qū)劃遮罩。
到此這篇關于基于SpringBoot和Leaflet的行政區(qū)劃地圖掩膜效果實戰(zhàn)的文章就介紹到這了,更多相關SpringBoot行政區(qū)劃地圖掩膜內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java 中的 BufferedReader 介紹_動力節(jié)點Java學院整理
BufferedReader 是緩沖字符輸入流。它繼承于Reader。接下來通過本文給大家介紹BufferedReader的相關知識,需要的朋友參考下吧2017-05-05
SpringBoot利用@Validated注解優(yōu)雅實現(xiàn)參數(shù)校驗
在開發(fā) Web 應用時,用戶輸入的合法性校驗是保障系統(tǒng)穩(wěn)定性的基礎,?Spring Boot 的 @Validated 注解 提供了一種更優(yōu)雅的解決方案,下面就跟隨小編一起學習一下吧2025-04-04
java WebSocket實現(xiàn)聊天消息推送功能
這篇文章主要為大家詳細介紹了java WebSocket實現(xiàn)聊天消息推送功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07

