This is a migrated thread and some comments may be shown as answers.

question to layer (custom provider)

6 Answers 145 Views
Map
This is a migrated thread and some comments may be shown as answers.
Ulrich Fiege
Top achievements
Rank 1
Ulrich Fiege asked on 19 Mar 2010, 02:24 PM
We have the following use-case:

We´ve build a custom map provider, that works. There is one problem:
An image with the labels of the map have to be overlayed of the hole
map-view and the label-image have to be requested and updated at every zooming- or panning-action.
Currently we load the label-image into an Image-control of an informationlayer.
The events "Zoomchanged" or "Centerchanged" are not useful to update the label-image, because
they won´t be triggered at the end of the action.
Are there any other events, like "Zooming-end", "Panning-end" or "Mapview-changed"?

We tried to use the tilelayer, too. But we can´t change the tilesize-property, because
it´s readonly. Is it possible to change the tilesize of a tilelayer?

Best regards

6 Answers, 1 is accepted

Sort by
0
Justin White
Top achievements
Rank 1
answered on 21 Mar 2010, 04:59 AM
I agree with Ulrich - we need a couple more events like he mentioned - ZoomEnd and PanEnd would

My specific case is a little different - I am updating UIElements that are positioned based and found that using the CenterChanged event doesn't quite work because it fires and then the map still moves slower and slower till it stops to not create a jarring stopping effect. Really nice to look at but again - means that the positions are slightly off because of when the event fires...

Maybe I missed something?

Excellent control by the way!

Justin
0
Andrey
Telerik team
answered on 24 Mar 2010, 01:05 PM

Hi,

If I quite get your points you are trying to create something like crosshair at the center of the map. I agree that using of the CenterChanged event isn't good enough for this purpose, because it may produce some strange visual effects when panning with mouse. It occurs because this event fires few times when you move mouse when left button is pressed.

We have crosshair control in our plans for 2010.Q2 release. We also will include some new events which can simplify handling the situations like this.

By the way, if you need something that is always centered over the map you can simply put a control with transparent background in the same container as RadMap and make it centered -- vertically and horizontally  (like it is done in the example below - green ring).

To Justing: I've checked how the FrameworkElements are positioned on the map using the CenterChanged event. They are placed right where they should be even after sprig animation. Please, make sure the elements are aligned correctly.You can find below a simple code which adds element to the information layer when map center is changed.

<UserControl x:Class="Telerik.Silverlight.RadMap.MainPage"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:map="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.DataVisualization"
             xmlns:layer="clr-namespace:Telerik.Windows.Controls.Map;assembly=Telerik.Windows.Controls.DataVisualization"
             Width="800"
             Height="600">
    <Grid x:Name="LayoutRoot">
        <Grid.Resources>
            <DataTemplate x:Key="RingTemplate">
                <Grid Width="14" Height="14">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="14" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>
                    <layer:MapLayer.HotSpot>
                        <layer:HotSpot X="0.5" Y="0.5" />
                    </layer:MapLayer.HotSpot>
                    <Path Fill="Yellow">
                        <Path.Data>
                            <GeometryGroup>
                                <EllipseGeometry Center="7,7" RadiusX="3" RadiusY="3" />
                                <EllipseGeometry Center="7,7" RadiusX="7" RadiusY="7" />
                            </GeometryGroup>
                        </Path.Data>
                    </Path>
                </Grid>
            </DataTemplate>
        </Grid.Resources>
  
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="80*" />
            <ColumnDefinition Width="20*" />
        </Grid.ColumnDefinitions>
  
        <map:RadMap x:Name="radMap" 
                    Center="37.7040701207995,-121.882780875908"
                    ZoomLevel="10"
                    CenterChanged="MapCenterChanged">
            <layer:InformationLayer Name="informationLayer" ItemTemplate="{StaticResource RingTemplate}">
            </layer:InformationLayer>
        </map:RadMap>
  
        <Grid>
            <Grid Width="14" Height="14" HorizontalAlignment="Center" VerticalAlignment="Center">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="14" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <layer:MapLayer.HotSpot>
                    <layer:HotSpot X="0.5" Y="0.5" />
                </layer:MapLayer.HotSpot>
                <Path Fill="Green">
                    <Path.Data>
                        <GeometryGroup>
                            <EllipseGeometry Center="7,7" RadiusX="3" RadiusY="3" />
                            <EllipseGeometry Center="7,7" RadiusX="7" RadiusY="7" />
                        </GeometryGroup>
                    </Path.Data>
                </Path>
            </Grid>
        </Grid>
    </Grid>
</UserControl>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Resources;
using System.Windows.Data;
using Telerik.Windows.Controls.Map;
  
namespace Telerik.Silverlight.RadMap
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
  
            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        }
  
        private void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            BingMapProvider provider = new BingMapProvider(MapMode.Aerial, true, "YOUR Bing Application ID");
            this.radMap.Provider = provider;
        }
  
        private void MapCenterChanged(object sender, EventArgs e)
        {
            this.informationLayer.Items.Clear();
            this.informationLayer.Items.Add(this.radMap.Center);
        }
    }
}

To Ulrich: I am afraid changing the tile size is not possible.

Regards,
Andrey Murzov
the Telerik team


Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
0
Ulrich Fiege
Top achievements
Rank 1
answered on 25 Mar 2010, 09:47 AM
Thank you for the example.

It works fine with build in providers like openstreetmapprovider.
But with our custom provider, the informationlayer-item is allways displayed in the left-top-corner.
Please have a look at the attached custom provider class code.
Could there probably a problem with "SpatialReference" is set to "MercatorProjection"?
This setting is nescessary for our mapserver.

 
Imports System  
Imports System.Windows  
Imports System.Windows.Browser  
Imports System.Windows.Controls  
Imports System.Windows.Media  
Imports System.Windows.Media.Imaging  
Imports System.Text.RegularExpressions  
Imports Telerik.Windows.Controls.Map  
Imports Telerik.Windows.Controls  
Imports System.Text  
 
 
Public Class XserverTileSource  
    Inherits MapProviderBase  
    Public Const TILE_SIZE As Integer = 256 
    Private _IsInitialized As Boolean  
    Private _mapparent As RadMap  
    Private baseUrl As String  
 
    'Constructor Called by XAML instanciation; Wait for MapMode to be set to initialize services  
    Public Sub New(ByVal baseUrl As String)  
        MyBase.New(MapMode.Road, True)  
        Me.baseUrl = baseUrl  
 
        MaxZoomLevel = 17 
        MinZoomLevel = 1 
 
        _IsInitialized = True 
        RaiseEvent InitializeCompleted(Me, Nothing)  
    End Sub  
 
    Public Overloads Overrides ReadOnly Property IsInitialized() As Boolean  
        Get  
            Return _IsInitialized  
        End Get  
    End Property  
 
    ' <summary> 
    ' Gets list of the supported map modes.  
    ' </summary> 
    ' <returns>List of the supported map modes.</returns> 
    Public Overloads Overrides ReadOnly Property SupportedModes() As IEnumerable(Of MapMode)  
        Get  
        End Get  
    End Property  
 
 
    ' <summary> 
    ' Gets value which indicates whether given mode is supported by map provider.  
    ' </summary> 
    ' <param name="mode">Map mode to check.</param> 
    ' <returns>true if given mode is supported. Otherwise - false.</returns> 
    Public Overloads Overrides Function IsModeSupported(ByVal mode As MapMode) As Boolean  
        Return True  
    End Function  
 
    ' <summary> 
    ' Gets value which indicates whether labels are supported by the map provider.  
    ' </summary> 
    Public Overloads Overrides ReadOnly Property IsLabelSupported() As Boolean  
        Get  
            Return False  
        End Get  
    End Property  
 
    ' <summary> 
    ' MapModeChanged handler.  
    ' </summary> 
    Protected Overloads Overrides Sub OnMapModeChanged(ByVal oldMode As MapMode, ByVal newMode As MapMode)  
        If Not Me.IsSuspended Then  
            Me.Initialize()  
        End If  
    End Sub  
 
    ' <summary> 
    ' Initialize provider.  
    ' </summary> 
    Public Overloads Overrides Sub Initialize()  
 
        'Me._IsInitialized = MyBase.IsInitialized  
    End Sub  
 
    ' <summary> 
    ' Returns the SpatialReference for the map provider.  
    ' </summary> 
    Public Overloads Overrides ReadOnly Property SpatialReference() As ISpatialReference  
        Get  
            Return New MercatorProjection()  
        End Get  
    End Property  
 
    Public Overloads Overrides ReadOnly Property TileColor() As Color  
        Get  
            Return Color.FromArgb(255, 255, 254, 185)  
        End Get  
    End Property  
 
    Public Overloads Overrides Function GetTile(ByVal tileLevel As Integer, ByVal tilePositionX As Integer, ByVal tilePositionY As Integer) As Uri  
        If IsInitialized Then  
            Dim zoom As Integer = ConvertTileToZoomLevel(tileLevel)  
 
            Dim quadKey As String = TileXYToQuadKey(tilePositionX, tilePositionY, zoom)  
 
            ' Use the quadkey to determine a bounding box for the requested tile  
            Dim boundingBox As BBox = QuadKeyToBBox(quadKey)  
 
            ' Get the lat longs of the corners of the box  
            Dim lon As Double = XToLongitudeAtZoom(boundingBox.x * TILE_SIZE, 18)  
            Dim lat As Double = YToLatitudeAtZoom(boundingBox.y * TILE_SIZE, 18)  
 
            Dim lon2 As Double = XToLongitudeAtZoom((boundingBox.x + boundingBox.width) * TILE_SIZE, 18)  
 
            Dim lat2 As Double = YToLatitudeAtZoom((boundingBox.y - boundingBox.height) * TILE_SIZE, 18)  
 
            Dim x1 As Double, y1 As Double  
            Wgs_2_Mercator(lon, lat, x1, y1)  
            Dim x2 As Double, y2 As Double  
            Wgs_2_Mercator(lon2, lat2, x2, y2)  
 
            ' Url of Http-Handler, that gets back the tiles as png  
            Dim xmapUrl As StringString = String.Concat(baseUrl, "?left=", System.Convert.ToInt32(CInt(x1)), "&top=", System.Convert.ToInt32(CInt(y1)), "&right=", _  
            System.Convert.ToInt32(CInt(x2)), "&bottom=", System.Convert.ToInt32(CInt(y2)))  
            Dim iru As Uri = New Uri(xmapUrl)  
            Return iru  
        End If  
        Return Nothing  
    End Function  
 
    Public Shared Sub Wgs_2_Mercator(ByVal xArg As Double, ByVal yArg As Double, ByRef xOut As Double, ByRef yOut As Double)  
        Dim lambda As Double, phi As Double  
        lambda = xArg 
        phi = yArg 
        xOut = 6371000.0R * ((Math.PI / 180) * ((lambda) - 0.0R))  
        yOut = 6371000.0R * Math.Log(Math.Tan((Math.PI / 4) + (Math.PI / 180) * phi * 0.5))  
    End Sub  
 
    ''' <summary> 
    ''' Retrieves a quad key from a Virtual Earth tile specifier URL  
    ''' </summary> 
    ''' <param name="url"></param> 
    ''' <returns></returns>  
    Public Function GetQuadKey(ByVal url As String) As String  
        Dim regex = New Regex(".*tiles/(.+)[.].*")  
        Dim match As Match = regex.Match(url)  
 
        Return match.Groups(1).ToString()  
    End Function  
 
    ''' <summary> 
    ''' Returns the bounding BBox for a grid square represented by the given quad key  
    ''' </summary> 
    ''' <param name="quadKey"></param> 
    ''' <param name="x"></param> 
    ''' <param name="y"></param> 
    ''' <param name="zoomLevel"></param> 
    ''' <returns></returns>  
    Public Function QuadKeyToBBox(ByVal quadKey As String, ByVal x As Integer, ByVal y As Integer, ByVal zoomLevel As Integer) As BBox  
        Dim c As Char = quadKey(0)  
 
        Dim tileSize As Integer = 2 << (18 - zoomLevel - 1)  
 
        If cc = "0"c Then  
            yy = y - tileSize  
 
        ElseIf cc = "1"c Then  
            yy = y - tileSize  
            xx = x + tileSize  
 
        ElseIf cc = "3"c Then  
            xx = x + tileSize  
        End If  
 
        If quadKey.Length > 1 Then  
            Return QuadKeyToBBox(quadKey.Substring(1), x, y, zoomLevel + 1)  
        End If  
        Return New BBox(x, y, tileSize, tileSize)  
    End Function  
 
    Public Function QuadKeyToBBox(ByVal quadKey As String) As BBox  
        Const x As Integer = 0 
        Const y As Integer = 262144 
        Return QuadKeyToBBox(quadKey, x, y, 1)  
    End Function  
 
    ''' <summary> 
    ''' Converts radians to degrees  
    ''' </summary> 
    ''' <param name="d"></param> 
    ''' <returns></returns>  
    Public Function RadToDeg(ByVal d As Double) As Double  
        Return d / Math.PI * 180.0R  
    End Function  
 
    ''' <summary> 
    ''' Converts a grid row to Latitude  
    ''' </summary> 
    ''' <param name="y"></param> 
    ''' <param name="zoom"></param> 
    ''' <returns></returns>  
    Public Function YToLatitudeAtZoom(ByVal y As Integer, ByVal zoom As Integer) As Double  
        Dim arc As Double = EarthCircumference / ((1 <zoom) * TILE_SIZE)  
        Dim metersY As Double = HalfEarthCircumference - (y * arc)  
        Dim a As Double = Math.Exp(metersY * 2 / EarthRadius)  
        Dim result As Double = RadToDeg(Math.Asin((a - 1) / (a + 1)))  
        Return result  
    End Function  
 
    ''' <summary> 
    ''' Converts a grid column to Longitude  
    ''' </summary> 
    ''' <param name="x"></param> 
    ''' <param name="zoom"></param> 
    ''' <returns></returns>  
    Public Function XToLongitudeAtZoom(ByVal x As Integer, ByVal zoom As Integer) As Double  
        Dim arc As Double = EarthCircumference / ((1 <zoom) * TILE_SIZE)  
        Dim metersX As Double = (x * arc) - HalfEarthCircumference  
        Dim result As Double = RadToDeg(metersX / EarthRadius)  
        Return result  
    End Function  
 
    Private Shared Function TileXYToQuadKey(ByVal tileX As Integer, ByVal tileY As Integer, ByVal levelOfDetail As Integer) As String  
        Dim quadKey = New StringBuilder()  
        For i As Integer = levelOfDetail To 1 Step -1  
            Dim digit As Integer = 48 
            Dim mask As Integer = 1 << (i - 1)  
            If (tileX And mask) <> 0 Then  
                digit += 1  
            End If  
            If (tileY And mask) <> 0 Then  
                digit += 1  
                digit += 1  
            End If  
            quadKey.Append(Convert.ToChar(digit))  
        Next  
        Return quadKey.ToString()  
    End Function  
 
 
    Public Shared Shadows Event InitializeCompleted As EventHandler  
End Class  
 
Public Class BBox  
    Public x As Integer  
    Public y As Integer  
    Public width As Integer  
    Public height As Integer  
 
    Public Sub New(ByVal x As Integer, ByVal y As Integer, ByVal width As Integer, ByVal height As Integer)  
        Me.x = x  
        Me.y = y  
        Me.width = width  
        Me.height = height  
    End Sub  
End Class 
0
Andrey
Telerik team
answered on 25 Mar 2010, 05:57 PM

Hi Ulrich,

The information layer will not arrange its items until the map control is initialized.
The initialization is completed when the InitializeCompleted event of a map provider is raised.
The MyBase.IsInitialized property always returns True and raises this event, but I see that it is commented in your code.

' <summary>  
' Initialize provider.   
' </summary>  
Public Overloads Overrides Sub Initialize()   
    
    'Me._IsInitialized = MyBase.IsInitialized   
End Sub

I think you can uncomment it or remove the Initialize method override in order to complete the initialization.

Greetings,
Andrey Murzov
the Telerik team


Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
0
Ulrich Fiege
Top achievements
Rank 1
answered on 29 Mar 2010, 12:01 PM
Hi,

the cod|e "||Me._IsInitialized = MyBase.IsInitialized| " is commented, because it throws an exception:

Message: The object reference was not specified on an object instance.
Stacktrace: at Telerik.Windows.Controls.Map.MapCommandBar.SetupMapStyleCommands()
at Telerik.Windows.Controls.Map.MapCommandBar.OnMapInitialize(RadMap oldMapControl, RadMap newMapControl)
atTelerik.Windows.Controls.Map.MapBaseControl.MapControlChanged(DependencyObject source, DependencyPropertyChangedEventArgs eventArgs)
at Telerik.Windows.PropertyMetadata.<>c__DisplayClass1.<Create>b__0(DependencyObject d, DependencyPropertyChangedEventArgs e)
atSystem.Windows.DependencyObject.RaisePropertyChangeNotifications(DependencyProperty dp, Object newValue, Object oldValue)
at System.Windows.DependencyObject.SetValueInternal(DependencyProperty dp, Object value, Boolean allowReadOnlySet, Boolean isSetByStyle, Boolean isSetByBuiltInStyle, PropertyInvalidationReason reason)
at System.Windows.DependencyObject.SetValueInternal(DependencyProperty dp, Object value)
at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
at Telerik.Windows.Controls.Map.MapBaseControl.set_MapControl(RadMap value)
at Telerik.Windows.Controls.RadMap.Initialize()
at Telerik.Windows.Controls.Map.TileLayer.ProviderInitializeCompleted(Object sender, EventArgs e)
at Telerik.Windows.Controls.Map.MapProviderBase.get_IsInitialized()
at TestRadmap.XserverTileSource.Initialize()

We can´t remove the initialize method, because it´s "mustoverride".

Greetings
0
Andrey
Telerik team
answered on 30 Mar 2010, 08:18 AM
Hello Ulrich,

The exception occurs because the SupportedModes property returns a Nothing value. Please change it to return empty IEnumerable value like to the following code:
Public Overloads Overrides ReadOnly Property SupportedModes() As IEnumerable(Of MapMode)
    Get
        Dim Modes As List(Of MapMode) = New List(Of MapMode)
        Return Modes
    End Get
End Property
Then un-comment the "Me._IsInitialized = MyBase.IsInitialized" string.

Greetings,
Andrey Murzov
the Telerik team

Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
Tags
Map
Asked by
Ulrich Fiege
Top achievements
Rank 1
Answers by
Justin White
Top achievements
Rank 1
Andrey
Telerik team
Ulrich Fiege
Top achievements
Rank 1
Share this question
or