从零开始学GeoServer源码十二(GeoServer中的切片规则)

快速导航

从零开始学GeoServer源码一(开篇)
从零开始学GeoServer源码二(搭建开发环境)
从零开始学GeoServer源码三(断点应该打在哪?)
从零开始学GeoServer源码四(自定义插件或拓展数据源)
从零开始学GeoServer源码五(切片原理及自定义插件支持wms、wmts、tms)
从零开始学GeoServer源码六(如何打包发布?)
从零开始学GeoServer源码七(如何注册服务并发布3dtiles和cesium的地形terrain?)
从零开始学GeoServer源码八(内存溢出?Out of Memory Error ?)
从零开始学GeoServer源码九(如何集成Cesium以实现预览3dtiles和terrain服务?)
从零开始学GeoServer源码十(如何修改菜单项以整合我们的功能?)
从零开始学GeoServer源码十一(如何解决No Multipart-config for Servlet错误)
从零开始学GeoServer源码十二(GeoServer中的切片规则)
从零开始学GeoServer源码十三(GeoServer生成的矢量切片缺失问题)
从零开始学GeoServer源码十四(GeoServer Cloud微服务版本初体验)

点我去AIGIS公众号查看本文

1.前言

  众所周知,GeoServer 中的切片并不是TMS 规则的切片,因此本期我们要聊一聊 GeoServer 中的切片规则,以及如何在 OpenLayers 中加载 GeoServer 的切片。以及如何在GeoServer 中设置切片的规则,和已经切好的切片如何转换为TMS 切片,当然我们也要聊一聊在这一过程中有哪些疑难杂症。

2.切片原理

  切片原理可以参考作者之前的另一篇博文从零开始学GeoServer源码五(切片原理及自定义插件支持wms、wmts、tms),我在这里要说的是,不论是 wmts 还是 tms ,他们都是同一个切片原理,只是存储方式的不同和访问时的方式不同而已。切片原理其实不难理解,只是在前端拼接的时候很多人容易产生混乱。

3.一探究竟,揭开GeoServer中切片的神秘面纱

  因为 GeoServer 会默认自动生成切片,也就是说当访问的切片不存在的时候,GeoServer 就会自动去生成这个切片。或者我们也可以手动去生成,即在左侧菜单栏点击 Tile Layers,然后找到想要的切片的图层,点击 Seed/Truncate 进入切片界面。当我们在 GeoServer 中生成了切片以后,默认会存储在 data_dir/gwc 文件夹中。
在这里插入图片描述
  我们以 china_bgmap_world 图层为例,生成了切片,我们点进去看看切片长什么样。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  怎么说呢?确实很魔性。和普通的 TMS 规则的切片命名完全不一样。乍一看,确实令人无从下手。但是当我们仔细阅读 GeoServer 的使用文档,就会发现官方文档里就有介绍。这里我把官方地址贴上来看看GeoServer2.19用户手册。详细的计算方式也贴出来GeoWebCache默认切片命名源码,读者可以自行查看。

在这里插入图片描述

  我们在左侧菜单里也能找到类似的设置:
在这里插入图片描述
在这里插入图片描述
  默认采用的是 GeoWebCache 的命名方式。回到第1级的文件夹里,对比官方文档,我们就知道了,第1级的文件夹中的两个文件夹是将全球分为了两个正方形,即左半球和右半球。他们的命名方式是 xc_yc ,其中 xcyc 的计算方式为:

xc=x/(2^(z/2))
yc=y/(2^(z/2))

也就是对应这个文件夹里的命名方式
在这里插入图片描述
  而 0_0 和 1_0 中的切片命名方式则是 x_yx为列,y为行,也就是说为 列_行。这两文件夹里的切片都可以拼接成一个正方形,我们来手动拼一下 0_0 来看看。
拼接前:
在这里插入图片描述
拼接后:
在这里插入图片描述
Perfect!完美!果然是个正方形,这是左半球,那显然1_0 就是右半球了。
在这里插入图片描述

4.坐标原点在哪?

  在搞明白切片的命名规则之后,我们接下来的工作就是如何加载了。我们还是以OpenLayers 为例来讲解怎么加载。但是在此之前,我们还有一个问题要搞明白,坐标原点在哪?因为坐标原点关系着我们的加载方式。
  还是参考官方文档,我们发现,默认的切片方式坐标原点在左下角,y轴方向为从南到北。和 OSGEO TMS 的坐标是一致的。而最后一种切片方式叫 SLIPYY ,有的版本叫做 XYZ 或者 Google TMSy轴方向为从北到南
在这里插入图片描述

5.加载

5.1 WMTS

  wmts 的加载比较简单,因为传递的参数里包括了很多信息,GeoServer 可以根据这些信息进行各种坐标系的转换,以及切片规则的转换。所以我们不再赘述,直接上 OpenLayers 代码。

var wmts = new ol.layer.Tile({
    source: new ol.source.WMTS({
        url: "http://localhost:8085/geoserver/gwc/service/wmts",
        layer: 'china:bgmap_world',
        matrixSet: 'EPSG:4326',
        format: 'image/jpeg',
        projection: 'EPSG:4326',
        tileGrid: new ol.tilegrid.WMTS({
            tileSize: [256,256],
            origin:[-180,90],
            resolutions: [0.703125, 0.3515625, 0.17578125, 0.087890625, 0.0439453125, 0.02197265625, 0.010986328125, 0.0054931640625, 0.00274658203125, 0.001373291015625, 6.866455078125E-4, 3.4332275390625E-4, 1.71661376953125E-4, 8.58306884765625E-5, 4.291534423828125E-5, 2.1457672119140625E-5, 1.0728836059570312E-5, 5.364418029785156E-6, 2.682209014892578E-6, 1.341104507446289E-6, 6.705522537231445E-7, 3.3527612686157227E-7],
            matrixIds: ['EPSG:4326:0', 'EPSG:4326:1', 'EPSG:4326:2', 'EPSG:4326:3', 'EPSG:4326:4', 'EPSG:4326:5', 'EPSG:4326:6', 'EPSG:4326:7', 'EPSG:4326:8', 'EPSG:4326:9', 'EPSG:4326:10', 'EPSG:4326:11', 'EPSG:4326:12', 'EPSG:4326:13', 'EPSG:4326:14', 'EPSG:4326:15', 'EPSG:4326:16', 'EPSG:4326:17', 'EPSG:4326:18', 'EPSG:4326:19', 'EPSG:4326:20', 'EPSG:4326:21']
        }),
        style: '',
        wrapX: false
    })
});

5.2 TMS

//google tms
var googletms=new ol.layer.Tile({
    source: new ol.source.XYZ({
        url: "http://localhost:8085/geoserver/gwc/service/tms/1.0.0/china:bgmap_world@EPSG:4326@jpeg/{z}/{x}/{y}.jpeg",
    }),
});
//osgeo tms
var osgeotms=new ol.layer.Tile({
    source: new ol.source.XYZ({
        url: "http://localhost:8085/geoserver/gwc/service/tms/1.0.0/china:bgmap_world@EPSG:4326@jpeg/{z}/{x}/{-y}.jpeg",
    }),
});

5.3 特殊情况下的TMS

  我们知道 OpenLayers 中的 ol.source.XYZ 类可以在 tileUrlFunction 中自定义 url 的路径,这种就比较适合一些特殊情况下的 tms 。什么叫做特殊情况?我们来看个例子。还是以 china:bgmap_world 图层为例。当我们在 GeoServer 中完成切片以后,进行加载。

var osgeotms=new ol.layer.Tile({
    source: new ol.source.XYZ({
        url: "http://localhost:8085/geoserver/gwc/service/tms/1.0.0/china:bgmap_world@EPSG:4326@jpeg/{z}/{x}/{-y}.jpeg",
    }),
});

在这里插入图片描述
发现了什么?明显加载不对,只加载了一部分,于是调整加载方式:

var tms=new ol.layer.Tile({
   source: new ol.source.XYZ({
       url: "http://localhost:8085/geoserver/gwc/service/tms/1.0.0/china:bgmap_world@EPSG:4326@jpeg/{z}/{x}/{y}.jpeg",
       format: "image/jpeg",
       projection: projection,
       tileGrid: new ol.tilegrid.WMTS({
           tileSize: [256,256],
           origin: [-180.0, 90.0],
           resolutions: [0.703125, 0.3515625, 0.17578125, 0.087890625, 0.0439453125, 0.02197265625, 0.010986328125, 0.0054931640625, 0.00274658203125, 0.001373291015625, 6.866455078125E-4, 3.4332275390625E-4, 1.71661376953125E-4, 8.58306884765625E-5, 4.291534423828125E-5, 2.1457672119140625E-5, 1.0728836059570312E-5, 5.364418029785156E-6, 2.682209014892578E-6, 1.341104507446289E-6, 6.705522537231445E-7, 3.3527612686157227E-7],
           matrixIds: ['EPSG:4326:0', 'EPSG:4326:1', 'EPSG:4326:2', 'EPSG:4326:3', 'EPSG:4326:4', 'EPSG:4326:5', 'EPSG:4326:6', 'EPSG:4326:7', 'EPSG:4326:8', 'EPSG:4326:9', 'EPSG:4326:10', 'EPSG:4326:11', 'EPSG:4326:12', 'EPSG:4326:13', 'EPSG:4326:14', 'EPSG:4326:15', 'EPSG:4326:16', 'EPSG:4326:17', 'EPSG:4326:18', 'EPSG:4326:19', 'EPSG:4326:20', 'EPSG:4326:21']
       }),
       wrapX: false
   }),
});

在这里插入图片描述
这次发现了什么?列号正常,行号反了。为什么会这样?这就是我们在从零开始学GeoServer源码五(切片原理及自定义插件支持wms、wmts、tms)中讲过的,osgeo tmsgoogle tms 行号是反的。现在我们切是用 osgeo tms 规则切的,加载使用 google tms 加载的,当然是反的,所以再调整一下代码:

var tms=new ol.layer.Tile({
    source: new ol.source.XYZ({
        extent:[-180,-90,180,90],
        wrapX: false,
        format: "image/jpeg",
        projection: projection,
        tileGrid: new ol.tilegrid.WMTS({
            extent:[-180,-90,180,90],
            tileSize: [256,256],
            origin: [-180.0, 90.0],
            resolutions: [0.703125, 0.3515625, 0.17578125, 0.087890625, 0.0439453125, 0.02197265625, 0.010986328125, 0.0054931640625, 0.00274658203125, 0.001373291015625, 6.866455078125E-4, 3.4332275390625E-4, 1.71661376953125E-4, 8.58306884765625E-5, 4.291534423828125E-5, 2.1457672119140625E-5, 1.0728836059570312E-5, 5.364418029785156E-6, 2.682209014892578E-6, 1.341104507446289E-6, 6.705522537231445E-7, 3.3527612686157227E-7],
            matrixIds: ['EPSG:4326:0', 'EPSG:4326:1', 'EPSG:4326:2', 'EPSG:4326:3', 'EPSG:4326:4', 'EPSG:4326:5', 'EPSG:4326:6', 'EPSG:4326:7', 'EPSG:4326:8', 'EPSG:4326:9', 'EPSG:4326:10', 'EPSG:4326:11', 'EPSG:4326:12', 'EPSG:4326:13', 'EPSG:4326:14', 'EPSG:4326:15', 'EPSG:4326:16', 'EPSG:4326:17', 'EPSG:4326:18', 'EPSG:4326:19', 'EPSG:4326:20', 'EPSG:4326:21']
        }),
        tileUrlFunction: function (tileCoord) {
            console.log(tileCoord)
            var z = tileCoord[0];
            var x = tileCoord[1];
            var y = tileCoord[2];
            y = Math.pow(2, z) - tileCoord[2] -1;
            // console.log("old",tileCoord,"new",[z,x,y]);
            return "http://localhost:8085/geoserver/gwc/service/tms/1.0.0/china:bgmap_world@EPSG:4326@jpeg/" + z + "/" + x + "/" + y + ".jpeg";
        },
        
    }),
});

注意重点是这句代码:

y = Math.pow(2, z) - tileCoord[2] -1;

相信这种写法很多人都知道,这是 osgeo tmsgoogle tms 行号转换的方法,但是很多人没有注意到的是,如果行号都是正数,的确用这种算法,但是不知什么原因,OpenLayers 中识别到的行号是个负值,所以就不能这么计算了。我们来看下控制台打印出来的行列号:
在这里插入图片描述
看到了吧,行号是负数,第0级是1行2列,分别对应的是 0/0/0 和0/1/0,显然控制台打印的行值是错误的,那么怎么改呢?第0级是总行数1行,也就是20=1,再加上这个错误的行值,-1+20=0,这不就拿到正确的行值了吗?所以,继续修改代码:

var tms=new ol.layer.Tile({
    source: new ol.source.XYZ({
        extent:[-180,-90,180,90],
        wrapX: false,
        format: "image/jpeg",
        projection: projection,
        tileGrid: new ol.tilegrid.WMTS({
            extent:[-180,-90,180,90],
            tileSize: [256,256],
            origin: [-180.0, 90.0],
            resolutions: [0.703125, 0.3515625, 0.17578125, 0.087890625, 0.0439453125, 0.02197265625, 0.010986328125, 0.0054931640625, 0.00274658203125, 0.001373291015625, 6.866455078125E-4, 3.4332275390625E-4, 1.71661376953125E-4, 8.58306884765625E-5, 4.291534423828125E-5, 2.1457672119140625E-5, 1.0728836059570312E-5, 5.364418029785156E-6, 2.682209014892578E-6, 1.341104507446289E-6, 6.705522537231445E-7, 3.3527612686157227E-7],
            matrixIds: ['EPSG:4326:0', 'EPSG:4326:1', 'EPSG:4326:2', 'EPSG:4326:3', 'EPSG:4326:4', 'EPSG:4326:5', 'EPSG:4326:6', 'EPSG:4326:7', 'EPSG:4326:8', 'EPSG:4326:9', 'EPSG:4326:10', 'EPSG:4326:11', 'EPSG:4326:12', 'EPSG:4326:13', 'EPSG:4326:14', 'EPSG:4326:15', 'EPSG:4326:16', 'EPSG:4326:17', 'EPSG:4326:18', 'EPSG:4326:19', 'EPSG:4326:20', 'EPSG:4326:21']
        }),
        tileUrlFunction: function (tileCoord) {
            console.log(tileCoord)
            var z = tileCoord[0];
            var x = tileCoord[1];
            var y = tileCoord[2];
            y = Math.pow(2, z) + tileCoord[2];
            // console.log("old",tileCoord,"new",[z,x,y]);
            return "http://localhost:8085/geoserver/gwc/service/tms/1.0.0/china:bgmap_world@EPSG:4326@jpeg/" + z + "/" + x + "/" + y + ".jpeg";
        },
        
    }),
});

在这里插入图片描述
注意重点是这句代码:

y = Math.pow(2, z) + tileCoord[2] ;

现在就正常了。

6.GeoServer默认切片怎么转为TMS规则切片

  这个其实很简单了,我们在上面已经对 GeoServer 中的切片规则进行了分析,那么只需要一个简单的小程序,就能转换,我给出一个 demo,以 java 代码为例:

@RequestMapping(value ="/totms",method = RequestMethod.GET)
@ResponseBody
public void GeoServerToTMS() throws IOException {
    String path="F:\\geoserver-2.19.3-bin)\\geoserver-2.19.3-bin\\data dir\\gwc\\china bgmap_world";
    ReadFileRecuritily(path);
}
private void ReadFileRecuritily(String path) throws IOException {
    File source = new File(path);
    File[] files = source.listFiles();
    String[] split = path.split("\\\\");
    String levelName = split[split.length - 2];
    String[] levelNameSplit = levelName.split("_");
    Integer level = 0;
    if (levelName.startsWith("EPSG")) {
        level = Integer.parseInt(levelNameSplit[levelNameSplit.length - 1]);
    }
    for (int i = 0; i < files.length; i++) {
        //如果是文件夹,递归
        if (files[i].isDirectory()) {
            ReadFileRecuritily(files[i].getPath());
        } else {
            //如果是文件,读取
            if (files[i].isFile()) {
                String name = files[i].getName().replace(".jpeg", "");
                String[] s = name.split("_");
                Integer col = Integer.parseInt(s[0]);
                Integer row = Integer.parseInt(s[1]);
                //接贝到另一个地方
                File tar = new File("D:\\test\\" + level + "\\" + col + "\\" + row + ".jpeg");
                FileUtils.copyFile(files[i], tar);
            }
        }
    }
}

7.总结

  本文我们通过深入的分析 osgeo tmsgoogle tms 的规则,以及 GeoServer 中的切片规则设置与切片原理,以及 OpenLayers 中出现的诡异行值进行了深入的分析,最后根据切片原理,计算出我们需要的行值。本文是一个发现问题,思考问题,解决问题的过程,在理解上是有一定难度的,希望读者仔细揣摩。

更多精彩内容见公众号AIGIS

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AIGIS.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值