純客戶端的地圖量算


 一般地圖量算指的是距離量算和面積量算。

 在線的各類地圖也都提供了純客戶端的地圖量算——不需要和服務器交互,技術上用三角函數就可以,雖然本文也討論這個話題,但是會比較下適用於不同情況的量算。

 簡單事情簡單做:直接計算地圖坐標系的距離和面積。貼OpenLayers的代碼。

 線段距離量算:

    getLength: function() {

 

        var length = 0.0;
        if ( this.components && (this.components.length > 1)) {
            for(var i=1, len=this.components.length; i<len; i++) {
                length += this.components[i-1].distanceTo(this.components[i]);
            }
        }
        return length;
    }

 通過節點間的距離和,得到距離長度,而兩點間的距離計算,a2+b2=c2的三角函數再熟悉不過了。

 多邊形面積量算:

    getArea: function() {
        var area = 0.0;
        if ( this.components && (this.components.length > 2)) {
            var sum = 0.0;
            for (var i=0, len=this.components.length; i<len - 1; i++) {
                var b = this.components[i];
                var c = this.components[i+1];
                sum += (b.x + c.x) * (c.y - b.y);
            }
            area = - sum / 2.0;
        }
        return area;
    }

 有了以上的公式,就能完成基本的客戶端量算功能了。 

 現在出現了一個問題,這種基於地圖的量算准確嗎?碰到了經緯度地圖的話結果難道是度為單位?

 由於地圖是經過地圖投影之后顯示到二維坐標系上的,所以地圖上兩點之間的直線映射到地球球面之后,可能就不是兩點之間的最短距離了(當然如果地圖本身是等角投影就沒問題了,這里就不再深入討論有關投影的知識了),誤差產生了。這個誤差對科學研究很重要,但對於我們日常生活來說,可能並不重要,為什么呢?通常我們的測距、測面積都是在城市級別,甚至只是在街道級別,那么這相對投影所帶來的地圖變形來說,微不足道,因此如果是這方面的應用,那么以上的測距和測面積的算法足夠用了。但是,如果非要更加精確的計算結果該如何辦?下面就講講在客戶端實現近似精確的計算方法:

 測地距離,摘自OpenLayers:

      getGeodesicLength: function(projection) {
        var geom = this;  // so we can work with a clone if needed
        if(projection) {
            var gg = new OpenLayers.Projection("EPSG:4326");
            if(!gg.equals(projection)) {
                geom = this.clone().transform(projection, gg);
            }
        }
        var length = 0.0;
        if(geom.components && (geom.components.length > 1)) {
            var p1, p2;
            for(var i=1, len=geom.components.length; i<len; i++) {
                p1 = geom.components[i-1];
                p2 = geom.components[i];
                // this returns km and requires lon/lat properties
                length += OpenLayers.Util.distVincenty(
                    {lon: p1.x, lat: p1.y}, {lon: p2.x, lat: p2.y}
                );
            }
        }
        // convert to m
        return length * 1000;
    }

 考慮到投影的問題,首先要對目標幾何對象做投影轉換,轉換為EPSG:4326,然后基於該大地參考系,計算兩點間的球面距離,相比於非測地距離量算,這里的重點是在一個特定的大地參考系下計算兩點或多點的球面距離,因此結果也更加精確。

 測地面積,摘自OpenLayers:

    getGeodesicArea: function(projection) {

 

        var ring = this;  // so we can work with a clone if needed
        if(projection) {
            var gg = new OpenLayers.Projection("EPSG:4326");
            if(!gg.equals(projection)) {
                ring = this.clone().transform(projection, gg);
            }
        }
        var area = 0.0;
        var len = ring.components && ring.components.length;
        if(len > 2) {
            var p1, p2;
            for(var i=0; i<len-1; i++) {
                p1 = ring.components[i];
                p2 = ring.components[i+1];
                area += OpenLayers.Util.rad(p2.x - p1.x) *
                        (2 + Math.sin(OpenLayers.Util.rad(p1.y)) +
                        Math.sin(OpenLayers.Util.rad(p2.y)));
            }
            area = area * 6378137.0 * 6378137.0 / 2.0;
        }
        return area;
    }

 同樣的,首先對目標幾何對象做投影轉換,轉成我們所熟知的EPSG:4326,然后再計算球面面積。球面面積計算也不同於平面多邊形的面積計算,需要立體幾何的知識了,呵呵。

 一般,這種計算更適合在經緯度地圖中使用,普通的米制投影地圖可以使用簡單的方法,城市級的量算誤差基本可以忽略。當然,也不能排除對距離和面積特別敏感的案例,之前也接觸過一些在通信領域的案例,最終使用了后面這種測地算法。

 

 最后,說說在iClient中如何用的問題。

 iClient自身是帶了服務器的量算功能的,而如果希望使用客戶端量算的話,iClient for JavaScript已經在Geometry的公共接口中提供了,可自行查找;而iClient for Flex沒有提供類似的功能,那么最好借助openscales-geometry庫中所帶的方法——getLength/getGeodesicLength/getArea/getGeodesicArea獲得幫助。為了簡化openscales和iClient for Flex之間的幾何對象轉換關系,可以用https://github.com/SuperMap/OpenScales-FeatureTransform 項目提供的方法來處理。

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM