Skip to content

SharpSbn with DotSpatial

FObermaier edited this page Nov 28, 2014 · 1 revision

A sample to use SharpSbn with DotSpatial

namespace DotSpatial.Data
{
    /// <summary>
    /// A class that associates a ShapeFile spatial index (SBN) with a <see cref="DotSpatial.Data.IFeatureSet"/>.
    /// </summary>
    public class SpatiallyIndexedFeatureSet : System.IDisposable
    {
        private readonly SharpSbn.SbnTree _tree;
        private readonly IFeatureSet _featureSet;

        /// <summary>
        /// Creates an instance of this class
        /// </summary>
        /// <param name="featureSet">The feature set to choose</param>
        public SpatiallyIndexedFeatureSet(IFeatureSet featureSet)
        {
            System.Diagnostics.Debug.Assert(featureSet != null);

            _featureSet = featureSet;
            _tree = TestShapefileTree(featureSet);

            if (_tree == null)
                _tree = SharpSbn.SbnTree.Create(GetFeaturesBoundingBoxes(featureSet),
                    SharpSbn.DataStructures.Interval.Create(),
                    SharpSbn.DataStructures.Interval.Create());
        }

        /// <summary>
        /// Class finalizer
        /// </summary>
        ~SpatiallyIndexedFeatureSet()
        {
            Dispose(false);
        }

        /// <summary>
        /// Method to dispose the feature set (if it is not locked=
        /// </summary>
        /// <param name="disposing">Disposing?</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (!_featureSet.IsDisposeLocked)
                    _featureSet.Dispose();
            }
        }

        void System.IDisposable.Dispose()
        {
            Dispose(true);
            System.GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Test if <paramref name="featureSet"/> is a <see cref="DotSpatial.Data.Shapefile"/> 
        /// and if so, try to read tree for it. Otherwise a new one is created.
        /// </summary>
        /// <param name="featureSet">The feature set</param>
        /// <returns>An index</returns>
        private static SharpSbn.SbnTree TestShapefileTree(IFeatureSet featureSet)
        {
            // Is this really a shapefile?
            var shapefile = featureSet as Shapefile;
            if (shapefile == null) return null;

            // Is there a sbn file floating around?
            if (!string.IsNullOrEmpty(shapefile.FilePath))
            {
                var sbnFilename = System.IO.Path.ChangeExtension(shapefile.FilePath, ".sbn");
                if (System.IO.File.Exists(sbnFilename))
                    return SharpSbn.SbnTree.Load(sbnFilename);
            }

            // Create one!
            var h = shapefile.Header;
            return SharpSbn.SbnTree.Create(GetFeaturesBoundingBoxes(featureSet),
                SharpSbn.DataStructures.Interval.Create(h.Zmin, h.Zmax),
                SharpSbn.DataStructures.Interval.Create(h.Mmin, h.Mmax));
        }

        private static System.Collections.Generic.ICollection<System.Tuple<uint, SharpSbn.DataStructures.Envelope>>
            GetFeaturesBoundingBoxes(IFeatureSet featuresSet)
        {
            var res =
                new System.Collections.Generic.List<System.Tuple<uint, SharpSbn.DataStructures.Envelope>>(
                    featuresSet.NumRows());
            foreach (var feature in featuresSet.Features)
            {
                var min = feature.Envelope.Minimum;
                var max = feature.Envelope.Maximum;
                res.Add(System.Tuple.Create((uint) feature.Fid, new SharpSbn.DataStructures.Envelope(
                    min.X, max.X, min.Y, max.Y)));
            }
            return res;
        }

        /// <summary>
        /// Method to query all features that intersect a given <see cref="extent"/>
        /// </summary>
        /// <param name="extent">The area used to look for features</param>
        /// <returns>An enumeration of features</returns>
        public System.Collections.Generic.IEnumerable<IFeature> Intersect(Extent extent)
        {
            var env = new SharpSbn.DataStructures.Envelope(extent.MinX, extent.MaxX, extent.MinY, extent.MaxY);
            foreach (var queryFid in _tree.QueryFids(env))
            {
                var fid = (int) queryFid - 1;
                yield return _featureSet.GetFeature(fid);
            }
        }

        /// <summary>
        /// Method to query all features that intersect a given <see cref="geometry"/>
        /// </summary>
        /// <param name="geometry">The geometry used to look for features</param>
        /// <returns>An enumeration of features</returns>
        public System.Collections.Generic.IEnumerable<IFeature> Intersect(Topology.IGeometry geometry)
        {
            var dsEnv = geometry.EnvelopeInternal.ToExtent();
            var env = new SharpSbn.DataStructures.Envelope(dsEnv.MinX, dsEnv.MaxX, dsEnv.MinY, dsEnv.MaxY);
            foreach (var queryFid in _tree.QueryFids(env))
            {
                var fid = (int) queryFid - 1;
                var f = _featureSet.GetFeature(fid);
                if (geometry.Intersects(Topology.Geometry.FromBasicGeometry(f)))
                    yield return f;
            }
        }

        public static System.Collections.Generic.IEnumerable<IFeature> PointsInPolygon(IFeatureSet polygons, IFeatureSet points,
            double bufferPolygon = 0d)
        {
            var sishp = new SpatiallyIndexedFeatureSet(points);
            foreach (var feature in polygons.Features)
            {
                var geom = Topology.Geometry.FromBasicGeometry(feature.BasicGeometry);
                if (bufferPolygon > 0) geom = geom.Buffer(bufferPolygon);

                var candidates = sishp.Intersect(geom);
                foreach (var candidate in candidates)
                {
                    if (geom.Contains(Topology.Geometry.FromBasicGeometry(candidate.BasicGeometry)))
                    {
                        yield return candidate;
                    }
                }
            }
        }
    }
}
Clone this wiki locally