目录
前言
一、全国风景区信息介绍
1、全国范围内数据分布
2、全国风景区分布
3、PostGIS空间关联查询
二、后台查询的设计与实现
1、Model和Mapper层
2、业务层和控制层设计
三、WebGIS可视化
1、省份范围可视化
2、省级风景区可视化展示
3、成果展示
总结
前言
旅行是心灵的洗涤,让每一步都充满感悟和思考。在旅途中,我学会了更加珍惜眼前的风景和时光。每一次旅行都是一次重新认识自己的机会,让我更加明白自己想要的是什么。旅途中的风景和经历,成为了我人生中最宝贵的财富。旅行让我感受到了世界的广阔和无限可能,让我更加勇敢地追求自己的梦想。在旅途中,我学会了用心去感受每一个细节,去品味生活的美好。旅行是一种生活方式,让我不断地探索和发现,不断地成长和进步。每一次旅行都是一次心灵的洗礼,让我更加清晰地认识自己和世界。旅途中的风景和经历成为了我人生中最珍贵的记忆,陪伴着我走过每一个春夏秋冬。旅行是一种自由和释放,让我在行走中找到自己的方向和目标。
不知道您是否还记得自己上一次旅行是在什么时候?是跟身边的谁一起去参加的旅行?在旅行中最令人难忘的风景是什么?除了风景还有什么让你印象深刻。身体和灵魂,总要有一个在路上。既然选择了在路上,那么我们就要去风景优美的地方看看,去美丽的景区游览,让身心得到放松,放松之后积蓄能量,为以后的生活持续奋斗。
各省风景区
不管是作为爱旅游的你,还是热爱文旅分析。使用WebGIS,我们来进行一场时空之旅,在地图上实现我们的地图旅行。本文以全国的旅游资源为例,分省份进行展示,将不同省份的风景区信息进行地图标注。我们也可以看一下全国范围内,风景区的时空分布。
本文即是在此背景下创作的,文章首先对数据库中的风景区数据进行简单介绍,然后介绍如何使SpringBoot进行风景区查询功能的设计与实现,接着介绍前端基于Leaflet进行可视化设计,最后使用实际应用,介绍和分析不同省份的风景区旅游资源。热爱旅游的你,一定不要错过,欣赏风景,让我们一起去看看吧。
一、全国风景区信息介绍
本节将对涉及的主要数据,即全国风景区信息进行简单介绍,其实在之前的一篇博客中,曾经对全国的风景区数据进行了详细的介绍,使用Java和PostGis的全国A级风景区数据入库实战。在这篇博客中,对全国A级风景区进行了入库的实践。
1、全国范围内数据分布
在之前的博客中,我们已经实现了数据的入库,为了避免让读者引起唐突的感觉,这里首先将数据表结构再次进行介绍,让读者熟悉和掌握对空间表的设计与实现。
对应的表结构脚本如下:
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 '全国风景区信息表';
数据库库一共有风景区14847条,在数据库中执行以下sql可以实现按照风景区级别进行分组统计,sql语句如下:
select count(level),level from biz_scenic_spot group by level;
按照景区级别分类如下,其中有3条没有具体的级别(应该是数据的问题):
序号 | 景区级别 | 数量 |
1 | 5A | 336 |
2 | 4A | 4486 |
3 | 3A | 7866 |
4 | 2A | 2066 |
5 | A | 93 |
2、全国风景区分布
首先从数据库层面,我们先来看一下全国的风景区分布是一个什么样的情况,这里使用表格的方式进行汇总。在上述的风景区表中执行以下语句,可以得到按省份分组的景区分布(注意:这里的分组没有使用关联外部的省份信息表,而是直接利用风景区表内的省份字段),在我们的系统中,我们有省份的空间信息表,在本文的空间分析中,我们使用省级行政区表。
select count(1) num,province from biz_scenic_spot group by province order by num desc;
执行后可以在数据库中看到以下数据:
序号 | 城市名称 | 景区数量 |
1 | 浙江 | 1322 |
2 | 山东 | 1205 |
3 | 四川 | 895 |
4 | 广西 | 662 |
5 | 安徽 | 605 |
6 | 江苏 | 600 |
7 | 广东 | 597 |
8 | 河南 | 580 |
9 | 湖北 | 576 |
10 | 新疆 | 574 |
11 | 贵州 | 570 |
12 | 云南 | 561 |
13 | 辽宁 | 558 |
14 | 湖南 | 550 |
16 | 陕西 | 527 |
17 | 河北 | 490 |
18 | 福建 | 463 |
19 | 甘肃 | 442 |
20 | 内蒙古 | 428 |
21 | 江西 | 421 |
22 | 黑龙江 | 409 |
23 | 吉林 | 275 |
24 | 重庆 | 272 |
25 | 山西 | 268 |
26 | 北京 | 216 |
27 | 青海 | 163 |
28 | 西藏 | 148 |
29 | 宁夏 | 147 |
30 | 上海 | 139 |
31 | 天津 | 100 |
32 | 海南 | 84 |
从上表中可以很直观的看出,浙江省、山东省、四川省的旅游资源相当丰富,居全国前三甲。而海南省、天津市、上海市位于后三甲。这里仅对数据进行表格可视化,为了让数据具有空间分布,我们将使用WebGIS的方式进行展示,让地图具有空间分布的特性。
3、PostGIS空间关联查询
为了让景区信息更加具有空间分布的特性,我们采用和biz_province即省级行政区划信息表进行关联,查询出指定省份对应的风景信息,这里使用st_contains(geom,geom)这个函数来进行对比。sql查询示例如下所示(以湖南省430000为例):
SELECT T.*,st_asgeojson ( T.geom ) AS geomJson FROMbiz_province P,biz_scenic_spot T WHEREP.code = '430000' AND st_contains ( P.geom, T.geom );
二、后台查询的设计与实现
本节重点讲解后台服务的设计,这里我们依然采用SpringBoot和Mybatis-Plus的组合,空间数据库采用PostGIS数据。因此后台服务的设计采用MVC模式,下面针对每一层进行详细的设计。
1、Model和Mapper层
model层包括实体类和相关视图层对象。Mapper即dao层,数据访问层。通过Mapper来操作数据库,基于MP进行实体数据库操作。
package com.yelang.project.extend.scenicspot.domain;import java.io.Serializable;import org.apache.ibatis.type.BlobByteObjectArrayTypeHandler;import org.apache.ibatis.type.BlobInputStreamTypeHandler;import org.apache.ibatis.type.BlobTypeHandler;import org.apache.ibatis.type.ByteArrayTypeHandler;import org.apache.ibatis.type.ByteObjectArrayTypeHandler;import org.apache.ibatis.type.ByteTypeHandler;import com.baomidou.mybatisplus.annotation.TableField;import com.baomidou.mybatisplus.annotation.TableId;import com.baomidou.mybatisplus.annotation.TableName;import com.yelang.framework.handler.PgGeometryTypeHandler;import lombok.AllArgsConstructor;import lombok.Getter;import lombok.NoArgsConstructor;import lombok.Setter;import lombok.ToString;/** * 全国风景区信息表* @author wzh**/@TableName(value = "biz_scenic_spot", autoResultMap = true)@NoArgsConstructor@AllArgsConstructor@Setter@Getter@ToStringpublic class ScenicSpot implements Serializable{private static final long serialVersionUID = 1830004907219610805L;@TableIdprivate Long id;private String name;//景区名称private String level;//景区级别private String province;//所属省份private String city;//所属城市private String area;//所属区县private String address;//地址@TableField(value="evaluation_time")private String evaluationTime;//评定时间@TableField(value="publish_time")private String publishTime;//发布时间@TableField(value="publish_link")private String publishLink;//发布时间@TableField(value="lng_GCJ02")private String lngGCJ02;@TableField(value="lat_GCJ02")private String latGCJ02;@TableField(value="lng_BD09")private String lngBD09;@TableField(value="lat_BD09")private String latBD09;@TableField(value="lng_WGS84")private String lngWGS84;@TableField(value="lat_WGS84")private String latWGS84;@TableField(typeHandler = PgGeometryTypeHandler.class)private String geom;@TableField(exist=false)private String geomJson;@TableField(exist=false)private byte[] tile;public ScenicSpot(String name, String level, String province, String city, String area, String address,String evaluationTime, String publishTime, String publishLink,String lngGCJ02, String latGCJ02, String lngBD09, String latBD09,String lngWGS84, String latWGS84, String geom) {super();this.name = name;this.level = level;this.province = province;this.city = city;this.area = area;this.address = address;this.evaluationTime = evaluationTime;this.publishTime = publishTime;this.publishLink = publishLink;this.lngGCJ02 = lngGCJ02;this.latGCJ02 = latGCJ02;this.lngBD09 = lngBD09;this.latBD09 = latBD09;this.lngWGS84 = lngWGS84;this.latWGS84 = latWGS84;this.geom = geom;}}
mapper层主要提供查询省级范围内的风景区列表方法,关键代码如下:
package com.yelang.project.extend.scenicspot.mapper;import java.util.List;import org.apache.ibatis.annotations.Param;import org.apache.ibatis.annotations.Result;import org.apache.ibatis.annotations.Results;import org.apache.ibatis.annotations.Select;import org.apache.ibatis.type.JdbcType;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.yelang.project.extend.scenicspot.domain.ScenicSpot;import com.yelang.project.extend.scenicspot.domain.TileBox;public interface ScenicSpotMapper extends BaseMapper<ScenicSpot>{static final String FIND_GEOJSON_SQL="<script>"+ "select st_asgeojson(geom) as geomJson from biz_scenic_spot "+ "where id = #{id} "+ "<if test='null != name'>and name like concat('%', #{name}, '%')</if>"+ "</script>";@Select(FIND_GEOJSON_SQL)ScenicSpot findGeoJsonById(@Param("id")Long id,@Param("name")String name);static final String FIND_LISTBY_PCODE = "<script>"+ " select t.*,st_asgeojson(t.geom) as geomJson from biz_province p,biz_scenic_spot t where p.code = #{code} and st_contains(p.geom, t.geom) "+ "</script>";/** * 根据省级行政区划code查询对应的风景名胜列表 * @param code 省份编码 * @return 对应的风景名胜区列表 */@Select(FIND_LISTBY_PCODE)List<ScenicSpot> findListByPcode(@Param("code")String code);}
2、业务层和控制层设计
业务层比较简单,实现控制层到数据库访问层的调用。这里贴出关键代码:
package com.yelang.project.extend.scenicspot.service.impl;@Servicepublic class ScenicSpotServiceImpl extends ServiceImpl<ScenicSpotMapper, ScenicSpot> implements IScenicSpotService{@Overridepublic List<ScenicSpot> findListByPcode(String code) {return this.baseMapper.findListByPcode(code);}}
package com.yelang.project.extend.scenicspot.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.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;@Controller@RequestMapping("/sspot/province")public class ScenicSpotProvinceController extends BaseController{private String prefix = "scenicspot/province";@Autowiredprivate IProvinceService provinceService;@Autowiredprivate IScenicSpotService sspotService;@RequiresPermissions("sspot:province:view") @GetMapping() public String map(){ return prefix + "/map"; } @RequiresPermissions("sspot:province:list") @PostMapping("/list") @ResponseBody public TableDataInfo list(Province province){ startPage(); List<Province> list = provinceService.selectList(province); return getDataTable(list); } @RequiresPermissions("sspot: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()); } @RequiresPermissions("sspot:province:sspotlist") @GetMapping("/datalist/{code}") @ResponseBody public AjaxResult sspotList(@PathVariable("code") String code){ List<ScenicSpot> list = sspotService.findListByPcode(code); AjaxResult ar = AjaxResult.success(); ar.put("data", list); return ar; }}
控制层提供四个主要的方法,第一个是跳转到页面的方法,第二个是获取省份列表,第三个是获取省份geojson数据,最后一个是获取省份下辖风景区列表。以上就是后台各层的代码设计与实现。通过这几个方法为前台提供服务。
三、WebGIS可视化
本节将重点讲解如何WebGIS当中采用leaflet框架实现对风景区信息的展示,这里采用熟悉的Leaflet架构。使用基于Canvas的文本标记方法。关于地图的定义,列表的展示非本文重点,这里不再赘述。
1、省份范围可视化
在界面中,首先会展示省份列表,通过点击省份列表的操作按钮,获取当前省份的code,然后根据code获取对应的省级行政区划范围GeoJSON,然后使用Leaflet将GeoJSON展示出来。关键代码如下:
function showProvince(id){ var myStyle = {color:"white",weight:5,"opacity":1,fillOpacity: 0}; $.ajax({ type:"get", url:prefix + "/geojson/" + id, data:{}, async: false, dataType:"json", cache:false, processData:false, success:function(result){ if(undefined != provinceAreaLayer ){ provinceAreaLayer.removeFrom(mymap);//先移除 } if(result.code == web_status.SUCCESS){ var geojson = JSON.parse(result.data); provinceAreaLayer = L.geoJSON(geojson,{style:myStyle}).addTo(mymap); //同时设置中心位置和级别,一般省份设置为7 mymap.setView(provinceAreaLayer.getBounds().getCenter(),7); } }, error:function(){ $.modal.alertWarning("获取空间信息失败"); } }); }
2、省级风景区可视化展示
通过code关联省级行政区划表和风景区信息表,进行两个图层的空间叠加分析,实现省级区域的风景区划覆盖。前台的核心代码如下所示:
function showScenicSpot(code){ $.ajax({ type:"get", url:prefix + "/datalist/" + code, dataType:"json", cache:false, processData:false, success:function(result){ if(result.code == web_status.SUCCESS){ var strokeStyleSet = "#23168d"; var lat,lng,cityInfo; for(var i=0;i<result.data.length;i++){ var dataInfo = result.data[i]; var geomObj = JSON.parse(dataInfo.geomJson); if(i == 0){ lat = geomObj.coordinates[1]; lng = geomObj.coordinates[0]; continue; } var radiusSize = 5; switch(dataInfo.level) { case '5A': strokeStyleSet = "#c50808"; radiusSize += 7; break; case '4A': strokeStyleSet = "#c37322"; radiusSize += 5; break; case '3A': strokeStyleSet = "#6f8d16"; radiusSize += 3; break; case '2A': strokeStyleSet = "#168d40"; radiusSize += 1; break; default: strokeStyleSet = "#23168d"; } var content = "<strong>名称:</strong>"+dataInfo.name + "<br/><strong>级别:</strong>"+ dataInfo.level; content += "<br/><strong>所属行政区划:</strong>"+ dataInfo.province + "/" + dataInfo.city + "/" + dataInfo.area; content += "<br/><strong>评定时间:</strong>"+ dataInfo.evaluationTime ; var latlng = new L.latLng(geomObj.coordinates[1], geomObj.coordinates[0]); let marker = L.circleMarker(latlng, { radius: radiusSize, color: strokeStyleSet, labelStyle: { offsetX: 0, //横坐标偏移(像素) offsetY: 30, //纵坐标偏移(像素) text: dataInfo.name, rotation: 0, zIndex: radiusSize, minZoom : 5, strokeStyle: strokeStyleSet } }).addTo(showLayerGroup); marker.bindPopup(content); } mymap.addLayer(showLayerGroup); } }, error:function(){ $.modal.alertWarning("获取信息失败"); } }); }
3、成果展示
通过以上的代码,大致实现了风景区的WebGIS展示,这里将选择几个省份将其旅游资源进行集中展示。也让大家了解项目实际的成果。快来看看你所在省份的风景区信息吧。如果没有的省份,感兴趣的可以私聊,可以私发可视化结果哦。
山西省风景区分布图
辽宁省风景区分布图
江苏省风景区分布图
安徽省风景区分布图
四川省风景区分布图
新疆维吾尔自治区风景区分布示意图
总结
以上就是本文的主要内容,本文即是在此背景下创作的,文章首先对数据库中的风景区数据进行简单介绍,然后介绍如何使SpringBoot进行风景区查询功能的设计与实现,接着介绍前端基于Leaflet进行可视化设计,最后使用实际应用,介绍和分析不同省份的风景区旅游资源。行文仓促,定有不足之处,还请朋友们不吝批评指正。