/// <summary> /// Recursive function that traverses the tree and looks for intersections with a boundingbox /// </summary> /// <param name="box">Boundingbox to intersect with</param> /// <param name="node">Node to search from</param> /// <param name="list">List of found intersections</param> private void IntersectTreeRecursive(BoundingBox box, QuadTree node, ref Collection<uint> list) { if (node.IsLeaf) //Leaf has been reached { foreach (BoxObjects boxObject in node._objList) { if(box.Intersects(boxObject.box)) list.Add(boxObject.ID); } /* for (int i = 0; i < node._objList.Count; i++) { list.Add(node._objList[i].ID); } */ } else { if (node.Box.Intersects(box)) { IntersectTreeRecursive(box, node.Child0, ref list); IntersectTreeRecursive(box, node.Child1, ref list); } } }
/// <summary> /// Creates a node and either splits the objects recursively into sub-nodes, or stores them at the node depending on the heuristics. /// Tree is built top->down /// </summary> /// <param name="objList">Geometries to index</param> /// <param name="depth">Current depth of tree</param> /// <param name="heurdata">Heuristics data</param> public QuadTree(List<BoxObjects> objList, uint depth, Heuristic heurdata) { _Depth = depth; _box = objList[0].box; for (int i = 0; i < objList.Count; i++) _box = _box.Join(objList[i].box); // test our build heuristic - if passes, make children if (depth < heurdata.maxdepth && objList.Count > heurdata.mintricnt && (objList.Count > heurdata.tartricnt || ErrorMetric(_box) > heurdata.minerror)) { List<BoxObjects>[] objBuckets = new List<BoxObjects>[2]; // buckets of geometries objBuckets[0] = new List<BoxObjects>(); objBuckets[1] = new List<BoxObjects>(); uint longaxis = _box.LongestAxis; // longest axis double geoavg = 0; // geometric average - midpoint of ALL the objects // go through all bbox and calculate the average of the midpoints double frac = 1.0f/objList.Count; for (int i = 0; i < objList.Count; i++) geoavg += objList[i].box.GetCentroid()[longaxis]*frac; // bucket bbox based on their midpoint's side of the geo average in the longest axis for (int i = 0; i < objList.Count; i++) objBuckets[geoavg > objList[i].box.GetCentroid()[longaxis] ? 1 : 0].Add(objList[i]); //If objects couldn't be splitted, just store them at the leaf //TODO: Try splitting on another axis if (objBuckets[0].Count == 0 || objBuckets[1].Count == 0) { _child0 = null; _child1 = null; // copy object list _objList = objList; } else { // create new children using the buckets _child0 = new QuadTree(objBuckets[0], depth + 1, heurdata); _child1 = new QuadTree(objBuckets[1], depth + 1, heurdata); } } else { // otherwise the build heuristic failed, this is // set the first child to null (identifies a leaf) _child0 = null; _child1 = null; // copy object list _objList = objList; } }
/// <summary> /// Saves a node to a stream recursively /// </summary> /// <param name="node">Node to save</param> /// <param name="sw">Reference to BinaryWriter</param> private void SaveNode(QuadTree node, ref BinaryWriter sw) { //Write node boundingbox sw.Write(node.Box.Min.X); sw.Write(node.Box.Min.Y); sw.Write(node.Box.Max.X); sw.Write(node.Box.Max.Y); sw.Write(node.IsLeaf); if (node.IsLeaf) { sw.Write(node._objList.Count); //Write number of features at node for (int i = 0; i < node._objList.Count; i++) //Write each featurebox { sw.Write(node._objList[i].box.Min.X); sw.Write(node._objList[i].box.Min.Y); sw.Write(node._objList[i].box.Max.X); sw.Write(node._objList[i].box.Max.Y); sw.Write(node._objList[i].ID); } } else if (!node.IsLeaf) //Save next node { SaveNode(node.Child0, ref sw); SaveNode(node.Child1, ref sw); } }
/// <summary> /// Disposes the node /// </summary> public void Dispose() { //this._box = null; _child0 = null; _child1 = null; _objList = null; }
/// <summary> /// Reads a node from a stream recursively /// </summary> /// <param name="depth">Current depth</param> /// <param name="br">Binary reader reference</param> /// <returns></returns> private static QuadTree ReadNode(uint depth, ref BinaryReader br) { QuadTree node = new QuadTree(); node._Depth = depth; node.Box = new BoundingBox(br.ReadDouble(), br.ReadDouble(), br.ReadDouble(), br.ReadDouble()); bool IsLeaf = br.ReadBoolean(); if (IsLeaf) { int FeatureCount = br.ReadInt32(); node._objList = new List<BoxObjects>(); for (int i = 0; i < FeatureCount; i++) { BoxObjects box = new BoxObjects(); box.box = new BoundingBox(br.ReadDouble(), br.ReadDouble(), br.ReadDouble(), br.ReadDouble()); box.ID = (uint) br.ReadInt32(); node._objList.Add(box); } } else { node.Child0 = ReadNode(node._Depth + 1, ref br); node.Child1 = ReadNode(node._Depth + 1, ref br); } return node; }
/// <summary> /// Forces a rebuild of the spatial index. If the instance of the ShapeFile provider /// uses a file-based index the file is rewritten to disk. /// </summary> public void RebuildSpatialIndex() { if (_FileBasedIndex) { if (File.Exists(_Filename + ".sidx")) File.Delete(_Filename + ".sidx"); tree = CreateSpatialIndexFromFile(_Filename); } else tree = CreateSpatialIndex(_Filename); if (HttpContext.Current != null) //TODO: Remove this when connection pooling is implemented: HttpContext.Current.Cache.Insert(_Filename, tree, null, Cache.NoAbsoluteExpiration, TimeSpan.FromDays(1)); }
private void LoadSpatialIndex(bool ForceRebuild, bool LoadFromFile) { //Only load the tree if we haven't already loaded it, or if we want to force a rebuild if (tree == null || ForceRebuild) { // Is this a web application? If so lets store the index in the cache so we don't // need to rebuild it for each request if (HttpContext.Current != null) { //Check if the tree exists in the cache if (HttpContext.Current.Cache[_Filename] != null) tree = (QuadTree) HttpContext.Current.Cache[_Filename]; else { if (!LoadFromFile) tree = CreateSpatialIndex(_Filename); else tree = CreateSpatialIndexFromFile(_Filename); //Store the tree in the web cache //TODO: Remove this when connection pooling is implemented HttpContext.Current.Cache.Insert(_Filename, tree, null, Cache.NoAbsoluteExpiration, TimeSpan.FromDays(1)); } } else if (!LoadFromFile) tree = CreateSpatialIndex(_Filename); else tree = CreateSpatialIndexFromFile(_Filename); } }
private void Dispose(bool disposing) { if (!disposed) { if (disposing) { Close(); _Envelope = null; tree = null; } disposed = true; } }
private void Dispose(bool disposing) { if (!_disposed) { if (disposing) { Close(); _envelope = null; _tree = null; } _disposed = true; } }