ThinkGeo.com    |     Documentation    |     Premium Support

DrawTilesProgressChangedTileOverlayEventArgs.ProgressPercentage determination

 Guys,


How exactly do you compute DrawTilesProgressChangedTileOverlayEventArgs.ProgressPercentage for a LayerOverlay?  What I am noticing is that this ProgressPercentage is 100 when WorldMapKitRenderLayer is still busy drawing.


Also, I am noticing sometimes that ProgressPercentage gets stuck below 90% although drawing activity is finished as indicated in Visual Studio's output window.  I am monitoring drawing progress of WMK render layer only.


Thanks


Klaus


 



Hi Klaus, 
  
 This value is actually a percentage of how many tiles are completely drawn. For example, a map might have 4*4=16 tiles in total. Every tile draws asynchronously; in another word, there will be 16 tiles draw at once in background thread; let’s forget the thread pool temporary. When one tile is absolutely drawn, we will increase the drawn number; and then raise DrawTilesProgressChanged event; so the Percentage equals drawnTileCount * 100 / totalTileCount. Hope it makes sense. 
  
 The stuck below 90% might caused by an exception during drawing, so could you please check your configuration of VS if it throws clr exception(Ctrl+E,D). If there is any exception, please let us know. Because I zoom/pan the WMK layer, it always stops at 100. 
  
 Thanks for reporting and looking forward your feedback. 
  
 Thanks, 
 Howard

 Thanks for the explanation Howard.   The WMK overlay's draw progress percentage is correct.   The problem occurs because we are trying to report draw progress of several overlays in the same progress bar.  



Hi Klaus,



Fine; I have a solution for you for all the tile overlays. Please check the demo attached.



Thanks,

Howard



DrawingProgressSample.zip (10.4 KB)

Just what the Doctor ordered!  Good stuff Howard, thanks.


To improve the performance a bit, I made a slight modification in  tileOverlay_DrawTilesProgressChanged handler.  This is the modified class for others.


 



 public class ExtendedWpfMap : WpfMap
    {       

        /// <summary>
        /// Occurs when drawing progress changed.
        /// </summary>
        public event EventHandler<DrawingProgressChangedExtendedWpfMapEventArgs> DrawingProgressChanged;

        public ExtendedWpfMap()
            : base()
        {
            Overlays.CollectionChanged += Overlays_CollectionChanged;
        }

        protected virtual void OnDrawingProgressChanged(DrawingProgressChangedExtendedWpfMapEventArgs e)
        {            
            if (DrawingProgressChanged != null)
            {
                DrawingProgressChanged(this, e);
            }
        }

        private void Overlays_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (Overlay overlay in e.NewItems)
                    {
                        TileOverlay tileOverlay = overlay as TileOverlay;
                        if (tileOverlay != null && tileOverlay.TileType != TileType.SingleTile)
                        {
                            tileOverlay.DrawTilesProgressChanged += tileOverlay_DrawTilesProgressChanged;
                        }
                    }
                    break;
                case NotifyCollectionChangedAction.Remove:
                    foreach (Overlay overlay in e.OldItems)
                    {
                        TileOverlay tileOverlay = overlay as TileOverlay;
                        if (tileOverlay != null && tileOverlay.TileType != TileType.SingleTile)
                        {
                            tileOverlay.DrawTilesProgressChanged -= tileOverlay_DrawTilesProgressChanged;
                        }
                    }
                    break;
                default:
                    break;
            }
        }        

        private void tileOverlay_DrawTilesProgressChanged(object sender, DrawTilesProgressChangedTileOverlayEventArgs e)
        {           

            TileOverlay currentTileOverlay = (TileOverlay)sender;
            currentTileOverlay.OverlayCanvas.Tag = e.ProgressPercentage;            

            IEnumerable<TileOverlay> tileOverlays = Overlays.OfType<TileOverlay>()
                .Where((to => to.TileType != TileType.SingleTile))
                .Where(to => to.OverlayCanvas.Tag != null && (int)to.OverlayCanvas.Tag != 100);

            int total = tileOverlays.Count();
            int currentProgress = 100;

            if (total != 0)
            {
                int progress = 0;
                foreach (TileOverlay tileOverlay in tileOverlays)
                {
                    if (tileOverlay.OverlayCanvas.Tag != null)
                    {
                        var overlayProgress = (int)tileOverlay.OverlayCanvas.Tag;
                        progress += overlayProgress;                        
                    }
                }

                currentProgress = progress / total;            
            }
           
            OnDrawingProgressChanged(new DrawingProgressChangedExtendedWpfMapEventArgs(currentProgress, null));          
        }
    }

 


One more thing, WPF progress bar leaks memory on .NET 3.5 SPI.  So any users of the progress bar using .NET 3.5 SP1 should refer to the following link for a work around.


 


connect.microsoft.com/Visual...emory-leak


 



Hi Klaus,  
  
 Thanks for sharing your code which is very cool as well as the article. 
  
 Just feel free to let us know if you have more queries. 
  
 Thanks, 
 Howard

Hi Guys, 
  
 I’m having the same problem here, drawing percentage is never greater than 96%. VS does not show any exception. But the extrangest thing is that If I include the following sentence in the tileOverlay_DrawTilesProgressChanged method then it never draw more than 20%, and the method is fired constantly: 
                      if (tileOverlay.OverlayCanvas.Tag != null) 
                     { 
                         var overlayProgress = (int)tileOverlay.OverlayCanvas.Tag; 
                         If (overlayProgress<100) {debug.writeline(tileOverlay.name + ’ ’ +overlayProgress)} 
  


Hi Carlos,


Here are answers for your questions:


1. I'm having the same problem here, drawing percentage is never greater than 96%. VS does not show any exception.


We can't recreate the issue you mentioned by using Howard's sample . If there aren't any exceptions, the progress bar should always reach 100%.


2. It never draw more than 20%, and the method is fired constantly


The method "tileOverlay_DrawTilesProgressChanged" is fired when any tile is drawn. There should be a great many of tiles in your project, so this method will be fired frequently but not constantly.


We'll keep an eye on your feedback to find the root cause.


Regards,


Ivan


 



Hi Ivan, 
  
  1. They aren’t any exception. But some times the drawing percentage gets struck 
  2. I placed a hit test counter in the tileOverlay_DrawTilesProgressChanged and figured out that it’s being fired thousands of times, that’s the reason why If I include a debug.writeline sentence the drawing proccess never ends (takes loooong) 
  
  I only have 5 overlays total, two of them multitile, with 22 and 3 layers respectively. Shoudn’t they be only 24 tiles? 
  
  How can I debug more preciselly this drawing process (know which overlay is causing the problem, and what problem it is)?

This afternoon I have been told of a installation where after a refresh some tiles are never drawn (I’ve seen the screenshot) and the drawing percentage is struck at 80%. It seems that some exception is happening, but it is not thrown. 
  
   The screenshot I’ve seen shows a map where all tiles was compleately drawn, except one that only contains the overlay coming from the cache, but not the InMemoryLayer overlay that is on top of it. While this overlay is actually properly drawn in the other tiles. 
  
  As you see I need to be able to determine somehow what’s going wrong, but as no exception is being fired I don’t know how to aproach this…

Hi Carlos,


Here are answers for your questions:


1. I only have 5 overlays total, two of them multitile, with 22 and 3 layers respectively. Shoudn't they be only 24 tiles? 


The default size for a tile is 256*256, so the number of tiles in each tileOverlay is determined by the map size. 


2. How can I debug more preciselly this drawing process (know which overlay is causing the problem, and what problem it is)?


Using tileOverlay's name to identify each tileOverlay's progress should be a proper solution to track precise drawing process. 


For example, you can use the following code to track each tileOverlay's drawing progress and examine which progress never reached 100.


private void tileOverlay_DrawTilesProgressChanged(object sender, DrawTilesProgressChangedTileOverlayEventArgs e)
{
    TileOverlay currentTileOverlay = (TileOverlay)sender;
    currentTileOverlay.OverlayCanvas.Tag = e.ProgressPercentage;

    IEnumerable<TileOverlay> tileOverlays = Overlays.OfType<TileOverlay>().Where((to => to.TileType != TileType.SingleTile));
    int total = tileOverlays.Count() * 100;
    int progress = 0;

    Debug.WriteLine("-------------");
    foreach (TileOverlay tileOverlay in tileOverlays)
    {
        if (tileOverlay.OverlayCanvas.Tag != null)
        {
            var overlayProgress = (int)tileOverlay.OverlayCanvas.Tag;
            progress += overlayProgress;
            if (overlayProgress <= 100) { Debug.WriteLine(tileOverlay.Name + ":" + overlayProgress); }
        }
    }
    Debug.WriteLine("-------------");

    int currentProgress = progress * 100 / total;
    OnDrawingProgressChanged(new DrawingProgressChangedExtendedWpfMapEventArgs(currentProgress, null));
}


Regards,


Ivan



Hi Ivan 
  
  1. This are 3x4 (4x5 including the 1 tile buffer) tiles for a 1024x768 resolution. 
  2. This issue was a bug in my code that never updated the DrawingProgress then it was 100% (in the last iteration), not seems to be working fine 
  
  Anyway It would be nice to be able to know if the WpfMap core is busy or not, as drawing process is just a part of the whole processing, and also be able to abort current operations in some way. Please consider to expose some kind of core status class at some point in the future. 
  
  Carlos.

Hi Carlos, 
  
 Thank you for your suggestion. In the future we may need to: 
 1. Expose more status for WpfMap 
 2. Add behaviors to abort operations when exceptions occur 
  
 These new features are still under discussion but we have added them to our work list, hopefully we can implement them in the next main release. 
  
 Regards, 
  
 Ivan 


 Posted By Ivan on 03-15-2011 01:55 AM 

Hi Carlos,


2. How can I debug more preciselly this drawing process (know which overlay is causing the problem, and what problem it is)?


Using tileOverlay’s name to identify each tileOverlay’s progress should be a proper solution to track precise drawing process. 


For example, you can use the following code to track each tileOverlay’s drawing progress and examine which progress never reached 100.


 


Regards,


Ivan





  Hi Ivan,


I’m facing a similar issue again. Under certain circunstances I cannot recreate on an isolated codesample the drawing percentage on certain LayerOverlay keeps struck at 95%, and one tile is never drawn. Again no exceptions are thrown.


 What can I do to solve it?



 Carlos, if this is an intermittent issue, it is probably being caused by bad data or an exception thrown either during creation of your features and least likely during the draw phase.  It will be a hard one to track but first make sure that your code, under no circumstance, creates an unhandled exception when trying to create features.


Once you have this covered, then this problem will typically be arising when the map is attempting to create tiles, which I highly doubt it.  Each time I run into this problem, it always turns out to be a bug in my code somewhere.  The last time this happened, I was accessing an object that was null, while trying to create a feature.



Hi Klaus, 
  
 Thank you for sharing your experience to solve Carlos’s issue. Every suggestion is greatly appreciated because it will help us to move forward.  
  
 Please feel free to ask us questions while using ThinkGeo products, we’ll do our best to assist you.  
  
 Regards, 
  
 Ivan

Hi Klaus, 
   
  If it was the case, I would expect an exception to be thrown at some point, either at features creation or at drawing time. In this particular instance, the error is not too much intermittent it happens quite consistently. It does not happen at the beginning when creating the features, but afterwards after changing the visibility and GeoImage to some PointShapes and refreshing the overlay and after some time restoring the visibility and default GeoImage of all. 
  All draws OK BUT a single (or couple of) tiles for a given zoom level, if you zoom in or out it might be OK for the new zoom level, but returning to the first one still shows a missing tile. Clearing the tiles of the overlay and refreshing it doesn’t seems to work either. 
  Strange, isn’t it?  
  
  The problem is that as no exception is thrown I don’t know where to look for. 


 Yes Carlos, this is a strange one.  


How may overlays do you have added to the map's overlay collection?  Also, are you caching any overlay as this also sounds to me like a bad tile is either being drawn and restored from the cache or there is something specific at this zoom level that is causing the map to throw and swallow an exception during its draw phase.   Is your custom style using zoom level info to style the features?  Maybe there is a problem there too.   


Also, did you try setting Overlay.DrawingExceptionMode to DrawingExceptionMode.DrawException?  Maybe this will also give us more info.


BTW, I sent you a PM.



Hi Master, 



I tryed setting Overlay.DrawingExceptionMode to DrawingExceptionMode.DrawException as you recommended and this helped me to find the exact mechanism that was causing the issue. 



The problem was InMemoryFeatureLayer.InternalFeatures being cleared without rebuilding the index on a specific procedure. Now it works perfectly again. 



Anyway, if something is wrong with the FeatureLayer that is causing a Tile drawing to fail, shouldn't it thow and WpfMap exception? I would expect so. The description of DrawingExceptionMode states "This means that the exception will be throw out and the rest of drawing will be terminated", but no exception is thown. 

Is the DrawingExceptionMode.DrawException expected behaiviour to paint a red "X", or should it write the exception description? (I have never used before)




 Good stuff Carlos. Very glad to know you found and fixed the problem.  BTW,  please stop calling me Master  as we are all becoming MapSuite masters 


As to why exception is not being thrown, do you have a catch somewhere that is probably swallowing this exception as I am sure an exception will be thrown if you set DrawingExceptionMode to ThrowException.   Which reminds me, are you using the SafelyRefresh(Overlay) method we added into the ExtendedWpfMap class?   That method swallows all exceptions and logs to file, if you have this implemented.


 


 



  public bool SafelyRefresh(Overlay overlay)
        {
            bool successfullyRefreshed = false;

            try
            {
                this.Refresh(overlay);
                successfullyRefreshed = true;
            }
            catch (Exception ex)
            {
                ExceptionPolicy.HandleException(ex, ExceptionHandlingPolicyNames.LogPolicy);                
            }

            return successfullyRefreshed;
        }