基于Java实现震中附近风景区预警可视化分析实践

目录

前言

一、空间数据说明

1、表结构信息展示

2、空间范围查询

二、Java后台开发实现

1、模型层设计与实现

2、控制层设计与实现

三、Leaflet地图开发

1、地震震中位置展示

2、百公里风景区列表展示

3、风景区列表展示

4、附近风景区展示

四、总结


前言

地震这类地址灾害的发生常常是不能提前预知的,而往往在人群聚集的地方会给人们带来巨大的财产损失。在人类活动过程中,旅游景点往往是人们热门的出行地方,尤其是5A、4A级这样的旅游景区。在出行的过程当中,往往是没有进行任何准备的。这里以2017年8月8日,发生在我国四川省阿坝州九寨沟县的M7.0级地震为例,详细展示了在震区100公里范围内的所有A级旅游景点。通过100公里范围内的旅游景点展示,同时展示直线距离。为地震发生后,结合地震的震级和影像范围,为地震范围内的旅游景点,尤其是热门旅游景点的影响评估做一个评估参考。突如其来的地震,已导致部分列车停运,不少游客被困景区。九寨沟风景名胜区位于九寨沟县漳扎镇境内,地震发生当日接待游客量为3.4万余人次。目前,九寨沟风景名胜区管理局正全力开展景区排险工作,为保证游客游览安全,九寨沟景区停止接待游客。具体接待游客时间另行通告。九寨沟风景名胜区管理局表示,已经预订了8月9日及以后门车票的游客可到阿坝文旅办理退票,已出8月9日票的游客,在九寨沟景区沟口售票大厅办理退票。

这次地震对旅游风景区的影响很大。地震及后续的余震对这些游客和当地的老板姓都是很大的安全威胁。本文基于地震信息和全国风景旅游区位置信息,在发震位置点构建一个直径100公里的影响范围景区列表。将地震震级、风景区里震中位置距离、风景区级别、旅游人数信息(可以从景点接待处获取),在实际工作当中,我们可以结合这些数据进行地震风险评估,为后续的救援和应急力量的输送与分配推送一个比较优化的方案。

本文使用Java开发语言,使用PostGIS空间数据库,构建这样一个空间范围分析模型,输出震中百公里影响景点。将作为下一步的数据分析基础提供数据支撑。通过构建源点位与目标点位的实际距离,构建模型分析的基本要素之一,感兴趣的朋友可以看看本文,了解相关的知识。

一、空间数据说明

本节将对空间数据结构和数据,100公里范围的空间分析查询进行简单介绍。让大家了解如何进行地震位置指定范围风景区查询。查询涉及的表有两张,第一张是地震信息表,第二张是风景区信息表。

1、表结构信息展示

这里直接展示地震信息表和风景区信息表两张表的物理结构,表结构信息如下:

上述这两张表的DDL语句如下所示:

sql 复制代码
CREATE TABLE "public"."biz_ceic_earthquake" (
  "pk_id" int8 NOT NULL,
  "auto_flag" varchar(30) COLLATE "pg_catalog"."default",
  "cata_id" varchar(30) COLLATE "pg_catalog"."default",
  "cata_type" varchar(30) COLLATE "pg_catalog"."default",
  "epi_depth" numeric(11,8),
  "epi_lat" varchar(15) COLLATE "pg_catalog"."default",
  "epi_lon" varchar(15) COLLATE "pg_catalog"."default",
  "eq_cata_type" varchar(30) COLLATE "pg_catalog"."default",
  "eq_type" varchar(30) COLLATE "pg_catalog"."default",
  "is_del" varchar(6) COLLATE "pg_catalog"."default",
  "location_c" varchar(255) COLLATE "pg_catalog"."default",
  "location_s" varchar(100) COLLATE "pg_catalog"."default",
  "loc_stn" varchar(20) COLLATE "pg_catalog"."default",
  "m" varchar(10) COLLATE "pg_catalog"."default",
  "mmb" varchar(10) COLLATE "pg_catalog"."default",
  "mmb2" varchar(10) COLLATE "pg_catalog"."default",
  "mml" varchar(10) COLLATE "pg_catalog"."default",
  "mms" varchar(10) COLLATE "pg_catalog"."default",
  "mms7" varchar(10) COLLATE "pg_catalog"."default",
  "new_did" varchar(16) COLLATE "pg_catalog"."default",
  "o_time" timestamp(6),
  "o_time_fra" varchar(10) COLLATE "pg_catalog"."default",
  "save_time" timestamp(6),
  "sum_stn" varchar(10) COLLATE "pg_catalog"."default",
  "sync_time" timestamp(6),
  "epi_id" varchar(10) COLLATE "pg_catalog"."default",
  "geom" "public"."geometry",
  CONSTRAINT "pk_biz_ceic_earthquake" PRIMARY KEY ("pk_id")
);

CREATE INDEX "idx_biz_ceic_earthquake_eqidept" ON "public"."biz_ceic_earthquake" USING btree (
  "epi_depth" "pg_catalog"."numeric_ops" ASC NULLS LAST
);
CREATE INDEX "idx_biz_ceic_earthquake_geom" ON "public"."biz_ceic_earthquake" USING gist (
  "geom" "public"."gist_geometry_ops_2d"
);
CREATE INDEX "idx_biz_ceic_earthquake_m" ON "public"."biz_ceic_earthquake" USING btree (
  "m" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
);
COMMENT ON COLUMN "public"."biz_ceic_earthquake"."pk_id" IS '主键id';
COMMENT ON COLUMN "public"."biz_ceic_earthquake"."epi_depth" IS '地震深度';
COMMENT ON COLUMN "public"."biz_ceic_earthquake"."epi_lat" IS '纬度';
COMMENT ON COLUMN "public"."biz_ceic_earthquake"."epi_lon" IS '经度';
COMMENT ON COLUMN "public"."biz_ceic_earthquake"."geom" IS '位置';
COMMENT ON TABLE "public"."biz_ceic_earthquake" IS '中国地震台网信息表';
sql 复制代码
CREATE TABLE "public"."biz_scenic_spot" (
  "id" int8 NOT NULL,
  "name" varchar(255) COLLATE "pg_catalog"."default",
  "level" varchar(4) COLLATE "pg_catalog"."default",
  "province" varchar(255) COLLATE "pg_catalog"."default",
  "city" varchar(255) COLLATE "pg_catalog"."default",
  "area" varchar(255) COLLATE "pg_catalog"."default",
  "address" varchar(255) COLLATE "pg_catalog"."default",
  "evaluation_time" varchar(255) COLLATE "pg_catalog"."default",
  "publish_time" varchar(255) COLLATE "pg_catalog"."default",
  "lng_gcj02" varchar(30) COLLATE "pg_catalog"."default",
  "lat_gcj02" varchar(30) COLLATE "pg_catalog"."default",
  "lng_bd09" varchar(30) COLLATE "pg_catalog"."default",
  "lat_bd09" varchar(30) COLLATE "pg_catalog"."default",
  "lng_wgs84" varchar(30) COLLATE "pg_catalog"."default",
  "lat_wgs84" varchar(30) COLLATE "pg_catalog"."default",
  "geom" "public"."geometry",
  "publish_link" varchar(255) COLLATE "pg_catalog"."default",
  CONSTRAINT "pk_biz_scenic_spot" PRIMARY KEY ("id")
);
CREATE INDEX "idx_biz_scenic_spot_geom" ON "public"."biz_scenic_spot" USING gist (
  "geom" "public"."gist_geometry_ops_2d"
);
COMMENT ON COLUMN "public"."biz_scenic_spot"."id" IS '主键';
COMMENT ON COLUMN "public"."biz_scenic_spot"."name" IS '景区名称';
COMMENT ON COLUMN "public"."biz_scenic_spot"."level" IS '景区级别';
COMMENT ON COLUMN "public"."biz_scenic_spot"."province" IS '所属省份';
COMMENT ON COLUMN "public"."biz_scenic_spot"."city" IS '所属城市';
COMMENT ON COLUMN "public"."biz_scenic_spot"."area" IS '所属区县';
COMMENT ON COLUMN "public"."biz_scenic_spot"."address" IS '地址';
COMMENT ON COLUMN "public"."biz_scenic_spot"."evaluation_time" IS '评定时间';
COMMENT ON COLUMN "public"."biz_scenic_spot"."publish_time" IS '发布时间';
COMMENT ON COLUMN "public"."biz_scenic_spot"."lng_gcj02" IS 'lng_GCJ02';
COMMENT ON COLUMN "public"."biz_scenic_spot"."lat_gcj02" IS 'lat_GCJ02';
COMMENT ON COLUMN "public"."biz_scenic_spot"."lng_bd09" IS 'lng_BD09';
COMMENT ON COLUMN "public"."biz_scenic_spot"."lat_bd09" IS 'lat_BD09';
COMMENT ON COLUMN "public"."biz_scenic_spot"."lng_wgs84" IS 'lng_WGS84';
COMMENT ON COLUMN "public"."biz_scenic_spot"."lat_wgs84" IS 'lat_WGS84';
COMMENT ON COLUMN "public"."biz_scenic_spot"."publish_link" IS '发布链接';
COMMENT ON TABLE "public"."biz_scenic_spot" IS '全国风景区信息表';

2、空间范围查询

与之前介绍过的附近城市分析涉及的空间分析查询一样,主要的空间分析函数是:st_dwithin,这里依然以九寨沟地震震发中心点为查询样例:

sql 复制代码
SELECT 
    T.*,
	st_x ( T.geom ) lon,
	st_y ( T.geom ) lat,
	st_distance ( T.geom :: geography, t1.geom :: geography ) dist 
FROM
	biz_scenic_spot T,
	biz_ceic_earthquake t1 
WHERE
	t1.pk_id = 1780964053414354949 
	AND st_dwithin ( T.geom :: geography, t1.geom :: geography, 5000 * 20 ) 
ORDER BY
	dist;

在PostGIS当中执行上述空间查询语句之后,可以看到以下结果:

下面的章节将实现把上面的查询结果进行Web应用开发,完全实现通过一个地震点来实时查询地震点附近的风景区列表分析。

二、Java后台开发实现

在了解了空间数据查询的SQL具体的写法之后,我们来开发针对性的后台。开发语言采用我们熟悉的Java开发语言。java开发采用熟悉的MVC三层开发模式。

1、模型层设计与实现

在模型层,我们主要介绍实体类的代码实现和Mapper即数据库访问层的设计与实现。实体类主要用来将数据库查询的结果集绑定到对象中。而Mapper则实现对PostGIS空间数据库的绑定和设置。实例代码如下:

java 复制代码
package com.yelang.project.extend.scenicspot.domain;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class ScenicSpotDistVo implements Serializable{
	private static final long serialVersionUID = -1403627497900208179L;
	private BigDecimal dist;//距离
	private String address;//位置
	private String name;//名称
	private String level;//风景区级别
	private String lon;//经度
	private String lat;
}

在Mapper接口中定义以下方法,用于实现从数据库中查询相应的风景区数据列表。

java 复制代码
static final String FIND_LIST_BY_EARTHID = "<script>"
			+ " select t.name,t.level,t.address,st_x(t.geom) lon,st_y(t.geom) lat, "
			+ " st_distance(t.geom :: geography, t1.geom :: geography) dist from biz_scenic_spot t,biz_ceic_earthquake t1 "
			+ " where t1.pk_id= #{eqId} and st_dwithin(t.geom :: geography,t1.geom :: geography,100000  )  order by dist "
			+ "</script>";
/**
 *  查询地震100公里范围内的风景区列表
* @param eqId
* @return 地震发生地点100公里范围内的景区
*/
@Select(FIND_LIST_BY_EARTHID)
List<ScenicSpotDistVo> findListByEarthId(@Param("eqId") Long eqId);

2、控制层设计与实现

业务层比较简单,不进行详细说明。在这个实例当中,业务层的实现比较简单。这里将控制层的代码实现进行说明。这里仅实现页面的跳转和一个ajax接口,关键代码如下:

java 复制代码
//跳转附近风景区页面,用get请求
@RequiresPermissions("ceiceq:info:nearbyss")
@GetMapping("/nearbyscenicspot/{pkId}")
public String nearbyScenicSpot(@PathVariable("pkId")Long pkId,ModelMap mmap){
    mmap.put("pkId", pkId);
    CeicEarthquake earthQuake = ceicEarthQuakeService.getById(pkId);
    mmap.put("earthQuake", earthQuake);
    List<ScenicSpotDistVo> dataList = scenicSpotService.findListByEarthId(pkId);
    mmap.put("dataList", dataList);
    return prefix + "/nearbyscenicspot";
}
    
/**
* 获取附近风景区列表数据,用post
* @param pkId
* @return
*/
@PostMapping("/nearbyscenicspot/{pkId}")
@ResponseBody
public AjaxResult nearbyScenicSpot(@PathVariable("pkId")Long pkId){
    AjaxResult ar = AjaxResult.success();
    List<ScenicSpotDistVo> dataList = scenicSpotService.findListByEarthId(pkId);
    ar.put("data", dataList);
    return ar;
}

以上代码即给出了后台Java实现的关键代码。通过以上代码即完成了空间数据分析查询接口开发。下面通过Leaflet组件来进行地图可视化开发。

三、Leaflet地图开发

本小节主要讲解如何使用Leaflet进行空间可视化展示开发。关于Leaflet的相关知识不再赘述,这里提供关键代码:

1、地震震中位置展示

在进行地震百公里风景区可视化展示时,首先需要对震中位置进行展示。关键的代码如下所示。

javascript 复制代码
var lon = [[${earthQuake.epiLon}]];  
var lat = [[${earthQuake.epiLat}]];
var cityInfo = [[${earthQuake.locationC}]];
javascript 复制代码
//矢量文本标签渲染器
var canvasLabel = new L.CanvasLabel({
	 defaultLabelStyle: {
	      collisionFlg: true,
	      scale: 1.2,
	      strokeStyle: "white",
	      fillStyle: "#fff",
	      lineWidth:15
	 }
});
javascript 复制代码
$(function() {
     var marker = L.circleMarker(new L.LatLng(lat, lon), {radius: 8,
		  labelStyle: {
			   text: cityInfo,
			   rotation: 0,
			   zIndex: 2,
			   strokeStyle :"red"
		  },
		  color : "red"
		}).addTo(mymap);
       //展示距离
       showDistance();
 });

2、百公里风景区列表展示

关键代码如下:

javascript 复制代码
function showDistance(){
     $.ajax({  
		   type:"post",  
		   url:prefix + "/nearbyscenicspot/" + [[${pkId + ''}]],  
		   dataType:"json",  
		   cache:false,
		   processData:false,
		   success:function(result){
		       if(result.code == web_status.SUCCESS){
		        	var strokeStyleSet = "green";
		        	for(var i=0;i<result.data.length;i++){
		        		var dataInfo = result.data[i];
		        		var dist = dataInfo.dist;
		        		if(parseFloat(dist) <= 30000){
		    				strokeStyleSet = "red";
		    			}
		        			
		        		if(parseFloat(dist) > 30000 && parseFloat(dist) <= 60000 ){
		    				strokeStyleSet = "orange";
		    			}
		        		if(parseFloat(dist) >= 60000){
		        			strokeStyleSet = "green";
		        		}
		        			
		        		var _dist = parseFloat(dataInfo.dist / 1000).toFixed(2);
		    			var content = "<strong>风景区名称:</strong>"+dataInfo.name + "<br/><strong>震中位置:</strong>"+ cityInfo;
		    		 content += "<br/><strong>风景区级别:</strong>"+dataInfo.level + "<br/><strong>距离(千米):</strong>"+_dist;
	    				var marker = L.circleMarker(new L.LatLng(dataInfo.lat, dataInfo.lon), {radius: 8,labelStyle: {
			    			          text: dataInfo.name,
			    			          rotation: 0,
			    			          zIndex: i,
			    			          strokeStyle :strokeStyleSet
			    			        },
			    			        color : strokeStyleSet
			    			    }).addTo(mymap);
		    				marker.bindPopup(content);    
		    				
		    				L.polyline(
	   					      [
	   					    	  [
	   					    		  [lat, lon],[dataInfo.lat, dataInfo.lon]]], {
	   					      labelStyle: {
	   					        text: _dist + "千米",
	   					        zIndex: 0,
	   					        collisionFlg: false,
	   					        textAlign:'center',
	   					     	strokeStyle :strokeStyleSet
	   					      },
	   					      color : strokeStyleSet
	   					    }).addTo(mymap);
		        		}
		        	}
		        },
		        error:function(){
		        	$.modal.alertWarning("获取信息失败");
		        }
		    });
        }

3、风景区列表展示

html 复制代码
<table class="table table-bordered white-bg" >
     <thead>
          <tr>
               <th width="50%">风景区名称</th>
               <th>风景区级别</th>
               <th>距离(公里)</th>
          </tr>
      </thead>
      <tbody>
          <tr th:each="data,itemStat:${dataList}">
                <td >[[${itemStat.count}]]、[[${data.name}]]</td>
                <td>[[${data.level}]]</td>
                <td th:text="${#numbers.formatDecimal((data.dist / 1000 ), 1, 2)}"></td>
           </tr>
       </tbody>
 </table>

4、附近风景区展示

地震点列表及震中位置定位功能示意图

可以看到,震中位置附近有很多的风景名胜区,其中就有5A级风景区,九寨沟。距离震中最近的还有8公里左右的爱情海景区。30公里范围内还有一个4A级的阿坝州九寨沟县嫩恩桑措旅游景区。完整的列表表单如下所示:

风景区名称 风景区级别 距离(公里)
1、爱情海景区 4A 8.21
2、九寨沟风景名胜区 5A 10.46
3、阿坝州九寨沟县嫩恩桑措旅游景区 4A 22.90
4、九寨沟县九寨庄园景区 3A 39.79
5、九寨沟柴门关景区 3A 39.92
6、九寨县甲勿海景区 3A 40.48
7、松潘县奇峡沟冰雪欢乐景区 3A 42.98
8、上磨水乡 2A 45.16
9、天堂香谷 2A 46.01
10、岷江源景区 3A 50.13
11、松潘县川主寺旅游景区 4A 50.29
12、黄龙国家级风景名胜区 5A 52.12
13、松潘县松州古城 3A 64.51
14、文县白马河民俗风情旅游景区 4A 65.44
15、舟曲县亚哈藏民俗旅游文化生态园景区 2A 66.19
16、涪阳古镇 3A 72.53
17、若尔盖县巴西会议红色旅游景区 3A 77.09
18、舟曲县拉尕山景区 4A 78.08
19、舟曲县土桥子国家景区 3A 81.56
20、舟曲特大山洪泥石流灾害纪念园 3A 82.49
21、舟曲县各皂坝国家景区 3A 82.96
22、舟曲县翠峰山景区 2A 84.63
23、迭部县茨日那毛主席旧居景区 3A 85.71
24、文县天池旅游景区 4A 85.98
25、武都区朝阳洞旅游景区 3A 86.05
26、若尔盖县花湖生态旅游区 4A 86.56
27、舟曲县巴寨沟国家景区 3A 87.94
28、迭部县俄界景区 4A 88.51
29、宕昌县山湾梦谷古羌民俗旅游景区 3A 90.58
30、黄河九曲第一湾 4A 91.27
31、西部牧场 3A 93.17
32、阿坝州红原县日干乔景区 4A 97.69
33、迭部县白云景区 3A 98.42

四、总结

以上就是本文的主要内容,本文使用Java开发语言,使用PostGIS空间数据库,构建这样一个空间范围分析模型,输出震中百公里影响景点。将作为下一步的数据分析基础提供数据支撑。通过构建源点位与目标点位的实际距离,构建模型分析的基本要素之一,感兴趣的朋友可以看看本文,了解相关的知识。

文章通过对空间数据库表的设计以及空间分析查询语句的编写,让大家掌握如何在PostGIS数据库中进行空间分析,最后使用Leaflet组件调用Java服务完成了震中附近风景区列表的分析实战。行文仓促,定有不足之处,不当之处,还请各位专家博主在评论区批评指正,万分感谢。