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
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
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: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.
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 |
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.
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
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
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.