ArcGIS Maritime Server 开发教程(五)Maritime Service 系列问题应对
本章导读:Maritime Service 前端没有专门的开发接口,其开发都是依赖传统的 ArcGIS JavaScript API ,这跟它的架构有着密切的关系,其 SOE 扩展本身就是针对服务器端的功能进行扩展,依赖于主体服务进行发布和管理,因此接口方面有着绝大部分的对应。但由于数据源方面是非传统 GIS 数据,所以也限制了它在传统开发中的顺利过渡。本章主要围绕开发过程中的几个主要问题进行论述,旨在让开发人员了解更多一点,少走一些弯路。By 李远祥
有些参数是必须的
上一章节提到的 identify 功能,虽然操作和接口都是跟传统方式类似,但 identifyParams.layerOption 实际上是不生效的,它不能处理查询的图层是否可见图层或全部图层查询,因为 S-57 数据本身就没有层的概念。所以,如果希望点击查询时操作对象为可见图层,那就肯定是白搭了,设置了等于没设置。必须设置 identifyParams.mapExtent,否则点选查询不作响应。
这两个参数属性比较坑,一不小心掉进去就出不来了。当初笔者还一直在调试代码,代码一直没有任何的问题,但一直却出不来效果。最后还是监测了很久才试出问题的所在。通过思考其原理,大概还是明白其具体原因,毕竟 S-57 数据跟 GIS 数据的差别,还是使得 Maritime Service 完全适配存在问题。但这些问题似乎没有在产品文档中体验出来( Maritime Server 能找到的帮助少之又少)。
具体情况可以参考上一章节《ArcGIS Maritime Server 开发教程(四)Maritime Service 开发实践》中的 【 Maritime Service identify 操作】的内容。
有些参数是无效的
在 Maritime Service 调用的过程中,有些参数无效的,有时候也会让人哭笑不得。由于 Maritime Service 没有 Query 的操作,所以在空间查询方面就只能依靠 identify 功能了。从传统的 IdentifyParameters 接口来看,它是有 geometry 属性的,是支持输入一个图形作为查询的几何图形的,如下图所示:
为此,笔者还暗自欢喜,这样子也不赖,可以通过 identify 的方式来实现 Query 的一些空间操作。但测试的实际结果,还是有点强差人意。
但实际结果是,IdentifyParameters 的 geometry 参数只支持点和规则矩形两种,并不是所有的 geometry 。 如果希望通过其他如 Polygon 、Polyline 或者一些图形 Buffer 之后做操作,那就会出现失败操作。
为了测试其支持的 geometry 类型,笔者特意写了一个比较通用的程序进行测试,其思路是编写一个 Draw 方式去做一个绘图的工具,在绘制图形之后使用该图形作为 IdentifyParameters 的 geometry 参数传入,并且支持 identify 操作。其代码如下:
<!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="http://localhost/arcgis_js_api/3.19/esri/css/esri.css">
<style>
html,
body,
#mapDiv {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
</style>
<script src="http://localhost/arcgis_js_api/3.19/init.js"></script>
<script>
var map, tb;
var drawGrapic;
require(["esri/map",
"esri/layers/ArcGISDynamicMapServiceLayer",
"esri/geometry/Extent",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/SimpleLineSymbol",
"esri/symbols/SimpleFillSymbol",
"esri/tasks/IdentifyTask",
"esri/tasks/IdentifyParameters",
"esri/tasks/IdentifyResult",
"dojo/Deferred",
"esri/Color", "esri/graphic",
"esri/toolbars/draw",
"esri/symbols/PictureFillSymbol", "esri/symbols/CartographicLineSymbol",
"dojo/on", "dojo/dom",
"dojo/domReady!"],
function (Map, DynamicLayer, Extent,
SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol,
IdentifyTask, IdentifyParameters, IdentifyResult, Deferred,
Color, Graphic, Draw,
PictureFillSymbol, CartographicLineSymbol,
on, dom) {
//初始化显示范围
var initexten = new Extent({ "xmin": 113.42, "ymin": 22.15, "xmax": 113.58, "ymax": 22.26, "spatialReference": { "wkid": 4326 } });
map = new Map("mapDiv", { extent: initexten });
//加载基础服务WGS 84
var basemap = new DynamicLayer("http://localhost:6080/arcgis/rest/services/SampleWorldCities/MapServer");
map.addLayer(basemap);
//ENC WGS 84
var enc84 = new DynamicLayer("http://localhost:6080/arcgis/rest/services/SampleWorldCities/MapServer/exts/MaritimeChartService/MapServer");
map.addLayer(enc84);
//map.on("load", initToolbar);
//注册点击查询事件
map.on("load", initToolbar);
//简单点符号
var markerSymbol = new SimpleMarkerSymbol();
markerSymbol.setColor(new Color("#00FFFF"));
markerSymbol.setStyle(SimpleMarkerSymbol.STYLE_CIRCLE);
markerSymbol.setSize(12);
//制图线符号
var lineSymbol = new CartographicLineSymbol(
CartographicLineSymbol.STYLE_SOLID,
new Color([255, 0, 0]), 10,
CartographicLineSymbol.CAP_ROUND,
CartographicLineSymbol.JOIN_MITER, 5
);
//图片填充
var fillSymbol = new PictureFillSymbol(
"/mangrove.png",
new SimpleLineSymbol(
SimpleLineSymbol.STYLE_SOLID,
new Color('#000'),
1
),
42,
42
);
//点击地图,通过屏幕上的点进行查询
function mapIdentifyByGeometry(geom) {
var identifyParams, identifyTask;
identifyTask = new IdentifyTask("http://localhost:6080/arcgis/rest/services/SampleWorldCities/MapServer/exts/MaritimeChartService/MapServer");
identifyParams = new IdentifyParameters();
identifyParams.tolerance = 30;
identifyParams.returnGeometry = true;
identifyParams.geometry = geom;
identifyParams.mapExtent = map.extent;
identifyParams.layerOption = IdentifyParameters.LAYER_OPTION_VISIBLE;
identifyParams.width = map.width;
identifyParams.height = map.height;
identifyTask.execute(identifyParams, getResult);
}
function getResult(results) {
//先清空地图上的要素,便于展示结果数据
//map.graphics.clear();
//遍历所有的查找结果
dojo.forEach(results, function (result) {
//对每个查找结果进行渲染
setGrapic(result.feature);
}
);
}
//渲染查找结果
function setGrapic(gra) {
//定义点线面符号
var pointSym = new SimpleMarkerSymbol();
var lineSym = new SimpleLineSymbol();
var polySym = new SimpleFillSymbol();
var color = new Color([255, 0, 0, 0.5]);
var outColor = new Color([0, 255, 0, 0.5]);
//点符号设置
pointSym.setColor(color);
pointSym.setSize(12);
pointSym.outline.setColor(outColor);
//线符号设置
lineSym.setColor(color);
lineSym.setWidth(3);
//面符号设置
polySym.setColor(color);
polySym.outline.setColor(outColor);
//对返回的物标要素进行渲染
//获取物标所有的属性,其中objectType(固定标识) 为物标的唯一标识
var featureAttributes = gra.attributes;
for (var attr in featureAttributes) {
if (attr == "objectType") {
var oType = featureAttributes[attr];
if (oType == "TSSBND") {
//对点线面图形设置符号
if (gra.geometry.type == "point") {
gra.symbol = pointSym;
} else if (gra.geometry.type == "polyline") {
gra.symbol = lineSym;
} else if (gra.geometry.type == "polygon") {
gra.symbol = polySym;
}
map.graphics.add(gra);
//跳转到地图位置
jumptoMap(gra);
}
}
}
}
//跳转到要素所在的位置
function jumptoMap(gra) {
if (gra.geometry.type == "point") {
map.centerAndZoom(gra.geometry);
} else {
map.setLevel(6);
map.setExtent(gra.geometry.getExtent());
}
}
//跳转到要素所在的位置
function jumptoMap(gra) {
if (gra.geometry.type == "point") {
map.centerAndZoom(gra.geometry);
} else {
map.setLevel(6);
map.setExtent(gra.geometry.getExtent());
}
}
function initToolbar() {
tb = new Draw(map);
tb.on("draw-end", addGraphic);
on(dom.byId("info"), "click", function (evt) {
if (evt.target.id === "info") {
return;
}
var tool = evt.target.id.toLowerCase();
map.disableMapNavigation();
tb.activate(tool);
});
}
function addGraphic(evt) {
tb.deactivate();
map.enableMapNavigation();
var symbol;
if (evt.geometry.type === "point" || evt.geometry.type === "multipoint") {
symbol = markerSymbol;
} else if (evt.geometry.type === "line" || evt.geometry.type === "polyline") {
symbol = lineSymbol;
}
else {
symbol = fillSymbol;
}
var drawGrapic2 = new Graphic(evt.geometry, symbol);
map.graphics.add(drawGrapic2);
mapIdentifyByGeometry(evt.geometry);
}
});
</script>
</head>
<body>
<div id="info">
<button id="Point">Point</button>
<button id="Multipoint">Multipoint</button>
<button id="Line">Line</button>
<button id="Polyline">Polyline</button>
<button id="FreehandPolyline">Freehand Polyline</button>
<button id="Triangle">Triangle</button>
<button id="Extent">Rectangle</button>
<button id="Circle">Circle</button>
<button id="Ellipse">Ellipse</button>
<button id="Polygon">Polygon</button>
<button id="FreehandPolygon">Freehand Polygon</button>
</div>
<div id="mapDiv"></div>
</body>
</html>
先看成功部分,Point 的操作,点击地图后顺利的查询并返回结果,如下图
然后是 Rectangle ,同样也是成功操作,并返回结果,如下图
再来测试最常见的 Polygon 操作,并打开监测工具查看。
同样的代码在传入不同图形的时候,出现了报错现象。这种现象在其他罗列的各种图形中同样存在。
所以,笔者非常肯定的是 Maritime Service 的 identify 操作是不支持点和矩形之外的其他图形操作的。读者也无需费大气力再去测试。
有些功能是特定场景才能使用的
例如 mcstpk 工具 ,它只能制作基于 web 墨卡托,并且基于谷歌地图的切片方案的切片包 。无论如何修改 ServerConfiguration.xml 的 wkid 都是无效的,反而还会导致切片生成出问题。 mcstpk 工具界面中没有任何可以设置坐标系和切片方案的按钮和菜单,顶多只有设置生成哪几个级别的切片,如下图所示
生成的 tpk 能在 ArcMap、ArcGIS earth 、ArcGIS Runtime、ArcGIS Online 中使用,但并不支持直接上传到 ArcGIS Portal 中发布切片服务( ArcGIS Online 上没问题) 。
使用 mcstpk 工具生成的 tpk 包跟传统的 tpk 有点区别,笔者解压后对二者进行过对比。后续有时间的话,笔者会和小伙伴们制作一个小程序来解决标准 tpk 生成的问题,是的 ArcGIS Portal 能够顺利读取并发布切片地图。
使用模式问题
由于 S-57 的数据结构限制了 Maritime Service 向传统 MapService 发展,使得使用传统的 ArcGIS JavaScript API 开发存在各种的问题。不管是操作失效还是功能限制,都让传统 ArcGIS 开发人员感觉到麻烦。
这个问题基本上不可能在短期内得到解决,笔者设置认为 ArcGIS 软件短期内应该不会针对 S-57 的 Maritime Service 进行跟多有效的改进。因为这是一个即将废弃的标准,只是目前 S-100 系列还没能有效的推行。从 ArcGIS Bathymetry 扩展非常有效的支持 S-102 数据可以看出,Esri 更加注重将海图产品压在未来的 S-100 系列标准中。
因为一些问题在短期内无法解决,所以只能探讨应用模式的问题。ArcGIS Maritime Server 的出现,解决了 ArcGIS 海图应用的一个非常重要的问题,就是配图。利用传统的配图方式去配置 S-52 样式,其工作量几乎让人无法忍受。而 Maritime Server 出现之后,该工作就完全不需要做了,直接有服务器端根据 S-57 数据自动生成。
地图的空间操作方面,由于使用的是非 GIS 数据,几乎是不能适配的了。就算解决掉 identify 存在的问题,或者加入 Query 的操作,都不能适应 GIS 的使用。因为很多的图层叠加计算无法操作,很多 GP 工具都无法使用。
从 Maritime Server 的角度来看,其主要作用还是解决 S-57 数据的发布以及 S-52 海图样式的出图显示两大问题,并不能解决从海图到传统地图的所有功能操作。
既然是这样,那么使用模式就很好确定了。利用 Maritime Service 作为前端的显示,解决 S-52 的显示问题;针对 S-57 数据提取或生成 GIS 数据,另外发布一个 GIS 服务(不作叠加显示),用作各种的空间分析和叠加分析,这样就可以顺利解决从海图向 GIS 过渡的各种问题。当然,这里也就引入了一些工作量,就是海图数据到 GIS 数据的提取和转换。
解决一个问题的同时,还是会引入另一个问题的。最需要考虑的地方则是成本问题,也就是传说中的值不值的问题。_
如果是强海图应用,这种方案是最省力的了,也是不二之选。如果只是调用海图作为底图,那就无所谓其他的空间操作了,直接将自身的专题数据制作成 GIS 格式,发布地图服务进行叠加即可。
总结
就目前来说,针对 S-57 的 Maritime Service 还是存在各种小问题,但在改变应用模式的基础上,似乎能够很好的解决其自身的不足,能够顺利的从海图到 GIS 的过渡。笔者认为,现阶段是海图向 GIS 过渡的阶段,不管是技术上还是使用模式上,均属于探索性的工作。海图的信息化进步应该在不远的 S-100 体系推广之后。
更多的GIS主流和非主流技术,可以持续关注CSDN的GIS制图乐园,以及微信公众号【GIS制图乐园】。BY 李远祥