本章导读:ArcGIS 软件体系是使用 ArcGIS Bathymetry 桌面扩展构建和管理水深数据库,并且利用 Image Server 对水深产品进行发布。本章主要针对入库后制作的水深产品,对其发布为二三维服务,并且通过 JavaScript API 进行查询。 By 李远祥
由于 ArcGIS Server 并没有所谓的 Bathymetry for Server 这样的扩展,水深产品如果要发布成可以被在线调用的服务,必须走 Image Server 的技术路线,因为水深产品的组织都是使用镶嵌数据集,因此无法绕开 Image Server 。
基于 ArcGIS 的水深管理应用,实际上是在 ArcGIS Image Server 体系下进行编码实现。二维水深服务类似于二维的 DEM 服务,需要发布原始的像元栅格数据,同时使用 ArcGIS 的栅格函数进行渲染,渲染的方式可以是后端渲染和前端通过脚本修改渲染。三维水深服务,则是使用影像服务的中的切片服务(切片格式为 LERC ,ArcGIS 10.3 以后的影像切片服务)来替代使用,这个 LERC 切片影像服务只用作三维地形的起伏显示,一般情况下还需要在此服务至上叠加一个渲染后的二维影像服务作为表面呈现,这样才能正常显示为水深三维。
二维水深服务的制作比较简单,直接参考上一章《ArcGIS Maritime Server 开发教程(八)ArcGIS Bathymetry 扩展模块》中的【水深产品共享发布】小节中的做法,对水深产品进行发布。
这里需要特别提出的是 BIS 库和水深产品库均可以发布为影像服务,但水深产品库更加贴近业务需求,就像已经整理好的专题数据一样,每个专题发布为一个服务。笔者不建议发布整个 BIS 库,除非有类似全库管理水深网格并下载对应的 Bag 数据的需求。
如果按照影像的特性来说,二维水深服务既可以发布为传统的 MapService 服务,也可以发布为 ImageService 服务,原则上是没有限制的。如果只考虑展示,不作任何的水深查询,MapService 会更加简单快捷。但如果需要做水深值查询(其实就是水深产品的象元值),那么就必须使用 ImageService 。
ImageService 对于连续像元栅格,默认都是灰白显示的,没有做任何的渲染。这种默认的显示,至适合计算机识别,并不适合人类查看。但是, ArcGIS Image Server 是允许使用栅格函数或者栅格模板来动态渲染影像服务,这样就可以兼顾地图展示和查询了。
关于栅格函数和栅格模板的调用,可以参考《ArcGIS 栅格函数在线调用详解》的相关章节内容。
关于二维水深的发布,不建议通过 ArcMap 的 MapService 方式直接发布出来,类似下图的操作。
考虑到后面的三维水深服务必须一个二维的地图进行叠加,那么在二维水深的时候就必须处理好数据的一些具体细节。
例如,S-102 栅格数据,本身由两个波段组成,通过 ArcMap 查看其数据,可以看到分别有 elevation 和 uncertainty 组成,即由确定的水深高程和不确定的水深高程两部分组成,如下图
同样,BIS 库也会将两部分的水深高程保存下来,也带到水深产品库中。通过 ArcMap 的目录,从属性中可以看到水深产品同样是两个波段,如下图所示

一般来说,只有波段1 ,即 elevation 才有比较完整的数据。所以,针对二维水深服务,只需要发布这个波段1 的数据即可。发布的方式也简单,不需要专门导出单个波段,只需要在水深产品右键–属性中先做一个波段提取的操作,动态将水深产品进行波段过滤。如下图所示
分别在波段和组合参数中输入 1 ,即可完成波段的过滤。
发布前可以先定义二维水深服务的展示样式,定义影像服务的方式,使用【栅格函数模板编辑器】进行设计。在工具栏空白的地方双击,调出【自定义】窗口,在【命令】标签中,输入“栅格”二字,过滤结果后选择左侧的“栅格”项,在右侧会出现“栅格函数模板编辑器”,如下图所示:
选中【栅格函数模板编辑器】,直接将其拖拽到工具条上即可使用。
打开【栅格函数模板编辑器】,出入一个栅格函数用于渲染水下地形,这里可以使用【晕渲地貌函数】,这个函数常用于渲染类似 DEM 一类的高程数据。
设置好相关的参数并且指定或者设计好一个显示用的渐变色带用于显示水深,如下图所示
设计好后点击【栅格模板函数编辑器】的【文件】 下拉菜单 ,并保存这个栅格函数。
栅格函数模板若要能被调用,必须存放在 ArcGIS Server 注册的目录中,文件名称建议使用英文命名。
波段过滤以及栅格函数模板制作完成后,便可以着手发布二维水深服务了。在【目录】窗口中,右键点击水深产品,选择【共享为影像服务】,如下图所示
根据向导进行发布,在服务参数设置中,选择【函数】项,将上一步保存下来的栅格函数模板添加进去,并且默认使用这个函数模板,如下图所示:
这样,影像在发布的时候就会自动套用该影像函数进行渲染。通过 ArcGIS Server 的服务浏览,可以看到该影像服务已经使用了指定的样式进行呈现。如下图所示:
ArcGIS 的影像服务是可以挂在多个函数模板,其中可以将一个设置为默认显示,其他的是备选,可以通过 ArcGIS JavaScript API 在前端进行指定。下图在影像服务中加入了另一个名为 deep2 的函数。
二维水深服务可以使用 ArcGIS JavaScript API 来调用,由于其本质是 ImageService ,因此,可以使用 ArcGISImageServiceLayer 来调用。下面是二维水深服务调用的例子
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<title>imageService</title>
<link rel="stylesheet" href="https://lazy.gis/arcgis_js_api/3.19/esri/css/esri.css">
<script src=" https://lazy.gis/arcgis_js_api/3.19/init.js"></script>
<script>
var map;
require([
"esri/map",
"esri/layers/ArcGISImageServiceLayer",
"esri/layers/ImageServiceParameters",
"dojo/domReady!"],
function (Map, ArcGISImageServiceLayer, ImageServiceParameters) {
map = new Map("mapDiv");
var imgParams = new ImageServiceParameters();
imgParams.onData = 0;
//加载二维水深服务(水深服务的本质为 ImageService)
var layerUrl = "https://lazy.gis/server/rest/services/longBeach/ImageServer";
var imageServiceLayer = new ArcGISImageServiceLayer(layerUrl, {
imageServiceParameters: imgParams,
opacity: 0.75
});
map.addLayer(imageServiceLayer);
});
</script>
<style>
html,
body,
#mapDiv {
padding: 0;
margin: 0;
height: 100%;
}
</style>
</head>
<body class="claro">
<div id="mapDiv"></div>
</body>
</html>
其调用的效果如下,调用是没有指定服务显示的样式,但服务会自动加载默认的函数模板。
动态更二维水深的样式,使用 RasterFunction 接口,在初始化的时候指定其函数模板。如果有多个模板时,在服务的信息中也会有所显示,例如在影像服务的元数据信息中,会记录设置好的模板。如下图,deep 和 deep2 就是在服务发布时上传的函数模板。
下面代码为指定非默认模板 deep2 ,作为二维水深服务的展示方式。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<title>指定二维水深的渲染模板</title>
<link rel="stylesheet" href="https://lazy.gis/arcgis_js_api/3.19/esri/css/esri.css">
<script src=" https://lazy.gis/arcgis_js_api/3.19/init.js"></script>
<script>
var map;
require([
"esri/map",
"esri/layers/ArcGISImageServiceLayer",
"esri/layers/ImageServiceParameters",
"dojo/domReady!"],
function (Map, ArcGISImageServiceLayer, ImageServiceParameters) {
map = new Map("mapDiv");
var rasterFunction = new esri.layers.RasterFunction();
rasterFunction.functionName = "deep2";//自定义栅格函数deep2
rasterFunction.variableName = "Raster";
var imgParams = new ImageServiceParameters();
imgParams.onData = 0;
imgParams.renderingRule = rasterFunction;
var layerUrl = "https://lazy.gis/server/rest/services/longBeach/ImageServer";
var imageServiceLayer = new ArcGISImageServiceLayer(layerUrl, {
imageServiceParameters: imgParams,
opacity: 0.75
});
map.addLayer(imageServiceLayer);
});
</script>
<style>
html,
body,
#mapDiv {
padding: 0;
margin: 0;
height: 100%;
}
</style>
</head>
<body class="claro">
<div id="mapDiv"></div>
</body>
</html>

三维水深服务其实质就也是一个影像服务,但这个服务是有它的一些特质,就是带有地形起伏,需要使用影像的切片来加速表达这些起伏特征。这跟发布一个 DEM 影像是一样的。
ArcGIS 产品体系中从 10.3 版本开始支持基于 Web 的三维,在地形呈现,采用的是 ImageService 。与之前的 ImageService 不一样,在制作 Web 三维地形服务,引入了新的 LERC 切片格式,这是地形三维特有的格式。因此,三维水深服务的发布必须对应将水深产品发布为 ImageService ,并且设置切片格式为 LERC 。
三维水深服务必须是在影像服务的基础上加入切片缓存,缓存格式为 LERC 。但这里必须注意的是,LERC 的切片格式只支持单波段的影像格式。一般情况下 DEM 数据时单波段的影像数据。但水深产品由于从 S-102 数据过来,一直保持着两个波段,所以,如果要制作 LERC 格式,必须加入栅格函数进行波段提取后才能发布服务。
这就是为什么前面笔者花大量的篇幅去描述二维影像服务发布的细节。
针对前面已经做过单波段提取的水深产品,在 ArcMap 中重新发布一个新的影像服务,这个服务不需要指定渲染模板方式,因为需要他用作地形起伏的展示。在发布向导中,在服务的属性,选择【缓存】项,选择【使用缓中的切片】,如下图
这里不需要像矢量切片那样设置缓存的级别,因为这次针对的是像元切片。
然后到下方【高级设置】项中,在【切片格式】的下拉菜单中选择 LERC 。这里值得注意的是,如果数据源不是单波段的,则是不会显示 LERC 选项。
其他选项可以不理会,直接使用默认即可。然后点击【确定】按钮,ArcGIS Server 就会在后台进行像元切片。
通过对原来的服务进行查看,切片之后,会出现类似矢量地图切片的模式,多了一些 Tile Info 这样的元数据描述。如下图所示
另外再发布一个带有一般渲染的二维地图服务 MapService ,这个 MapService 是用作叠加在三维水深服务之上作为展现。
这样,通过发布两个服务,一个是二维的 MapService ,一个是三维的 ImageService ,共同用作呈现三维水深。
一般的 DEM 三维呈现也是这么做的。
ArcGIS JavaScript API 在 4.x 系列才能支持三维服务的在线调用。所以,如果要使用三维服务,则必须部署 4.x 版本。
笔者为了省事,二维服务统一都是使用 3.x 版本去调用,如果要做三维的系统,则必须尽快切换到 4.x 系列来。
三维的地形服务是不会显示地图的渲染的,所以,要呈现出三维的水深,必须针对同一个水深产品,发布一个二维的水深地图和一个三维的水深服务。
在 ArcGIS JavaScript API 4.x 中,二维地图加载使用的是 MapImageLayer 接口,地形服务(指的就是这里的三维水深服务)使用的是 ElevationLayer 接口。
但如果要使用三维球体来加载的话,那就需要在这两个服务发布之前对数据再进一步处理,因为三维球体的 SceneView ,只支持 WGS84 和 Web 墨卡托投影,其他坐标系一概不支持。
试想一下,如果要使用球体,例如 Google 地球那样,那就真的只能指定唯一的坐标系了。
在发布之转换坐标系也不需要单独导出数据, ArcGIS 的栅格函数同样也是提供了坐标转换的函数,这样就非常方便。例如针对水深产品数据,可以在栅格函数处继续加如重投影函数,用作指定 Web 墨卡托投影。

针对处理好的这个水深产品再重新发布成二维和三维服务。所以,三维水深服务队数据的要求更多一些,以下可以参考所使用的栅格函数。
以下是调用的代码例子,使用的是 ArcGIS JavaScript API 4.x 的代码,接口与之前的 3.x 版本有着不同的区别。
首先加载一个卫星底图,方便大面积的查看,然后加载二维和三维的服务,创建一个球体 SceneView 用于显示。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<title>三维水深加载</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link rel="stylesheet" href="https://lazy.gis/arcgis_js_api/4.6/esri/css/main.css">
<script src="https://lazy.gis/arcgis_js_api/4.6/init.js"></script>
<script>
require([
"esri/Map",
"esri/views/SceneView",
"esri/layers/ElevationLayer",
"esri/layers/ImageryLayer",
"esri/layers/MapImageLayer",
"dojo/on",
"dojo/dom",
"dojo/domReady!"
], function (Map, SceneView, ElevationLayer, ImageryLayer, MapImageLayer, on, dom) {
var elevationLayer; //定义三维水深服务变量
//创建一个地图,指定底图为 Esri 的卫星地图
var map = new Map({
basemap: "satellite"
});
//定义并加载二维水深服务
var deepMap2D = new MapImageLayer();
deepMap2D.url = "https://lazy.gis/server/rest/services/longBeach2/MapServer";
map.layers.add(deepMap2D);
//加载三维水深服务,并作为地形显示
elevationLayer = new ElevationLayer();
elevationLayer.url = "https://lazy.gis/server/rest/services/longBeach_elevation/ImageServer";
map.ground.layers.add(elevationLayer);
//创建一个球体视图,指定初始化的位置
var view = new SceneView({
container: "viewDiv",
map: map,
camera: {
position: [-118.242, 33.72, 1346],
heading: 300,
tilt: 60
}
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
再来看加载后的效果,如下图
本章从水深产品的数据处理,服务发布以及代码调用,详细介绍了从水深产品到水深应用的全过程。水深产品的应用,实际上可以看做是 DEM 的应用,完全通过 ArcGIS Image Server 和镶嵌数据的技术路线去实现。
更多的GIS主流和非主流技术,可以持续关注CSDN的GIS制图乐园,以及微信公众号【GIS制图乐园】。BY 李远祥