前言
胸怀祖国,放眼世界。在全球的世界当中,许多国家是陆地相连的,而邻国因为领土争议,是很多国家长期面临的斗争问题,比如朝鲜和韩国的问题,还有印度和巴基斯坦的领土争端问题。通过地理来探索世界,我们来看一下,如何使用WebGIS来对全球的国家基础信息进行查询,快速的通过空间分析来构建一个国家邻国查询的应用,通过空间的角度来看一下不同的国家,它的陆地领土相邻关系。如下图所示:
本文以SpringBoot框架和PostGIS空间数据库为例,重点讲解如何实现一个国家的邻国查询以及WebGIS可视化应用的实现。文章首先讲解如何在PostGIS数据中进行空间相邻的求解,然后讲解使用Java来开发相应的查询接口,接着介绍在Leaflet当中进行数据的WebGIS展示,最后围绕一些国家及其邻国空间展示为大家做重点的介绍。地图会将故事,通过本文,不仅可以学习如何在SpringBoot中进行GIS的应用开发,而且通过WebGIS来展示我们的地球。如果您对这方面也感兴趣,不妨来看看呢。
一、空间数据查询基础
在进行相关应用的开发之前,我们首先要对涉及的空间表以及空间查询函数进行介绍。让大家对相关的空间物理结构和空间检索函数有一个充分的认识。因此首先我们先来介绍一下在国家邻国查询的应用中需要使用的数据基础知识。
1、空间数据库基础
虽然在前面的博文中,这里不再做过多的介绍,将空间表的物理结构给大家进行展示。
CREATE TABLE "public"."biz_world_country" (
"pk_id" int8 NOT NULL,
"full_english_name" varchar(255) COLLATE "pg_catalog"."default",
"short_english_name" varchar(255) COLLATE "pg_catalog"."default",
"min_english_name" varchar(50) COLLATE "pg_catalog"."default",
"full_chinese_name" varchar(255) COLLATE "pg_catalog"."default",
"short_chinese_name" varchar(255) COLLATE "pg_catalog"."default",
"continent" varchar(50) COLLATE "pg_catalog"."default",
"unreg" varchar(50) COLLATE "pg_catalog"."default",
"geom" "public"."geometry",
CONSTRAINT "pk_biz_world_contry" PRIMARY KEY ("pk_id")
);
CREATE INDEX "idx_biz_world_country_continent" ON "public"."biz_world_country" USING btree (
"continent" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
);
CREATE INDEX "idx_biz_world_country_geom" ON "public"."biz_world_country" USING gist (
"geom" "public"."gist_geometry_ops_2d"
);
CREATE INDEX "idx_biz_world_country_min_englis" ON "public"."biz_world_country" USING btree (
"min_english_name" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
);
COMMENT ON COLUMN "public"."biz_world_country"."pk_id" IS 'pk_id';
COMMENT ON COLUMN "public"."biz_world_country"."full_english_name" IS '英文全称';
COMMENT ON COLUMN "public"."biz_world_country"."short_english_name" IS '英文简称';
COMMENT ON COLUMN "public"."biz_world_country"."min_english_name" IS '最简名称';
COMMENT ON COLUMN "public"."biz_world_country"."full_chinese_name" IS '中文全称';
COMMENT ON COLUMN "public"."biz_world_country"."short_chinese_name" IS '中文简称';
COMMENT ON COLUMN "public"."biz_world_country"."continent" IS '所属大洲,如Asia';
COMMENT ON COLUMN "public"."biz_world_country"."unreg" IS '大洲详情';
COMMENT ON COLUMN "public"."biz_world_country"."geom" IS 'geom';
COMMENT ON TABLE "public"."biz_world_country" IS '世界国家信息表';
以上就是全球国家信息表的主表物理结构和SQL管理语句。下面来看一下数据库中的数据,如下图所示:
select * from biz_world_country t;
2、空间相邻查询
介绍完空间表结构和相应的表结构之后,我们再来介绍一下如何实现上述的需求,如何利用空间函数来进行国家以及其邻国的求解运算。这里我们使用PostGIS空间数据库为例,基于PostGIS来介绍如何进行相邻求解。要想实现求解这个需求,其实方法较多,最简单的方法就是直接利用相邻函数,在数据库中可以使用ST_Touches(Geometry geom1,Geometry geom2)。这里我们对这个ST_Touches函数来进行简单的介绍。掌握这个函数是求解空间相邻的关键。
boolean **ST_Touches**(
raster rastA , raster rastB
)
;来看一下这个函数的介绍。如果raster RastA在空间上接触raster rastB,则返回TRUE。这意味着Rasta和rastB至少有一个共同点,但它们的内部并不相交。如果未提供波段编号(或设置为空),则在测试中仅考虑栅格的凸包。如果提供了波段编号,则在测试中只考虑那些有值的像素(不考虑NODATA)。
国家相邻的概念与上述的需求一致,即两个面至少有一个共同点,即认为国家相邻。因此我们就使用这个函数来进行相邻国家的求解。首先基于PgAdmin来进行空间查询的结果展示。查询语句如下所示,这里以欧洲国家德国为例:
SELECT
pk_id,
short_english_name,
min_english_name,
full_chinese_name,
continent,
geom,
st_asgeojson ( geom ) geomJson
FROM
biz_world_country
WHERE
pk_id = 1843094270664282114
UNION
SELECT
other_c.pk_id,
other_c.short_english_name,
other_c.min_english_name,
other_c.full_chinese_name,
other_c.continent,
other_c.geom,
st_asgeojson ( other_c.geom ) geomJson
FROM
biz_world_country AS target_c
JOIN biz_world_country AS other_c ON ST_Touches ( target_c.geom, other_c.geom )
WHERE
target_c.pk_id = 1843094270664282114
前面的数据是查询当前国家,后面是查询跟所属国家相邻的国家的信息,包括空间坐标信息。在数据库中运行以上的sql后可以在下面看到执行的数据。
可以点击空间结果字段可以在PgAdmin中进行空间数据的查看,如下所示:
当然,您也可以直接在查询客户端中进行数据的查询,但是明显是不够方便的。而且,我们的终端用户更加不方便,需要使用Web的界面来进行操作和查询。因为需要我们基于PostGIS空间数据库来进行相关功能的开发。
二、SpringBoot后台功能设计
在进行空间业务数据库表的介绍之后,下面来具体介绍如何使用SpringBoot来进行相关的业务实现。这一块其实比较简单,主要是提供后台的检索接口,为前台提供根据国家来查询所有相邻国家的功能。
1、后台查询接口的实现
为了便于对空间数据库的操作,这里以Mybatis-Plus进行介绍。由于需要使用自定义的SQL查询来实现,因此这里我们需要自定义查询脚本。可以在Mapper接口中进行相关的查询定义。关键代码如下所示:
static final String NEIGHBOR_COUNTRY_BYID = "<script>"
+ " SELECT pk_id,short_english_name,min_english_name,full_chinese_name,continent,"
+ " st_asgeojson(geom) geomJson FROM biz_world_country where pk_id = #{id} union "
+ " SELECT other_c.pk_id,other_c.short_english_name,other_c.min_english_name, "
+ " other_c.full_chinese_name,other_c.continent,st_asgeojson(other_c.geom) geomJson "
+ " FROM biz_world_country AS target_c JOIN biz_world_country AS other_c "
+ " ON ST_Touches(target_c.geom, other_c.geom) "
+ " where target_c.pk_id = #{id} "
+ "</script>";
/**
* - 根据国家主键ID查询邻国列表 add by 夜郎ling in 2024-10-31
* @param id 查询国家ID
* @return 该国对应的邻国列表
*/
@Select(NEIGHBOR_COUNTRY_BYID)
List<WorldCountries> findNeighborCountryById(@Param("id")Long id);
2、业务接口设计
业务层其实非常简单,就是在service中调用mapper层中的方法,即上一节中分享的方法,获取指定国家的邻国信息。这里分享业务接口的实现,业务接口包含两个方法,第一个是跳转到相应的地图界面,另外一个是查询接口。下面给出相应的实现代码:
@RequiresPermissions("eq:nearcountry:map")
@GetMapping("/nearcountry")
public String nearCountry(){
return prefix + "/nearcountry";
}
@RequiresPermissions("eq:nearcountry:list")
@GetMapping("/nearcountry/list/{id}")
@ResponseBody
public AjaxResult nearCountryList(@PathVariable("id") Long id){
List<WorldCountries> countries = wCountryService.findNeighborCountryById(id);
return AjaxResult.success().put("data", countries);
}
以上就是java后台接口的相关实现。在介绍完后台的实现后,接下来看一下如何进行WebGIS界面开发。
三、Leaflet进行WebGIS开发
本节将重点介绍如何在Leaflet当中如何进行WebGIS开发,根据一个国家查询该国对应的所有邻国,并且将所有国家信息在页面上进行相应的标注。通过这最后一节,将让大家对邻国查询有一个直观的掌握。
1、整体结构介绍
为了很好的进行国家和邻国展示,需要对国家列表进行展示。这里我们采用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");
//初始化行政区划表格
initWorldCountryTable();
}
function initWorldCountryTable(){
var options = {
url: prefix + "/list",
createUrl: prefix + "/add",
updateUrl: prefix + "/edit/{id}",
modalName: "全球国家列表",
columns: [{
checkbox: true
},
{
field: 'pkId',
title: '',
visible: false
},
{
field: 'shortChineseName',
title: '名称',
formatter: function(value, row, index) {
return row.shortChineseName + "(" + row.minEnglishName + ")";
}
},
{
title: '操作',
align: 'center',
formatter: function(value, row, index) {
var actions = [];
actions.push('<a class="btn btn-success btn-xs ' + editFlag + '" href="javascript:void(0)" onclick="previewTown(\'' + row.pkId + '\',\''+row.fullChineseName+'\',\''+row.shortEnglishName+'\')"><i class="fa fa-paper-plane"></i>邻国</a>');
return actions.join('');
}
}]
};
$.table.init(options);
}
2、相邻国家展示可视化
当选择一个国家之后,会将国家的数据库编号进行空间查询,调用空间函数来进行查询。将相邻的国家查询出来,查询的结果是一个列表,因此我们需要使用list来进行循环获取。
function previewTown(gid,cnName,enName){
$.ajax({
type:"get",
url:prefix + "/nearcountry/list/" + gid,
data:{},
dataType:"json",
cache:false,
processData:false,
success:function(result){
if(result.code == web_status.SUCCESS){
showLayerGroup.clearLayers();
for(var i = 0;i< result.data.length;i++){
var countryData = result.data[i];
var color = gid == countryData.pkId ? "" : getRandomColor();
var ccolor = gid == countryData.pkId ? "red" : color;
var areaLayer = L.geoJSON(JSON.parse(countryData.geomJson),{style: {color:ccolor,weight:6,"opacity":0.85}}).addTo(mymap);
var myIcon = L.divIcon({
iconSize: null,
className: '',
popupAnchor:[5,5],
shadowAnchor:[5,5],
html: "<div class='marsBlackPanel' style='background:" + color + ";' animation-spaceInDown><div class='marsBlackPanel-text'>" + countryData.fullChineseName+"<span class='temperature'></span></div><div class='marsBlackPanel-text'>"+countryData.shortEnglishName+"<span class='temperature'></span></div></div>"
});
showLayerGroup.addLayer(areaLayer);
//中心点位
L.marker(areaLayer.getBounds().getCenter(), { icon: myIcon}).addTo(showLayerGroup);
}
mymap.fitBounds(showLayerGroup.getBounds());
}
},
error:function(){
$.modal.alertWarning("获取空间信息失败");
}
});
}
从以上代码中可以看到,我们使用for循环来进行国家和空间信息的展示。请注意在上述的代码中,有一个生成随机颜色的方法,代码如下:
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
到这里,基本我们就完成了所有的如何进行邻国和邻国空间查询求解的过程。从最开始的空间数据库的基础查询、到springboot后台计算、到leaflet的前端生成。下面我们来看一下实际的效果。
四、成果展示
经过了前面的编码环节,我们基本掌握了如何进行国家的展示以及如何进行空间相邻的求解。最后我们来看一下实际的效果,结合一些实际的国家信息和邻国信息来进行深入介绍。
1、印度及其邻国
首先来看看我们的邻居,印度。印度位于印度洋,是亚洲一个国家。看看它有哪些邻国呢?首先来看一下其自身空间位置及邻国信息,如下图所示:
通过上图可知,与印度相邻的国家有缅甸、中国、孟加拉国、不丹、尼泊尔、巴基斯坦。印度不仅与我国有边界争端,与巴基斯坦同样有争端问题。克什米尔一直是两国一道难以逾越的坎。我国与印度的领土争端也有,最近双方后撤,希望是好消息。
2、乌克兰及其邻国
俄乌战争一直在持续,当然这里无异于对战争进行评论,这里仅对乌克兰及其邻国进行相关的介绍。也让大家对使用gis的视角来对空间冲突有所了解。乌克兰是欧洲国家,是这两年的战争焦点。下面随着WebGIS来看一下乌克兰的位置和其邻国信息吧。
可以看到,与乌克兰相邻的国家有俄罗斯、白俄罗斯 、摩尔多瓦、匈牙利、波兰、斯洛伐克。
3、中东小霸王及其邻国
再来看一个最近经常上榜而且很热闹的地区和国家。这个国家就是我们熟悉的中东小霸王,以色列。那是一言不合就开干,最近经常上新闻榜。这里来看一些它的区位和邻国信息。
可以看到,以色列周边都是阿拉伯国家,比如埃及、约旦、黎巴嫩、叙利亚。最近、黎巴嫩和以色列的冲突一直持续。
五、总结
以上就是本文的主要内容,本文以SpringBoot框架和PostGIS空间数据库为例,重点讲解如何实现一个国家的邻国查询以及WebGIS可视化应用的实现。文章首先讲解如何在PostGIS数据中进行空间相邻的求解,然后讲解使用Java来开发相应的查询接口,接着介绍在Leaflet当中进行数据的WebGIS展示,最后围绕一些国家及其邻国空间展示为大家做重点的介绍。地图会将故事,通过本文,不仅可以学习如何在SpringBoot中进行GIS的应用开发,而且通过WebGIS来展示我们的地球。行文仓促,定有许多不足支持,如有不足,还恳请各位专家朋友在评论区留下真知灼见,不胜感激。
站在巨人的肩膀上,更能看得高,看得远。博文编写过程中参考以下文献,在此表示感谢:
1、RT_ST_Touches介绍。
2、【PostGIS入门】三、空间关系与空间连接。
版权归原作者 夜郎king 所有, 如有侵权,请联系我们删除。