扩展iServer数据服务REST资源实现点靠近线、打断线(三)

作者: MR

    之前两篇(扩展一扩展二)完成了服务端的设计和实现,本篇介绍扩展iClient for JavaScript对新扩展的资源进行对接。先放张应用效果预览图:

预览

###一、入门
    扩展一个iClient for JavaScript类的基本方式如下:

变量名=SuperMap.Class(继承的父类,{本类的实现});

    其中,本类的实现要有一个初始化的initialize方法,初始化时可以继承父类(父类.prototype.apply(this, arguments)),当然也可以不继承。

###二、输入输出类的准备以及JSON与本地对象互转
    输入类即向服务端请求的对象(会转成JSON发给服务端);输出类则是服务端返回的结果(服务端返回的JSON转成本地类型)。显而易见的,输入类对应服务端创建参数映射的Map对象,即:{params:LCPInputFormat[] };输出类对应LCPOutputFormat类。这里输入类命名SuperMap.REST.LineCapturePointParameter;输出类命名SuperMap.REST.LineCapturePointResult,注意 SuperMap是iClient for JavaScript定义的全局变量,SuperMap.REST也是,所以我们的命名实际是给SuperMap.REST指向的对象添加一个属性,所以,不能任意命名,变量名必须有效。当然,这里只对接了JSON格式的表述,xml等格式(服务端是支持的)的输入输出这里不考虑。
    定义字段就不介绍了,跟服务端的对应类一致就行了,主要介绍iClient for JavaScript的对象转成服务端接受的对象的方法。

    iClient for JavaScript已经定义好了与服务端一致的Geometry类及Feature类,分别叫做:SuperMap.REST.ServerGeometrySuperMap.REST.ServerFeature,并且提供了,与iClient for JavaScript自己的Geometry类及Feature类互转的方法,我们只需要直接使用即可。在输入类转为服务端接受类型的时候需要做些兼容,以兼容多种参数类型。另外输入类还初步检查了参数是否合法,规则和服务端检查的一致。还有就是,若设置了返回指定字段,脚本里过滤了下上传点和上传线的字段(服务端不处理上传点和上传线的字段,原样返回);设置了上传线的ID,因为服务端打断线时是通过判断线的ID来实现同一条线上多个捕获点(或叫垂足)进行打断的,所以每条线都需要不同的ID,唯一的问题是,若是同时还查询数据集线,而数据集线的ID(就是属性表SmID值)和上传的线ID一致时,打断线存放对应线的结果不对,若有需要上传线和查询数据集线在同一个输入项同时设置,可以自己修改,上传线的ID给加上一个数值(比如加1w,10000 + “1234” = “100001234”),使不可能和数据集线ID一致。这里不再贴代码,将iClient for JavaScript的Geometry对象转为ServerGeometry对象的方法如下,其中lineFeature为线要素对象:

SuperMap.REST.ServerGeometry.fromGeometry(lineFeature.geometry)

    服务端返回JSON转为对象后,再转成iClient for JavaScript的Feature对象的方法如下,其中jsonObj是JSON转成的JavaScript对象:

SuperMap.REST.ServerFeature.fromJson(jsonObj.line).toFeature()

    因为服务端返回的点、线等可能为null,所以处理输出需要先判断一下。

三、构造服务类,处理发送请求及执行回调

    iClient for JavaScript是开源的,可以去GitHub下载到源码,在libs/SuperMap/REST目录下找一个服务类照着写行了,默认会根据是否跨域发POST请求(JSON表述)或GET请求(使用iServer JSONP表述)。因为设计了一个轮询功能,所以这里扩展的服务类只会发POST请求。简单解释下这里的轮询功能:

服务设计的请求体是LCPInputFormat[],轮询的作用和别的iClient for JavaScript的轮询功能一致,就是将请求分散发送给不同的URL进行处理,这样每个URL处理的数据量就会减少,也就是意味着需要多个服务端发布同一个工作空间(或数据相同的服务);这里拆分的单位是一个LCPInputFormat输入项,并且加入失败重试功能,直到所有URL都请求失败才会返回失败。

    服务类命名为:SuperMap.REST.LineCapturePointService,其请求方法如下:

	/**
     * APIMethod: processAsync
     * 发送请求
     * Parameters:
     * params - {<SuperMap.REST.LineCapturePointParameter>} 点靠近线参数类.
     * Returns:
     * {<SuperMap.REST.LineCapturePointResult>} 返回点靠近线结果。
     */
	processAsync: function (params) {
		var me = this, Parameters = null;

		if (!params || !me.eventListeners || !me.url.length) {
			console.log("请检查服务类及请求参数!");
			return;
		}
		Parameters = params.getParameter();
		if (!Parameters || !Parameters.params.length) {
			console.log("点靠近线参数无效!");
			return;
		}
		//暂存SuperMap.Credential.CREDENTIAL
		me.CD = SuperMap.Credential.CREDENTIAL;
		me.setUrl(me.url);
		//检查url,虽然可以不这么严
		if (!(typeof (me.urls) == "object" && me.urls.length && typeof (me.urls[0]) == "string"))
		{ return; }

		var PLEN = Parameters.params.length;
		var count = Math.floor(PLEN / me.urls.length);
		var extra = PLEN % me.urls.length;
		var LEN = me.urls.length;
		var Result = new SuperMap.REST.LineCapturePointResult({
			totalSucessCount: 0,
			totalFailedCount: 0,
			msg: "",
			results: []
		});
		var failRt = [];//失败一次性返回每个结果最后一个失败url的commit对象
		//成功加失败=拆数据的份数 时返回
		var Num = 0;
		var part = extra == 0 ? LEN : extra;
		var hasSucess = false;
		//最终失败的也调用下它,使二者都能正常返回
		var handleSucess = function (rt) {
			if (rt) {
				Result.totalSucessCount += rt.totalSucessCount;
				Result.totalFailedCount += rt.totalFailedCount;
				Result.msg += Result.msg.length ? ("|" + rt.msg) : rt.msg;
				Result.results = Result.results.concat(rt.results);
				if (Num == part) {
					handleFailure(false, false, LEN, 0);
				}
			}
			if (Num == part && hasSucess) {
				me.getFeatureComplete(Result);
			}
		};
		//递归(不太算,只是顺序执行请求,失败换个url再来)直到成功或所有url都失败,可以加个重试次数
		//fe: ajax请求对象,pr: 请求参数对象,deep: 轮询url,ep: 已经失败的url
		var handleFailure = function (fe, pr, deep, ep) {
			//过滤掉已经失败的url
			if (deep == ep) {
				++deep;
			}
			//失败重试
			if (deep < LEN) {
				SuperMap.Credential.CREDENTIAL = null;
				// me.request({
				// 	method: "POST",
				// 	url: me.urls[deep],
				// 	data: SuperMap.Util.toJSON(pr),
				// 	scope: me,
				// 	success: function (e) {
				// 		hasSucess = true;
				// 		++Num;
				// 		handleSucess(me.getResult(e));
				// 	},
				// 	failure: function (e) {//pr指向未变
				// 		handleFailure(e, pr, ++deep, ep);
				// 	}
				// });
				SuperMap.Util.committer({
					method: "POST",
					url: me.urls[deep],
					data: SuperMap.Util.toJSON(pr),
					scope: me,
					isInTheSameDomain: me.isInTheSameDomain,
					success: function (e) {
						if (me.isReturnPartial) {
							me.getFeatureComplete(me.getResult(e));
						} else {
							hasSucess = true;
							++Num;
							handleSucess(me.getResult(e));
						}
					},
					failure: function (e) {//pr指向未变
						handleFailure(e, pr, ++deep, ep);
					}
				});
				SuperMap.Credential.CREDENTIAL = me.CD;
			} else {//所有url都失败
				if (fe) {
					++Num;
					//可修改为每次失败结果都返回之类的比如可按url返回每个url失败的结果,从me.url[deep]取
					if (!me.isReturnPartial) {
						failRt.push(fe);
					}
					if (Num == part) {
						handleSucess(false);
					}
				}
				if (fe && me.isReturnPartial) {
					me.getFeatureError([fe]);
				} else if (Num == part && failRt.length) {//或全部执行完毕
					me.getFeatureError(failRt);
				}
			}
		};
		//拆数据&发请求
		for (var i = 0; i < LEN; ++i) {
			//拆数据
			var start, end, pm = { params: [] };
			if (i < extra) {
				start = i * (count + 1);
				end = start + count + 1;
			} else {
				start = i * count + extra;
				end = start + count;
			}
			if (start >= PLEN) { break; }
			while (start < end) {
				pm.params.push(Parameters.params[start]);
				++start;
			}

			SuperMap.Credential.CREDENTIAL = null;
			// 发请求
			// me.request({
			// 	method: "POST",
			// 	url: me.urls[i],
			// 	data: SuperMap.Util.toJSON(pm),
			// 	scope: me,
			// 	success: function (e) {
			// 		if (me.isReturnPartial) {
			// 			me.getFeatureComplete(me.getResult(e));
			// 		} else {
			// 			hasSucess = true;
			// 			++Num;
			// 			handleSucess(me.getResult(e));
			// 		}
			// 	},
			// 	failure: (function (mp, j) {
			// 		return function (e) {
			// 			//修正mp指向,和i值
			// 			//从me.urls[0]开始尝试,跳过me.urls[i]
			// 			handleFailure(e, mp, 0, j);
			// 		}
			// 	})(pm, i)
			// });
			SuperMap.Util.committer({
				method: "POST",
				url: me.urls[i],
				data: SuperMap.Util.toJSON(pm),
				scope: me,
				isInTheSameDomain: me.isInTheSameDomain,
				success: function (e) {
					if (me.isReturnPartial) {
						me.getFeatureComplete(me.getResult(e));
					} else {
						hasSucess = true;
						++Num;
						handleSucess(me.getResult(e));
					}
				},
				failure: (function (mp, j) {
					return function (e) {
						//修正mp指向,和i值
						//从me.urls[0]开始尝试,跳过me.urls[i]
						handleFailure(e, mp, 0, j);
					}
				})(pm, i)
			});
			SuperMap.Credential.CREDENTIAL = me.CD;
		}
	}

    失败重试使用的是顺序执行的方式,即,失败之后才重试,而不是每个URL都发请求,第一个成功则中断其余请求,各有优缺点,可以自己尝试这种方式。
    getFeatureCompletegetFeatureError分别是请求成功和失败时的回调,其它请参考源码。
    SuperMap.Util.committer()方法用于发送请求、绑定回调,父类(SuperMap.ServiceBase)的request方法也是使用它,这里我们和父类的逻辑不同,所以直接使用SuperMap.Util.committer();可以看到还做了些别的操作,比如若填了多个URL并且该服务设置了授权,那么这里让iClient for JavaScript的认证类失效,自己处理认证(tooken字符串需要和URL一一对应)。其它请参见下方源码,使用时可以先压缩一下,因为注释很多。

###四、测试

    边写边测,上述扩展完成后,需要在iClient for JavaScript的类库(SuperMap.Include.js)之后引入或者修改SuperMap.Include.js文件。使用方式和别的iClient for JavaScript对接iServer服务的类一致,如下:

// 请求参数
var param = new SuperMap.REST.LineCapturePointParameter.requestItem({具体输入设置项});
var param1 = new SuperMap.REST.LineCapturePointParameter.requestItem({具体输入设置项});
var params = new SuperMap.REST.LineCapturePointParameter({
            parameters: [param,param1] //也可以不是数组,直接填param
        });

// 服务类
var service = new SuperMap.REST.LineCapturePointService(
            [url1,url2,url3...],  //也可以不是数组,直接填一个url字符串
            {
                eventListeners: { // 注册事件监听
                    "processCompleted": function (e) {
                        console.log("成功结果", e);
                    },
                    "processFailed": function (e) {
                        console.log("失败结果", e);
                    }
                }
                //以下可选
                , isReturnPartial: false   //分批返回,url为数组时生效,true时回调可能将被执行多次,默认false
                , CREDENTIALS: ["","",""...]          //tooken字符串数组,若服务需要tooken验证,与url数组一一对应,SuperMap.Credential.CREDENTIAL对本类无效

//将参数发送给服务端
service.processAsync(params);

    验证情况:

JS验证

    验证设置了两个输入项,有上传点及线,也有查询点,故意在URL数组写了两个一定会请求失败的URL,之后才是能成功执行的URL,验证了脚本无误,当然,也有别的测试,但是难免也会出现问题,包括服务端,欢迎大家一起交流讨论。

###五、实例验证

    这就到了最开头那张图片了,验证情况如下:

实例验证

    使用的是iServer自带示范数据,长春市区图(坐标系为平面无投影,也就是没有坐标系,但是数据应该是投影坐标系下采集的,因为根据坐标计算的距离大致准确),也有试过经纬度坐标系,经纬度下,一般实际容限比设置的容限略大,返回成功结果的距离可能略大于设置的容限,这里不在服务端处理(本来就是为了快),也不在js脚本再次过滤结果。

###六、完结&致谢

    到这里,这个扩展就全部完结了,感谢SuperMap,感谢CSDN。下面提供对接脚本和应用示例的下载链接,欢迎传播和围观。
http://download.csdn.net/detail/supermapsupport/9809547
    最后,特别感谢 柳慧珠 小仙女帮我找bug和提供应用示例(动图那个)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值