空間索引詳解


原文鏈接:https://www.cnblogs.com/mafeng/p/7909387.html

https://www.cnblogs.com/arxive/p/8139002.html

一、問題

  先思考個常見的問題:如何根據自己所在位置查詢來查詢附近50米的POI(point of interest,比如商家、景點等)呢(圖1a)?

  每個POI都有經緯度信息,我用圖1b的SQL語句在mySQL中建立了POI_spatial的表,其中lat和lng兩個字段來代表緯度和經度。為后續分析方便起見,我人造了40萬個POI數據。

 二、傳統的解決思路

方法一:暴力方法

  該方法的思路很直接:計算位置與所有POI的距離,並保留距離小於50米的POI。

  插句題外話,計算經緯度之間的距離不能像求歐式距離那樣平方開根號,因為地球是個不規整的球體(圖2a),按最簡單的完美球體假設,兩點之間的距離函數應該如圖2b所示。

   該方法的復雜度為:40萬*距離函數。我們將球體距離函數寫為mysql存儲過程distance,之后我們執行查詢操作(圖3),發現花費了4.66秒。

該方法耗時的原因顯而易見,執行了40萬次復雜的距離計算函數。

方法二:矩形過濾方法

  該方法分為兩部:

  a)先用矩形框過濾(圖4a),判斷一個點在矩形框內很簡單,只要進行兩次判斷(LtMin<lat<LtMax; LnMin<lng<LnMax),落在矩形框內的POI個數為n(n<<40萬);

  b)用球面距離公式計算位置與矩形框內n個POI的距離(圖4b),並保留距離小於50米的POI

  矩形過濾方法的復雜度為:40萬*矩形過濾函數 + n*距離函數(n<<40萬)。

 

  根據這個思路我們執行SQl查詢(圖5)(注: 經度或緯度每隔0.001度,距離相差約100米,由此推算出矩形左下角和右上角坐標),發現過濾后正好剩下兩個POI。

  此查詢花費了0.36秒,相比於方法一查詢時間大大降低,但是對於一次查詢來說還是很長。時間長的原因在於遍歷了40萬次。

方法三:B樹對經度或緯度建立索引

  方法二耗時的原因在於執行了遍歷操作,為了不進行遍歷,我們自然想到了索引。我們對緯度進行了B樹索引。

  此方法包括三個步驟:

  a)通過B樹快速找到某緯度范圍的POI(圖6a),個數為m(m<40萬),復雜度為Log(40萬)*過濾函數;

  b)在步驟a過濾得到的m個POI中查找某經度范圍的POI(圖6b),個數為n(n<m),復雜度為m*過濾函數;

  c) 用球面距離公式計算位置與步驟b得到的n個POI的距離(圖6c),並保留距離小於50米的POI

 

  執行SQL查詢(圖7),發現時間已經大大降低,從方法2的0.36秒下降到0.01秒。

三、B樹能索引空間數據嗎?

  這時候有人會說了:“方法三效果如此好,能夠滿足我們附近POI查詢問題啊,看來B樹用來索引空間數據也是可以的嘛!”

  那么B樹真的能夠索引空間數據嗎?

1)只能對經度或緯度索引(一維索引),與期望的不符

  我們期待的是快速找出落在某一空間范圍的POI(如矩形)(圖8a),而不是快速找出落在某緯度或經度范圍的POI(圖8b),想象一下,我要查詢北京某區的POI,但是B樹索引不僅給我找出了北京的,還有與北京同一維度的天津、大同、甚至國外城市的POI,當數據量很大時,效率很低。

2)當數據是多維,比如三維(x,y,z),B樹怎么索引?

  比如z可能是高程值,也可能是時間。有人會說B樹其實可以對多個字段進行索引,但這時需要指定優先級,形成一個組合字段,而空間數據在各個維度方向上不存在優先級,我們不能說緯度比經度更重要,也不能說緯度比高程更重要。

3)當空間數據不是點,而是線(道路、地鐵、河流等),面(行政區邊界、建築物等),B樹怎么索引?

  對於面來說,它由一系列首尾相連的經緯度坐標點組成,一個面可能有成百上千個坐標,這時數據庫怎么存儲,B樹怎么索引,這些都是問題。

  既然傳統的索引不能很好的索引空間數據,我們自然需要一種方法能對空間數據進行索引,即空間索引。

四、空間索引有哪幾種?

  傳統索引使用哈希和樹這兩類最基本的數據結構。空間索引雖然更為復雜,但仍然發展於這兩種數據結構。因此可以將空間索引划分為兩大類:1)基於哈希思想,如網格索引等;2)基於樹思想,有四叉樹、R樹等。

 

五、網格索引

  哈希是通過一個哈希函數將關鍵字映射到內存或外存的數據結構,如何擴展到空間數據呢?

5.1. 網格索引原理

  擴展方法:對地理空間進行網格划分,划分成大小相同的網格,每個網格對應着一塊存儲空間,索引項登記上落入該網格的空間對象。

  舉個例子,我們將地理空間進行網格划分,並進行編號。該空間范圍內有三個空間對象,分別是id=5的街道,23的河流和11的商圈。這時候我們可以按照哈希的數據結構存儲,每個網格對應着一個存儲桶,而桶里放着空間對象,比如對2號網格,里面存儲着id=5的空間對象,對35號網格,桶里放着id=5和id=23的空間對象。

 

  假如我們要查詢某一空間范圍內有哪些空間對象,比如下面的紅框就表示空間范圍,我們可以很快根據紅框的空間范圍算出它與35號和36號網格相交,然后分別到35號和36號網格中查找空間對象,最終找出id=5和id=23的空間對象。

 

5.2. 網格索引缺點

1)索引數據冗余

  網格與對象之間多對多關系在空間對象數量多、大小不均時造成索引數據冗余。比如11號商圈這個空間對象在68,69,100,101這4個網格都有存儲,浪費了大量空間。

2)網格的大小難以確定

  網格的划分大小難以確定。網格划分得越密,需要的存儲空間越多,網格划分的越粗,查找效率可能會降低。對於圖a,這個查詢需要查詢4個網格,由於4個網格覆蓋了整個空間,因此這個查找其實是將空間范圍內所有的點數據都遍歷一遍,失去了索引的意義。

 

3)很多網格沒有數據

  空間數據具有明顯的聚集性,比如POI只在幾個熱點商貿區聚集,在郊區等地方很稀疏,這將導致很多網格內沒有任何空間數據。

 

六、四叉樹空間索引原理與實現

四叉樹索引的基本思想是將地理空間遞歸划分為不同層次的樹結構。它將已知范圍的空間等分成四個相等的子空間,如此遞歸下去,直至樹的層次達到一定深度或者滿足某種要求后停止分割。四叉樹的結構比較簡單,並且當空間數據對象分布比較均勻時,具有比較高的空間數據插入和查詢效率,因此四叉樹是GIS中常用的空間索引之一。常規四叉樹的結構如圖所示,地理空間對象都存儲在葉子節點上,中間節點以及根節點不存儲地理空間對象。

 

四叉樹示意圖

 

四叉樹對於區域查詢,效率比較高。但如果空間對象分布不均勻,隨着地理空間對象的不斷插入,四叉樹的層次會不斷地加深,將形成一棵嚴重不平衡的四叉樹,那么每次查詢的深度將大大的增多,從而導致查詢效率的急劇下降。

 

本節將介紹一種改進的四叉樹索引結構。四叉樹結構是自頂向下逐步划分的一種樹狀的層次結構。傳統的四叉樹索引存在着以下幾個缺點:

(1)空間實體只能存儲在葉子節點中,中間節點以及根節點不能存儲空間實體信息,隨着空間對象的不斷插入,最終會導致四叉樹樹的層次比較深,在進行空間數據窗口查詢的時候效率會比較低下。

(2)同一個地理實體在四叉樹的分裂過程中極有可能存儲在多個節點中,這樣就導致了索引存儲空間的浪費。

(3)由於地理空間對象可能分布不均衡,這樣會導致常規四叉樹生成一棵極為不平衡的樹,這樣也會造成樹結構的不平衡以及存儲空間的浪費。

相應的改進方法,將地理實體信息存儲在完全包含它的最小矩形節點中,不存儲在它的父節點中,每個地理實體只在樹中存儲一次,避免存儲空間的浪費。首先生成滿四叉樹,避免在地理實體插入時需要重新分配內存,加快插入的速度,最后將空的節點所占內存空間釋放掉。改進后的四叉樹結構如下圖所示。四叉樹的深度一般取經驗值4-7之間為最佳。

 

圖改進的四叉樹結構

 

為了維護空間索引與對存儲在文件或數據庫中的空間數據的一致性,作者設計了如下的數據結構支持四叉樹的操作。

(1)四分區域標識

分別定義了一個平面區域的四個子區域索引號,右上為第一象限0,左上為第二象限1,左下為第三象限2,右下為第四象限3。

typedef enum

{

      UR = 0,// UR第一象限

      UL = 1, // UL為第二象限

      LL = 2, // LL為第三象限

      LR = 3  // LR為第四象限

}QuadrantEnum;

 

(2)空間對象數據結構

空間對象數據結構是對地理空間對象的近似,在空間索引中,相當一部分都是采用MBR作為近似。

/*空間對象MBR信息*/

typedef struct SHPMBRInfo

{

      int nID;       //空間對象ID號

      MapRect Box;    //空間對象MBR范圍坐標

}SHPMBRInfo;

 

nID是空間對象的標識號,Box是空間對象的最小外包矩形(MBR)。

(3)四叉樹節點數據結構

四叉樹節點是四叉樹結構的主要組成部分,主要用於存儲空間對象的標識號和MBR,也是四叉樹算法操作的主要部分。

/*四叉樹節點類型結構*/

typedef struct QuadNode

{

      MapRect            Box;                   //節點所代表的矩形區域

      int                nShpCount;        //節點所包含的所有空間對象個數

      SHPMBRInfo* pShapeObj;          //空間對象指針數組

      int         nChildCount;            //子節點個數

      QuadNode *children[4];             //指向節點的四個孩子

}QuadNode;

 

Box是代表四叉樹對應區域的最小外包矩形,上一層的節點的最小外包矩形包含下一層最小外包矩形區域;nShpCount代表本節點包含的空間對象的個數;pShapeObj代表指向空間對象存儲地址的首地址,同一個節點的空間對象在內存中連續存儲;nChildCount代表節點擁有的子節點的數目;children是指向孩子節點指針的數組。

上述理論部分都都講的差不多了,下面就貼上我的C語言實現版本代碼。

頭文件如下:

#ifndef __QUADTREE_H_59CAE94A_E937_42AD_AA27_794E467715BB__  
    #define __QUADTREE_H_59CAE94A_E937_42AD_AA27_794E467715BB__  
      
      
      
      
    /* 一個矩形區域的象限划分:: 
     
    UL(1)   |    UR(0) 
    ----------|----------- 
    LL(2)   |    LR(3) 
    以下對該象限類型的枚舉 
    */  
    typedef enum  
    {  
        UR = 0,  
        UL = 1,  
        LL = 2,  
        LR = 3  
    }QuadrantEnum;  
      
    /*空間對象MBR信息*/  
    typedef struct SHPMBRInfo  
    {  
        int nID;        //空間對象ID號  
        MapRect Box;    //空間對象MBR范圍坐標  
    }SHPMBRInfo;  
      
    /* 四叉樹節點類型結構 */  
    typedef struct QuadNode  
    {  
        MapRect     Box;            //節點所代表的矩形區域  
        int         nShpCount;      //節點所包含的所有空間對象個數  
        SHPMBRInfo* pShapeObj;      //空間對象指針數組  
        int     nChildCount;        //子節點個數  
        QuadNode  *children[4];     //指向節點的四個孩子   
    }QuadNode;  
      
    /* 四叉樹類型結構 */  
    typedef struct quadtree_t  
    {  
        QuadNode  *root;  
        int         depth;           // 四叉樹的深度                      
    }QuadTree;  
      
      
        //初始化四叉樹節點  
        QuadNode *InitQuadNode();  
      
        //層次創建四叉樹方法(滿四叉樹)  
        void CreateQuadTree(int depth,GeoLayer *poLayer,QuadTree* pQuadTree);  
      
        //創建各個分支  
        void CreateQuadBranch(int depth,MapRect &rect,QuadNode** node);  
      
        //構建四叉樹空間索引  
        void BuildQuadTree(GeoLayer*poLayer,QuadTree* pQuadTree);  
      
        //四叉樹索引查詢(矩形查詢)  
        void SearchQuadTree(QuadNode* node,MapRect &queryRect,vector<int>& ItemSearched);  
      
        //四叉樹索引查詢(矩形查詢)並行查詢  
        void SearchQuadTreePara(vector<QuadNode*> resNodes,MapRect &queryRect,vector<int>& ItemSearched);  
      
        //四叉樹的查詢(點查詢)  
        void PtSearchQTree(QuadNode* node,double cx,double cy,vector<int>& ItemSearched);  
      
        //將指定的空間對象插入到四叉樹中  
        void Insert(long key,MapRect &itemRect,QuadNode* pNode);  
      
        //將指定的空間對象插入到四叉樹中  
        void InsertQuad(long key,MapRect &itemRect,QuadNode* pNode);  
      
        //將指定的空間對象插入到四叉樹中  
        void InsertQuad2(long key,MapRect &itemRect,QuadNode* pNode);  
      
        //判斷一個節點是否是葉子節點  
        bool IsQuadLeaf(QuadNode* node);  
      
        //刪除多余的節點  
        bool DelFalseNode(QuadNode* node);  
      
        //四叉樹遍歷(所有要素)  
        void TraversalQuadTree(QuadNode* quadTree,vector<int>& resVec);  
      
        //四叉樹遍歷(所有節點)  
        void TraversalQuadTree(QuadNode* quadTree,vector<QuadNode*>& arrNode);  
      
        //釋放樹的內存空間  
        void ReleaseQuadTree(QuadNode** quadTree);  
      
        //計算四叉樹所占的字節的大小  
        long CalByteQuadTree(QuadNode* quadTree,long& nSize);  
      
      
    #endif  

 

源文件如下:

#include "QuadTree.h"  
      
      
    QuadNode *InitQuadNode()  
    {  
        QuadNode *node = new QuadNode;  
        node->Box.maxX = 0;  
        node->Box.maxY = 0;  
        node->Box.minX = 0;  
        node->Box.minY = 0;  
      
        for (int i = 0; i < 4; i ++)  
        {  
            node->children[i] = NULL;  
        }  
        node->nChildCount = 0;  
        node->nShpCount = 0;  
        node->pShapeObj = NULL;  
      
        return node;  
    }  
      
    void CreateQuadTree(int depth,GeoLayer *poLayer,QuadTree* pQuadTree)  
    {  
        pQuadTree->depth = depth;  
      
        GeoEnvelope env;    //整個圖層的MBR  
        poLayer->GetExtent(&env);  
          
        MapRect rect;  
        rect.minX = env.MinX;  
        rect.minY = env.MinY;  
        rect.maxX = env.MaxX;  
        rect.maxY = env.MaxY;  
          
        //創建各個分支  
        CreateQuadBranch(depth,rect,&(pQuadTree->root));  
      
        int nCount = poLayer->GetFeatureCount();  
        GeoFeature **pFeatureClass = new GeoFeature*[nCount];  
        for (int i = 0; i < poLayer->GetFeatureCount(); i ++)  
        {  
            pFeatureClass[i] = poLayer->GetFeature(i);   
        }  
      
        //插入各個要素  
        GeoEnvelope envObj; //空間對象的MBR  
        //#pragma omp parallel for  
        for (int i = 0; i < nCount; i ++)  
        {  
            pFeatureClass[i]->GetGeometry()->getEnvelope(&envObj);  
            rect.minX = envObj.MinX;  
            rect.minY = envObj.MinY;  
            rect.maxX = envObj.MaxX;  
            rect.maxY = envObj.MaxY;  
            InsertQuad(i,rect,pQuadTree->root);  
        }  
      
        //DelFalseNode(pQuadTree->root);  
    }  
      
    void CreateQuadBranch(int depth,MapRect &rect,QuadNode** node)  
    {  
        if (depth != 0)  
        {  
            *node = InitQuadNode(); //創建樹根  
            QuadNode *pNode = *node;  
            pNode->Box = rect;  
            pNode->nChildCount = 4;  
      
            MapRect boxs[4];  
            pNode->Box.Split(boxs,boxs+1,boxs+2,boxs+3);  
            for (int i = 0; i < 4; i ++)  
            {  
                //創建四個節點並插入相應的MBR  
                pNode->children[i] = InitQuadNode();  
                pNode->children[i]->Box = boxs[i];  
      
                CreateQuadBranch(depth-1,boxs[i],&(pNode->children[i]));  
            }  
        }  
    }  
      
    void BuildQuadTree(GeoLayer *poLayer,QuadTree* pQuadTree)  
    {  
        assert(poLayer);  
        GeoEnvelope env;    //整個圖層的MBR  
        poLayer->GetExtent(&env);  
        pQuadTree->root = InitQuadNode();  
      
        QuadNode* rootNode = pQuadTree->root;  
      
        rootNode->Box.minX = env.MinX;  
        rootNode->Box.minY = env.MinY;  
        rootNode->Box.maxX = env.MaxX;  
        rootNode->Box.maxY = env.MaxY;  
      
        //設置樹的深度(   根據等比數列的求和公式)  
        //pQuadTree->depth = log(poLayer->GetFeatureCount()*3/8.0+1)/log(4.0);  
        int nCount = poLayer->GetFeatureCount();  
      
        MapRect rect;  
        GeoEnvelope envObj; //空間對象的MBR  
        for (int i = 0; i < nCount; i ++)  
        {  
            poLayer->GetFeature(i)->GetGeometry()->getEnvelope(&envObj);  
            rect.minX = envObj.MinX;  
            rect.minY = envObj.MinY;  
            rect.maxX = envObj.MaxX;  
            rect.maxY = envObj.MaxY;  
            InsertQuad2(i,rect,rootNode);  
        }  
      
        DelFalseNode(pQuadTree->root);  
    }  
      
    void SearchQuadTree(QuadNode* node,MapRect &queryRect,vector<int>& ItemSearched)  
    {  
        assert(node);  
      
        //int coreNum = omp_get_num_procs();  
        //vector<int> * pResArr = new vector<int>[coreNum];  
      
        if (NULL != node)  
        {  
            for (int i = 0; i < node->nShpCount; i ++)  
            {  
                if (queryRect.Contains(node->pShapeObj[i].Box)  
                    || queryRect.Intersects(node->pShapeObj[i].Box))  
                {  
                    ItemSearched.push_back(node->pShapeObj[i].nID);  
                }  
            }  
      
            //並行搜索四個孩子節點  
            /*#pragma omp parallel sections 
            { 
                #pragma omp section 
                if ((node->children[0] != NULL) &&  
                    (node->children[0]->Box.Contains(queryRect) 
                    || node->children[0]->Box.Intersects(queryRect))) 
                { 
                    int tid = omp_get_thread_num(); 
                    SearchQuadTree(node->children[0],queryRect,pResArr[tid]); 
                } 
     
                #pragma omp section 
                if ((node->children[1] != NULL) &&  
                    (node->children[1]->Box.Contains(queryRect) 
                    || node->children[1]->Box.Intersects(queryRect))) 
                { 
                    int tid = omp_get_thread_num(); 
                    SearchQuadTree(node->children[1],queryRect,pResArr[tid]); 
                } 
     
                #pragma omp section 
                if ((node->children[2] != NULL) &&  
                    (node->children[2]->Box.Contains(queryRect) 
                    || node->children[2]->Box.Intersects(queryRect))) 
                { 
                    int tid = omp_get_thread_num(); 
                    SearchQuadTree(node->children[2],queryRect,pResArr[tid]); 
                } 
     
                #pragma omp section 
                if ((node->children[3] != NULL) &&  
                    (node->children[3]->Box.Contains(queryRect) 
                    || node->children[3]->Box.Intersects(queryRect))) 
                { 
                    int tid = omp_get_thread_num(); 
                    SearchQuadTree(node->children[3],queryRect,pResArr[tid]); 
                } 
            }*/  
            for (int i = 0; i < 4; i ++)  
            {  
                if ((node->children[i] != NULL) &&   
                    (node->children[i]->Box.Contains(queryRect)  
                    || node->children[i]->Box.Intersects(queryRect)))  
                {  
                    SearchQuadTree(node->children[i],queryRect,ItemSearched);  
                    //node = node->children[i];  //非遞歸  
                }  
            }  
        }  
      
        /*for (int i = 0 ; i < coreNum; i ++) 
        { 
            ItemSearched.insert(ItemSearched.end(),pResArr[i].begin(),pResArr[i].end()); 
        }*/  
      
    }  
      
    void SearchQuadTreePara(vector<QuadNode*> resNodes,MapRect &queryRect,vector<int>& ItemSearched)  
    {  
        int coreNum = omp_get_num_procs();  
        omp_set_num_threads(coreNum);  
        vector<int>* searchArrs = new vector<int>[coreNum];  
        for (int i = 0; i < coreNum; i ++)  
        {  
            searchArrs[i].clear();  
        }  
      
        #pragma omp parallel for  
        for (int i = 0; i < resNodes.size(); i ++)  
        {  
            int tid = omp_get_thread_num();  
            for (int j = 0; j < resNodes[i]->nShpCount; j ++)  
            {  
                if (queryRect.Contains(resNodes[i]->pShapeObj[j].Box)  
                    || queryRect.Intersects(resNodes[i]->pShapeObj[j].Box))  
                {  
                    searchArrs[tid].push_back(resNodes[i]->pShapeObj[j].nID);  
                }  
            }  
        }  
      
        for (int i = 0; i < coreNum; i ++)  
        {  
            ItemSearched.insert(ItemSearched.end(),  
                searchArrs[i].begin(),searchArrs[i].end());  
        }  
      
        delete [] searchArrs;  
        searchArrs = NULL;  
    }  
      
    void PtSearchQTree(QuadNode* node,double cx,double cy,vector<int>& ItemSearched)  
    {  
        assert(node);  
        if (node->nShpCount >0)       //節點            
        {  
            for (int i = 0; i < node->nShpCount; i ++)  
            {  
                if (node->pShapeObj[i].Box.IsPointInRect(cx,cy))  
                {  
                    ItemSearched.push_back(node->pShapeObj[i].nID);  
                }  
            }  
        }  
      
        else if (node->nChildCount >0)                //節點  
        {  
            for (int i = 0; i < 4; i ++)  
            {  
                if (node->children[i]->Box.IsPointInRect(cx,cy))  
                {  
                    PtSearchQTree(node->children[i],cx,cy,ItemSearched);  
                }  
            }  
        }  
      
        //找出重復元素的位置  
        sort(ItemSearched.begin(),ItemSearched.end());  //先排序,默認升序  
        vector<int>::iterator unique_iter =   
            unique(ItemSearched.begin(),ItemSearched.end());  
        ItemSearched.erase(unique_iter,ItemSearched.end());  
    }  
      
    void Insert(long key, MapRect &itemRect,QuadNode* pNode)  
    {  
        QuadNode *node = pNode;     //保留根節點副本  
        SHPMBRInfo pShpInfo;  
          
        //節點有孩子  
        if (0 < node->nChildCount)  
        {  
            for (int i = 0; i < 4; i ++)  
            {    
                //如果包含或相交,則將節點插入到此節點  
                if (node->children[i]->Box.Contains(itemRect)  
                    || node->children[i]->Box.Intersects(itemRect))  
                {  
                    //node = node->children[i];  
                    Insert(key,itemRect,node->children[i]);  
                }  
            }  
        }  
      
        //如果當前節點存在一個子節點時  
        else if (1 == node->nShpCount)  
        {  
            MapRect boxs[4];  
            node->Box.Split(boxs,boxs+1,boxs+2,boxs+3);  
      
            //創建四個節點並插入相應的MBR  
            node->children[UR] = InitQuadNode();  
            node->children[UL] = InitQuadNode();  
            node->children[LL] = InitQuadNode();  
            node->children[LR] = InitQuadNode();  
      
            node->children[UR]->Box = boxs[0];  
            node->children[UL]->Box = boxs[1];  
            node->children[LL]->Box = boxs[2];  
            node->children[LR]->Box = boxs[3];  
            node->nChildCount = 4;  
      
            for (int i = 0; i < 4; i ++)  
            {    
                //將當前節點中的要素移動到相應的子節點中  
                for (int j = 0; j < node->nShpCount; j ++)  
                {  
                    if (node->children[i]->Box.Contains(node->pShapeObj[j].Box)  
                        || node->children[i]->Box.Intersects(node->pShapeObj[j].Box))  
                    {  
                        node->children[i]->nShpCount += 1;  
                        node->children[i]->pShapeObj =   
                            (SHPMBRInfo*)malloc(node->children[i]->nShpCount*sizeof(SHPMBRInfo));  
                          
                        memcpy(node->children[i]->pShapeObj,&(node->pShapeObj[j]),sizeof(SHPMBRInfo));  
      
                        free(node->pShapeObj);  
                        node->pShapeObj = NULL;  
                        node->nShpCount = 0;  
                    }  
                }  
            }  
      
            for (int i = 0; i < 4; i ++)  
            {    
                //如果包含或相交,則將節點插入到此節點  
                if (node->children[i]->Box.Contains(itemRect)  
                    || node->children[i]->Box.Intersects(itemRect))  
                {  
                    if (node->children[i]->nShpCount == 0)     //如果之前沒有節點  
                    {  
                        node->children[i]->nShpCount += 1;  
                        node->pShapeObj =   
                            (SHPMBRInfo*)malloc(sizeof(SHPMBRInfo)*node->children[i]->nShpCount);  
                    }  
                    else if (node->children[i]->nShpCount > 0)  
                    {  
                        node->children[i]->nShpCount += 1;  
                        node->children[i]->pShapeObj =   
                            (SHPMBRInfo *)realloc(node->children[i]->pShapeObj,  
                            sizeof(SHPMBRInfo)*node->children[i]->nShpCount);  
                    }  
      
                    pShpInfo.Box = itemRect;  
                    pShpInfo.nID = key;  
                    memcpy(node->children[i]->pShapeObj,  
                        &pShpInfo,sizeof(SHPMBRInfo));  
                }  
            }  
        }  
      
        //當前節點沒有空間對象  
        else if (0 == node->nShpCount)  
        {  
            node->nShpCount += 1;  
            node->pShapeObj =   
                (SHPMBRInfo*)malloc(sizeof(SHPMBRInfo)*node->nShpCount);  
      
            pShpInfo.Box = itemRect;  
            pShpInfo.nID = key;  
            memcpy(node->pShapeObj,&pShpInfo,sizeof(SHPMBRInfo));  
        }  
    }  
      
    void InsertQuad(long key,MapRect &itemRect,QuadNode* pNode)  
    {  
        assert(pNode != NULL);  
      
        if (!IsQuadLeaf(pNode))    //非葉子節點  
        {  
            int nCorver = 0;        //跨越的子節點個數  
            int iIndex = -1;        //被哪個子節點完全包含的索引號  
            for (int i = 0; i < 4; i ++)  
            {  
                if (pNode->children[i]->Box.Contains(itemRect)  
                    && pNode->Box.Contains(itemRect))  
                {  
                    nCorver += 1;  
                    iIndex = i;  
                }  
            }  
      
            //如果被某一個子節點包含,則進入該子節點  
            if (/*pNode->Box.Contains(itemRect) ||  
                pNode->Box.Intersects(itemRect)*/1 <= nCorver)  
            {   
                InsertQuad(key,itemRect,pNode->children[iIndex]);  
            }  
      
            //如果跨越了多個子節點,直接放在這個節點中  
            else if (nCorver == 0)  
            {  
                if (pNode->nShpCount == 0)    //如果之前沒有節點  
                {  
                    pNode->nShpCount += 1;  
                    pNode->pShapeObj =   
                        (SHPMBRInfo*)malloc(sizeof(SHPMBRInfo)*pNode->nShpCount);  
                }  
                else  
                {  
                    pNode->nShpCount += 1;  
                    pNode->pShapeObj =   
                        (SHPMBRInfo *)realloc(pNode->pShapeObj,sizeof(SHPMBRInfo)*pNode->nShpCount);  
                }  
      
                SHPMBRInfo pShpInfo;  
                pShpInfo.Box = itemRect;  
                pShpInfo.nID = key;  
                memcpy(pNode->pShapeObj+pNode->nShpCount-1,&pShpInfo,sizeof(SHPMBRInfo));  
            }  
        }  
      
        //如果是葉子節點,直接放進去  
        else if (IsQuadLeaf(pNode))  
        {  
            if (pNode->nShpCount == 0)    //如果之前沒有節點  
            {  
                pNode->nShpCount += 1;  
                pNode->pShapeObj =   
                    (SHPMBRInfo*)malloc(sizeof(SHPMBRInfo)*pNode->nShpCount);  
            }  
            else  
            {  
                pNode->nShpCount += 1;  
                pNode->pShapeObj =   
                    (SHPMBRInfo *)realloc(pNode->pShapeObj,sizeof(SHPMBRInfo)*pNode->nShpCount);  
            }  
      
            SHPMBRInfo pShpInfo;  
            pShpInfo.Box = itemRect;  
            pShpInfo.nID = key;  
            memcpy(pNode->pShapeObj+pNode->nShpCount-1,&pShpInfo,sizeof(SHPMBRInfo));  
        }  
    }  
      
    void InsertQuad2(long key,MapRect &itemRect,QuadNode* pNode)  
    {  
        QuadNode *node = pNode;     //保留根節點副本  
        SHPMBRInfo pShpInfo;  
      
        //節點有孩子  
        if (0 < node->nChildCount)  
        {  
            for (int i = 0; i < 4; i ++)  
            {    
                //如果包含或相交,則將節點插入到此節點  
                if (node->children[i]->Box.Contains(itemRect)  
                    || node->children[i]->Box.Intersects(itemRect))  
                {  
                    //node = node->children[i];  
                    Insert(key,itemRect,node->children[i]);  
                }  
            }  
        }  
      
        //如果當前節點存在一個子節點時  
        else if (0 == node->nChildCount)  
        {  
            MapRect boxs[4];  
            node->Box.Split(boxs,boxs+1,boxs+2,boxs+3);  
      
            int cnt = -1;  
            for (int i = 0; i < 4; i ++)  
            {    
                //如果包含或相交,則將節點插入到此節點  
                if (boxs[i].Contains(itemRect))  
                {  
                    cnt = i;  
                }  
            }  
      
            //如果有一個矩形包含此對象,則創建四個孩子節點  
            if (cnt > -1)  
            {  
                for (int i = 0; i < 4; i ++)  
                {  
                    //創建四個節點並插入相應的MBR  
                    node->children[i] = InitQuadNode();  
                    node->children[i]->Box = boxs[i];  
                }  
                node->nChildCount = 4;  
                InsertQuad2(key,itemRect,node->children[cnt]);   //遞歸  
            }  
      
            //如果都不包含,則直接將對象插入此節點  
            if (cnt == -1)  
            {  
                if (node->nShpCount == 0)     //如果之前沒有節點  
                {  
                    node->nShpCount += 1;  
                    node->pShapeObj =   
                        (SHPMBRInfo*)malloc(sizeof(SHPMBRInfo)*node->nShpCount);  
                }  
                else if (node->nShpCount > 0)  
                {  
                    node->nShpCount += 1;  
                    node->pShapeObj =   
                        (SHPMBRInfo *)realloc(node->pShapeObj,  
                        sizeof(SHPMBRInfo)*node->nShpCount);  
                }  
      
                pShpInfo.Box = itemRect;  
                pShpInfo.nID = key;  
                memcpy(node->pShapeObj,  
                    &pShpInfo,sizeof(SHPMBRInfo));  
            }  
        }  
      
        //當前節點沒有空間對象  
        /*else if (0 == node->nShpCount) 
        { 
            node->nShpCount += 1; 
            node->pShapeObj =  
                (SHPMBRInfo*)malloc(sizeof(SHPMBRInfo)*node->nShpCount); 
     
            pShpInfo.Box = itemRect; 
            pShpInfo.nID = key; 
            memcpy(node->pShapeObj,&pShpInfo,sizeof(SHPMBRInfo)); 
        }*/  
    }  
      
    bool IsQuadLeaf(QuadNode* node)  
    {  
        if (NULL == node)  
        {  
            return 1;  
        }  
        for (int i = 0; i < 4; i ++)  
        {  
            if (node->children[i] != NULL)  
            {  
                return 0;  
            }  
        }  
      
        return 1;  
    }  
      
    bool DelFalseNode(QuadNode* node)  
    {  
        //如果沒有子節點且沒有要素  
        if (node->nChildCount ==0 && node->nShpCount == 0)  
        {  
            ReleaseQuadTree(&node);  
        }  
      
        //如果有子節點  
        else if (node->nChildCount > 0)  
        {  
            for (int i = 0; i < 4; i ++)  
            {  
                DelFalseNode(node->children[i]);  
            }  
        }  
      
        return 1;  
    }  
      
    void TraversalQuadTree(QuadNode* quadTree,vector<int>& resVec)  
    {  
        QuadNode *node = quadTree;  
        int i = 0;   
        if (NULL != node)  
        {  
            //將本節點中的空間對象存儲數組中  
            for (i = 0; i < node->nShpCount; i ++)  
            {  
                resVec.push_back((node->pShapeObj+i)->nID);  
            }  
      
            //遍歷孩子節點  
            for (i = 0; i < node->nChildCount; i ++)  
            {  
                if (node->children[i] != NULL)  
                {  
                    TraversalQuadTree(node->children[i],resVec);  
                }  
            }  
        }  
      
    }  
      
    void TraversalQuadTree(QuadNode* quadTree,vector<QuadNode*>& arrNode)  
    {  
        deque<QuadNode*> nodeQueue;  
        if (quadTree != NULL)  
        {  
            nodeQueue.push_back(quadTree);  
            while (!nodeQueue.empty())  
            {  
                QuadNode* queueHead = nodeQueue.at(0);  //取隊列頭結點  
                arrNode.push_back(queueHead);  
                nodeQueue.pop_front();  
                for (int i = 0; i < 4; i ++)  
                {  
                    if (queueHead->children[i] != NULL)  
                    {  
                        nodeQueue.push_back(queueHead->children[i]);  
                    }  
                }  
            }  
        }  
    }  
      
    void ReleaseQuadTree(QuadNode** quadTree)  
    {  
        int i = 0;  
        QuadNode* node = *quadTree;  
        if (NULL == node)  
        {  
            return;  
        }  
      
        else  
        {  
            for (i = 0; i < 4; i ++)  
            {   
                ReleaseQuadTree(&node->children[i]);  
            }  
            free(node);  
            node = NULL;  
        }  
      
        node = NULL;  
    }  
      
    long CalByteQuadTree(QuadNode* quadTree,long& nSize)  
    {  
        if (quadTree != NULL)  
        {  
            nSize += sizeof(QuadNode)+quadTree->nChildCount*sizeof(SHPMBRInfo);  
            for (int i = 0; i < 4; i ++)  
            {  
                if (quadTree->children[i] != NULL)  
                {  
                    nSize += CalByteQuadTree(quadTree->children[i],nSize);  
                }  
            }  
        }  
      
        return 1;  
    }  

 


免責聲明!

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



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