SharpMap app crashes at GeometryFeatureProvider.ExecuteIntersectionQuery

Topics: General Topics, SharpMap v0.9 / v1.x
Feb 17, 2012 at 11:45 AM
Edited Feb 17, 2012 at 11:49 AM

I have a vector layer that is added to variablelayercollection to be able to track moving objects.

Sometimes it crashes - see the stack bellow. I think I found its reason: there is a thread in my app that periodically gets moving objects data and adds them to my tracking layer as markers. But markers also can be manipulated from main UI thread by user (hide all, filter by query etc). Crash happens, when user manipulates markers and some new data arrive at the same time.

So my question is: is GeometryFeatureProvider (I use it as my variable vector layer datasource) thread safe or not? Is it thread safe when adding/removing objects to/from that?

If not, I should lock it when adding/filtering items in it. As I think and see in your source code it is not thread safe, but I'd like to double check it before changing my code.

Thanks for your help!

Application: xy.exe Framework Version: v4.0.30319 Description: The process was terminated due to an unhandled exception. Exception Info: System.InvalidOperationException Stack: at System.Data.RBTree`1+RBTreeEnumerator[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].MoveNext() at SharpMap.Data.Providers.GeometryFeatureProvider.ExecuteIntersectionQuery(SharpMap.Geometries.BoundingBox, SharpMap.Data.FeatureDataSet) at SharpMap.Layers.VectorLayer.RenderInternal(System.Drawing.Graphics, SharpMap.Map, SharpMap.Geometries.BoundingBox, SharpMap.Rendering.Thematics.ITheme) at SharpMap.Layers.VectorLayer.Render(System.Drawing.Graphics, SharpMap.Map) at SharpMap.Map.RenderMap(System.Drawing.Graphics, SharpMap.Layers.LayerCollectionType, Boolean) at SharpMap.Forms.MapBox.GetMap(SharpMap.Map, SharpMap.Layers.LayerCollection, SharpMap.Layers.LayerCollectionType, SharpMap.Geometries.BoundingBox) at SharpMap.Forms.MapBox.HandleVariableLayersRequery(System.Object, System.EventArgs) at SharpMap.Layers.VariableLayerCollectionRequeryHandler.Invoke(System.Object, System.EventArgs) at SharpMap.Layers.VariableLayerCollection.OnRequery(System.Object) at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(System.Object) at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() at System.Threading.ThreadPoolWorkQueue.Dispatch() at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() 

Developer
Feb 17, 2012 at 5:49 PM

I suggest to see the WinFormSamples:

Background, Static and Variable Layer [MapBox]

You should put all the RealTime Layers in the Variable Layer Collection. I think you can look at the example.

cofee

Feb 17, 2012 at 5:54 PM

It is OK, i have been using it for months, but now this threading thing come into stage, my question is only for that. 

Feb 18, 2012 at 5:30 PM
Edited Feb 18, 2012 at 6:13 PM

The problem seems more complex. E.g. I have 100 moving objects. As new location data come from a device I refresh its position and call VariableLayerCollection.TouchTimer();

Position data  from more than one device can arrive at the same time. Then VariableLayerCollection.TouchTimer(); is called again. And user can manipulate trackinglayer (turn labels on/off, filter objects by category) then I also must call VariableLayerCollection.TouchTimer(); to refresh. And when the user click on a device id in a list view, then I should zoom to it, so after zooming I must call VariableLayerCollection.TouchTimer(); again. So there can be more TouchTimer calls at the same time. 

Note, it is not enough me to Refresh my tracking layer periodically (e.g. at every half seconds), I must refresh it right after a new location data arrives/user interaction. That is why I call TouchTimer right after the events (data comes, user interaction) and not by a timer. Timer would make it very ugly: e.g. my user clicks on the list to zoom on a position. Zooming would not be smooth, if position marker would not be refreshed right after the zoom.

May be it leads to my crash. Any opinion?

Isn't it possible to work around this touchtimer thing? Isn't it possible somehow to call a refresh on my layer by me in my code without calling touchtimer? 

Apr 20, 2012 at 10:57 AM

Hi,

I had the same problem - I have 2000 moving geometries with custom theming.

I order to avoid the crush in GeometryFeatureProvider.ExecuteIntersectionQuery() execution I made the following changes, in SharpMap\Data\Providers\GeometryFeatureProvider.cs:

        /// <summary>
        /// Throws an NotSupportedException. Attribute data is not supported by this datasource
        /// </summary>
        /// <param name="geom"></param>
        /// <param name="ds">FeatureDataSet to fill data into</param>
        public void ExecuteIntersectionQuery(Geometry geom, FeatureDataSet ds)
        {
            FeatureDataTable fdt = new FeatureDataTable();
            fdt = _features.Clone();

            //foreach (FeatureDataRow fdr in _features)
            //    if (FilterDelegate == null || FilterDelegate(fdr))
            //    {
            //        if (fdr.Geometry.GetBoundingBox().Intersects(geom))
            //        {
            //            fdt.LoadDataRow(fdr.ItemArray, false);
            //            (fdt.Rows[fdt.Rows.Count - 1] as FeatureDataRow).Geometry = fdr.Geometry;
            //        }
            //    }

            for (int i = 0; i < _features.Count; i++)
            {
                FeatureDataRow fdr = _features[i];
                if (FilterDelegate == null || FilterDelegate(fdr))
                {
                    if (fdr.Geometry.GetBoundingBox().Intersects(geom))
                    {
                        fdt.LoadDataRow(fdr.ItemArray, false);
                        (fdt.Rows[fdt.Rows.Count - 1] as FeatureDataRow).Geometry = fdr.Geometry;
                    }
                }
            }

            ds.Tables.Add(fdt);
        }

        /// <summary>
        /// Throws an NotSupportedException. Attribute data is not supported by this datasource
        /// </summary>
        /// <param name="box"></param>
        /// <param name="ds">FeatureDataSet to fill data into</param>
        public void ExecuteIntersectionQuery(BoundingBox box, FeatureDataSet ds)
        {
            FeatureDataTable fdt = new FeatureDataTable();
            fdt = _features.Clone();

            //foreach (FeatureDataRow fdr in _features)
            //    if (fdr.Geometry != null)
            //    {
            //        if (FilterDelegate == null || FilterDelegate(fdr))
            //        {
            //            if (fdr.Geometry.GetBoundingBox().Intersects(box))
            //            {                        
            //                fdt.LoadDataRow(fdr.ItemArray, false);
            //                (fdt.Rows[fdt.Rows.Count - 1] as FeatureDataRow).Geometry = fdr.Geometry;
            //            }
            //        }
            //    }

            for (int i = 0; i < _features.Count; i++)
            {
                FeatureDataRow fdr = _features[i];
                if (fdr.Geometry != null)
                {
                    if (FilterDelegate == null || FilterDelegate(fdr))
                    {
                        if (fdr.Geometry.GetBoundingBox().Intersects(box))
                        {
                            fdt.LoadDataRow(fdr.ItemArray, false);
                            (fdt.Rows[fdt.Rows.Count - 1] as FeatureDataRow).Geometry = fdr.Geometry;
                        }
                    }
                }
            }

            ds.Tables.Add(fdt);
        }

The crash is happing when the features collection is changing while in the ExecuteIntersectionQuery() is executing. The for loop does not care about changes in collection. Perhaps I could do something better, writing code that would check for an out-of-bounds exception, but the propability to happen, is very low.

With this change I managed to see more than 2500 features on the map, with an 1500 millisecond Interval. Use of BackgroundLayer collection is adviced.

 

George J.