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

Using RadMap with custom WebRequest

5 Answers 141 Views
Map
This is a migrated thread and some comments may be shown as answers.
Markus
Top achievements
Rank 1
Markus asked on 09 Aug 2010, 08:36 PM
Hello all,

I am currently evaluating your RadMap-Control for my company.

Since we have a scenario where the map provider and the RadMap-Control are on the same machine we would like to optimize the tile-transfer.
Therefore I derived from WebRequest, WebResponse and IWebRequestCreate and registered my custom IWebRequestCreate-implementation for my URI-scheme. So I can return a specific URI (matching my registered IWebRequestCreate) in the GetTile-Method of my MapProvider. Then my custom WebRequest-Implementation is used, which can use my local map provider.
So far so good - but unfortunately there is a bug in the WebClient-Class that is used by the RadMap-Control which prevents the use of a custom WebRequest. The Method "ReadDataAsyncCallback" of WebClient which is used by the RadMap-Control casts the IAsyncResult-Parameter to a LazyAsyncResult which is an internal class of the System.Net-Namespace. Therefore I cannot provide an IAsyncResult-Implementation in my custom WebRequest that derives from LazyAsyncResult as you can see.

I am wondering if it is possible to fix this issue. I would have two solutions in mind:

   1. Instead of using the WebClient-Class in the RadMap-Control just use the WebRequest-Class
   2. Make it possible to write a MapProvider that returns a stream instead of an URI

best regards
   Markus

5 Answers, 1 is accepted

Sort by
0
Andrey
Telerik team
answered on 10 Aug 2010, 04:15 PM
Hi Markus,

It seems this could be achieved in an easier way. Since your tile images are located on the same PC you can simply return file system URI from the GetTile method. Please, find below implementation of the file system custom map provider which can work with OpenStreet tiles stored in the file system on the local PC:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Telerik.Windows.Controls.Map;
  
namespace Telerik.RadMap.WPF
{
    public class FileSystemProvider : MapProviderBase
    {
        private const int TILE_SIZE = 256;
        private bool initialized;
        private string tileLocation;
  
        /// <summary>
        /// Initializes a new instance of the FileSystemProvider class.
        /// </summary>
        /// <param name="mode">Map mode.</param>
        /// <param name="labelVisible">Is labels visible.</param>
        public FileSystemProvider(string tileLocation)
            : base(MapMode.Road, true)
        {
            this.tileLocation = tileLocation;
        }
  
        /// <summary>
        /// Initializes a new instance of the FileSystemProvider class.
        /// </summary>
        public FileSystemProvider()
            : this(null)
        {
        }
  
        /// <summary>
        /// Gets or sets location of the map tiles.
        /// </summary>
        public string TileLocation
        {
            get
            {
                return this.tileLocation;
            }
  
            set
            {
                this.tileLocation = value;
            }
        }
  
        /// <summary>
        /// Gets the IsInitialized property.
        /// Indicates that the provider is initialized.
        /// </summary>
        public override bool IsInitialized
        {
            get
            {
                return this.initialized;
            }
        }
  
        /// <summary>
        /// Gets value which indicates whether labels are supported by the map provider.
        /// </summary>
        public override bool IsLabelSupported
        {
            get
            {
                return false;
            }
        }
  
        /// <summary>
        /// Returns the SpatialReference for the map provider.
        /// </summary>
        public override ISpatialReference SpatialReference
        {
            get
            {
                return new MercatorProjection();
            }
        }
  
        /// <summary>
        /// Gets list of the supported map modes.
        /// </summary>
        /// <returns>List of the supported map modes.</returns>
        public override IEnumerable<MapMode> SupportedModes
        {
            get
            {
                yield return MapMode.Road;
            }
        }
  
        /// <summary>
        /// Gets the image URI.
        /// </summary>
        /// <param name="tileLevel">Tile level.</param>
        /// <param name="tilePositionX">Tile X.</param>
        /// <param name="tilePositionY">Tile Y.</param>
        /// <returns>URI of image.</returns>
        public override Uri GetTile(int tileLevel, int tilePositionX, int tilePositionY)
        {
            int zoomLevel = ConvertTileToZoomLevel(tileLevel);
  
            string tileFileName = string.Format("os_{0}_{1}_{2}.png", tilePositionX, tilePositionY, zoomLevel);
            tileFileName = Path.Combine(this.TileLocation, tileFileName);
  
            if (File.Exists(tileFileName))
            {
                return new Uri(tileFileName);
            }
            else
            {
                return null;
            }
        }
  
        /// <summary>
        /// Initialize provider.
        /// </summary>
        public override void Initialize()
        {
            this.initialized = base.IsInitialized;
        }
  
        /// <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 override bool IsModeSupported(MapMode mode)
        {
            return (mode == MapMode.Road);
        }
  
        /// <summary>
        /// MapModeChanged handler.
        /// </summary>
        protected override void OnMapModeChanged(MapMode oldMode, MapMode newMode)
        {
            if (!this.IsSuspended)
            {
                this.Initialize();
            }
        }
    }
}


Sincerely yours,
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
Markus
Top achievements
Rank 1
answered on 11 Aug 2010, 01:18 PM
Hi Andrey,

thanks for the fast reply and the code example.
This approach was one of our first ideas, as well. But unfortunately it is not suitable for us since we'll actually be rendering on the fly and we want to save us the cost of writing the image to the filesystem and then read it again.

Best regards,
   Markus
0
Andrey
Telerik team
answered on 12 Aug 2010, 03:25 PM
Hello Markus,

For a case with rendering tiles on the fly it is possible to return a stream from the custom provider.
You can override the GetCachedTile method instead of overriding the GetTile.
The sample method is below. The CreateRenderedOnTheFlyTile is the method that should contain the tile rendering logic.
public override Stream GetCachedTile(int tileLevel, int tilePositionX, int tilePositionY)
{
    Stream stream = base.GetCachedTile(tileLevel, tilePositionX, tilePositionY);
    if (stream == null)
    {
        int zoomLevel = ConvertTileToZoomLevel(tileLevel);
  
        stream = this.CreateRenderedOnTheFlyTile(zoomLevel, tilePositionX, tilePositionY);
    }
  
    return stream;
}

All the best,
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
Markus
Top achievements
Rank 1
answered on 13 Aug 2010, 11:58 AM
Hi Andrey,

thanks for the answer. That was exactly what I was looking for.
There is one more problem. We want to develop a WPF- and a Silverlight-Application. Both should use the same sourcecode. But as far as I've seen the MapProviderBase has this method only in the WPF version. Am I right there?

Best regards
   Markus
0
Andrey
Telerik team
answered on 13 Aug 2010, 03:30 PM
Hi Markus,

Yes, you are right. There are few differences between WPF and Silverlight versions of the RadMap control. The tiles cache is one of them. All these differences are based on the nature of the base class which is used to show map tiles. In the Silverlight version we use standard MultiScaleImage provided by Microsoft as part of the Silverlight assemblies. In the WPF this class does not exist in the standard assemblies, so we use our own implementation. As you can imagine, we have total control on behavior of our own class in WPF, and we have very low control on behavior of the Microsoft one in Silverlight. Silverlight has also some platform limitations. So some interesting approaches (tiles caching, load tiles from file system and so on) can be done using WPF version and can’t be done in the Silverlight.

So if you need to have same code works with WPF and Silverlight, then you should use standard approach to create custom map provider and return tile URIs from GetTile method.

Sincerely yours,
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
Markus
Top achievements
Rank 1
Answers by
Andrey
Telerik team
Markus
Top achievements
Rank 1
Share this question
or