Converting BufferedDataProviderProxy

Topics: SharpMap v0.9 / v1.x
Apr 17, 2013 at 3:39 PM
Edited Apr 17, 2013 at 3:41 PM
Hi,

I’m in the process of updating to the latest version of SharpMap (1.0 RC2) and I’ve come across a problem trying to convert some of my old code. I was using the BufferedDataProviderProxy from the 1.1 experimental branch, and I’m unsure as to what I need to do to get it working.

The original code is as follows:
using System.Collections.ObjectModel;
using SharpMap.Geometries;

namespace SharpMap.Data.Providers {
    public class BufferedDataProviderProxy<TRealProvider>
        : IProvider
        where TRealProvider : IProvider {
        public BufferedDataProviderProxy(TRealProvider realProvider)
            : this(realProvider, 1.0) { }


        public BufferedDataProviderProxy(TRealProvider realProvider, double bufferScaleFactor) {
            this.RealProvider = realProvider;
            this.BufferScaleFactor = bufferScaleFactor;

        }

        TRealProvider _realProvider;
        /// <summary>

        /// Returns the real provider being proxied.

        /// </summary>

        public TRealProvider RealProvider {
            get {
                return _realProvider;
            }
            private set {
                _realProvider = value;
            }
        }

        private double _bufferScaleFactor;
        /// <summary>

        /// The scale factor we are multiplying the box by.

        /// A scale factor &gt; 1.0 will expand the BoundingBox used in ExecuteIntersectionQuery

        /// &lt; 1.0 will shrink the BoundingBox

        /// </summary>

        public double BufferScaleFactor {
            get { return _bufferScaleFactor; }
            set { _bufferScaleFactor = value; }
        }

        #region IProvider Members

        /// <summary>

        /// returns a BoundingBox whos width and height are 

        /// scaled by BufferScaleFactor

        /// </summary>

        /// <param name="box"></param>

        /// <returns></returns>

        private BoundingBox ExpandBox(BoundingBox box) {
            double dx = (box.Width / 2) * BufferScaleFactor;
            double dy = (box.Height / 2) * BufferScaleFactor;

            Point p = box.GetCentroid();
            return new BoundingBox(
                p.X - dx,
                p.Y - dy,
                p.X + dx,
                p.Y + dy);

        }

        public Collection<Geometry> GetGeometriesInView(BoundingBox bbox) {
            return RealProvider.GetGeometriesInView(ExpandBox(bbox));
        }

        public Collection<uint> GetObjectIDsInView(BoundingBox bbox) {
            return RealProvider.GetObjectIDsInView(ExpandBox(bbox));
        }

        public Geometry GetGeometryByID(uint oid) {
            return RealProvider.GetGeometryByID(oid);
        }

        public void ExecuteIntersectionQuery(Geometry geom, FeatureDataSet ds) {
            /* Buffer is not implemented in any of the Geometries 
             * so we will just relay to the real provider. 
             * Alternatively you could use NTS to buffer properly.
             */
            RealProvider.ExecuteIntersectionQuery(geom, ds);
        }

        public void ExecuteIntersectionQuery(BoundingBox box, FeatureDataSet ds) {
            RealProvider.ExecuteIntersectionQuery(ExpandBox(box), ds);
        }

        public int GetFeatureCount() {
            return RealProvider.GetFeatureCount();
        }

        public FeatureDataRow GetFeature(uint RowID) {
            return RealProvider.GetFeature(RowID);
        }

        public BoundingBox GetExtents() {
            return RealProvider.GetExtents();
        }

        public string ConnectionID {
            get { return RealProvider.ConnectionID; }
        }

        public void Open() {
            RealProvider.Open();
        }

        public void Close() {
            RealProvider.Close();
        }

        public bool IsOpen {
            get { return RealProvider.IsOpen; }
        }

        public int SRID {
            get {
                return RealProvider.SRID;
            }
            set {
                RealProvider.SRID = value;
            }
        }

        #endregion

        #region IDisposable Members

        public void Dispose() {
            RealProvider.Dispose();
        }

        #endregion
    }
}
Obviously I need to change it to account for the fact that SharpMap.Geometries doesn’t exist any more, but I’m having trouble when it comes to the BoundingBox-related methods.

Can anyone help with this?

Thanks,

Dylan
Apr 17, 2013 at 4:19 PM
I’ve played around with the code, and have come up with the following:
using System.Collections.ObjectModel;
using GeoAPI.Geometries;

namespace SharpMap.Data.Providers {
    public class BufferedDataProviderProxy<TRealProvider>
        : IProvider
        where TRealProvider : IProvider {
        public BufferedDataProviderProxy(TRealProvider realProvider)
            : this(realProvider, 1.0) { }


        public BufferedDataProviderProxy(TRealProvider realProvider, double bufferScaleFactor) {
            this.RealProvider = realProvider;
            this.BufferScaleFactor = bufferScaleFactor;

        }

        TRealProvider _realProvider;
        /// <summary>

        /// Returns the real provider being proxied.

        /// </summary>

        public TRealProvider RealProvider {
            get {
                return _realProvider;
            }
            private set {
                _realProvider = value;
            }
        }

        private double _bufferScaleFactor;
        /// <summary>

        /// The scale factor we are multiplying the box by.

        /// A scale factor &gt; 1.0 will expand the BoundingBox used in ExecuteIntersectionQuery

        /// &lt; 1.0 will shrink the BoundingBox

        /// </summary>

        public double BufferScaleFactor {
            get { return _bufferScaleFactor; }
            set { _bufferScaleFactor = value; }
        }

        #region IProvider Members

        /// <summary>

        /// returns a BoundingBox whos width and height are 

        /// scaled by BufferScaleFactor

        /// </summary>

        /// <param name="box"></param>

        /// <returns></returns>

        private Envelope ExpandBox(Envelope box) {
            double dx = (box.Width / 2) * BufferScaleFactor;
            double dy = (box.Height / 2) * BufferScaleFactor;

            Coordinate c = box.Centre;
            return new Envelope(
                c.X - dx,
                c.Y - dy,
                c.X + dx,
                c.Y + dy);

        }

        public Collection<IGeometry> GetGeometriesInView(Envelope bbox) {
            return RealProvider.GetGeometriesInView(ExpandBox(bbox));
        }

        public Collection<uint> GetObjectIDsInView(Envelope bbox) {
            return RealProvider.GetObjectIDsInView(ExpandBox(bbox));
        }

        public IGeometry GetGeometryByID(uint oid) {
            return RealProvider.GetGeometryByID(oid);
        }

        public void ExecuteIntersectionQuery(IGeometry geom, FeatureDataSet ds) {
            /* Buffer is not implemented in any of the Geometries 
             * so we will just relay to the real provider. 
             * Alternatively you could use NTS to buffer properly.
             */
            RealProvider.ExecuteIntersectionQuery(geom, ds);
        }

        public void ExecuteIntersectionQuery(Envelope box, FeatureDataSet ds) {
            RealProvider.ExecuteIntersectionQuery(ExpandBox(box), ds);
        }

        public int GetFeatureCount() {
            return RealProvider.GetFeatureCount();
        }

        public FeatureDataRow GetFeature(uint RowID) {
            return RealProvider.GetFeature(RowID);
        }

        public Envelope GetExtents() {
            return RealProvider.GetExtents();
        }

        public string ConnectionID {
            get { return RealProvider.ConnectionID; }
        }

        public void Open() {
            RealProvider.Open();
        }

        public void Close() {
            RealProvider.Close();
        }

        public bool IsOpen {
            get { return RealProvider.IsOpen; }
        }

        public int SRID {
            get {
                return RealProvider.SRID;
            }
            set {
                RealProvider.SRID = value;
            }
        }

        #endregion

        #region IDisposable Members

        public void Dispose() {
            RealProvider.Dispose();
        }

        #endregion
    }
}
The problem is that it’s incredibly slow and makes everything so much slower than it should be. Any ideas where I might be going wrong? I’m using the buffered proxy with the SQL Server 2008 provider—I’m wondering if it’s somehow ignoring the spatial indexes now that I’m using the proxy?
Coordinator
Apr 17, 2013 at 7:39 PM
The problem is that it’s incredibly slow and makes everything so much slower than it should be. Any ideas where I might be going wrong? I’m using the buffered proxy with the SQL Server 2008 provider—I’m wondering if it’s somehow ignoring the spatial indexes now that I’m using the proxy?
It was faster with the old code? I don't see why it should be slower now, as long as you have set up the underlying SqlServer2008 provider.
Apr 17, 2013 at 8:14 PM
Edited Apr 17, 2013 at 8:15 PM
Yes, it’s a lot slower. Here are a couple of tests I’ve put together:

Without the proxy
VectorLayer test1 = new VectorLayer("noproxy");
SqlServer2008 test1Data = new SqlServer2008(ConfigurationManager.ConnectionStrings["WebMappingServices"].ConnectionString, "[osm-roads]", "geom", "ID");
test1Data.DefinitionQuery = "type='motorway'";
test1.DataSource = test1Data;
test1.SRID = 3857;
test1.Style.Line = new Pen(Color.FromArgb(188, 234, 251), 8);
test1.Style.EnableOutline = true;
test1.Style.Outline = new Pen(Color.FromArgb(0, 185, 242), 10);
map.Layers.Add(test1);
Example tile without proxy

With the proxy
VectorLayer test2 = new VectorLayer("proxy");
SqlServer2008 test2Data = new SqlServer2008(ConfigurationManager.ConnectionStrings["WebMappingServices"].ConnectionString, "[osm-roads]", "geom", "ID");
test2Data.DefinitionQuery = "type='motorway'";
BufferedDataProviderProxy<SqlServer2008> proxy = new BufferedDataProviderProxy<SqlServer2008>(test2Data, 1.2);
test2.DataSource = proxy;
test2.SRID = 3857;
test2.Style.Line = new Pen(Color.FromArgb(188, 234, 251), 8);
test2.Style.EnableOutline = true;
test2.Style.Outline = new Pen(Color.FromArgb(0, 185, 242), 10);
map.Layers.Add(test2);
Example tile with the proxy

If you take into account that the application might not have been compiled when you visit, and refresh the tile to see its true loading speed, the one without the proxy takes no time at all but the one with the proxy takes around seven seconds to return an image.

Am I doing something wrong in the code for the proxy version?
Apr 17, 2013 at 8:50 PM
Just did some more digging. The tile without the proxy produces the following SQL query:
SELECT g.geom.STAsBinary()  FROM [osm-roads] g  WHERE type='motorway' AND geom.STIntersects(geometry::STGeomFromText('POLYGON ((-243129.69563113 6773136.7378071994, -243129.69563113 6774039.8484306, -242226.5850077 6774039.8484306, -242226.5850077 6773136.7378071994, -243129.69563113 6773136.7378071994))', 3857)) = 1
It returns seven rows from the database.

Then adding in the proxy produces this:
SELECT g.geom.STAsBinary()  FROM [osm-roads] g  WHERE type='motorway' AND geom.STIntersects(geometry::STGeomFromText('POLYGON ((-243220.006693473 -242136.27394535698, -243220.006693473 6774130.15949294, 6773046.42674486 6774130.15949294, 6773046.42674486 -242136.27394535698, -243220.006693473 -242136.27394535698))', 3857)) = 1
As you can see, the area it’s querying for the proxied version is huge. It returns nearly 2000 rows—much more than I was expecting.

There’s almost certainly something wrong with the code for generating the new bounding box/envelope. Not sure what it is at this time, but will try and figure it out. If you have any suggestions, please let me know :)

Thanks
Apr 17, 2013 at 8:57 PM
Looks like it was being caused by something really simple. The coordinates for an envelope were in a different order from those in the old bounding box. I changed the ExpandBox method to:
private Envelope ExpandBox(Envelope box) {
      double dx = (box.Width / 2) * BufferScaleFactor;
      double dy = (box.Height / 2) * BufferScaleFactor;

      Coordinate c = box.Centre;
      return new Envelope(
          c.X - dx,
          c.X + dx,
          c.Y - dy,
          c.Y + dy);
 }
Simply swapping the Y1 and X2 arround. This solved the problem and the proxied queries are now lightening fast too :)
Coordinator
Apr 17, 2013 at 9:27 PM
Glad you solved it. Didn't notice the glitch in your code, though I was aware of it. Sorry
Apr 17, 2013 at 9:30 PM
No worries. Having to figure it out myself means I now understand how it works rather than relying on it as a black box! Cheers.