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

来源:互联网 发布:pix飞控调参软件下载 编辑:程序博客网 时间:2024/04/30 17:25

作者: 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.ServerGeometry和SuperMap.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
原创粉丝点击