This project is read-only.

WMS Layer functionality in your application

Aug 21, 2014 at 3:04 PM
Edited Aug 21, 2014 at 3:09 PM
I managed to build a custom WMS provider that allows me to add an WMS layer to the legend and map. I took me a few days, but basically it not that difficult once you figured it out. I started by changing the WmsServiceProvider which derives from BrutileServiceProvider. What you should do, is create a new namespace and add a copy of the WmsServiceProvider to it , to leave the WebMap Plugin intact. I changed the constructor a bit because it doesn't have a plugin purpose and we don't need the configure bit. I could have used the BrutileServiceProvider directly but that would leave me with less configurable options. The provider retrieves that actual bitmap from the server using a TileSource. Please note that the code below are snippets and NOT full code, just to give you a general idea.
public WmsServiceProvider(string name, WmsInfo _WmsInfo) 
            : base(name, null, new MemoryCache<byte[]>())
        {
            _data = _WmsInfo;
            if (_data != null)
            {
                TileSource = WmsTileSource.Create(_data);
                TileCache = new MemoryCache<byte[]>();
            }
        }
The next thing I did was making a few changes to the constructor of WmsTileSource to allow me to add my layername directly, nothing spectacular. You can leave it as it is. Then I created TileManagerWMS.cs , which is a direct copy of TileManager.cs. I needed this, because I don't want to use the same TileManager that the map uses to stitch its maps together. I altered the constructor to accept my WmsServiceProvider directly without having to cast it.
private readonly WmsServiceProvider _serviceProvider;
public TileManagerWMS(WmsServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
I duplicated UpdateStichedMap from the map section and called it UpdateStichedWMSlayer. I converted it to suit my needs, but essentially it works the same, just using a different TileManager. Then I made a method that I call everytime the map's viewextend changes that creates a new background worker that operates in the same way as the map backgroundworker :
private _tileManager;
public void UpdateWMS()
        {
            WmsInfo _wmsInfo = _WmsInfo();

            if (_wmsInfo != null)
            {
                WmsServiceProvider provider = new WmsServiceProvider(layerName, _wmsInfo);
                _tileManager = new TileManagerWMS(provider);

                //Setup the background worker
                _bw = new BackgroundWorker { WorkerSupportsCancellation = true, WorkerReportsProgress = true };
                _bw.DoWork += BwDoWork;
                _bw.RunWorkerCompleted += BwRunWorkerCompleted;
                _bw.ProgressChanged += BwProgressChanged;

                RunOrCancelBW();
            }
        }

void BwDoWork(object sender, DoWorkEventArgs e)
        {
            var worker = sender as BackgroundWorker;

            //if (worker != null && _baseMapLayer != null) //Use this later to check if the layer to update exists
            if (worker != null)
            {
                if (worker.CancellationPending)
                {
                    e.Cancel = true;
                }
                else
                {
                    worker.ReportProgress(10);
                    UpdateStichedWMSlayer(e);
                }
            }
        }

private void RunOrCancelBW()
        {
            if (_bw.IsBusy != true)
                _bw.RunWorkerAsync(iml);
            else
                _bw.CancelAsync();
        }

 void UpdateStichedWMSlayer(DoWorkEventArgs e)
{
    //Here you can retrieve the images, and update your IMapLayer
    var tiles = _tileManager.GetTiles(geogEnv, rectangle, _bw);
    //Stitch them into a single image
    var stitchedBasemap = TileCalculator.StitchTiles(tiles.Bitmaps, _opacity);
    var tileImage = new InRamImageData(stitchedBasemap);
    
    //If you want to create a new layer, you could do something like :
    IMapImageLayer iml = uxMap.Layers.Add(tileImage) as IMapImageLayer;
    iml.Projection = uxMap.Projection;
    iml.LegendText = layerName;
}
The tricky bit is getting the WmsInfo. This is basically done by first retrieving the capabilities from the server and then extracting all the information you need from the capabilities layer.
if (String.IsNullOrWhiteSpace(serverUrl)) return null;

                if (serverUrl.IndexOf("Request=GetCapabilities", StringComparison.OrdinalIgnoreCase) < 0)
                {
                    serverUrl = serverUrl + "&Request=GetCapabilities";
                }
                if (serverUrl.IndexOf("SERVICE=WMS", StringComparison.OrdinalIgnoreCase) < 0)
                {
                    serverUrl = serverUrl + "&SERVICE=WMS";
                }

                try
                {
                    var myRequest = WebRequest.Create(serverUrl);
                    myRequest.Credentials = Network.GetUserCredentials(dtProps.Rows[0]);
                    using (var myResponse = myRequest.GetResponse())
                    using (var stream = myResponse.GetResponseStream())
                        _wmsCapabilities = new BruTile.Web.Wms.WmsCapabilities(stream);
                }
                catch (System.Exception ex)
                {
                    Log.Write(ex, false);
                    return null;
                }
That will get you the capabilities ( assuming it uses version 1.1.1.) , and from there you can extract data and hack WmsInfo together .

I hope this helps to get you started creating your own WMS layer functionality. If I have time, I'll trim my code and post it , so everyone can pick it up and dump it in their own application. You'll need to do a little bit of tinkering, but the code above should help you on your way.

HTH Hactic
Aug 28, 2014 at 8:21 PM
Hi,
nice sample,
I've different problem but related to WMS tiles - is any way to adjust D.S to use only zoom levels that doesn't need to scale tiles during painting in drawing buffer ? In other words, to have only 12 zoom levels, but to get map not blurred in return