#region Copyright and License /**************************************************************************** ** ** Copyright (C) 2008 - 2011 Winston Fletcher. ** All rights reserved. ** ** This file is part of the EGIS.ShapeFileLib class library of Easy GIS .NET. ** ** Easy GIS .NET is free software: you can redistribute it and/or modify ** it under the terms of the GNU Lesser General Public License version 3 as ** published by the Free Software Foundation and appearing in the file ** lgpl-license.txt included in the packaging of this file. ** ** Easy GIS .NET is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License and ** GNU Lesser General Public License along with Easy GIS .NET. ** If not, see . ** ****************************************************************************/ #endregion using System; using System.Collections.Generic; using System.Drawing; using System.Linq; namespace SharpMap.Data.Providers.EGIS.ShapeFileLib { public class QuadTree { internal int maxLevels; internal bool isPointData; public QuadTree(RectangleF bounds, int maxLevels, bool isPointData) { this.isPointData = isPointData; this.maxLevels = maxLevels; RootNode = new QTNode(bounds, 0, this); } public QTNode RootNode { get; set; } public void Insert(int recordIndex, RectangleF bounds) { if (isPointData) { RootNode.Insert(recordIndex, bounds); } else { RootNode.Insert(recordIndex, ref bounds); } } public IEnumerable GetIndices(ref RectangleF rect, float maximumFeaturesSize) { if (RootNode.Bounds.IntersectsWith(rect)) { List indices = new List(); Dictionary duplicates = new Dictionary(); RootNode.GetIndices(ref rect, indices, duplicates, maximumFeaturesSize); return indices; } return Enumerable.Empty(); } } public class QTNode { private const int TL = 0; private const int TR = 1; private const int BL = 2; private const int BR = 3; internal List indexList; internal List boundsList; //public static int MaxLevels = 7; //public static int MinLevels = 2; //public static int MaxIndicesPerNode = 16; private readonly int _level = 0; private readonly QuadTree tree; public QTNode(RectangleF bounds, int level, QuadTree tree) { this.tree = tree; Bounds = bounds; _level = level; //if (level < MinLevels) //{ // //create children //} if (Level == tree.maxLevels) { indexList = new List(); boundsList = new List(); } } public QTNode[] Children { get; private set; } public RectangleF Bounds { get; private set; } public int Level { get { return _level; } } public void Insert(int recordIndex, RectangleF bounds) { if (Level == tree.maxLevels) { indexList.Add(recordIndex); boundsList.Add(bounds); } else { if (tree.isPointData) { if (Children == null) { CreateChildren(); } if (Children[TL].Bounds.Contains(bounds)) { Children[TL].Insert(recordIndex, bounds); } else if (Children[TR].Bounds.Contains(bounds)) { Children[TR].Insert(recordIndex, bounds); } else if (Children[BL].Bounds.Contains(bounds)) { Children[BL].Insert(recordIndex, bounds); } else if (Children[BR].Bounds.Contains(bounds)) { Children[BR].Insert(recordIndex, bounds); } else { throw new InvalidOperationException("point " + bounds + " is not contained in children bounds"); } } else { if (Children == null) { CreateChildren(); } int c = 0; if (Children[TL].Bounds.IntersectsWith(bounds)) { c++; Children[TL].Insert(recordIndex, bounds); } if (Children[TR].Bounds.IntersectsWith(bounds)) { c++; Children[TR].Insert(recordIndex, bounds); } if (Children[BL].Bounds.IntersectsWith(bounds)) { c++; Children[BL].Insert(recordIndex, bounds); } if (Children[BR].Bounds.IntersectsWith(bounds)) { c++; Children[BR].Insert(recordIndex, bounds); } } } } internal void Insert(int recordIndex, ref RectangleF recBounds) { if (Level == tree.maxLevels) { indexList.Add(recordIndex); boundsList.Add(recBounds); } else { if (tree.isPointData) {} else { if (Children == null) { CreateChildren(); } int c = 0; if (Children[TL].Bounds.IntersectsWith(recBounds)) { c++; Children[TL].Insert(recordIndex, ref recBounds); } if (Children[TR].Bounds.IntersectsWith(recBounds)) { c++; Children[TR].Insert(recordIndex, ref recBounds); } if (Children[BL].Bounds.IntersectsWith(recBounds)) { c++; Children[BL].Insert(recordIndex, ref recBounds); } if (Children[BR].Bounds.IntersectsWith(recBounds)) { c++; Children[BR].Insert(recordIndex, ref recBounds); } } } } internal void GetIndices(ref RectangleF rect, List indices, Dictionary foundIndicies, float maximumFeatureSize) { if (Children != null) { //check each child bounds if (Children[TL].Bounds.IntersectsWith(rect)) { Children[TL].GetIndices(ref rect, indices, foundIndicies, maximumFeatureSize); } if (Children[TR].Bounds.IntersectsWith(rect)) { Children[TR].GetIndices(ref rect, indices, foundIndicies, maximumFeatureSize); } if (Children[BL].Bounds.IntersectsWith(rect)) { Children[BL].GetIndices(ref rect, indices, foundIndicies, maximumFeatureSize); } if (Children[BR].Bounds.IntersectsWith(rect)) { Children[BR].GetIndices(ref rect, indices, foundIndicies, maximumFeatureSize); } } else { if (indexList != null) { RectangleF e1, e2 = new RectangleF(); //assumes already checked node's Bounds intersect rect //add the node'x indices, checking if it has already been added //We need to check for duplicates as a shape may intersect more than 1 node for (int n = indexList.Count - 1; n >= 0; --n) { var i = indexList[n]; if (maximumFeatureSize != 0) { if (!tree.isPointData) { var b = boundsList[n]; if (b.Width < maximumFeatureSize && b.Height < maximumFeatureSize) { continue; } } else // points { e1 = boundsList[n]; if (n != indexList.Count - 1) { if (Math.Abs(e1.X - e2.X) < maximumFeatureSize && Math.Abs(e1.Y - e2.Y) < maximumFeatureSize) { continue; } } e2 = e1; } } if (!boundsList[n].IntersectsWith(rect)) { continue; } if (!foundIndicies.ContainsKey(i)) { indices.Add(i); foundIndicies.Add(i, 0); } } } } } private void CreateChildren() { Children = new QTNode[4]; Children[TL] = new QTNode(RectangleF.FromLTRB(Bounds.Left, Bounds.Top, 0.5f*(Bounds.Left + Bounds.Right), 0.5f*(Bounds.Top + Bounds.Bottom)), Level + 1, tree); Children[TR] = new QTNode(RectangleF.FromLTRB(0.5f*(Bounds.Left + Bounds.Right), Bounds.Top, Bounds.Right, 0.5f*(Bounds.Top + Bounds.Bottom)), Level + 1, tree); Children[BL] = new QTNode(RectangleF.FromLTRB(Bounds.Left, 0.5f*(Bounds.Top + Bounds.Bottom), 0.5f*(Bounds.Left + Bounds.Right), Bounds.Bottom), Level + 1, tree); Children[BR] = new QTNode(RectangleF.FromLTRB(0.5f*(Bounds.Left + Bounds.Right), 0.5f*(Bounds.Top + Bounds.Bottom), Bounds.Right, Bounds.Bottom), Level + 1, tree); } } }