Yogurt是一名學GIS的學生,今天要跟大家分享的是四叉樹這種空間索引方式的Morton編碼方法,接下來我將在小課堂中簡單介紹一下空間索引以及其幾種編碼方式~~
---------------------------------------------------------yogurt小課堂開課啦--------------------------------------------------------
GIS所涉及到的都是有關空間的數據信息,即也屬於所謂的大數據了,那么怎么將客觀的物體對象存儲到計算機中,以及怎么從計算機中讀取所需要的數據呢?
首先我們要知道計算機的存儲器有內存和外存,內存空間小但是讀寫快,外存空間大卻讀寫慢,訪問外存所花費的時間是訪問內存的十萬倍以上!在GIS的實際應用中大量的數據都是存儲在外存上的,想象一下如果這些數據全都雜亂無章的堆放在那里,那么每需要查詢一個數據就需要掃描整個數據文件,這樣訪問磁盤的代價是非常大的,嚴重影響了系統效率!所以,我們必須記錄好每個數據存放的位置,以便於組織和管理,在這個過程中就需要用到索引技術啦!
【(這里引自我老師的課件哈,低調低調!!!)
從傳統的索引技術觀點來看,可以把空間索引技術大致分為四大類:基於R樹,基於Hashing,基於二叉樹,基於空間填充。
在建立索引時,按照划分區域是否與空間對象的分布特征有關的標准,空間索引又可以分為兩大類:無關的(網格索引、四叉樹),有關的(BSP樹、KD樹、KDB樹、R樹及其變種樹)。
我們來看看幾種索引方法的實際應用:
(1)ESRI的ArcSDE采用的是固定格網索引;
(2)目前國內外主要的空間數據庫如ESRI的ArcView,Mapinfo公司的Maoinfo和Informix的GeoSpatial DataBlade采用的是R樹系列作為空間索引的方式;
(3)Oracle公司的Spatial同時采用固定格網索引以及R樹索引;
(4)中國地質大學的MapGIS和中科院的SuperMap采用的是四叉樹。
以上來自我的一個大牛老師的PPT~~】
好啦,既然今天要講矩陣四叉樹的Morton編碼,那么接下來就介紹一下四叉樹以及Morton碼的編碼規則吧:
【四叉樹】:
區域型物體的四叉樹表示方法最早出現在加拿大地理信息系統CGIS中,20世紀80年代以來,四叉樹在圖象分割、數據壓縮、 地理信息系統等方面進行了大量的研究,對四叉樹數據結構提出了許多編碼方案。四叉樹分為常規四叉樹與線性四叉樹,下圖簡單的說明了兩者的區別:(不要嫌棄我字丑!!!)
編碼規定:
【線性四叉樹的編碼方式】: 例如有這樣一個矩陣線性四叉樹,以紅色圈中的9的編碼為例,有自上而下的方法和自下而上的方法:
(1)基於深度和層次碼線性四叉樹編碼:(自上而下的方法)
層次碼:第一層(在位置2,用兩位二進制表示為:10),第二層(在位置1,用兩位二進制表示為:01),第三層(在位置2,用兩位二進制表示為:10);
深度碼:有3層深,(用四位二進制表示為:0011);
“9”的位置編碼為:10 01 10 0011,該位置碼的十進制為2^0+2^1+2^5+2^6+2^9=611.
(2)基於四進制的線性四叉樹編碼:
(自上而下的方法):第一層2,第二層1,第三層2,位置碼:212
(自下而上的方法,說明四進制編碼的過程):二進制的行列號Iyb、Ixb(從第0行0列開始),四進制編碼M=2*Iyb+ Ixb;那么這里就是:第5行(101)第2列(010):M=2*101+10=212
(3)基於十進制的線性四叉樹編碼:
(自下而上的方法,說明四進制編碼的過程):二進制的行列號Iyb、Ixb(從第0行0列開始),十進制編碼M=奇數位用列號填充,偶數位用行號填充;那么這里就是:第5行(101)第2列(010):M=10 01 10
(4)在相鄰四個碼中若屬性值相同,進行合並,除去最低位得到合並后的新編碼。
-----------------------------------------------------------下課啦!!!--------------------------------------------------------------
編寫該程序的思路:
第一步:讀入矩陣四叉樹,並將其輸出;
第二步:利用four_decimal函數得到每一個位置的四進制M碼,利用Change函數得到規定格式的三位四進制M碼;最后利用checkcombine_four函數,將屬性值一樣的位置的M碼合並,並輸出;
第三步:同第二步類似,利用ten_decimal函數得到每一個位置的十進制M碼,利用checkcombine_ten函數,將屬性值一樣的位置的M碼合並,並輸出。
具體實現過程:
(1)將十進制行列號轉換為二進制:利用函數Tobinary :
(2)得到四進制M碼:利用函數four_decimal:
(3)得到十進制M碼:利用函數ten_decimal:(注意按位交錯)
(4)對屬性值一樣的M碼進行合並的處理操作checkcombine_ten:(以十進制為例)
在第二層里:(方法與第一層類似,只是合並條件變成了w==2)
最后輸出數組即可,對於四進制的M碼,由於合並時還要除去最低位的,所以需要特殊處理(便於輸出):如在合並第一層時,給第二三位賦予標志值99999999:
合並第二層時,給第三位賦予標志值99999999:
輸出時:
好啦,接下來是整體代碼:

#include<stdio.h> #include<stdlib.h> #include<math.h> typedef int newc[3]; typedef int ceng[2]; void read(int a[][8]); //讀入矩形四叉樹 int Tobinary(int k); //將十進制的行列號k轉換為二進制. int eq(int m, int n); //判斷m和n是否相等,相等則返回1,否則返回0. void judge_four(int a[][8], newc b[][8], ceng c[4]);//判斷數組c表示的矩形范圍內的值是否一樣,若一樣就更新數組b. void judge_ten(int a[][8], int b[][8], ceng c[4]); void checkcombine_four(int a[][8], newc b[][8]); //將屬性值一樣的單元進行合並. void checkcombine_ten(int a[][8], int b[][8]); void Change(int m[][8], newc n[][8]); //將四進制的M碼按照規定格式輸出. void four_decimal(int b[][8]); //四進制編碼. void ten_decimal(int c[][8]); //十進制編碼. void output_i(int a[][8]); //輸出int型數組. void output_c(newc a[][8]); //輸出newc型數組. void main() { int a[8][8], b[8][8], c[8][8]; newc bb[8][8]; read(a); printf("矩陣四叉樹為:\n"); output_i(a); four_decimal(b); Change(b, bb); checkcombine_four(a,bb); printf("\n四叉樹對應的四進制編碼為:\n"); output_c(bb); ten_decimal(c); checkcombine_ten(a,c); printf("\n四叉樹對應的十進制編碼為:\n"); output_i(c); } void read(int a[][8]) { FILE *fp = fopen("四叉樹.txt","r"); if (!fp) exit; else for (int i = 0; i < 8;i++) for (int j = 0; j < 8; j++) fscanf(fp, "%d", &a[i][j]); fclose(fp); } int Tobinary(int k) { int s[10],rem,i=0,t=0; do { rem = k % 2; k = k / 2; s[i++] = rem; } while (k != 0); //當十進制數是0時也要進行一遍此循環,所以必須用do……while循環,而不是while循環 for (int j = --i; j >= 0; j--) { t += s[j] * pow(10.0, j); } return t; } int eq(int m, int n) { if (m == n) return 1; else return 0; } void judge_four(int a[][8], newc b[][8], ceng c[4]) { for (int i = 0; i < 4; i++) { int w = 0; for (int m = c[i][0]; m <c[i][0] + 2; m++) for (int n = c[i][1]; n <c[i][1] + 1; n++) w += eq(a[m][n], a[m][n + 1]); if (w == 2)//4個值屬性一樣 { for (int m = c[i][0]; m <c[i][0] + 2; m++) for (int n = c[i][1]; n < c[i][1] + 2; n++) { *b[m][n] = *b[(c[i][0])][(c[i][1])]; *(b[m][n] + 1) = *(b[(c[i][0])][(c[i][1])] + 1); *(b[m][n] + 2) = 99999999; } } } } void judge_ten(int a[][8], int b[][8], ceng c[4]) { for (int i = 0; i < 4; i++) { int w = 0; for (int m = c[i][0]; m <c[i][0]+2; m++) for (int n = c[i][1]; n <c[i][1]+1; n++) w += eq(a[m][n], a[m][n + 1]); if (w == 2)//4個值屬性一樣 { for (int m = c[i][0]; m <c[i][0] + 2; m++) for (int n = c[i][1]; n < c[i][1] + 2; n++) { b[m][n] = b[(c[i][0])][(c[i][1])]; } } } } void checkcombine_ten(int a[][8], int b[][8]) { //第一層 ceng c[4] = { { 0, 0 }, { 0, 4 }, { 4, 0 }, { 4, 4 } }; for (int i = 0; i < 4; i++) { int w = 0; for (int m = c[i][0]; m < 4; m++) for (int n = c[i][1]; n < 3; n++) w += eq(a[m][n], a[m][n + 1]); if (w == 12)//16個值屬性一樣 { for (int m = c[i][0]; m < c[i][0] + 4; m++) for (int n = c[i][1]; n < c[i][1] + 4; n++) { b[m][n] = b[(c[i][0])][(c[i][1])]; } } } //第二層 ceng d[4] = { { 0, 0 }, { 0, 2 }, { 2, 0 }, { 2, 2 } }, e[4] = { { 0, 4 }, { 0, 6 }, { 2, 4 }, { 2, 6 } }, f[4] = { { 4, 0 }, { 4, 2 }, { 6, 0 }, { 6, 2 } }, g[4] = { { 4, 4 }, { 4, 6 }, { 6, 4 }, { 6, 6 } }; judge_ten(a, b, d); judge_ten(a, b, e); judge_ten(a, b, f); judge_ten(a, b, g); } void checkcombine_four(int a[][8], newc b[][8]) { //第一層 ceng c[4] = { { 0, 0 }, { 0, 4 }, { 4, 0 }, { 4, 4 } }; for (int i = 0; i < 4; i++) { int w = 0; for (int m = c[i][0]; m < 4; m++) for (int n = c[i][1]; n < 3; n++) w += eq(a[m][n], a[m][n + 1]); if (w == 12)//16個值屬性一樣 { for (int m = c[i][0]; m < c[i][0] + 4; m++) for (int n = c[i][1]; n < c[i][1] + 4; n++) { *b[m][n] =*b[(c[i][0])][(c[i][1])]; *(b[m][n] + 1) =99999999; *(b[m][n] + 2) =99999999; } } } //第二層 ceng d[4] = { { 0, 0 }, { 0, 2 }, { 2, 0 }, { 2, 2 } }, e[4] = { { 0, 4 }, { 0, 6 }, { 2, 4 }, { 2, 6 } }, f[4] = { { 4, 0 }, { 4, 2 }, { 6, 0 }, { 6, 2 } }, g[4] = { { 4, 4 }, { 4, 6 }, { 6, 4 }, { 6, 6 } }; judge_four(a, b, d); judge_four(a, b, e); judge_four(a, b, f); judge_four(a, b, g); } void Change(int m[][8], newc n[][8]) { int t[3]; int q; for (int i = 0; i < 8;i++) for (int j = 0; j < 8; j++) { q = m[i][j]; t[0] = q / 100; q = q % 100; t[1] = q / 10; q = q % 10; t[2] = q; *n[i][j] = *t; //數組賦值,數組名稱不能直接做左值 *(n[i][j] + 1) = *(t + 1); *(n[i][j] + 2) = *(t + 2); } } void four_decimal(int b[][8]) { for (int i = 0; i < 8;i++) for (int j = 0; j < 8;j++) { int m=Tobinary(i); int n=Tobinary(j); b[i][j] = 2 * m + n; } } void ten_decimal(int c[][8]) { for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++) { int m = Tobinary(i); int n = Tobinary(j); int t[8]; t[0] = m / 1000; m = m % 1000; t[2] = m / 100; m = m % 100; t[4] = m / 10; m = m % 10; t[6] = m; t[1] = n / 1000; n = n % 1000; t[3] = n / 100; n = n % 100; t[5] = n / 10; n = n % 10; t[7] = n; int y = 0; for (int w = 0; w < 8; w++) { y+= t[w] * pow(2.0,7-w); } c[i][j] = y; } } void output_i(int a[][8]) { for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { printf("%6d", a[i][j]); } printf("\n"); } } void output_c(newc a[][8]) { for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) if (*(a[i][j] + 1)==99999999&&*(a[i][j] + 2)==99999999) printf("%6d", *a[i][j]); else if (*(a[i][j] + 2) == 99999999) printf("%5d%d", *a[i][j], *(a[i][j] + 1)); else printf("%4d%d%d", *a[i][j], *(a[i][j] + 1), *(a[i][j] + 2)); printf("\n"); } }
最后結果: