等值線的追蹤算法

轉載自:http://dev.21tx.com/2008/12/15/13505.html

        在等值線的追蹤中有一類相對來說比較簡單的,也就是涉及不是很深的問題,那就是本文要說的基於規則的等值線的追蹤網格。先說一下等值線的形成,等值線的繪製是有兩種方法的,一種就是完全測量,也就是在實際的施工中找到所有的等值點,標記上x,y,value,還有一種就是按一定的方式用幾個預測點來採用一些插值方法形成規則的網格,每一個網格點上都有着座標和value,也就是高程值,然後在用這些網格上的值來估計出等值點,顯然用第一種方法是不好的,常常也是不切實際的;這樣也就有了一系列的等值線的追蹤方法的形成;在這裏,也有一個不得不說的就是基於幾個觀測點的網格化問題,這裏面也有很多的值得研究的地方,我在做項目時用的是Kriging插值算法,當然還有一些其它的很多,有加權反距離,有最小曲率等等很多,這些算法都是比較不好做的,大家也可以到網上搜的瞭解一下,我自己也沒有弄好它們,也是慚愧,在項目中用到的Kriging算法也是別人老師做的,但也沒有做的非常的好,這個Kriging算法也有做的比較好的,我們做的是地質軟件,象我們參照過的Srufer,就是這方面比較做的比較好 的,地質上也是用的比較多的,它的這方面的算法就做的比較好,可是它也還有一點問題,也就是我前面說過的,它也沒有把斷層做好,當然我也沒有說別人的資格,我其實還遠遠沒有做到他們的軟件那樣;在Kriging算法方面,也有一些開源的,比較好的就是斯坦福大學做的開源的Fortran的代碼,那可真是利害,也做了開源軟件的最高境界, 我開源了,你看不懂,等你看懂了,也過時了;他做的就是我開源了,你看不懂,那個可是真的太難看了,順便說一句,如果大家有興趣那個也真是值得大家研究研究,研究好了,那起碼碩士論文那絕對是不成問題的,因爲別人的這方面的博士論文也就那樣,呵呵。

         以下就開始等值線追蹤算法的講解;等值線可以分成爲非封閉等值線和封閉等值線,等值線的追蹤先從網格邊界或者網格內部上一等值點出發, 求得下一等值點,然後以此點出發搜索下一等值點(主要是求出座標),一直這樣下去,若遇到網格邊界或者又回到起點(封閉等值線),則說明已經找到了一條等值上點所在的位置, 也就是找到了等值線。要注意網格,什麼是網格?網格又意味着什麼?等值線在網格中又有什麼不一樣的地方?大家可要知道,我現在要說的是一種基於規則網格的等值線的追蹤,就是那種矩形的網格,只有網格上纔有座標和高程值,而在算法中又要找出座標,難道是找那些網格點,把網格點上的等值點長出來,這當然不是,我們要找的點應該是在那些網格的每一個小格子的邊上,在遇到了等值點正好在網格點上時我們還不得不做一些小的處理,否則是無法追蹤下去的,待會我再講原因; 大家又可能會問,那沒有值我們追蹤個啥,這也就是這種方法要做的,記住在每一個小格上的四個點的座標和高程值我們是知道的,那就可以根據這些值來估計了,有人會說,是估計的話那不就不準了,那我問你,你的插值算法準嗎?你要準,那你就自己去一點點測量吧。那怎麼樣根據這些值來估計呢,這就相當於在做線性插值,這裏可以放心,因爲網格化後的每一個網格的小格在座標的距離都並不會很大,用什麼插值也沒有就爭議了,這裏用一個圖來說明

圖1

大家看到了如果說有圖那樣的情況,是不是那一個等值點就找到了呢,提示大家一下,大家有沒有看到這張圖裏的那個黑點是不是比較靠近10,這可就是線性插值的真實情況了。在一個這樣的小格中,這樣的點只會有0,2 ,4 個,爲什麼不會有1,3 個呢,大家想想,如果只有一個,那等值線進去了要怎麼樣出去呢,3個同樣如此,5 個以上那就更不可能,爲什麼,要注意我們做的是線性插值。這裏大家可能想那如果上圖中的5變成10,那可怎麼辦呢,你這樣想說明你有點上路了,這個我們會在程序中將其中的一個10加上一個修正值來解決。如果出現有四個等值點的情況,則必須做一下處理,我們就按上圖來說明,如果是有四個點,就說明上,右,下邊都有等值點,那怎麼樣就認爲8這個點是從那一邊出去呢,


圖2    

(3)這種情況是不可能出現的,如果出現,那下一次追蹤到這一個小格時就不得不將上下兩個點連接起來,這樣在畫等值線就出現了交叉的情況,這是不允許的,現在要解決的就是究竟是(1)還是(2)的問題,這個問題上也有幾種方法,也有比較麻煩的,我只是按照別人的方法用了一個比較簡單的方法,就是上下兩個點誰8近的方法,如果兩個都一樣近就看8那面的那個點是偏離上下邊哪 個近離上邊近就是(1),否則就是(2),按這種方法,上圖中就應該是(2)這種方式。要說算法,就有一些不得不說的,那就是算法裏用到的算定義的數據結構,

struct IsoPoint
{
  public int _column;     //這裏的行列的最大值要比網格的行列數分別小1
  public int _row;      //因爲這裏的行列是按行列線(注意是線)來算的
  public bool _isHorizon;   //等值點是否在X軸上(水平線上)  true--X  false--Y
}
//每條邊上的信息(有無等值點,有時點在何處(_rate)
struct EdgeIsoInfo    
{
  public float _rate;   //比率是代表在網格邊上的比率位置
  public bool _isIsoPoint;  //在此邊上是否有等值點
}


這兩種結構就可以將整個網格的各個小格邊上是否有等值點,在哪一條邊,在哪什麼座標位置(你注意到了_rate了嗎?)。

  在開始前,我們先對整個網格做一次預處理,就是用二維數組保存各個邊上的信息    

privateEdgeIsoInfo[,]_xSide;
privateEdgeIsoInfo[,]_ySide;
其中_xSide, _ySide分別指的是x,y邊上的等值點信息。預處理就相當於找到此高程值的所有等值點(爲什麼,自己想),我這裏做的是循環,就是說每一個高程值做一次預處理循環,有了此高程值相對應的點信息,現在的問題就是如何將所有的這些點分類,分成一條條的等值線,這樣就必須要做一些處理,那就決定從哪裏開始找起,這樣就有網格的左,上,右,底邊四個邊界和網格內部(用於追蹤封閉等值線),我們就先做開等值線的追蹤,後做封閉等值線的追蹤,對於開等值線,我們拿一上面的第一個圖來說,也就是追蹤左邊界,我們從左邊界的最下端開始追起,也就是追_ySide[0,0], 看它的那個_isIsoPoints是否爲true, 不是就在找_ySide[1,0],依次類推,假如我們到達10,5 那個位置,也就產圖1的情況就達到了示例程序中的從左到右追蹤的條件TracingFromLeft2Right(具體見代碼),處理完了以後,爲了避免再一次追蹤到這一點,那我們就將 _isIsoPoints設爲false, 這樣下次就不會再找到這個點了。對於封閉等值線的處理同樣如此,就不再說了。

  其餘詳細的看示例代碼吧,其實我也是第一次寫博客,才知道寫東西真的不是那麼的容易,以前 看別人一天博客更新一次還嫌慢了,現在到了自己的頭上才發現真的不容易,我再說一次,我這裏寫的,還遠遠沒有將算法講清楚,也只能想當於介紹了一下子而已,想要弄清楚的大牛們就自己去看源碼吧,也可以歡迎和我聯繫。下一篇就講光滑了,下次力爭寫的好一點。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章