前言:
如果您對JTS這三個詞還是沒有一個概念,那么推薦您關注一下sinoly的博客。這個我能夠找到為數不多的關于jts的中文資料。
http://m.tkk7.com/sinoly/archive/2007/02/09/99042.html
下面這段話就是摘抄自sinoly老兄的博客:
............它主要是完成了java對幾何對象、空間拓撲得核心操作算法。.......集成了java對幾何對象(點、線、面等)的對象管理外更大一部分工作是在完成對各種幾何對象的buffer、analyze以及空間索引.
本文想要向您展示的就是一個空間索引的小小demo!
jts本身有一個包就叫Index,當中分別給出了SpatialIndex接口的幾個實現。本文展現的就是quadtree的用法。
quadtree翻譯過來就是四叉樹的意思,有一個很簡單的例子可以來理解四叉樹和空間索引的關系。那就是把中國的版圖來個十字型的分割,這樣就形成一個中心點和四個子節點。然后將每個子節點再次的進行十字分割。當分割到極小處時,整個中國的版圖就可以使用一個掛載很多節點的四叉樹來表示。每一個節點可以認為是一個具有經緯度及其描述信息的對象。
當我們手中有很多點的時候,想要建立成一顆四叉樹,只要把上邊的思路反過來就可以了(對于每個節點之間的距離是否相等,目前我還沒有好的認識)。
正如我們研發部local search的文斌而言,思路很簡單,但是不是每個人你都能夠實現。接下來,讓demo的代碼說話。
1.建立一個對象,這個對象描述point除了經緯度外的其他信息(如果需要可以加上經緯度)。
1
/** *//**
2
*
3
* @author lang
4
* @date 2007-12-17
5
* @email lanfanss@126.com
6
* @desc 代表point的信息
7
* @since
8
*
9
*/
10
public class PointInfo
{
11
/** *//**
12
* 名稱
13
*/
14
private String name;
15
private String info;
16
17
public String getInfo()
{
18
return info;
19
}
20
21
public void setInfo(String info)
{
22
this.info = info;
23
}
24
25
public PointInfo(String name)
{
26
super();
27
this.name = name;
28
}
29
30
public String getName()
{
31
return name;
32
}
33
34
public void setName(String name)
{
35
this.name = name;
36
}
37
}
2.接下來就是建立一顆樹,代碼如下

public static void main(String[] args)
{
Quadtree quatree = new Quadtree();
// 構建幾個點
PointInfo info1 = new PointInfo("故宮");
quatree.insert(new Envelope(new Coordinate(11638937, 3992178)), info1);
PointInfo info2 = new PointInfo("太和殿");
info2
.setInfo("太和殿,俗稱金鑾殿,是宮殿群中最大的建筑。殿高36米,寬63米,面積為2380平方米。 “太和”語出《周易》,太和是“陰陽會和,沖和之氣也”,“混同宇內以玉太和”之意,即指宇宙萬物,和諧圓滿。太和殿坐落在“工”字形須彌座上,漢白玉雕成,分上中下三層,稱為丹墀或丹陛。雕欄稱為望柱,柱頭雕以云龍云圖案。那些伸出的為螭首,口中小孔為出水孔。共有螭首1142個。如遇雨天,可見千龍吐水之奇觀。 臺基上放置18個大銅爐,據說代表當時的18個省份。太和殿臺基上面的大平臺,放置銅龜、銅鶴各一對,象征“龜鶴千秋”,意為長壽。東有日晷西有喜量,象征皇權公正平允。這里是舉行大典奏九韶之樂的地方。");
quatree.insert(new Envelope(new Coordinate(11639073, 3991605)), info2);
PointInfo info3 = new PointInfo("中和殿");
info3
.setInfo(" 中和殿在太和殿后,平面呈方形。“中和”語出《禮記.中庸》,指不偏不倚,凡事做到恰如其分。 殿為方形攢尖頂,在三大殿中居中,也最小,是皇帝去太和殿大典之前休息的地方。皇帝去天、地、日、月四壇祭祀時,前一天也要在中和殿里看祭文。每年二月皇帝到先農壇舉行親耕儀式,前一天要來這里閱視種子、農具、祝文。這里現陳列的是乾隆年間的兩頂肩輿,即八抬大轎。 ");
quatree.insert(new Envelope(new Coordinate(11639081, 3991647)), info3);
PointInfo info4 = new PointInfo("保和殿");
info4
.setInfo("保和殿,其意為“志不外馳,恬神守志”,就是說神志得專一,以保持宇內的和諧,才能福壽安樂,天下太平。 保和殿比太和殿規模小些,為重檐歇山頂。明朝冊立皇后,太子時,皇帝在此殿受賀。在清朝是舉行盛大宴會的地方。每年初一和十五在此宴請外族王公大臣,場面是十分壯觀的。公主下嫁時,也在這個殿里宴請附馬。這個殿最有名的事是舉行殿試。殿試就是皇帝本人親自監考、主考,是科舉考試的最高層次。前三名分別為狀元、榜眼、探花。 保和殿后是故宮最大的一塊雕石——云龍雕石,這塊苑葉青石長16.57米,寬3.07米,厚1.7米,總重二百多噸。上雕游龍,雙龍戲珠,游于云霧之中。 ");
quatree.insert(new Envelope(new Coordinate(11639079, 3991698)), info4);
PointInfo info5 = new PointInfo("乾清宮");
info5
.setInfo("乾清宮明朝時為皇帝居住之處,皇帝在此處理政務、召見臣仆和外國使節。從清朝雍正皇帝之后,皇帝遷到養心殿居住,但仍在此批閱奏報,召見大臣。 乾清宮除是皇帝的寢宮和日常自理政務外,還舉行元旦、燈節、端午、中秋、冬至、萬壽等節的家宴。殿內寶座上方有一塊匾,上書“正大光明”四個漂亮的正楷字,其意為公正、光明磊落。這塊匾很有名氣,與秘密立儲關系密切。皇帝生前,親自從皇子中選一個德才兼備的作為皇太子——嗣皇帝,不予宣布,而是由皇帝秘密親書預立皇太子的名字的“御書”,密封匣內,茂于那塊匾后,等皇帝死后或死前,由御前大臣、軍機大臣等共同啟示,按御書所定,嗣皇帝即位。相傳雍正的第四子弘歷即乾隆皇帝,就是這…");
quatree.insert(new Envelope(new Coordinate(11639067, 3991873)), info5);
PointInfo info6 = new PointInfo("坤寧宮");
quatree.insert(new Envelope(new Coordinate(11639067, 3991907)), info6);
// 幾個離故宮較遠的點
PointInfo info7 = new PointInfo("香山公園");
quatree.insert(new Envelope(new Coordinate(11619151, 3999031)), info7);
PointInfo info8 = new PointInfo("香山寺");
quatree.insert(new Envelope(new Coordinate(11619150, 3999039)), info8);
PointInfo info9 = new PointInfo("眼鏡湖");
quatree.insert(new Envelope(new Coordinate(11618154, 3999566)), info9);
當中需要說明的部分如下:
quatree.insert(new Envelope(new Coordinate(11638937, 3992178)), info1);
當中的Coordinate代表了一個只是包含經緯度的點(point)。在jts中,還有另外一個專門的對象表示點,就是point對象,但是這里我們只是想用經緯度來建立好節點之間的關系,故而只是采用了Coordinate。
而Envelope,這個對象其實代表的是一個矩形框。可能是因為信封就是一個矩形框的原因,所以,加拿大人就用這個單詞來表示矩形了。Envelope的構造函數中需要給出矩形的對角坐標。但是我們這里只是有一個點,所以,jts會把這一個單獨的點也作為一個矩形。
這就是說明,jts中結點不是簡單的point,而是一個矩形(Envelope)。
至于最后的info,則是表示點的其他描述信息。
需要補充的是,jts目前只是支持二維,三維的z坐標永遠是0。
3. 建立一顆樹的目的不是等待他發芽,而是為了搜索(everyone has his purpose!)
quatree 提供了三個搜索方法,簽名分別如下:

public List query(Envelope searchEnv)
{

/** *//**
* the items that are matched are the items in quads which overlap the
* search envelope
*/
ArrayListVisitor visitor = new ArrayListVisitor();
query(searchEnv, visitor);
return visitor.getItems();
}


public void query(Envelope searchEnv, ItemVisitor visitor)
{

/** *//**
* the items that are matched are the items in quads which overlap the
* search envelope
*/
root.visit(searchEnv, visitor);
}


/** *//**
* Return a list of all items in the Quadtree
*/

public List queryAll()
{
List foundItems = new ArrayList();
root.addAllItems(foundItems);
return foundItems;
}
每個函數的含義也是很簡單的,分別表示搜索范圍搜索,范圍過濾搜索和全部搜索。
接下來我們就分別嘗試分為搜索和范圍過濾搜索:
范圍搜索很簡單,只要給出一個矩形框,然后傳入就可以了,代碼如下:
// 看看在故宮旁邊能夠找到什么
System.out.println("看看在故宮旁邊能夠找到什么");
List<PointInfo> points = quatree.query(new Envelope(new Coordinate(
11638937, 3992178)));

for (PointInfo pointInfo : points)
{
// 結果是什么,居然就是故宮一個結果
System.out.println(pointInfo.getName());
}
// 只能夠調整經緯度,來看看故宮周圍有什么
System.out.println("=================================");
System.out.println("只能夠調整經緯度,來看看故宮周圍有什么,這個就相當于地圖上的周邊搜索的概念");
points = quatree.query(new Envelope(new Coordinate(11638937, 3991605),
new Coordinate(11639081, 3992178)));

for (PointInfo pointInfo : points)
{
// 結果是什么?
System.out.println(pointInfo.getName());
}
可以看到,當只是輸入故宮的點的時候,結果只是一個故宮。所以,我就找了一范圍來搜,模擬文斌在
http://www.51ditu.com上實現的那個周邊搜索的概念。結果會是什么呢?
說起周邊搜索,不可能沒有關鍵詞,比如我們經常在群里說得那個搜索模式,我想知道在上地周邊哪里有好的酒吧,下班后可以去喝一杯!
這就是周邊過濾搜索的概念了。
quadtree存在著這樣的一個方法:
public void query(Envelope searchEnv, ItemVisitor visitor) ,這個當中的ItemVisitor 是一個接口。實現的類似于一個訪問者的功能,挨個詢問過路人:“你可見到戈多,我在等他”!我們可以看一下默認的ArrayListVisitor。看后,也會覺得jts在這個地方多此一舉了。

/** *//**
* @version 1.7
*/
public class ArrayListVisitor
implements ItemVisitor


{

private ArrayList items = new ArrayList();

public ArrayListVisitor()
{
}

public void visitItem(Object item)

{
items.add(item);
}


public ArrayList getItems()
{ return items; }

}
可以看到,這個類對于過路人什么都沒有問。
接下來,我就仿照著實現一個詢問器,問問那個宮殿中懸掛著“正大光明”牌匾。

public void visitItem(Object item)
{
PointInfo info = (PointInfo) item;
if (info.getInfo() != null && !"".equals(info.getInfo())

&& info.getInfo().contains("正大光明"))
{
items.add(item);
}
}
最后在quatree中用法如下:
// 既然可以實現周邊搜索,那么就可以在故宮旁邊搜索那個宮殿中含有正大光明的牌匾
System.out.println("=================================");
System.out.println("既然可以實現周邊搜索,那么就可以在故宮旁邊搜索那個宮殿中含有正大光明的牌匾");
UseArrayListVisitor visitor = new UseArrayListVisitor();
quatree.query(new Envelope(new Coordinate(11638937, 3991605),
new Coordinate(11639081, 3992178)), visitor);
points = visitor.getItems();

for (PointInfo pointInfo : points)
{
// 結果是什么?
System.out.println(pointInfo.getName());
}
結果是什么呢,當然就是乾清宮!
可以說,Jts是一個優雅的空間實用包,但是如果我在剛才過濾器中搜索的詞不是那么簡單呢,我想向文本搜索一樣牽涉到分詞,排序的概念呢。
如果我真的要在中國范圍內找某一個酒吧呢?
我對于這些問題的一個思路就是,讓lucene和jts合作!不過這是下一篇博客的內容了。
posted on 2007-12-21 13:47
張氏兄弟 閱讀(3881)
評論(3) 編輯 收藏 所屬分類:
51ditu.com