ThinkGeo.com    |     Documentation    |     Premium Support

Best approach to display thousands different points

 Hello,



 I have a class that contains point definition like lat, lon and angle. Basically I need to display gridded points with different angle (same image)   and also display certain points based on the zoom level.


For example, at zoom level 1, I need to display points every 10 degree and at zoom level 10 every 0.5 degree.


I tried using InMemoryFeatureLayer and a custom point style but rendering time is too high and not useful.


 


Please advise.


 


Thank you in advance,

Inna




points.png (95.6 KB)

Hi Inna,  
  
 Thanks for your question! 
 Perhaps you can define more clearly what rendering times you are seeing and perhaps provide some insight into how you are currently refreshing your points? 
 Map Suite can display many points and refresh them quite quickly as shown in the Dynamic Shapes - RefreshPointsRandomly sample application. In this sample we are displaying up to 2000 points and changing their color and position up to every 300 milliseconds. By changing some of the variables you can actually get the points to move quite fluidly.

Hi Ryan, 
  
 Let me clarify – I have a gridded points and I need to display them every 0.75 degree (480x240 points), but not all together. 
 I want to display every 10 degree on the first zoom level (for entire world), every 5 degree on the second and so on. 
 Each point is a static image but with different angle and I don’t need to move them  (see attached image from my previous post). 
  
 Right now I’m using InMemoryFeatureLayer and overriding DrawCore event of PointStyle class. I’m using canvas.DrawWorldImage to rotate an image. 
 Every time I’m refreshing overlay, it takes about 4 to 6 seconds to render. 
  
 Maybe I’m approaching this wrong way… 
  
 Inna

Hi Inna,
 
I think you can use the ClusterPointStyle to reach your goal, here is our code sample, please have a look.
 

            ClusterPointStyle clusterPointStyle = new ClusterPointStyle(new GeoImage(@“YOUR PATH”));
            clusterPointStyle.CellSize = 50;
            InMemoryFeatureLayer pointsLayer = new InMemoryFeatureLayer();
            pointsLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle = clusterPointStyle;
            pointsLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
            for (double i = -180; i < 180; i += 0.75)
            {
                for (double n = -90; n < 90; n += 0.75)
                {
                    PointShape pointShape = new PointShape(i, n);
                    string id = GetRandomDirection().ToString();
                    Feature feature = new Feature(pointShape);
                    pointsLayer.InternalFeatures.Add(feature);
                }
            }

            LayerOverlay pointsOverlay = new LayerOverlay();
            pointsOverlay.TileType = TileType.SingleTile;
            pointsOverlay.Layers.Add(“PointsLayer”, pointsLayer);
            wpfMap1.Overlays.Add(“PointsOverlay”, pointsOverlay);

            wpfMap1.Refresh();


 
The render is very slow in high zoom level (1~5), but it;'s very fast in deep zoomlevel.
 
Regards,
Edgar

Hi Edgar, 
  
 Thank you for your help. 
  
 I tested you example and it is really slow (on a higher zoom levels), even before changing the direction of the icon. 
 By the way, in you example you calling to the GetRandomDirection function, but not using it anywhere. Am I missing something? 
  
 Do you have any other ideas? 
  
 Inna

So you mean that the style you override is more faster than the ClusterPointStyle? A good way is using Parallel in the method which contains loop statements, this will save a lot of time. 
  
 Regards, 
 Edgar 


I have to do more testing with the ClusterPointStyle, but from the first glance, it was slower than using my own custom style. 
 How can I set an angle of a point with a ClusterPointStyle?

Hi Inna, 



Another idea would be to setup a specialized Layer that could mathematically determine specifically which of the points from your datasource need to be drawn based on the ZoomLevel. In reviewing your comments it seems that most of the slowdown might be related to the InMemoryFeatureLayer needing to loop through all the available points to decide what needs to be drawn. With a custom Layer we could refrain from looping through the entire dataset and quickly determine which points should be displayed.


To start out are you only using this points for display or do you need to query them as well?



 Hi Ryan,


 
Thank you very much for your idea.
I don’t know exactly where is a problem -  It can be all the looping, or the problem can be in the DrawWorldImage function that I’m using to rotate an image.
I’m using this points only for display and I don’t need to query them.
 
If you can, please send me example of a custom layer that you had in mind.
 
Please see attached sample for your reference.
 
Inna

PointsLayer.zip (13.6 KB)

Inna,


Ryan's idea is very good, in your sample, there are still some optimizations could be added, attached is the new sample I modified, which was added with a InMemoryFeatureLayer.BuildIndex(), and the DrawCore of MyPointStyle was optimized a little.


Here is the time used before and after the optimization.


before



after



Regards,


Edgar



001_PointsLayer.zip (13.2 KB)

Hi Edgar, 
  
 My original code is based on KML Extension example you provided in wiki. I’m calling BuildIndex inside KmlFeatureSource class. 
  
 I wanted to change it to use InMemoryFeatureLayer and  InMemoryFeatureSource to be able to use BuildIndex() outside of the KmlFeatureSource class , but I’m having troubles to get feature’s ColumnValues in DrawCore event. Is it possible at all?   
  
 Thanks, 
 Inna

Inna,
 
You need to add the keys of features get from Kml to InMemoryFeatureLayer.Columns, e.g.
 

Collection<Feature> features = kmlFeatureSource.GetAllFeatures(ReturningColumnType.AllColumns);
foreach(var item in features[0].ColumnValues.Keys)
{
    inMemoryFeatureLayer.Columns.Add(new FeatureSourceColumn(item);
}

 
Hope it helps,
Edgar

 Hi Edgar,


 
I did what you suggested, but still cannot see column values in DrawCore.
 
Please see attached example. 
 
Inna

PointsLayer1.zip (13.9 KB)

Inna,


Sorry I missed one part, the MyPointStyle should override the GetRequiredColumnNamesCore like this,



            foreach (var item in pointsLayer.InternalFeatures[0].ColumnValues.Keys) 
            { 
                pointsLayer.Columns.Add(new FeatureSourceColumn(item)); 
            }

and in your sample code, the add column lines should be



            foreach (var item in pointsLayer.InternalFeatures[0].ColumnValues.Keys) 
            { 
                pointsLayer.Columns.Add(new FeatureSourceColumn(item)); 
            }

because the features returned by GetAllFeatures method have no column names cause the layer.Columns hasn't been added.


Regards,


Edgar



Hi Inna,


This is an interesting way of displaying points. I spoke with one of our lead architects who has some thoughts on how you might best render these points:


"After looking at your sample I think there are two ways to get better speed.  

The first, and one I don’t recommend but is easier, is to break up your data into different InMemoryFeatureLayers and specify them to draw at different scales.  In this way each layer will only hold data that it will display at that scale so the system does not have to look though all of the data we won’t draw just to find some data we will draw.  

We attempted to do this in your sample however with your custom zoom levels and other factors we decided it would take longer than the time we had allocated for a discussion forum post. 


The second way, which I think is better and faster, would be to create your own layer either inherited from Layer or from FeatureLayer and approach things a bit differently.  It seems like you have a matrix where each cell has a world extent and at various scales different cells will draw.  For example at scale 1 you might draw every fourth cell because it is at the 1 degree mark.  At another scale it might be every other one etc.  My suggest is that, since you know the algorithm, you put at the core of your new layer your matrix and when the DrawCore gets called you look at the extent it passes in and the scale you are at and you use a bit of math to determine which cells you pull out of your matric to draw. 


For example you know the boundary of your matrix and you know the cell size in world extent so you can isolate just the cells you know are visible and then you can augment that with knowing which of those cells are appropriate at that scale.  I think if you did this it would be very fast because the index is  bit of math and you can just use the matrix you have and do not need to generate anything else besides a two dimensional array, which you may already have.  We do things like this to find the tiles to draw for a particular extent when the tile matrix is huge and we adjust what we find by the scale. 


The problem is to do an example based on your sample falls outside the realm of a forums post.  If I did a good job explaining it I think you have a good shot at writing it.  I can answer your questions but if you need us to write a sample integrated into your code we will have to do that as a small professional services job."



Hi Ryan and Edgar, 
  
 Thank you very much for your help. 
  
 I converted Kml’s FeatureLayer to InMemoryFeatureLayer, so I can use BuildIndex() after projection. It helped a little, and I think I will take this approach for now.  
 But I’m really interesting in Ryan’s second idea about creating custom layer, because in the future, I want to use gridded data and eliminate usage of kml files at all. 
 Ryan  - what do you mean “small professional services job”? Can I create a ticket for this?  
  
 Thanks again, 
 Inna

Hi Inna,


A Professional Services project would not go through our Support department/Customer Portal Ticket. Rather we would setup you up with a member of our Professional Services Team who could create the custom FeatureSource/Layer specifically for your needs. Professional Services contracts are handled through our Sales department so you would need to contact our sales department through one of the numbers listed on our Contact Us page: gis.thinkgeo.com/Contact/tabid/147/Default.aspx