读取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 }
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 }
今天看了下DotSpatial中读取Shp的代码,真正实现的四叉树空间索引。