商业地图纠偏接口次数限制的处理策略

来源:互联网 发布:淘宝扣分规则2017 编辑:程序博客网 时间:2024/06/15 19:40

商业地图纠偏接口次数限制的处理策略

现在的地图服务提供商,都会有自己的坐标系统,这样就存在坐标转换相关的处理,然而有些坐标转换会在服务端有一定的次数限制,不能满足一些应用场景。本文从纠偏出现的缘由出发,以百度地图为例,给出服务端纠偏的一些通用的处理策略。

相关坐标系

目前国内主要有以下三种坐标系:

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采集端,定位精度也就是几米,这样完全可以满足需求。同时可以扩大采样间隔来满足不同需求,用户可以自己测试。

原创粉丝点击