ThinkGeo.com    |     Documentation    |     Premium Support

Is it possible to customize GeoCache / FeatureCache?

Hi guys,


I am admitedly completely inexperienced with creating a custom object, inheriting it's properties and methods and overriding. I don't really expect an indepth explanation of all of that as I think I get the premise but I had a question. There is a lot of advocacy to customize this product and I am excited to try.


There is an odd behavior with using the GeoCache and WFSFeatureLayer and I think I would like to inherit from the typical FeatureCache and then override one of it's functions and perhaps add a couple of properties.


Basically, the GeoCache is causing a big slow down when I pan to a section of the layer that falls out side of it's bounding box. I think it isn't too difficult to maybe storage the entire bounding box coordinates and if we are requesting any data, make sure we are not looking outside of the bounding box and if so to trim out the part of the box that falls outside.. I'm sure I'll run into snags along the way but I am really interested to check this out. I wouldn't even be opposed to throwing the source on the nice Code Community section of the site. I don't know how much real-world use it will gain for anyone, but for me I could be shaving 5-25 seconds on those first pans outside of the bounding box and that will go a long way for the user experience for us.


Of course, if you think this isn't possible within your frame work, please feel free to say so. I think it probably is possible though because Map Suite seems to have a lot of customizing possibility.


By the way, the build I am using is working quite well and I appreciate the continued support even in between releases.


Thanks guys!



Nelson,



   I can help you with that.  I was just thinking about your problem the other day and I think I have an idea.  The good part is that we can solve this but the bad part is that there are a few assumptions.  The first assumption is that you get all of the features from the FeatureSource to pre-populate the cache.  You need to reference the NetTopologySuite and GeoAPI dlls that come with the Desktop Edition.  The dlls will be with the rest of the Map Suite ones but you need them in your project.



You will also need the following usings



using GisSharpBlog.NetTopologySuite.Index.Quadtree;

using GisSharpBlog.NetTopologySuite.Geometries;

using System.Collections;





What this code will do is to create a quad tree index for you of all of your records.  The quad tree is a good choice because it is quick to build and the search time is pretty fast considering the index build time.  Once we have the items cached then whenever you pan around we will queryt he quad tree and only return the records that are in the extent, it should od this quickly.  This should fix the problem when you pan outside of the large bounding box.  It also should increase the speed when you zoom in as well as you do not have to draw everything.



Here is an example on how you would use it. Assume the layer has already been created.



            vehiclesLayer.Open();

            Collection<Feature> features = vehiclesLayer.QueryTools.GetAllFeatures(ReturningColumnsType.AllColumns);



            IndexedFeatureCache indexedFeatureCache = new IndexedFeatureCache();

            indexedFeatureCache.IsActive = true;

            indexedFeatureCache.BuildIndex(features);



            vehiclesLayer.FeatureSource.GeoCache = indexedFeatureCache;



After you do this go ahead and use the layer as you did before.  There is no need to query the big bounding box anymore.





    public class IndexedFeatureCache : FeatureCache
    {
        private Quadtree qTree = null;

        protected override Collection<Feature> GetFeaturesCore(RectangleShape worldExtent)
        {
            Envelope envelope = new Envelope(worldExtent.UpperLeftPoint.X, worldExtent.LowerRightPoint.X, worldExtent.UpperLeftPoint.Y, worldExtent.LowerRightPoint.Y);
            ArrayList features = (ArrayList)qTree.Query(envelope);

            Collection<Feature> resultsFeatures = new Collection<Feature>();
            foreach (Feature feature in features)
            {
                resultsFeatures.Add(feature);
            }

            return resultsFeatures;
        }

        public void BuildIndex(IEnumerable<Feature> features)
        {
            qTree = new Quadtree();

            foreach (Feature feature in features)
            {
                RectangleShape boundingBox = feature.GetBoundingBox();
                Envelope envelope = new Envelope(boundingBox.UpperLeftPoint.X, boundingBox.LowerRightPoint.X, boundingBox.UpperLeftPoint.Y, boundingBox.LowerRightPoint.Y);

                qTree.Insert(envelope, feature);
            }          
        }

        protected override void OpenCore()
        {
            // Do nothing
        }

        protected override void CloseCore()
        {            
            qTree = null;
        }

        protected override void AddCore(RectangleShape worldExtent, Collection<Feature> features)
        {
            // We do nothing here as this cache needs to be pre-created
        }
    }
 





David

 



Thanks, David. Our WFS server seems to be down so I’m stuck on getting that back on, but I look forward to trying this out as soon as we are back up and running. I’ll post any feedback.  
  
 Thanks, 
 Nelson

Bad news :/ 
  
 Turns out this won’t work at all because GisSharpBlog.NetTopologySuite.Index.Quadtree.Quadtree is not marked as seriailzable and I CloneDeep all of my layers. I imagine this is a third party dll so you probably don’t have any control over that, which is a shame. Any other ideas? 
  
 One thing I noticed you mentioned was “it will fix my issue with zooming in since it won’t draw everything” but it seems like that is true now with the current cache? The smaller my bounding box becomes, the faster my response time; I don’t think it draws anything outside of my map.CurrentExtent. The problem is when map.CurrentExtent becomes anything other than contained within the layers bounding box. Does that make sense? Just wanted to make sure we were on the same page.  
  
 I would like to prevent the map from even bother to draw anything that falls outside of the layers entire bounding box.

Nelson, 
  
   All is not lost.  Can you explain why you use the CloneDeep?  If you put a Serializable attribute on the class I sent then as long as the IndexedFeatureCache has had close called it should serialize.  As long as the qtree class is null we should have no problems.  There is a bug in the Close of the FeatureSouce that it does not call the Close on the cache class so for now you would need to call it before you clone. 
  
   To the other point even though it is faster is is because the features are being rejected from drawing by GDI+ I bet.  They are actually being sent to be drawn but then not drawing.  In any event if we don’t even send then that will be a speedup a promise. 
  
   If the serialization still doesn’t work I have one more trick to pull out.  Keep me advised and let me know how and why you clone so we can figure something out. 
  
 David

I CloneDeep every layer because I add the layer twice; One to my main map and one to my locus map. It seemed to be a natural choice and has worked well since we first implemented it.  
  
 You’re correct in that by rearranging some code to clone deep before the cache generation I was able to avoid running into the serialization issue with the NetTopologySuite.  
  
 Performance does seem better! I see what you mean in regards to GDI+ and why there was a difference before. I am not noticing a huge difference in performance while zoomed in (it was already quite fast in this scenario) but I believe there is some. The huge benefit here is we got rid of the main issue we had with the FeatureCache, which was the latency when drawing outside of the layer’s bounding box.  
  
 This is seemingly working great right now so it’ll require some thorough testing to make sure we didn’t accidently break something in the process but it seems to be very snappy! 
  
 Thanks, David!

Nelson, 
  
   I am so glad this is working for you.  It has been a long road but we have something in there.  Let me know any issues you run into.  Actually when working with the QTree I kinda like it better than the RTree as you don’t have to build the index, just add the node.  Building RTree indexes takes more time but is faster in the end for querying.  Anyway let me know if there is anything else. 
  
 I think also in a version in the near future we will have the issue fixed where the GeoCache will close itself when you close the layer.  It should have worked like that and you should not have needed and code but we found the bug too late before we shipped. 
  
 David