[SharpMap]二叉树索引

读取shp文件建立二叉树索引的基本思路分析:

 

 1  /// <summary>
 2         /// Generates a spatial index for a specified shape file.
 3         /// 根据特定的shp文件构建空间索引
 4         /// </summary>
 5         /// <param name="filename"></param>
 6         private QuadTree CreateSpatialIndex(string filename)
 7         {
 8             List<QuadTree.BoxObjects> objList = new List<QuadTree.BoxObjects>();
 9             //Convert all the geometries to boundingboxes 
10             uint i = 0;
11             foreach (BoundingBox box in GetAllFeatureBoundingBoxes())
12             {
13                 if (!double.IsNaN(box.Left) && !double.IsNaN(box.Right) && !double.IsNaN(box.Bottom) &&
14                     !double.IsNaN(box.Top))
15                 {
16                     QuadTree.BoxObjects g = new QuadTree.BoxObjects();
17                     g.box = box;
18                     g.ID = i;
19                     objList.Add(g);
20                     i++;
21                 }
22             }
23 
24             Heuristic heur;
25             heur.maxdepth = (int) Math.Ceiling(Math.Log(GetFeatureCount(), 2));
26             heur.minerror = 10;
27             heur.tartricnt = 5;
28             heur.mintricnt = 2;
29             return new QuadTree(objList, 0, heur);
30         }
View Code

 

  1 // Copyright 2005, 2006 - Morten Nielsen (www.iter.dk)
  2 //
  3 // This file is part of SharpMap.
  4 // SharpMap is free software; you can redistribute it and/or modify
  5 // it under the terms of the GNU Lesser General Public License as published by
  6 // the Free Software Foundation; either version 2 of the License, or
  7 // (at your option) any later version.
  8 // 
  9 // SharpMap is distributed in the hope that it will be useful,
 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12 // GNU Lesser General Public License for more details.
 13 
 14 // You should have received a copy of the GNU Lesser General Public License
 15 // along with SharpMap; if not, write to the Free Software
 16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 17 
 18 using System;
 19 using System.Collections.Generic;
 20 using System.Collections.ObjectModel;
 21 using System.IO;
 22 using SharpMap.Geometries;
 23 
 24 namespace SharpMap.Utilities.SpatialIndexing
 25 {
 26     /// <summary>
 27     /// Heuristics used for tree generation
 28     /// </summary>
 29     public struct Heuristic
 30     {
 31         /// <summary>
 32         /// Maximum tree depth
 33         /// </summary>
 34         public int maxdepth;
 35 
 36         /// <summary>
 37         /// Minimum Error metric ?the volume of a box + a unit cube.
 38         /// The unit cube in the metric prevents big boxes that happen to be flat having a zero result and muddling things up.
 39         /// </summary>
 40         public int minerror;
 41 
 42         /// <summary>
 43         /// Minimum object count at node
 44         /// </summary>
 45         public int mintricnt;
 46 
 47         /// <summary>
 48         /// Target object count at node
 49         /// </summary>
 50         public int tartricnt;
 51     }
 52 
 53     /// <summary>
 54     /// Constructs a Quad-tree node from a object list and creates its children recursively
 55     /// 二叉树?四叉树?
 56     /// </summary>
 57     public class QuadTree : IDisposable
 58     {
 59         private BoundingBox _box;
 60         private QuadTree _child0;
 61         private QuadTree _child1;
 62 
 63         /// <summary>
 64         /// Nodes depth in a tree
 65         /// </summary>
 66         protected uint _Depth;
 67 
 68         /// <summary>
 69         /// Node ID
 70         /// </summary>
 71         public uint? _ID;
 72 
 73         private List<BoxObjects> _objList;
 74 
 75         /// <summary>
 76         /// Creates a node and either splits the objects recursively into sub-nodes, or stores them at the node depending on the heuristics.
 77         /// Tree is built top->down
 78         /// </summary>
 79         /// <param name="objList">Geometries to index</param>
 80         /// <param name="depth">Current depth of tree树的深度</param>
 81         /// <param name="heurdata">Heuristics data</param>
 82         public QuadTree(List<BoxObjects> objList, uint depth, Heuristic heurdata)
 83         {
 84             _Depth = depth;
 85 
 86             _box = objList[0].box;
 87             for (int i = 0; i < objList.Count; i++)
 88                 _box = _box.Join(objList[i].box);
 89 
 90             // test our build heuristic探试性的- if passes, make children
 91             if (depth < heurdata.maxdepth && objList.Count > heurdata.mintricnt &&
 92                 (objList.Count > heurdata.tartricnt || ErrorMetric(_box) > heurdata.minerror))
 93             {
 94                 List<BoxObjects>[] objBuckets = new List<BoxObjects>[2]; // buckets of geometries大量的几何对象,一桶几何对象
 95                 objBuckets[0] = new List<BoxObjects>();
 96                 objBuckets[1] = new List<BoxObjects>();
 97 
 98                 uint longaxis = _box.LongestAxis; // longest axis
 99                 double geoavg = 0; // geometric average - midpoint of ALL the objects
100 
101                 // go through all bbox and calculate the average of the midpoints
102                 double frac = 1.0f / objList.Count;
103                 for (int i = 0; i < objList.Count; i++)
104                     geoavg += objList[i].box.GetCentroid()[longaxis] * frac;
105 
106                 // bucket bbox based on their midpoint's side of the geo average in the longest axis
107                 for (int i = 0; i < objList.Count; i++)
108                     objBuckets[geoavg > objList[i].box.GetCentroid()[longaxis] ? 1 : 0].Add(objList[i]);
109 
110                 //If objects couldn't be splitted, just store them at the leaf
111                 //TODO: Try splitting on another axis
112                 if (objBuckets[0].Count == 0 || objBuckets[1].Count == 0)
113                 {
114                     _child0 = null;
115                     _child1 = null;
116                     // copy object list
117                     _objList = objList;
118                 }
119                 else
120                 {
121                     // create new children using the buckets
122                     _child0 = new QuadTree(objBuckets[0], depth + 1, heurdata);
123                     _child1 = new QuadTree(objBuckets[1], depth + 1, heurdata);
124                 }
125             }
126             else
127             {
128                 // otherwise the build heuristic failed, this is 
129                 // set the first child to null (identifies a leaf)
130                 _child0 = null;
131                 _child1 = null;
132                 // copy object list
133                 _objList = objList;
134             }
135         }
136 
137         /// <summary>
138         /// This instantiator is used internally for loading a tree from a file
139         /// </summary>
140         private QuadTree()
141         {
142         }
143 
144         #region Read/Write index to/from a file
145 
146         private const double INDEXFILEVERSION = 1.0;
147 
148         /// <summary>
149         /// Loads a quadtree from a file
150         /// </summary>
151         /// <param name="filename"></param>
152         /// <returns></returns>
153         public static QuadTree FromFile(string filename)
154         {
155             FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
156             BinaryReader br = new BinaryReader(fs);
157             if (br.ReadDouble() != INDEXFILEVERSION) //Check fileindex version
158             {
159                 fs.Close();
160                 fs.Dispose();
161                 throw new ObsoleteFileFormatException(
162                     "Invalid index file version. Please rebuild the spatial index by either deleting the index");
163             }
164             QuadTree node = ReadNode(0, ref br);
165             br.Close();
166             fs.Close();
167             return node;
168         }
169 
170         /// <summary>
171         /// Reads a node from a stream recursively
172         /// </summary>
173         /// <param name="depth">Current depth</param>
174         /// <param name="br">Binary reader reference</param>
175         /// <returns></returns>
176         private static QuadTree ReadNode(uint depth, ref BinaryReader br)
177         {
178             QuadTree node = new QuadTree();
179             node._Depth = depth;
180             node.Box = new BoundingBox(br.ReadDouble(), br.ReadDouble(), br.ReadDouble(), br.ReadDouble());
181             bool IsLeaf = br.ReadBoolean();
182             if (IsLeaf)
183             {
184                 int FeatureCount = br.ReadInt32();
185                 node._objList = new List<BoxObjects>();
186                 for (int i = 0; i < FeatureCount; i++)
187                 {
188                     BoxObjects box = new BoxObjects();
189                     box.box = new BoundingBox(br.ReadDouble(), br.ReadDouble(), br.ReadDouble(), br.ReadDouble());
190                     box.ID = (uint)br.ReadInt32();
191                     node._objList.Add(box);
192                 }
193             }
194             else
195             {
196                 node.Child0 = ReadNode(node._Depth + 1, ref br);
197                 node.Child1 = ReadNode(node._Depth + 1, ref br);
198             }
199             return node;
200         }
201 
202         /// <summary>
203         /// Saves the Quadtree to a file
204         /// </summary>
205         /// <param name="filename"></param>
206         public void SaveIndex(string filename)
207         {
208             FileStream fs = new FileStream(filename, FileMode.Create);
209             BinaryWriter bw = new BinaryWriter(fs);
210             bw.Write(INDEXFILEVERSION); //Save index version
211             SaveNode(this, ref bw);
212             bw.Close();
213             fs.Close();
214         }
215 
216         /// <summary>
217         /// Saves a node to a stream recursively
218         /// </summary>
219         /// <param name="node">Node to save</param>
220         /// <param name="sw">Reference to BinaryWriter</param>
221         private void SaveNode(QuadTree node, ref BinaryWriter sw)
222         {
223             //Write node boundingbox
224             sw.Write(node.Box.Min.X);
225             sw.Write(node.Box.Min.Y);
226             sw.Write(node.Box.Max.X);
227             sw.Write(node.Box.Max.Y);
228             sw.Write(node.IsLeaf);
229             if (node.IsLeaf)
230             {
231                 sw.Write(node._objList.Count); //Write number of features at node
232                 for (int i = 0; i < node._objList.Count; i++) //Write each featurebox
233                 {
234                     sw.Write(node._objList[i].box.Min.X);
235                     sw.Write(node._objList[i].box.Min.Y);
236                     sw.Write(node._objList[i].box.Max.X);
237                     sw.Write(node._objList[i].box.Max.Y);
238                     sw.Write(node._objList[i].ID);
239                 }
240             }
241             else if (!node.IsLeaf) //Save next node
242             {
243                 SaveNode(node.Child0, ref sw);
244                 SaveNode(node.Child1, ref sw);
245             }
246         }
247 
248         internal class ObsoleteFileFormatException : Exception
249         {
250             /// <summary>
251             /// Exception thrown when layer rendering has failed
252             /// </summary>
253             /// <param name="message"></param>
254             public ObsoleteFileFormatException(string message)
255                 : base(message)
256             {
257             }
258         }
259 
260         #endregion
261 
262         /// <summary>
263         /// Determines whether the node is a leaf (if data is stored at the node, we assume the node is a leaf)
264         /// </summary>
265         public bool IsLeaf
266         {
267             get { return (_objList != null); }
268         }
269 
270         ///// <summary>
271         ///// Gets/sets the list of objects in the node
272         ///// </summary>
273         //public List<SharpMap.Geometries.IGeometry> ObjList
274         //{
275         //    get { return _objList; }
276         //    set { _objList = value; }
277         //}
278 
279         /// <summary>
280         /// Gets/sets the Axis Aligned Bounding Box
281         /// </summary>
282         public BoundingBox Box
283         {
284             get { return _box; }
285             set { _box = value; }
286         }
287 
288         /// <summary>
289         /// Gets/sets the left child node
290         /// </summary>
291         public QuadTree Child0
292         {
293             get { return _child0; }
294             set { _child0 = value; }
295         }
296 
297         /// <summary>
298         /// Gets/sets the right child node
299         /// </summary>
300         public QuadTree Child1
301         {
302             get { return _child1; }
303             set { _child1 = value; }
304         }
305 
306         /// <summary>
307         /// Gets the depth of the current node in the tree
308         /// </summary>
309         public uint Depth
310         {
311             get { return _Depth; }
312         }
313 
314         #region IDisposable Members
315 
316         /// <summary>
317         /// Disposes the node
318         /// </summary>
319         public void Dispose()
320         {
321             //this._box = null;
322             _child0 = null;
323             _child1 = null;
324             _objList = null;
325         }
326 
327         #endregion
328 
329         /// <summary>
330         /// Calculate the floating point error metric 
331         /// </summary>
332         /// <returns></returns>
333         public double ErrorMetric(BoundingBox box)
334         {
335             Point temp = new Point(1, 1) + (box.Max - box.Min);
336             return temp.X * temp.Y;
337         }
338 
339         /// <summary>
340         /// Searches the tree and looks for intersections with the boundingbox 'bbox'
341         /// </summary>
342         /// <param name="box">Boundingbox to intersect with</param>
343         public Collection<uint> Search(BoundingBox box)
344         {
345             Collection<uint> objectlist = new Collection<uint>();
346             IntersectTreeRecursive(box, this, ref objectlist);
347             return objectlist;
348         }
349 
350         /// <summary>
351         /// Recursive function that traverses the tree and looks for intersections with a boundingbox
352         /// </summary>
353         /// <param name="box">Boundingbox to intersect with</param>
354         /// <param name="node">Node to search from</param>
355         /// <param name="list">List of found intersections</param>
356         private void IntersectTreeRecursive(BoundingBox box, QuadTree node, ref Collection<uint> list)
357         {
358             if (node.IsLeaf) //Leaf has been reached
359             {
360                 foreach (BoxObjects boxObject in node._objList)
361                 {
362                     if (box.Intersects(boxObject.box))
363                         list.Add(boxObject.ID);
364 
365                 }
366                 /*
367                 for (int i = 0; i < node._objList.Count; i++)
368                 {
369                     list.Add(node._objList[i].ID);
370                 }
371                 */
372             }
373             else
374             {
375                 if (node.Box.Intersects(box))
376                 {
377                     IntersectTreeRecursive(box, node.Child0, ref list);
378                     IntersectTreeRecursive(box, node.Child1, ref list);
379                 }
380             }
381         }
382 
383         #region Nested type: BoxObjects
384 
385         /// <summary>
386         /// BoundingBox and Feature ID structure used for storing in the quadtree 
387         /// 包围盒和要素ID数据结构,用于在四叉树中存储
388         /// </summary>
389         public struct BoxObjects
390         {
391             /// <summary>
392             /// Boundingbox
393             /// 包围盒
394             /// </summary>
395             public BoundingBox box;
396 
397             /// <summary>
398             /// Feature ID
399             /// 要素ID
400             /// </summary>
401             public uint ID;
402         }
403 
404         #endregion
405     }
406 }
View Code

今天看了下DotSpatial中读取Shp的代码,真正实现的四叉树空间索引。

posted @ 2014-04-13 14:48  太一吾鱼水  阅读(685)  评论(0编辑  收藏  举报