OpenLayer3读取Geoserver切割的离线瓦片

前言:

由于在网上搜索了很久都没有找到相关的东西,只能自己埋头研究,本文我付出了整整40个小时的心血来解读,希望大家若要转载一定要注上作者名及地址。

Geoserver的离线瓦片生成

主要的功能全在Tile Caching上,要注意的是,必须先设置好你所要的图层的坐标系(注:本例为了讲解改变了坐标系范围,建议不要改,以配合其他图层)。
  • 在Gridsets下选择/新建一个坐标系分割模版,计算好范围后,选择add zoom level,添加完毕后保存(注意,这些参数非常重要,勿乱改)
OpenLayer3读取Geoserver切割的离线瓦片_第1张图片
  • 在你的图层设置中,找到tile设置,更换为你的分割模版

  • 进入Tile Layers中,新建/选择你的图层,选中Seed/Truncate(如果你事先做过,先选清空),进入界面后如图设置(第一项是线程,想快点就多,除了Gridset必须一致外,其他随你便)
OpenLayer3读取Geoserver切割的离线瓦片_第2张图片
  • 完成后在你的geoserver默认数据文件夹内的gwc下找到你的图层文件,文件结构如下图
OpenLayer3读取Geoserver切割的离线瓦片_第3张图片

Geoserver瓦片分割原理

瓦片主要由xyz三部分组成,z是放大级别,x为水平轴,y为垂直轴(不同体系下的原点不同,在Geoserver中原点在图层的左下角,即xy轴就是最日常的笛卡尔,所以x可称为列号,y可称为行号)。
OpenLayer3读取Geoserver切割的离线瓦片_第4张图片

瓦片行列号计算

必要条件:地图范围(即上图红框)、当前级别分辨率、瓦片的范围
var deltaX = Math.ceil(((extent[2] - extent[0]) / resolutions[z]) / 256); //地图的宽度除以(当前分辨率*瓦片宽度)= 列数,向上取整
var deltaY = Math.ceil(((extent[3] - extent[1]) / resolutions[z]) / 256); //地图的长度除以(当前分辨率*瓦片长度)= 行数,向上取整
条件获取方式:最快的是在tile Layer里打开你的瓦片图层,右键查看源代码。
OpenLayer3读取Geoserver切割的离线瓦片_第5张图片

在程序中实现读取


/**
 * 本文件用于调用geoserver切割的瓦片
 * @author Wilson.Yan
 * @version 2016.8.21
 */
//改变数字格式
function fix(str,length){
	var temp = "00" + str.toString();
	return temp.substr(temp.length-2,length);
}

//外部调用测试函数
function addLocal(){
	//实例化地图
	var map = new ol.Map({
		target:'map',
		layers:[], //空图层
		view:new ol.View({
			projection: new ol.proj.Projection({
		          code: 'EPSG:4610',
		          units: 'degrees', //一定要换成经纬度显示,不然根本没法计算
		          axisOrientation:'neu'
		         
		      }),
			zoom:0,
			maxZoom:10,
			minZoom:2,  //较为特殊的地方,要注意
			center:[100,35] //必填,不然不显示(待定)
		})
	});
	var extent = [73.62,3.157730076656914,135.315902709961,53.80867363506534];//地图边框,在geoserver内查看
	//gridset分辨率,geoserver内查看
	var resolutions = [0.2388671875, 0.11943359375, 0.059716796875, 0.0298583984375, 0.01492919921875];
	var url = './test_bou2_4l/{z}/{f}/{x}_{y}.png'; //初步的url
	//定义数据源
	var source = new ol.source.TileImage({
		projection:ol.proj.get('EPSG:4610'),
		tileSize:256,
		maxZoom:4,
		minZoom:0,
/*		urls:getUrls(z,extent,resolutions),*/
		tileGrid:new ol.tilegrid.TileGrid({
			origin:[73.62,3.1577],//ol.extent.getBottomLeft() Geoserver瓦片原点在左下角
			resolutions:resolutions, //5个
			tileSize:[256, 256]
			}),
		tileUrlFunction:function(tileCoord){
			var z = tileCoord[0];
			var x = tileCoord[1];
			var y = tileCoord[2];
			var re = [z,x,y];
			console.log(re);
			switch(z){ //暂时没有太好的方法解决,只能用较笨的方法
			case 0: //0级
				return url.replace('{z}','EPSG_4610_0' + z.toString())
				.replace('{f}','0_0')
				.replace('{x}',fix(x,2))
				.replace('{y}',fix(y,2));
			  break;
			case 1: //1级
				if(x<2){
					return url.replace('{z}','EPSG_4610_0' + z.toString())
					.replace('{f}','0_0')
					.replace('{x}',fix(x,2))
					.replace('{y}',fix(y,2));
				}
				else{
					return url.replace('{z}','EPSG_4610_0' + z.toString())
					.replace('{f}','1_0')
					.replace('{x}',fix(x,2))
					.replace('{y}',fix(y,2));
				}
			  break;
			case 2:
				if(x<4){
					return url.replace('{z}','EPSG_4610_0' + z.toString())
					.replace('{f}','0_0')
					.replace('{x}',fix(x,2))
					.replace('{y}',fix(y,2));
				}
				else{
					return url.replace('{z}','EPSG_4610_0' + z.toString())
					.replace('{f}','1_0')
					.replace('{x}',fix(x,2))
					.replace('{y}',fix(y,2));
				}
				break;		
			case 3:
				if(x==8){
					if(y<4){
						return url.replace('{z}','EPSG_4610_0' + z.toString())
						.replace('{f}','2_0')
						.replace('{x}',fix(x,2))
						.replace('{y}',fix(y,2));
					}
					else{
						return url.replace('{z}','EPSG_4610_0' + z.toString())
						.replace('{f}','2_1')
						.replace('{x}',fix(x,2))
						.replace('{y}',fix(y,2));
					}
				}
				else{
					if(x<4){
						if(y<4){
							return url.replace('{z}','EPSG_4610_0' + z.toString())
							.replace('{f}','0_0')
							.replace('{x}',fix(x,2))
							.replace('{y}',fix(y,2));
						}
						else{
							return url.replace('{z}','EPSG_4610_0' + z.toString())
							.replace('{f}','0_1')
							.replace('{x}',fix(x,2))
							.replace('{y}',fix(y,2));
						}
					}
					else{
						if(y<4){
							return url.replace('{z}','EPSG_4610_0' + z.toString())
							.replace('{f}','1_0')
							.replace('{x}',fix(x,2))
							.replace('{y}',fix(y,2));
						}
						else{
							return url.replace('{z}','EPSG_4610_0' + z.toString())
							.replace('{f}','1_1')
							.replace('{x}',fix(x,2))
							.replace('{y}',fix(y,2));
						}
					}
				}
				break;
			case 4:
				if(x==16){
					if(y<8){
						return url.replace('{z}','EPSG_4610_0' + z.toString())
						.replace('{f}','2_0')
						.replace('{x}',fix(x,2))
						.replace('{y}',fix(y,2));
					}
					else{
						return url.replace('{z}','EPSG_4610_0' + z.toString())
						.replace('{f}','2_1')
						.replace('{x}',fix(x,2))
						.replace('{y}',fix(y,2));
					}
				}
				else{
					if(x<8){
						if(y<8){
							return url.replace('{z}','EPSG_4610_0' + z.toString())
							.replace('{f}','0_0')
							.replace('{x}',fix(x,2))
							.replace('{y}',fix(y,2));
						}
						else{
							return url.replace('{z}','EPSG_4610_0' + z.toString())
							.replace('{f}','0_1')
							.replace('{x}',fix(x,2))
							.replace('{y}',fix(y,2));
						}
					}
					else{
						if(y<8){
							return url.replace('{z}','EPSG_4610_0' + z.toString())
							.replace('{f}','1_0')
							.replace('{x}',fix(x,2))
							.replace('{y}',fix(y,2));
						}
						else{
							return url.replace('{z}','EPSG_4610_0' + z.toString())
							.replace('{f}','1_1')
							.replace('{x}',fix(x,2))
							.replace('{y}',fix(y,2));
						}
					}
				}
			default:
			  return null;
			}
		}
		});
	
	//创建离线瓦片图层
	var LocalLayer = new ol.layer.Tile({
		extent:extent,
		source:source,
		name:'尼玛'
	});
	map.addLayer(LocalLayer);
	
	/*//视图变换事件
	view.on('change:resolution',function(){
		z = view.getZoom();
		source.setUrls(getUrls(z,extent,resolutions));
		console.log(z);
		map.removeLayer(LocalLayer);
		map.addLayer(LocalLayer);
	},this);*/

	return map;
}

注意事项:

  • 你自己也可以利用setUrls的方法进行调用(反正你已经知道了当前级别有多少行多少列,来个嵌套循环就搞定),但不会比tileUrlFunction更好用
  • 视图的缩放级别不一定跟tile的缩放级别对应的上,你可以利用开发者工具进行比较(例如本文的视图为2时,才被识别成0)
  • 目前还没有太好的解决geoserver生成的恶心文件名的好方法,大家有解决的可以告诉我一下
  • 善用开发者工具OpenLayer3读取Geoserver切割的离线瓦片_第6张图片

文件路径问题补充:

又经过8小时的奋战,基本解决了文件的问题。
首先是最麻烦的计算文件数目,因为要亲手画图的缘故,所以特别辛苦。(目前8级以内的都是每隔两个级别才扩充一次文件块的存储数量)

//--------计算文件数方法-------------//
function calFileNum(minZoom,maxZoom){
	var fileDelta = new Array(maxZoom-minZoom+1);//每个文件夹的边数组
	for(var i=0;i<=maxZoom;i++){
		if(i%2==0){ //偶数
			fileDelta[i] = Math.pow(2,i+2);
		}
		else{ //奇数
			fileDelta[i] = fileDelta[i-1];
		}
	}
	return fileDelta;
}
然后,在TileUrlFunction函数中改成新的方法
tileUrlFunction:function(tileCoord){
			var z = tileCoord[0];
			var x = tileCoord[1];
			var y = tileCoord[2];
			var tempFile = Math.sqrt(fileDel[z]); //边数
			var tryx = Math.ceil((x+1)/tempFile) - 1; //x所在列(关键点)
			var tryy = Math.ceil((y+1)/tempFile) - 1; //y所在行(关键点)
			var file = tryx.toString() + '_' + tryy.toString();
			var ss = url.replace('{z}','EPSG_4610_0' + z.toString()).replace('{f}',file).replace('{x}',fix(x,2)).replace('{y}',fix(y,2));
			console.log(ss);
			return ss;
		    
		}



你可能感兴趣的:(开源WebGIS)