SpringBoot+PostGIS震中影响范围可视化实战评测
对应的建表SQL:
```sql
-- ------------------------------
-- Table structure for biz_earthquake_info
-- ----------------------------
DROP TABLE IF EXISTS "public"."biz_earthquake_info";
CREATE TABLE "public"."biz_earthquake_info" (
"id" int8 NOT NULL,
"eq_time" timestamp(6) NOT NULL,
"eq_lng" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,
"eq_lat" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,
"eq_depth" varchar(16) COLLATE "pg_catalog"."default" NOT NULL,
"eq_level" varchar(8) COLLATE "pg_catalog"."default",
"eq_location" varchar(255) COLLATE "pg_catalog"."default",
"create_by" varchar(64) COLLATE "pg_catalog"."default",
"create_time" timestamp(6),
"update_by" varchar(64) COLLATE "pg_catalog"."default",
"update_time" timestamp(6)
);
COMMENT ON COLUMN "public"."biz_earthquake_info"."id" IS '主键';
COMMENT ON COLUMN "public"."biz_earthquake_info"."eq_time" IS '发震时间';
COMMENT ON COLUMN "public"."biz_earthquake_info"."eq_lng" IS '发震经度';
COMMENT ON COLUMN "public"."biz_earthquake_info"."eq_lat" IS '发震纬度';
COMMENT ON COLUMN "public"."biz_earthquake_info"."eq_depth" IS '震源深度,单位千米';
COMMENT ON COLUMN "public"."biz_earthquake_info"."eq_level" IS '震级';
COMMENT ON COLUMN "public"."biz_earthquake_info"."eq_location" IS '震中位置';
COMMENT ON COLUMN "public"."biz_earthquake_info"."create_by" IS '创建人';
COMMENT ON COLUMN "public"."biz_earthquake_info"."create_time" IS '创建时间';
COMMENT ON COLUMN "public"."biz_earthquake_info"."update_by" IS '修改人';
COMMENT ON COLUMN "public"."biz_earthquake_info"."update_time" IS '修改时间';
-- Indexes
CREATE INDEX "idx_biz_earthquake_info_depth" ON "public"."biz_earthquake_info" USING btree ("eq_depth" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST);
CREATE INDEX "idx_biz_earthquake_info_etime" ON "public"."biz_earthquake_info" USING btree ("eq_time" "pg_catalog"."timestamp_ops" ASC NULLS LAST);
CREATE INDEX "idx_biz_earthquake_info_qlevel" ON "public"."biz_earthquake_info" USING btree ("eq_level" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST);
-- Primary Key
ALTER TABLE "public"."biz_earthquake_info" ADD CONSTRAINT "pk_biz_earthquake_info" PRIMARY KEY ("id");
```
### 2、全国行政村
行政村点位表的逻辑结构:
建表SQL:
```sql
-- ------------------------------
-- Table structure for biz_village
-- ----------------------------
DROP TABLE IF EXISTS "public"."biz_village";
CREATE TABLE "public"."biz_village" (
"id" int8 NOT NULL,
"province_name" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
"city_code" varchar(16) COLLATE "pg_catalog"."default" NOT NULL,
"city_name" varchar(512) COLLATE "pg_catalog"."default",
"area_code" varchar(64) COLLATE "pg_catalog"."default",
"area_name" varchar(512) COLLATE "pg_catalog"."default",
"township_code" varchar(64) COLLATE "pg_catalog"."default",
"township_name" varchar(512) COLLATE "pg_catalog"."default",
"village_code" varchar(64) COLLATE "pg_catalog"."default",
"village_name" varchar(512) COLLATE "pg_catalog"."default",
"address" varchar(512) COLLATE "pg_catalog"."default",
"type" varchar(32) COLLATE "pg_catalog"."default",
"lng" varchar(24) COLLATE "pg_catalog"."default",
"lat" varchar(24) COLLATE "pg_catalog"."default",
"geom" "public"."geometry"
);
COMMENT ON COLUMN "public"."biz_village"."id" IS '主键';
COMMENT ON COLUMN "public"."biz_village"."province_name" IS '省份名称';
COMMENT ON COLUMN "public"."biz_village"."city_code" IS '市级编码';
COMMENT ON COLUMN "public"."biz_village"."city_name" IS '市级名称';
COMMENT ON COLUMN "public"."biz_village"."area_code" IS '区县编码';
COMMENT ON COLUMN "public"."biz_village"."area_name" IS '区县名称';
COMMENT ON COLUMN "public"."biz_village"."township_code" IS '乡镇编码';
COMMENT ON COLUMN "public"."biz_village"."township_name" IS '乡镇名称';
COMMENT ON COLUMN "public"."biz_village"."village_code" IS '乡村编码';
COMMENT ON COLUMN "public"."biz_village"."village_name" IS '乡村名称';
COMMENT ON COLUMN "public"."biz_village"."address" IS '地址';
COMMENT ON COLUMN "public"."biz_village"."type" IS '类型';
COMMENT ON COLUMN "public"."biz_village"."lng" IS '经度';
COMMENT ON COLUMN "public"."biz_village"."lat" IS '纬度';
COMMENT ON COLUMN "public"."biz_village"."geom" IS 'geom';
-- Indexes
CREATE INDEX "idx_biz_village_areacode" ON "public"."biz_village" USING btree ("area_code" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST);
CREATE INDEX "idx_biz_village_city_code" ON "public"."biz_village" USING btree ("city_code" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST);
CREATE INDEX "idx_biz_village_geom" ON "public"."biz_village" USING gist ("geom" "public"."gist_geometry_ops_2d");
-- Primary Key
ALTER TABLE "public"."biz_village" ADD CONSTRAINT "pk_biz_village" PRIMARY KEY ("id");
```
## 二、Ja va后台服务设计
后台用Ja va开发,框架选了SpringBoot,ORM用的MyBatis-Plus。整体是标准的MVC三层架构。这套系统访问压力不大,所以直接上单体架构,够用。
### 1、实体类设计
这次只需要做地震覆盖范围查询,因此定义个VO视图对象就够了。关键代码:
```ja va
package com.yelang.project.extend.earthquake.domain;
import ja va.io.Serializable;
import ja va.math.BigDecimal;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class EarthquakeVillageVo implements Serializable {
private static final long serialVersionUID = -4857307169183564693L;
private BigDecimal dist; //距离
private String address; //位置
private String villageName; //村庄名称
private String lng; //经度
private String lat; //纬度
}
```
### 2、Mapper类设计
```ja va
package com.yelang.project.extend.earthquake.mapper;
import ja va.util.List;
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.EarthquakeVillageVo;
import com.yelang.project.extend.earthquake.domain.Village;
public interface VillageMapper extends BaseMapper
### 3、行政村点查询及标记
前端用Ajax把地震经纬度传给后台,后台算出结果后返回每个点的经纬度、距离和行政区名称。前端再把这些点动态绘到地图上:
```ja vascript
$.ajax({
type: "post",
url: prefix + "/villageinfo",
data: {"lng": lng, "lat": lat},
success: function(rsData) {
var villageData = rsData.data;
for (var i = 0; i < villageData.length; i++) {
var info = villageData[i];
var dist = info.dist;
var strokeStyleSet = "green";
if (parseFloat(dist) > 1000 && parseFloat(dist) <= 3500) {
strokeStyleSet = "yellow";
}
if (parseFloat(dist) <= 1000) {
strokeStyleSet = "red";
}
var marker = L.circleMarker(new L.LatLng(info.lat, info.lng), {
radius: 8,
labelStyle: {
text: info.villageName,
rotation: 0,
zIndex: i,
strokeStyle: strokeStyleSet
}
});
var content = "地址:" + info.address + "震中位置:" + name; content += "
距离震中(千米):" + info.dist; marker.bindPopup(content); marker.addTo(showLayerGroup); } mymap.fitBounds(showLayerGroup.getBounds()); } }); ``` 最终效果是这样的:
## 总结
整个思路其实不复杂:地震数据有了,行政村点位数据也有了,关键是利用PostGIS的空间查询能力,把“距离震中多远”这个问题跑通。前后端打通之后,地图上就能直观看到哪些村庄在哪个影响范围里。对于救援部门来说,这个信息能帮他们更快地判断重点区域在哪,往哪个方向调派资源。当然,这里只是一个技术验证,真正投入实战需要考虑的因素还有很多,但至少,这个路子是走得通的。