ThinkGeo.com    |     Documentation    |     Premium Support

Notification of Imagery from Remote WMS Server

MapSuite Team,

I’ve been using WmsServer for four years now and it has functioned very well, thank you.

WmsServer was used to develop a proxy server. In other words, the ASP.NET application that was developed is used as a concentrator that takes in WMS image requests from client workstations and then passes them to a remote WMS Server located on the Internet.

With the WmsServer api’s SendingWebRequest and SentWebRequest logging has been developed for tracing what has been sent to the Remote WMS Server.

Are there WmsServer api’s that would allow the capture of what has been received from the Remote WMS Server to be used logging purposes? I’m not so much interested in the image itself, but rather the URL that would have been received.

Also, with the Sending/Set Web Requests is there a mechanism to capture the final version of the URL sent to the Remote WMS Server?

Regards,
Dennis Berry
OriStar Mapping, Inc.

Hey @Dennis,

So, if I understand correctly, you have two MapSuite WmsServers: one is a Proxy to gather all client requests, and the other is a Remote that actually does all the work of generating the image. You are already logging the WebRequest on the Proxy (via SendingWebRequest and SentWebRequest), but now you would also like to log the WebResponse (not necessarily the body/image of the response) on the Proxy.

How you can do that is all going to depend on how the Proxy is communicating to the Remote.

Given that you mention SendingWebRequest and SentWebRequest, I assume the communication line the Proxy uses is via the WmsRasterLayer in the GetMapCore of the Proxy’s custom WmsLayerPlugin. If that’s the case, then I think what you would have to do is create a custom WmsRasterLayer and WmsRasterSource that overrides the GetImageCore method (which is where we get the WebResponse). You could setup new events in this custom WmsRasterSource that the Proxy can listen to.

I can send you the code for the WmsRasterSource once I know for sure that this is what you are using so that you are free to modify it with the new event.

Thanks,
Kyle

hi Kyle,

Yes, your description is exactly how our Proxy functions.

Would appreciate your code sample.

Regards,
Dennis

hi Kyle,

Did you have a chance to find the sample code?

Thanks,
Dennis

Hey @Dennis,

Sorry for the delayed response. I was on another project momentarily.

I started digging out the WmsRasterLayer code from MapSuite v9, but I found that you should already be able to do this with the existing code the more I dug into it:

void Main()
{
    WmsRasterLayer wmsRasterLayer = new WmsRasterLayer();
    wmsRasterLayer.SentWebRequest += WmsRasterLayerOnSentWebRequest;
}
private void WmsRasterLayerOnSentWebRequest(object sender, SentWebRequestEventArgs e)
{
    Debug.WriteLine(e.Response.ResponseUri);
}

You see, the web response is embedded into the SentWebRequestEventArgs already. I think this is all you need to do in order to log the response from the Remote Server, correct?

Thanks,
Kyle

hi Kyle,

My application has always subscribed to event SentWebRequest. I had always believed that this event was fired when a request was sent to the remote WMS Server, not when the response was received from the remote WMS Server. One thing that always confused me is the fact that there are items in SentWebRequestEventArgs related to response.

As an experiment, I disabled the Internet connection and moved the extent of the map. The SentWebRequest event was still fired. This proves to me that this event is not fired upon receipt of response, but only on sending the request.

My logging shows the ResponseUri string exactly the same as the RequestUri string. I would think they would be different.

SentWebRequestEventArgs has the property IsFromCache. It makes no sense that this property is here, because when the tiles are retrieved from cache the SentWebRequest event is not fired. So, there’s no way to make use of this property, that I can figure out anyway.

Would you please consider my comments and whether I’m correct or not?

Seems to me there should be an event named ReceivedWebResponse.

Regards,
Dennis

Hey @Dennis,

Yeah, I was expecting a ReceivedWebResponse as well, but it’s more of a naming convention issue. I don’t mind sharing some of the source code since this is an older version of our software. Here is the code from the class directly:

As you can see, we are using the standard System.Net.WebRequest to send the request and then getting back a standard System.Net.WebResponse object back when we call the GetResponse method on the request method. And then we take that response and send it off to the SentWebRequest event handler.

And here is where we are getting the Image from the WMS:

This is where we create the WebRequest object and then send it off to the SendWebRequest method from the previous screenshot. As you can see, it’s a pretty standard implementation. What tripped me up in my initial report was, just like you, the name of the event itself, and also that call to the SendWebRequest method returned the response, but not raising the event within the GetImageCore method. Especially when it appears we used to, given the commented out lines on 736 and 737.

The SentWebRequestEventArgs just has the WebResponse on it, so the IsFromCache property is just a part of that standard .NET class. I think that only gets used when you specifically setup a cache on the request.

I would think that the ResponseUri and RequestUri would be the same, unless there is some redirecting that you have setup. Both should be pointing to the remote server, correct?

When you disabled the internet connection, what did that SentWebRequest's WebResponse look like?

Thanks,
Kyle

hi Kyle,

Thanks for the information as it’s helpful.

Just so you know I’m using ThinkGeo.MapSuite.WmsServer.dll V11.0.0-beta007.

My application has tile caching enabled. How does one determine if the image is from the Internet or from cache?

ResponseUri is equal to RequestUri.

When my Internet connection was disabled the SentWebRequest is fired and the ResponseUri is equal to RequestUri.

Looking at the code you provided I noticed that there is no error checking of any kind.

What if errors occur, are they reported in any way?

What if a request is sent, but there is no response?

Thanks,
Dennis

Hey @Dennis,

For Tile Caching, you shouldn’t get a request/response event raised, as that is handled higher up the chain. I’m pretty sure that the RasterSource wouldn’t be involved at all in that case. How do you have the Tile Cache setup? Can you show me a code snippet? There should be events on the TileCache class itself that will be raised if the resulting image came from the cache.

The ResponseUri and RequestUri should be the same. Per the Microsoft docs on HttpWebResponse, ReponseUri “Gets the URI of the Internet resource that responded to the request.” If you were expecting to see the proxy server’s URI instead, you may want to inspect the Response’s Headers to see if there is an origin URI attached to it or something like that, but I think you would have to set that up manually on the Remote server’s side to enable that.

If there was an error that occurred, it would be handled in that try..catch block on line 758 in the last image I provided. It would then be bubbled up as a DrawingException in the DrawingException event. Just make sure that DrawingExceptionMode is set to ThrowException:

wmsRasterLayer.DrawingExceptionMode = DrawingExceptionMode.ThrowException;
wmsRasterLayer.DrawingException += (_, args) => Debug.WriteLine(args.Exception);

But that doesn’t mean that request errors will throw exceptions. Let’s say you weren’t authenticated with your remote server, or the remote server went down. The response’s StatusCode would be something like “403 Not Authorized” or “500 Server Not Found.” So that’s something else to watch out for when inspecting/recording the response object.

Thanks,
Kyle

hi Kyle,

I’ve implemented the events for TileCache, but they are not being fired, even though I can see that tiles are being retrieved from cache because they are rendered faster than if going thru the Internet. Below is the basic code.

Is there some other property that must be set somewhere?

I already had the Drawing & Drawn Exception events in my application, and they are not being fired.

Thanks,
Dennis

// instantiate TileCache Object
TheFileBitmapTileCache = new FileBitmapTileCache();

TheFileBitmapTileCache.CacheDirectory = the directory;
TheFileBitmapTileCache.CacheId        = the CacheId;
TheFileBitmapTileCache.ExpirationTime = 12 hours;
TheFileBitmapTileCache.TileAccessMode = ReadAddDelete;
TheFileBitmapTileCache.TileMatrix     = TheTileMatrix with Scale=147647947;
TheFileBitmapTileCache.ImageFormat    = JPeg;

TheFileBitmapTileCache.GettingCacheImage += TheFileBitmapTileCache_GettingCacheImage;
TheFileBitmapTileCache.GottenCacheImage += TheFileBitmapTileCache_GottenCacheImage;

TheMapConfiguration.TileCache         = TheFileBitmapTileCache;

Hey @Dennis,

Hm, not sure why you are having issues with the events not firing. I didn’t have issues getting it to work on my side:

LayerOverlay staticOverlay = new LayerOverlay();
staticOverlay.Layers.Add("InMemoryFeatureLayer", inMemoryLayer);
winformsMap1.Overlays.Add(staticOverlay);

var tileCache = new FileBitmapTileCache(@"c:/temp", "inmem", TileImageFormat.Jpeg, new MapSuiteTileMatrix(147647947));
tileCache.TileAccessMode = TileAccessMode.ReadAddDelete;
tileCache.ExpirationTime = TimeSpan.FromHours(12);
tileCache.GettingCacheImage += TileCacheOnGettingCacheImage;
tileCache.GottenCacheImage += TileCacheOnGottenCacheImage;

staticOverlay.TileCache = tileCache;

If the code doesn’t work for you, you might need to update your Nuget packages to the latest versions available.

Thanks,
Kyle

hi Kyle,

Your sample code looks to be for a WinForm UI Application, where mine is WmsServer.

There might be difference between the two.

Dennis

Hey @Dennis,

Yep, you’re right. WmsServer wasn’t firing those events because of the way it retrieves tiles from the cache. I’ve updated the API that get the tiles from the cache to fire those events and should be available in the ThinkGeo.MapSuite 11 beta package by tomorrow. Note that the “Getting” event should fire for every tile requested, but the “Gotten” event will fire only if a tile was found in the cache, so you may not need the first event at all.

Thanks,
Kyle

hi Kyle,

I installed the latest DLL’s and now the events fire. There are some issues with the events as listed below.

It would be nice if they could be resolved.

Thanks,
Dennis

Issue 1
These properties do not contain data when the events fire:
TheEventArgs.BitmapTile.Bitmap.PathName;
TheEventArgs.BitmapTile.Bitmap.FileName;
TheEventArgs.BitmapTile.Bitmap.PathFilename;

Issue 2
The reason I’m logging the TileCache events is so that I can corralate a request with either a SentWebRequest or TileCache events. This is for debugging purposes.

In order to do this I need the original requesting URL. It would be awfully nice to have the original in the EventArgs for TileCache events. Would you add this information?

Issue 3
My logging shows that GottenCacheImage is fired before GettingCacheImage.

Issue 4
In my code I was setting the events in the following fashion and they were not being fired:
TheFileBitmapTileCache.GettingCacheImage += TheFileBitmapTileCache_GettingCacheImage;
TheFileBitmapTileCache.GottenCacheImage += TheFileBitmapTileCache_GottenCacheImage;

            MapConfiguration.TileCache = TheFileBitmapTileCache;

Once I changed it to the following the events did fire:
MapConfiguration.TileCache = oWmsLayerActive.FileBitmapTileCache;

            MapConfiguration.TileCache.GettingCacheImage += TheFileBitmapTileCache_GettingCacheImage;
            MapConfiguration.TileCache.GottenCacheImage  += TheFileBitmapTileCache_GottenCacheImage;

Kyle,

Today I’ve noticed that even though tiles are not retrieved from cache the GottenCacheImage event is fired.

My logs show a series of GottenCacheImage/GettingCacheImage events followed by SendingWebResponse and SentWebResponse.

Dennis

Hey @Dennis,

Issue 1
These properties do not contain data when the events fire:
TheEventArgs.BitmapTile.Bitmap.PathName;
TheEventArgs.BitmapTile.Bitmap.FileName;
TheEventArgs.BitmapTile.Bitmap.PathFilename;

In certain instances, Bitmap may or may not have a filepath because it may have been stored in memory or elsewhere. Instead, you can get the filepath by casting the BitmapTile as a FileBitmapTile:

private void TileCacheOnGottenCacheImage(object sender, GottenCacheImageBitmapTileCacheEventArgs e)
{
    Trace.WriteLine($"[Tile Cache - Retrieved] {((FileBitmapTile)e.BitmapTile).PathFilename}");
}

Issue 2
The reason I’m logging the TileCache events is so that I can corralate a request with either a SentWebRequest or TileCache events. This is for debugging purposes.
In order to do this I need the original requesting URL. It would be awfully nice to have the original in the EventArgs for TileCache events. Would you add this information?

The TileCache is oblivious to whatever the layers are requesting. It only knows the requested tile’s row, column, scale, and extent and tries to find an image that matches that description in the cache. I’ve added these properties to the EventArgs that might help you correlate the WebRequests in today’s nuget package update. Now my event handlers look like:

private void TileCacheOnGottenCacheImage(object sender, GottenCacheImageBitmapTileCacheEventArgs e)
{
    Trace.WriteLine($"[Tile Cache - Retrieved] {((FileBitmapTile)e.BitmapTile).PathFilename}");
    Trace.WriteLine($"[Tile Cache - Retrieved] {_tileCache.CacheDirectory}/{_tileCache.CacheId}/{e.Scale}/{e.Row}/{e.Column}.png");
    Trace.WriteLine($"[Tile Cache - Retrieved] {e.Extent}");
}

private void TileCacheOnGettingCacheImage(object sender, GettingCacheImageBitmapTileCacheEventArgs e)
{
    Trace.WriteLine($"[Tile Cache - Get] {_tileCache.CacheDirectory}/{_tileCache.CacheId}/{e.Scale}/{e.Row}/{e.Column}.png");
    Trace.WriteLine($"[Tile Cache - Get] {e.Extent}");
}

Example Output:

[Tile Cache - Get] c:/temp/inmem/73957193.82/203/204.png
[Tile Cache - Get] 21910369.4956324,-13155182.3988256,26919734.0519836,-18164546.9551768
[Tile Cache - Retrieved] c:/temp\inmem\73957193.82\203\204.png
[Tile Cache - Retrieved] c:/temp/inmem/73957193.82/203/204.png
[Tile Cache - Retrieved] 21910369.4956324,-13155182.3988256,26919734.0519836,-18164546.9551768
[Tile Cache - Get] c:/temp/inmem/73957193.82/199/196.png
[Tile Cache - Get] -18164546.9551767,6882275.82657886,-13155182.3988256,1872911.27022772
[Tile Cache - Retrieved] c:/temp\inmem\73957193.82\199\196.png
[Tile Cache - Retrieved] c:/temp/inmem/73957193.82/199/196.png
[Tile Cache - Retrieved] -18164546.9551767,6882275.82657886,-13155182.3988256,1872911.27022772
[Tile Cache - Get] c:/temp/inmem/73957193.82/198/199.png
[Tile Cache - Get] -3136453.28612328,11891640.38293,1872911.27022786,6882275.8265789
[Tile Cache - Retrieved] c:/temp\inmem\73957193.82\198\199.png
[Tile Cache - Retrieved] c:/temp/inmem/73957193.82/198/199.png
[Tile Cache - Retrieved] -3136453.28612328,11891640.38293,1872911.27022786,6882275.8265789

Issue 3
My logging shows that GottenCacheImage is fired before GettingCacheImage.

This should be resolved in today’s Nuget package.

Issue 4
In my code I was setting the events in the following fashion and they were not being fired:
TheFileBitmapTileCache.GettingCacheImage += TheFileBitmapTileCache_GettingCacheImage;
TheFileBitmapTileCache.GottenCacheImage += TheFileBitmapTileCache_GottenCacheImage;

I’m not sure how or why that would happen. Those events are fairly standard. In my test cases, I’ve tried setting the events both directly on the TileCache and on MapConfiguration’s TileCache and both ways had their events fire. Either that event handler got removed somewhere down the line or MapConfiguration’s TileCache got replaced by a different TileCache.

Today I’ve noticed that even though tiles are not retrieved from cache the GottenCacheImage event is fired.
My logs show a series of GottenCacheImage/GettingCacheImage events followed by SendingWebResponse and SentWebResponse.

This usually means one of two things: either the tile didn’t perfectly line up to what the layer was expecting, or more likely, the tile was expired after 12 hours.

Thanks,
Kyle