基于SpringBoot实现各省距离Excel导出实战

目录

前言

一、列表及图表信息展示

1、数据过滤调整

2、信息列表及图表展示

3、Excel写入

二、界面可视化

1、Echarts图表和列表展示

2、城市详情和下载功能设计

三、成果展示

1、图表展示

2、部分城市数据分析

总结


前言

今天是五一黄金周假期第二天,不知道各位小伙伴们是出门在外旅游还是路上。在上一篇博文中基于SpringBoot和PostGIS的各省与地级市空间距离分析,在这篇博客中使用SpringBoot基于PostGIS空间数据库实现了对各省省会城市与地级市的空间距离分析。虽然地图保留了很好的空间位置信息,但是对于对于展示数据不是很直观,同时有一些小伙伴说想要提供完整的地市距离列表和统计图表,最好还能分省将对应地市的空间距离导出成excel以方便共享和使用。

本文即是在这样的需求场景下诞生,博文重点介绍如何将地级市空间距离信息在进行列表展示,同时如何制作Echarts地级市柱状图,然后讲述了如何进行Excel数据导出,最后以几个省份为例,分析这几个省份的空间地理距离特点。祖国大好河山,让我们共同奋斗。

一、列表及图表信息展示

本小节重点讲解在应用中如何进行列表及图表信息的展示,其中图表控件采用Echarts进行柱状图展示。这里对各省信息列表进行一个重新过滤。考虑到北京市、上海市、天津市、重庆市这四个直辖市的市中心与下面的区县距离都比较近,因此在省级行政范围中,将直辖市去掉。于此同时,香港特别行政区和澳门特别行政区也是一样的原因,在信息展示页面一并把两个特别行政区去掉,在后面的空间分析中,直辖市和特别行政区都不再进行地级市距离分析范围内,对于区县级别的距离分析中倒是可以考虑纳入进来。

1、数据过滤调整

要实现数据的过滤,在biz_province这张表的类型中进行type过滤,由于采用了Mybatis-Plus框架,这里在写代码时,只需要进行type的not in 过滤即可,下面来看具体的实现:

java 复制代码
@Override
public List<Province> selectList4Gn(Province province) {
	QueryWrapper<Province> queryWrapper = new QueryWrapper<Province>();
    if(StringUtils.isNotBlank(province.getName())){
        queryWrapper.like("name", province.getName());
    }
    queryWrapper.select(" id,code,name,type ");
    List<String> inData = new ArrayList<String>(2);
    inData.add("直辖市");
    inData.add("特别行政区");
    queryWrapper.notIn("type", inData);//过滤特别行政区和直辖市
    queryWrapper.orderByAsc("code");
    return this.baseMapper.selectList(queryWrapper);
}

2、信息列表及图表展示

这里我们在省级行政区划的操作栏新增两个按钮开一个是查看详情按钮,另外一个是下载当前省份空间距离的按钮。先来进行查看详情按钮的设计与实现。在后台的控制器中新增以下两个方法,主要用于详情页面的展示和图表信息的展示。

java 复制代码
@RequiresPermissions("eq:province:distance:details")
@GetMapping("/details/{code}")
public String echartsView(ModelMap mmap,@PathVariable("code") String code){
    List<GeographicName> list = geoNameService.findListByProvinceCode(code);
    mmap.put("dataList", list);
    mmap.put("code", code);
    return prefix + "/details";
 }
    
 @PostMapping("/echarts/provinceline/{code}")
 @ResponseBody
 public AjaxResult provinceLine(@PathVariable("code") String code){
    String charts = geoNameService.buildProvinceChart(code);
    AjaxResult result = AjaxResult.success();
    result.put("data", charts);
    return result;
 }

这里需要注意的是,echarts的图表数据采用后端生成的模式,使用后台的Json构建。

java 复制代码
@Override
	public String buildProvinceChart(String code) {
		String result = "";
		GsonOption option = new GsonOption();
		option.title().text("地级市与省会距离统计图(单位:千米)");
        option.tooltip().trigger(Trigger.axis);
        option.legend("距离");
        option.legend().y(Y.bottom).padding(0);
        option.toolbox().show(true).feature(new MagicType(Magic.line, Magic.bar).show(true), Tool.saveAsImage);
        option.calculable(true);
        List<String> provinces = new ArrayList<String>();
        List<BigDecimal> eqCounts = new ArrayList<BigDecimal>();
        List<GeographicName> dataList = this.findListByProvinceCode(code);
        int index = 0;
        for (GeographicName vo : dataList) {
        	if(index == 0) {
        		index ++;
        		continue;
        	}
        	index ++;
        	provinces.add(vo.getName());
        	eqCounts.add(vo.getDist().divide(new BigDecimal("1000")));
		}
        CategoryAxis categoreAxis = new CategoryAxis();
        categoreAxis.data(provinces.toArray()).axisLabel().interval(0).rotate(20);
        option.xAxis(categoreAxis);
        option.yAxis(new ValueAxis());
        Bar bar = new Bar("距离");
        bar.data(eqCounts.toArray());
        bar.markPoint().data(new PointData().type(MarkType.max).name("最大值"), new PointData().type(MarkType.min).name("最小值"));
        bar.markLine().data(new PointData().type(MarkType.average).name("平均值"));
        option.series(bar);
        result = option.toString();
		return result;
	}

3、Excel写入

为了方便直接下载指定省份下辖地级市的空间城市距离。我们将对应省份下属行政区直接以excel信息的形式给出,同时在excel中直接把各地级市与省会城市的直线距离也列了出来。为了直接使用JavaBean的模式来定义excel列,需要在model层进行属性绑定。通过类中的@Excel(name = "城市距离(单位:米)")来配置当前属性对应的导出excel列。

java 复制代码
package com.yelang.project.extend.earthquake.domain;
import java.io.Serializable;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.yelang.framework.aspectj.lang.annotation.Excel;
import com.yelang.framework.handler.PgGeometryTypeHandler;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
@TableName(value ="biz_geographic_name",autoResultMap = true)
public class GeographicName implements Serializable{
	private static final long serialVersionUID = -3694849578429480952L;
	@TableId(value = "pk_id")
	@Excel(name = "序号")
	private Long pkId;
	@Excel(name = "城市名称")
	private String name;
	@Excel(name = "城市汉语拼音")
	private String pinyin;
	@Excel(name = "类别")
	private String classz;
	@Excel(name = "备注")
	private String bz;
	private String slx;
	public GeographicName(String name, String pinyin, String classz, String bz, String slx, String geom) {
		super();
		this.name = name;
		this.pinyin = pinyin;
		this.classz = classz;
		this.bz = bz;
		this.slx = slx;
		this.geom = geom;
	}

	@TableField(typeHandler = PgGeometryTypeHandler.class)
	private String geom;
	@TableField(exist=false)
	private String geomJson;  
	@TableField(exist=false)
	@Excel(name = "城市距离(单位:米)")
	private BigDecimal dist;//距离
}

在controller中调用api进行excel的写入,关键的业务逻辑代码如下所示:

java 复制代码
@RequiresPermissions("eq:province:distance:export")
@Log(title = "城市距离", businessType = BusinessType.EXPORT)
@PostMapping("/export/{code}")
@ResponseBody
public AjaxResult export(@PathVariable("code") String code){
   List<GeographicName> list = geoNameService.findListByProvinceCode(code);
   ExcelUtil<GeographicName> util = new ExcelUtil<GeographicName>(GeographicName.class);
   Province province = provinceService.findByCode(code);
   return util.exportExcel(list, province.getName() + "城市距离信息表");
}

通过以上的代码基本就实现了数据导出和可视化的后台代码的编写。下面根据需要,我们来提供对应的前端页面代码逻辑。

二、界面可视化

本小节主要对界面可视化的设计与代码实现进行深度说明,主要包含两个子功能点,第一个Echarts图表和列表展示,第二个是下载指定省份的城市信息列表,闲言少语,直接进入正题。

1、Echarts图表和列表展示

俗话说,一图胜千言,一个比较好的统计表格,能快速的让大家迅速的理解数据要素。所以与枯燥的静态表格相比,我们提供一个对应地级市的空间距离柱状分布图。为了有一个直观的感受,我们将图表放置在页面的左边,而列表信息放在页面的右边。

javascript 复制代码
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
	<th:block th:include="include :: header('ECharts')" />
</head>
<body class="gray-bg">
         <div class="row">
           <div class="col-sm-7">
               <div class="ibox float-e-margins">
                   <div class="ibox-title">
                       <h5>距离统计</h5>
                       <div class="ibox-tools">
                           <a class="collapse-link">
                               <i class="fa fa-chevron-up"></i>
                           </a>
                           <a class="dropdown-toggle" data-toggle="dropdown" href="#">
                               <i class="fa fa-wrench"></i>
                           </a>
                           <ul class="dropdown-menu dropdown-user">
                               <li><a href="#">选项1</a>
                               </li>
                               <li><a href="#">选项2</a>
                               </li>
                           </ul>
                           <a class="close-link">
                               <i class="fa fa-times"></i>
                           </a>
                       </div>
                   </div>
                   <div class="ibox-content">
                       <div style="height:350px" id="echarts-province-chart"></div>
                   </div>
               </div>
           </div>
           <div class="col-sm-5">
               <div class="ibox float-e-margins">
                   <div class="ibox-content">
                      <table class="table table-bordered white-bg">
                           <thead>
                               <tr>
                                   <th width="50%">城市名称</th>
                                   <th>距离(公里)</th>
                               </tr>
                           </thead>
                           <tbody>
                           	<tr th:each="data,itemStat:${dataList}" th:if="${itemStat.count > 1}">
                                   <td >[[${itemStat.count - 1}]]、[[${data.name}]]</td>
                                   <td th:text="${#numbers.formatDecimal((data.dist / 1000 ), 1, 2)}"></td>
                               </tr>
                           </tbody>
                       </table>
                   </div>
               </div>
           </div>
    </div>
    <th:block th:include="include :: footer" />
    <th:block th:include="include :: echarts-js" />
    <script type="text/javascript">
    	var charts = new Array();
    	var provinceLineChart;
    	var minprovinceChart;
    	var mapoption;
	    $(function () {
	     provinceLineChart = echarts.init(document.getElementById("echarts-province-chart"));
        $(window).resize(provinceLineChart.resize);
	        charts.push(provinceLineChart);
 	        initProvinceChart();
	        
	        $(window).resize(function() {
	            for(var i = 0; i < charts.length; i++) {
	                charts[i].resize();
	            }
	        });
	        
	        //解决tab切换不显示问题 在加载窗口后重新渲染。
	        $('a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
	            for(var i = 0; i < charts.length; i++) {
	                charts[i].resize();
	            }
	        });
	    });
	    
	    function initProvinceChart(){
	    	var code = [[${code}]];
			$.ajax({
		    	   type: "POST",
		    	   url: ctx + "eq/province/distance/echarts/provinceline/" + code,
		    	   data: {},
		    	   dataType : "json",
		    	   success: function(serverdata){
		    		   if(serverdata != ""){
		    		   		var serverJsonData = eval('('+serverdata.data+')');
		    		   		provinceLineChart.setOption(serverJsonData,true);
		    		   		$(window).resize(provinceLineChart.resize);
		    		   }
		    	   },
		    	   error:function(data){
		    		   parent.layer.alert('系统发生错误!', {icon: 5});
		    	   }
	    	});
		}
    </script>
</body>
</html>

2、城市详情和下载功能设计

我们在省级行政区的操作栏中增加一个"查看详情"按钮和"下载"按钮。用户点击"查看详情"会自动打开一个弹出框,将城市对应的所有地级市进行数据展示。点击"下载"按钮,则会自动下载对应行政区划的数据成excel格式的。因此需要在原来的列表中进行修改,同时增加两个处理的逻辑函数。

javascript 复制代码
{
   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="preview(\'' + row.code + '\',\''+row.id+'\')""><i class="fa fa-send-o"></i></a> ');
             actions.push('<a class="btn btn-info btn-xs ' + editFlag + '" href="javascript:void(0)" onclick="details(\'' + row.code + '\',\''+row.id+'\')""><i class="fa fa-eye"></i></a>  ');
              actions.push('<a class="btn btn-primary btn-xs ' + editFlag + '" href="javascript:void(0)" onclick="downloadFile(\'' + row.code + '\',\''+row.id+'\')""><i class="fa fa-download"></i></a>');
             return actions.join('');
      }
}

对应的查看详情的处理方法如下:

javascript 复制代码
function details(code,id){
	parent.layer.open({
		type: 2,
		title: ["详情",'font-size:16px;text-align:center;font-weight: bold;'],
		scrollbar:false,
		area: ['80%', '90%'],
		content: prefix + "/details/" + code ,
		btn:[],
		yes:function(index,layero){},
		cancel: function(index, layero){ 
		parent.layer.close(index);
		return false; 
	   } 
	}); 
 }

下载附件的时候需要注意,实际生成excel时,会先将文件写入到本次,然后通过公共接口去下载数据,然后在后台删除对应的物理文件。实际情况可以根据我们的需求来合理调整相应的逻辑。

javascript 复制代码
function downloadFile(code,id){
    var url = prefix + '/export/' + code;
    $.post(url, {}, function(result) {
    if (result.code == web_status.SUCCESS) {
          window.location.href = ctx + "common/download?fileName=" + encodeURI(result.msg) + "&delete=" + true;
    } else if (result.code == web_status.WARNING) {
           $.modal.alertWarning(result.msg)
    } else {
           $.modal.alertError(result.msg);
    }
       $.modal.closeLoading();
    });
}

经过以上的步骤,基本完成前端的业务逻辑的编写,下面进行前后端的功能继承,测试相关功能是否达到我们的实际需求。

三、成果展示

本小节主要完成对相关开发成果的综合展示,包括echarts图表和标准网页列表,以及excel附件的下载。除了展示具体的成果,我们也结合相关的统计数据,对展示的图表信息进行一个简单分析,当然,欢迎其它小伙伴对数据进行准确的分析,也可以结合空间位置和其他参数进行空间建模。一起发现时空之美。

1、图表展示

这里主要以系统截图的方式实现对系统图表功能和相关列表的展示进行演示。

河北省地级市距离展示示意图

点击列表操作栏中的第二个按钮,即查看详情按钮,点击查看详情后,可以详细看到其图表统计分析结果和列表统计信息。

地级市与省会距离详情页面

与查看详情类似,在操作栏中可以直接点击下载,将实现直接下载对应省份的相关数据表格。 说明,由于该数据采用的是2015年的,随着现在城市化的进程,涉及到一些行政区划合并、删除、划转。本文中的数据有一个不是很准确,如果数据不对,仅作为参考,最标准的数据还是以自然资源部们发布的官方数据为准。

|---------------------|----------|------------------|--------|--------|-----------------|
| 序号 | 城市名称 | 城市汉语拼音 | 类别 | 备注 | 城市距离(单位:米) |
| 1783889052488028162 | 石家庄市 | Shijiazhuang Shi | AD | 省会城市 | 0 |
| 1783888852373487639 | 衡水市 | Hengshui Shi | AD | 地级市 | 106931.86747708 |
| 1783888852373487637 | 邢台市 | Xingtai Shi | AD | 地级市 | 107691.70790783 |
| 1783888852373487635 | 保定市 | Baoding Shi | AD | 地级市 | 124101.09462634 |
| 1783888852373487643 | 邯郸市 | Handan Shi | AD | 地级市 | 157107.89379521 |
| 1783888852373487636 | 沧州市 | Cangzhou Shi | AD | 地级市 | 205780.92881549 |
| 1783888852373487632 | 廊坊市 | Langfang Shi | AD | 地级市 | 251191.02372759 |
| 1783888850716737539 | 张家口市 | Zhangjiakou Shi | AD | 地级市 | 304396.28680783 |
| 1783888852373487633 | 唐山市 | Tangshan Shi | AD | 地级市 | 363795.21794606 |
| 1783888850716737540 | 承德市 | Chengde Shi | AD | 地级市 | 438525.3515243 |
| 1783888852373487634 | 秦皇岛市 | Qinhuangdao Shi | AD | 地级市 | 487995.12411063 |

2、部分城市数据分析

下满我们依然选取几个省份来进行数据分析。以上面河北省为例,首先我们在百度中查询河北的城市信息。

河北省(中华人民共和国省级行政区),别称冀,全省面积18.88万平方千米,辖石家庄、唐山、秦皇岛、邯郸、邢台、保定、张家口、承德、沧州、廊坊、衡水11个市。从河北的城市距离来看,300公里范围内的城市就有7个,所有城市不超过500公里。

下面来看经济比较强的一些省份的空间距离,先来看看江苏省的。

江苏省下辖南京市、无锡市 、徐州市、常州市、苏州市、南通市、连云港市、淮安市、盐城市、扬州市、镇江市、泰州市、宿迁市13个地级市。其各城市空间距离如下:

城市名称 距离(公里)
1、镇江市 60.78
2、扬州市 68.81
3、常州市 114.65
4、泰州市 114.78
5、无锡市 156.80
6、淮安市 167.91
7、苏州市 189.46
8、盐城市 191.73
9、南通市 195.58
10、宿迁市 216.50
11、徐州市 276.52
12、连云港市 283.91

可以看到,江苏省内的各地级市,其空间距离9个事200公里以内,所有城市都在300公里以内。这样的距离使得各个地方的经济发展更加密切吧。

为了节约篇幅,这里再列一个省份,新疆维吾尔自治区。新疆现有14个地(州、市),包括5个自治州、5个地区和乌鲁木齐、克拉玛依、吐鲁番、哈密4个地级市。

城市名称 距离(公里)
1、昌吉回族自治州 32.24
2、吐鲁番市 160.23
3、巴音郭楞蒙古自治州 258.77
4、克拉玛依市 291.06
5、阿勒泰地区 448.38
6、博尔塔拉蒙古自治州 458.59
7、塔城地区 487.24
8、塔城地区 487.24
9、哈密地区 491.18
10、伊犁哈萨克自治州 505.86
11、阿克苏地区 672.69
12、和田地区 989.58
13、克孜勒苏柯尔克孜自治州 1054.77
14、喀什地区 1081.40

通过空间距离可以看到,新疆真的地大,全疆离乌鲁木齐市300公里内的只有3个城市,和田地区和喀什地区等将近1000公里。这么远的距离,对于经济和人员交流都是一种限制吧。

总结

好了,以上就是本文的主要内容。 本文即是在这样的需求场景下诞生,博文重点介绍如何将地级市空间距离信息在进行列表展示,同时如何制作Echarts地级市柱状图,然后讲述了如何进行Excel数据导出,最后以几个省份为例,分析这几个省份的空间地理距离特点。五一劳动节,致敬伟大的劳动人民,致敬每一位奋斗者,让我们都保持昂的扬斗志不断奋斗。