商业地图纠偏接口次数限制的处理策略
2017-09-18 17:53
351 查看
商业地图纠偏接口次数限制的处理策略
现在的地图服务提供商,都会有自己的坐标系统,这样就存在坐标转换相关的处理,然而有些坐标转换会在服务端有一定的次数限制,不能满足一些应用场景。本文从纠偏出现的缘由出发,以百度地图为例,给出服务端纠偏的一些通用的处理策略。相关坐标系
目前国内主要有以下三种坐标系:WGS84:为一种大地坐标系,也是目前广泛使用的GPS全球卫星定位系统使用的坐标系。
GCJ02:又称火星坐标系,是由中国国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。
BD09:为百度坐标系,在GCJ02坐标系基础上再次加密。其中bd09ll表示百度经纬度坐标,bd09mc表示百度墨卡托米制坐标。
非中国地区地图,服务坐标统一使用WGS84坐标。
那么为什么会有火星坐标这一说,是因为国家测绘局为了信息安全原因,对WGS84坐标进行加偏的结果,其实我们口口声声所说的纠偏,其实是加偏,国家认为地理信息数据是保密数据,不应该这么容易就被获取。真正显示在地图服务上的也是经过加偏处理,显示的内容也是经过过滤的。同样的原因,只有一定的测绘资质的公司,才可以有权采集处理地理信息数据。
总而言之,百度地图对应的是BD09坐标系,我们采集来的WGS84坐标下的数据必须经过加偏才能准确的展示在地图上。
坐标转换
百度对于JavaScript和移动端的坐标转换没有次数限制,然而有些场景我们需要在服务端进行坐标转换,然而服务端的接口调用会有次数限制:分类 | 未认证 | 个人认证 | 企业认证 |
---|---|---|---|
日配额(次) | 100,000 | 300,000 | 3000,000 |
分钟并发量(次/分钟) | 6,000 | 12,000 | 30,000 |
原理
首先按照一定的间隔调用百度坐标转换接口进行采样,获取采样点的偏移量;然后根据需要纠偏点的坐标,获取该点的周围的采样点,根据采样点计算该点的偏移值,进而获得该点的百度坐标。采样
前端代码如下<#import "common/spring.ftl" as spring /> <#assign base=request.contextPath /> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta name="viewport" content="initial-scale=1.0, user-scalable=no"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <link rel="stylesheet" href="${base}/css/midlay/v6/midlay_mw_transport.css" type="text/css"/> <title>纠偏采集</title> <style type="text/css"> html { height: 100% } body { height: 100%; margin: 0px; padding: 0px } #map_canvas { height: 80% } </style> <script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=U0VvRG8vCFLW3VG49WlNA7GZ"></script> <script type="text/javascript" src="${base}/js/jquery/jquery-1.9.1.min.js"></script> <script type="text/javascript" src="${base}/js/jquery/jquery.min.js"></script <#include "script/locale/messages.ftl"> <#include "script/custom/transportGrid.ftl"> <#include "script/layer/layer.ftl"> <#include "midlay/v6/mwtransport/function/tempModel.ftl"> </head> <body> <div id="map_canvas"> </div> 选择城市<input value="南京市" id="city"/> 采样间隔<input value="0.001" id="interval"/>度 采集范围:<input value="0" id="startCol"/>列-》<input value="10000" id="endCol"/> 断点处行:<input value="0" id="interruptRow"/>行 <button id="btnPreCollect">计算配置信息</button> <button id="btnCollect">开始采集</button> <button id="btnTest">测试纠偏</button> <div> 采集信息 <span id="collectInfo"></span> </div> <div> 本次采集信息 <span id="collectInfoSingle"></span> </div> <div> 百分比 <span id="percent"></span> 个数 <span id="exeNum"></span> 耗时<span id="consumeTime"></span> 当前执行<span id="nowExe"></span> </div> <div> 经度范围<input value="" id="startLng"/>-》<input value="" id="endLng"/> 纬度范围<input value="" id="startLat"/>-》<input value="" id="endLat"/> 测试个数<input value="1000" id="testNum"/> </div> <script type="text/javascript"> var base='${base}'; // 百度地图API功能 var map = new BMap.Map("map_canvas"); // 创建Map实例 map.addControl(new BMap.MapTypeControl()); //添加地图类型控件 map.centerAndZoom("南京",15); // 设置地图显示的城市 此项是必须设置的 map.enableScrollWheelZoom(true); //开启鼠标滚轮缩放 var convertor = new BMap.Convertor(); var geoc = new BMap.Geocoder(); var timer; var marker; var maxLng=-1000; var minLng=1000; var maxLat=-1000; var minLat=1000; var interval; var sumNum; var sumCol; var sumRow; var prodNum=0; var curLng; var curLat; var startExeTime; $("#btnPreCollect").on("click",function () { var city=$("#city").val(); interval=parseFloat($("#interval").val()); var bdary = new BMap.Boundary(); bdary.get(city, function(rs){ //获取行政区域 map.clearOverlays(); //清除地图覆盖物 var count = rs.boundaries.length; //行政区域的点有多少个 if (count === 0) { alert('未能获取当前输入行政区域'); return ; } var pointArray = []; for (var i = 0; i < count; i++) { var ply = new BMap.Polygon(rs.boundaries[i], {strokeWeight: 2, strokeColor: "#ff0000"}); //建立多边形覆盖物 map.addOverlay(ply); //添加覆盖物 pointArray = pointArray.concat(ply.getPath()); } map.setViewport(pointArray); //调整视野 for(var i=0;i<pointArray.length;i++){ if(pointArray[i].lng>maxLng){ maxLng=pointArray[i].lng; } if(pointArray[i].lng<minLng){ minLng=pointArray[i].lng; } if(pointArray[i].lat>maxLat){ maxLat=pointArray[i].lat; } if(pointArray[i].lat<minLat){ minLat=pointArray[i].lat; } } var collectInfo=""; $("#startLng").val(minLng); $("#endLng").val(maxLng); $("#startLat").val(minLat); $("#endLat").val(maxLat); collectInfo+=("经度"+minLng+"->"+maxLng+";") collectInfo+=("纬度"+minLat+"->"+maxLat+";") sumNum=parseInt((maxLng-minLng)*(maxLat-minLat)/(interval*interval)); sumCol=parseInt((maxLng-minLng)/interval); sumRow=parseInt((maxLat-minLat)/interval); collectInfo+=("采集总数目->"+sumNum+";"); collectInfo+=("采集总列数->"+sumCol+";"); collectInfo+=("采集总行数->"+sumRow+";"); $("#collectInfo").text(collectInfo); var startCol=parseInt($("#startCol").val()); var endCol=parseInt($("#endCol").val()); var interruptRow= parseInt($("#interruptRow").val()); if(startCol<0){ startCol=0; $("#startCol").val(startCol); } if(endCol>sumCol){ endCol=sumCol-1; $("#endCol").val(endCol); } if(interruptRow<0){ interruptRow=0; } if(interruptRow>sumRow){ interruptRow=sumRow-1; } $("#interruptRow").val(interruptRow); maxLng=minLng+endCol*interval+0.00001; minLng=minLng+startCol*interval; curLat=minLat+interruptRow*interval; curLng=minLng; collectInfo="本次采集:"; collectInfo+=("经度"+minLng+"->"+maxLng+";") collectInfo+=("纬度"+minLat+"->"+maxLat+";") sumNum=(endCol-startCol+1)*sumRow-interruptRow; collectInfo+=("总数目->"+sumNum+";"); $("#collectInfoSingle").text(collectInfo); var point = new BMap.Point(curLng, curLat); marker=new BMap.Marker(point); // 创建标注 map.addOverlay(marker); // 将标注添加到地图中 startExeTime=new Date(); }); }); $("#btnCollect").on("click",function () { timer=setInterval(fetch,5*1000); }); function fetch() { var pts=[]; for(var i=0;i<10;i++){ curLat+=interval; if(curLat>maxLat){ curLng+=interval; curLat=minLat; pts.push(new BMap.Point(curLng, curLat)); } else{ pts.push(new BMap.Point(curLng, curLat)); } } if(curLng>maxLng){ clearInterval(timer); } convertor.translate(pts, 1,5 , function (data) { if(data.status === 0) { var realPts=""; var baiduPts=""; for(var i=0;i<pts.length;i++){ var curLngBaidu=data.points[i].lng; var curLatBaidu=data.points[i].lat; var realLng=pts[i].lng; var realLat=pts[i].lat; if(realPts==""){ realPts=realLng+","+realLat; baiduPts=curLngBaidu+","+curLatBaidu; }else{ realPts+=(";"+realLng+","+realLat); baiduPts+=(";"+curLngBaidu+","+curLatBaidu); } } marker.setPosition(data.points[0]); $.ajax({ url: base + "/gpsDataDeal/positiondiffbatch", data: { realPts: realPts, baiduPts: baiduPts }, type: "post", dataType: "json", async: true, success: function (result) { if(result.succeed){ prodNum+=pts.length; $("#percent").text(prodNum/sumNum*100+"%"); $("#exeNum").text(prodNum); $("#consumeTime").text(new Date().getTime()-startExeTime.getTime()); $("#nowExe").text(curLng+","+curLat); }else{ } }, error: function (xhr, statusText, errStatus) { } }); }else { } }); }; $("#btnTest").on("click",function (){ var startLng=$("#startLng").val(); var endLng=$("#endLng").val(); var startLat=$("#startLat").val(); var endLat=$("#endLat").val(); var testNum=$("#testNum").val(); $.ajax({ url: base + "/gpsDataDeal/test", data: { startLng: startLng, endLng:endLng, startLat: startLat, endLat:endLat, testNum:testNum }, type: "post", dataType: "json", async: true, success: function (result) { }, error: function (xhr, statusText, errStatus) { } }); }) </script> </body> </html>
后端代码如下
public void upsert(PosDiffGrid posDiffGrid) { String id = this.formGridId(posDiffGrid); tlCriteria.set(Criteria.where("id").is(id)); PosDiffGrid oldPosDiffGrid = (PosDiffGrid)this.get(); if(oldPosDiffGrid == null) { posDiffGrid.setId(id); posDiffGrid.setTsCreate(new Date()); posDiffGrid.setTsUpDate(new Date()); posDiffGrid.setDiffLon(posDiffGrid.getBaiduPoint().getLongitude() - posDiffGrid.getRealPoint().getLongitude()); posDiffGrid.setDiffLat(posDiffGrid.getBaiduPoint().getLatitude() - posDiffGrid.getRealPoint().getLatitude()); List<PosDiffGrid> diffs = new ArrayList(); diffs.add(posDiffGrid); this.insertList(diffs); } else { Update update = new Update(); update.set("diffLon", Double.valueOf(posDiffGrid.getBaiduPoint().getLongitude() - posDiffGrid.getRealPoint().getLongitude())); update.set("diffLat", Double.valueOf(posDiffGrid.getBaiduPoint().getLatitude() - posDiffGrid.getRealPoint().getLatitude())); update.set("tsUpdate", new Date()); update.set("province", posDiffGrid.getProvince()); update.set("city", posDiffGrid.getCity()); update.set("district", posDiffGrid.getDistrict()); update.set("address", posDiffGrid.getAddress()); tlUpdate.set(update); this.update(); } }
纠偏
纠偏代码如下public PosDiffGrid near(PosDiffGrid posDiffGrid) { PosDiffGrid result = new PosDiffGrid(); Point point = new Point(posDiffGrid.getRealPoint().getLongitude(), posDiffGrid.getRealPoint().getLatitude()); Criteria criteria = Criteria.where("realPoint").near(point).maxDistance(0.0013498920086393088D); tlCriteria.set(criteria); List<PosDiffGrid> grids = this.list(); double minDis = 2000.0D; if(CollectionUtils.isEmpty(grids)) { return null; } else { PosDiffGrid st = null; Iterator var9 = grids.iterator(); while(var9.hasNext()) { PosDiffGrid pdg = (PosDiffGrid)var9.next(); double dis = GeoUtil.calcDistance(posDiffGrid.getRealPoint().getLongitude(), posDiffGrid.getRealPoint().getLatitude(), pdg.getRealPoint().getLongitude(), pdg.getRealPoint().getLatitude()); if(minDis > dis) { minDis = dis; st = pdg; } } if(st == null) { return null; } else { GiPoint baiduPt = new GiPoint(); baiduPt.setLongitude(posDiffGrid.getRealPoint().getLongitude() + st.getDiffLon()); baiduPt.setLatitude(posDiffGrid.getRealPoint().getLatitude() + st.getDiffLat()); result.setRealPoint(posDiffGrid.getRealPoint()); result.setBaiduPoint(baiduPt); result.setProvince(st.getProvince()); result.setCity(st.getCity()); result.setDistrict(st.getDistrict()); result.setAddress(st.getAddress()); return result; } } } public PosDiffGrid distanceRight(PosDiffGrid posDiffGrid, double n) { PosDiffGrid result = new PosDiffGrid(); Point point = new Point(posDiffGrid.getRealPoint().getLongitude(), posDiffGrid.getRealPoint().getLatitude()); Criteria criteria = Criteria.where("realPoint").near(point).maxDistance(0.0010799136069114472D); tlCriteria.set(criteria); List<PosDiffGrid> grids = this.list(); if(CollectionUtils.isEmpty(grids)) { return null; } else if(grids.size() == 1) { result = this.near(posDiffGrid); return result; } else { double minDis = 2000.0D; double sumDis = 0.0D; PosDiffGrid st = null; Iterator var13 = grids.iterator(); double latDiff; while(var13.hasNext()) { PosDiffGrid pdg = (PosDiffGrid)var13.next(); latDiff = GeoUtil.calcDistance(posDiffGrid.getRealPoint().getLongitude(), posDiffGrid.getRealPoint().getLatitude(), pdg.getRealPoint().getLongitude(), pdg.getRealPoint().getLatitude()); sumDis += Math.pow(latDiff, n); if(minDis > latDiff) { minDis = latDiff; st = pdg; } } double lngDiif = 0.0D; latDiff = 0.0D; PosDiffGrid pdg; double dis; for(Iterator var17 = grids.iterator(); var17.hasNext(); latDiff += Math.pow(dis, n) / sumDis * pdg.getDiffLat()) { pdg = (PosDiffGrid)var17.next(); dis = GeoUtil.calcDistance(posDiffGrid.getRealPoint().getLongitude(), posDiffGrid.getRealPoint().getLatitude(), pdg.getRealPoint().getLongitude(), pdg.getRealPoint().getLatitude()); lngDiif += Math.pow(dis, n) / sumDis * pdg.getDiffLon(); } GiPoint baiduPt = new GiPoint(); baiduPt.setLongitude(posDiffGrid.getRealPoint().getLongitude() + lngDiif); baiduPt.setLatitude(posDiffGrid.getRealPoint().getLatitude() + latDiff); result.setRealPoint(posDiffGrid.getRealPoint()); result.setBaiduPoint(baiduPt); result.setProvince(st.getProvince()); result.setCity(st.getCity()); result.setDistrict(st.getDistrict()); result.setAddress(st.getAddress()); return result; } } private String formGridId(PosDiffGrid posDiffGrid) { return posDiffGrid.getRealPoint().getLongitude() * 1000.0D + "_" + posDiffGrid.getRealPoint().getLatitude() * 1000.0D; }
前端页面
采集量级及纠偏结果
对于南京这样的城市,采用100m*100m的网格,大概采集了120W条数据。采用最近距离算法误差会在1m以内,然而对于民用的GPS采集端,定位精度也就是几米,这样完全可以满足需求。同时可以扩大采样间隔来满足不同需求,用户可以自己测试。相关文章推荐
- 限制http接口请求次数
- 微信公众号开发接口权限与次数限制-微信开发教程13
- spring boot中的拦截器限制用户访问接口次数
- 微信公众号获取素材接口调用次数限制的解决办法
- ebay api调用接口达到限制的次数
- win7利用组策略设置电脑开机登录密码次数限制技巧图解
- 使用redis进行用户接口访问时间次数限制
- 恶意访问网站的策略处理,IP访问限制
- 自己开发小程序,有了这些免费的无次数限制API 接口,再也不愁没有服务器支持做不出APP了
- nginx限制IP恶意调用短信接口处理方法
- 接口访问次数限制
- PHP中Yii2框架用redis实现限制接口访问次数
- nginx+lua 限制接口访问次数
- 在java项目中,如何限制每个用户访问接口的次数?
- 各类无次数限制的免费API接口整理
- sas中的sql(6)创建表格、展现表格、插入行、删除行、规定限制条件(constriants)、处理输入错误(undo策略)、update表格、更改列
- 关于调取第三方服务限制次数的处理方式
- 恶意访问网站的策略处理,IP访问限制
- 限制每个用户访问接口的次数(Java)
- cxf+spring开发(三)--- 限制固定Ip地址对接口的访问次数