Edit this page

Items Virtualization

There are numerous scenarios where large number of items should be visualized on the map surface. Using large data source within the visualization layer can decrease the overall performance of the map control, especially the initial loading and the performance during zooming or panning. This is why the visualization layer API supports UI Virtualization, which processes information only inside the current viewport of the RadMap thus boosting the initial loading and the overall performance of the control.

Properties

The VisualizationLayer exposes the following properties:

  • RenderWhileMotion - a property of type bool that gets or sets a value indicating whether the layer should request and render items while the user is zooming or panning.

  • VirtualizationSource - a property of type IMapVirtualizationSource that gets or sets the item source of the layer.

  • ZoomLevelGridList - a property of type ZoomLevelGridCollection that gets the collection of ZoomLevelGrids used to specify the map division.

If both VirtualizationSource and ZoomLevelGridList properties are set then the VisualizationLayer switches to use UI Virtualization. When this feature is enabled the layer doesn’t take its items from the ItemsSource property. Instead, it raises an items request event when the ZoomLevel or Center properties of the RadMap control are changed.

Map Division

The VisualizationLayer needs the map surface to be divided into regions. The division can be defined using the VisualizationLayer.ZoomLevelGridList collection. Each ZoomLevelGrid should define a minimum zoom level for its division. The maximum zoom level for a grid is the minimum zoom level of the next grid in the list.

The ZoomLevelGrid exposes the following properties:

  • CellWidth - it is of type int and gets or sets the width of the cell in pixels.

  • CellHeight - it is of type int and gets or sets the height of the cell in pixels.

  • MinZoom - it is of type int and gets or sets the minimum zoom.

By default the cell has a size which equals the size of tile (256x256) which is usually used by the map providers like Bing and OpenStreet.

Specifying the size of а cell for the zoom level affects a number of requests to a Virtualization Source which is depended on the viewport size of the map.

Virtualization Source

The data provided to the VisualizationLayer should be wrapped in a class that implements the IMapItemsVirtualizationSource interface. This interface contains a MapItemsRequest method, which is used by the VisualizationLayer to request new data whenever the ZoomLevel or Center properties of the RadMap control are changed. The number of requests depends on the number of the grid cells visualized in the current viewport of the control. Below you can see examples of different viewports.

If the cell size is 256x256 pixels (the default one) and there are 9 cells in the viewport of the control - 9 requests will be sent to the virtualization source whenever this geographical region comes into the view.
Rad Map visualization layer virtualization

In this case only 6 requests will be sent to the virtualization source whenever this geographical region comes into the view.
Rad Map visualization layer virtualization 2

Here is an example of how to implement simple virtualization source:

public class MyVirtualizationSource : IMapItemsVirtualizationSource
{
    private XmlDocument document;

    public MyVirtualizationSource()
    {
        StreamResourceInfo streamInfo = Application.GetResourceStream(
            new Uri(
                "/TestMapFeatures;component/Resources/GeoData/StoresLocation.xml", 
                UriKind.Relative));

        this.document = new XmlDocument();
        this.document.Load(streamInfo.Stream);
    }

    public void MapItemsRequest(object sender, MapItemsRequestEventArgs eventArgs)
    {
        double minZoom = eventArgs.MinZoom;
        Location upperLeft = eventArgs.UpperLeft;
        Location lowerRight = eventArgs.LowerRight;

        if (this.document == null)
            return;

        if (minZoom == 3)
        {
            // request areas
            List<StoreLocation> list = this.GetStores(
                upperLeft.Latitude,
                upperLeft.Longitude,
                lowerRight.Latitude,
                lowerRight.Longitude,
                StoreType.Area);

            eventArgs.CompleteItemsRequest(list);
        }

        if (minZoom == 9)
        {
            // request areas
            List<StoreLocation> list = this.GetStores(
                upperLeft.Latitude,
                upperLeft.Longitude,
                lowerRight.Latitude,
                lowerRight.Longitude,
                StoreType.Store);

            eventArgs.CompleteItemsRequest(list);
        }
    }

    internal List<StoreLocation> GetStores(
        double upperLeftLat,
        double upperLeftLong,
        double lowerRightLat,
        double lowerRightLong,
        StoreType storeType)
    {
        List<StoreLocation> locations = new List<StoreLocation>();

        string latLonCondition = "[number(@Latitude) < " 
            + upperLeftLat.ToString(CultureInfo.InvariantCulture) 
            + " and number(@Latitude) > " 
            + lowerRightLat.ToString(CultureInfo.InvariantCulture) 
            + " and number(@Longitude) > " 
            + upperLeftLong.ToString(CultureInfo.InvariantCulture) 
            + " and number(@Longitude) < " 
            + lowerRightLong.ToString(CultureInfo.InvariantCulture) 
            + "]";

        switch (storeType)
        {
            case StoreType.Area:
                {
                    XmlNodeList nodeList = document.SelectNodes(
                        "/StoresLocation/Area" + latLonCondition);
                    foreach (XmlNode node in nodeList)
                    {
                        XmlElement element = (XmlElement)node;

                        locations.Add(new StoreLocation(
                            Convert.ToDouble(
                                element.GetAttribute("Latitude"), 
                                CultureInfo.InvariantCulture),
                            Convert.ToDouble(
                                element.GetAttribute("Longitude"), 
                                CultureInfo.InvariantCulture),
                            element.GetAttribute("Name"), StoreType.Area));
                    }
                }
                break;

            case StoreType.Store:
                {
                    XmlNodeList nodeList = document.SelectNodes(
                        "/StoresLocation/Area/" + latLonCondition);
                    foreach (XmlNode node in nodeList)
                    {
                        XmlElement element = (XmlElement)node;

                        locations.Add(new StoreLocation(
                            Convert.ToDouble(
                                element.GetAttribute("Latitude"), 
                                CultureInfo.InvariantCulture),
                            Convert.ToDouble(
                                element.GetAttribute("Longitude"), 
                                CultureInfo.InvariantCulture),
                            element.GetAttribute("Name"),
                            element.LocalName == "Market" ? StoreType.Market : StoreType.Store));
                    }
                }
                break;
        }

        return locations;
    }
}   
Public Class MyVirtualizationSource
    Implements IMapItemsVirtualizationSource
    Private document As XmlDocument

    Public Sub New()
        Dim streamInfo As StreamResourceInfo =
            Application.GetResourceStream(
                New Uri("/TestMapFeatures;component/Resources/GeoData/StoresLocation.xml",
                        UriKind.Relative))

        Me.document = New XmlDocument()
        Me.document.Load(streamInfo.Stream)
    End Sub

    Public Sub MapItemsRequest(sender As Object, eventArgs As MapItemsRequestEventArgs)
        Dim minZoom As Double = eventArgs.MinZoom
        Dim upperLeft As Location = eventArgs.UpperLeft
        Dim lowerRight As Location = eventArgs.LowerRight

        If Me.document Is Nothing Then
            Return
        End If

        If minZoom = 3 Then
            ' request areas '
            Dim list As List(Of StoreLocation) =
                Me.GetStores(upperLeft.Latitude,
                             upperLeft.Longitude,
                             lowerRight.Latitude,
                             lowerRight.Longitude,
                             StoreType.Area)

            eventArgs.CompleteItemsRequest(list)
        End If

        If minZoom = 9 Then
            ' request areas'
            Dim list As List(Of StoreLocation) =
                Me.GetStores(upperLeft.Latitude,
                             upperLeft.Longitude,
                             lowerRight.Latitude,
                             lowerRight.Longitude,
                             StoreType.Store)

            eventArgs.CompleteItemsRequest(list)
        End If
    End Sub

    Friend Function GetStores(upperLeftLat As Double,
                              upperLeftLong As Double,
                              lowerRightLat As Double,
                              lowerRightLong As Double,
                              storeType__1 As StoreType) As List(Of StoreLocation)
        Dim locations As New List(Of StoreLocation)()

        Dim latLonCondition As String =
            "[number(@Latitude) < "
            & upperLeftLat.ToString(CultureInfo.InvariantCulture) 
            & " and number(@Latitude) > " 
            & lowerRightLat.ToString(CultureInfo.InvariantCulture) 
            & " and number(@Longitude) > " 
            & upperLeftLong.ToString(CultureInfo.InvariantCulture) 
            & " and number(@Longitude) < " 
            & lowerRightLong.ToString(CultureInfo.InvariantCulture) 
            & "]"

        Select Case storeType__1
            Case StoreType.Area
                If True Then
                    Dim nodeList As XmlNodeList = document.SelectNodes(
                        "/StoresLocation/Area" & latLonCondition)
                    For Each node As XmlNode In nodeList
                        Dim element As XmlElement = DirectCast(node, XmlElement)

                        locations.Add(New StoreLocation(
                                      Convert.ToDouble(
                                          element.GetAttribute("Latitude"),
                                          CultureInfo.InvariantCulture),
                                      Convert.ToDouble(
                                          element.GetAttribute("Longitude"),
                                          CultureInfo.InvariantCulture),
                                      element.GetAttribute("Name"),
                                      StoreType.Area))
                    Next
                End If
                Exit Select

            Case StoreType.Store
                If True Then
                    Dim nodeList As XmlNodeList = document.SelectNodes(
                        "/StoresLocation/Area/" & latLonCondition)
                    For Each node As XmlNode In nodeList
                        Dim element As XmlElement = DirectCast(node, XmlElement)

                        locations.Add(New StoreLocation(
                                      Convert.ToDouble(
                                          element.GetAttribute("Latitude"),
                                          CultureInfo.InvariantCulture),
                                      Convert.ToDouble(
                                          element.GetAttribute("Longitude"),
                                          CultureInfo.InvariantCulture),
                                      element.GetAttribute("Name"),
                                      If(element.LocalName = "Market", StoreType.Market, StoreType.Store)))
                    Next
                End If
                Exit Select
        End Select

        Return locations
    End Function
End Class

Here is a sample of the VisualizationLayer declaration with UI Virtualization enabled:

<UserControl x:Class="TestMapFeatures.Views.VisualizationLayer.Virtualization.ItemsVirtualization"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"&#13;     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&#13;     xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"&#13;     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:TestMapFeatures.Views.VisualizationLayer.Virtualization"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     mc:Ignorable="d" 
     d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <DataTemplate x:Key="AreaTemplate">
            <Ellipse telerik:MapLayer.Location="{Binding Location}"
             Width="20" Height="20"
             VerticalAlignment="Stretch"
             HorizontalAlignment="Center"
             Fill="Red" />
        </DataTemplate>

        <DataTemplate x:Key="MarketTemplate">
            <Rectangle telerik:MapLayer.Location="{Binding Location}"
               Width="20" Height="20"
               VerticalAlignment="Stretch"
               HorizontalAlignment="Center"
               Stroke="Orange"
               StrokeThickness="8"
               Fill="#01000000" />
        </DataTemplate>

        <DataTemplate x:Key="StoreTemplate">
            <Ellipse telerik:MapLayer.Location="{Binding Location}"
             Width="20" Height="20"
             VerticalAlignment="Stretch"
             HorizontalAlignment="Center"
             Stroke="Green"
             StrokeThickness="3"
             Fill="#01000000" />
        </DataTemplate>

        <local:StoreTemplateSelector x:Key="StoreTemplateSelector"
                             AreaTemplate="{StaticResource AreaTemplate}"
                             MarketTemplate="{StaticResource MarketTemplate}"
                             StoreTemplate="{StaticResource StoreTemplate}"/>

        <local:MyVirtualizationSource x:Key="MyVirtualizationSource" />
    </UserControl.Resources>
    <Grid>
        <telerik:RadMap x:Name="radMap"
                ZoomLevel="4"
                Center="37.684297,-99.06924">
            <telerik:RadMap.Provider>
                <telerik:OpenStreetMapProvider />
            </telerik:RadMap.Provider>
            <telerik:VisualizationLayer x:Name="visualizationLayer"
                                ItemTemplateSelector="{StaticResource StoreTemplateSelector}"
                                VirtualizationSource="{StaticResource MyVirtualizationSource}">
                <telerik:VisualizationLayer.ZoomLevelGridList>
                    <telerik:ZoomLevelGrid MinZoom="3" />
                    <telerik:ZoomLevelGrid MinZoom="9" />
                </telerik:VisualizationLayer.ZoomLevelGridList>
            </telerik:VisualizationLayer>
        </telerik:RadMap>

    </Grid>
</UserControl>

When there are no ZoomLevelGrids that satisfy the current zoom level, no request will be made. Also, when the changed value of the zoom level or the region stays in the range of a ZoomLevelGrid or one of its cells, no request is made.

In the above sample, for example, when the ZoomLevel is 1 or 2, no requests will be made, because there is no grid which represents this range. When the value is bigger than 9, requests will be made only when the grid cell changes, because the grid is defined for the range from 9 to the maximum zoom level.

In the snapshots below you can see how the above example will be displayed with different ZoomLevel settings

The result at ZoomLevel 3:
radmap-visualization-layer-virtualization

The result at ZoomLevel 9:
radmap-visualization-layer-virtualization-zoomed

See Also

Was this article helpful? Yes No

Give article feedback

Tell us how we can improve this article

Dummy