ThinkGeo.com    |     Documentation    |     Premium Support

WMS Server Cache Query and architecture advice

Hi,


Following the advice given here


gis.thinkgeo.com/Support/Dis...fault.aspx


Also noting the advice at the end here about the latest build


gis.thinkgeo.com/Support/Dis...fault.aspx


I need to confirm if this refers to the WebEdition.Map version



? ThinkGeo.MapSuite.WebEdition.Map.Version


"MapSuiteCore:4.0.90.0;WebEdition:4.0.90.0"


I'm unable to log into the customer section at any rate unfortunately. [We are still at the evaluation stage of the product]



We have some evaluation done on the Web Edition but performance reasons have forced us to continue the evaluation on the WMS product so we are moving some functionality across.


I'm trying to get an understanding of where best to locate the functionality


Can you confirm that in a configuration that uses the WMS would the WMS also serve the markers also or would it mainly be used to serve the static maps. [Static maps only was my understanding until I saw the SimulateVehicleTrackingWmsLayerPlugin.cs sample.]


If I draw shapes on a map and query the features should the functionality reside on  the code behind of our web app our should a plug in be created for such functionality. 


In a typical scenario are there usually a lot of plug ins required or just a few.


I had not really come across MEF until today so I'm just trying to get some concepts clear.


Rgds,


Liam



Hi, Liam


As for the “Server Side Cache Part 2” post and it refer to the Daily Build (4.0.101.0 or later) that means MapSuiteCore: 4.0.101.0; WebEdition: 4.0.101.0 or later version.
That sounds weird that you can’t log into the ThinkGeo Customer Portal. No matter whether you are at the evaluation stage or others we also provide users with the automatically released daily build through the customer portal.  If you still can’t log in please contact support through support@thinkgeo.com and you will get quick response.
As for the first link you posted, I think David has clarified the advantages for WMS Server product definitely.
WMS server mainly used to serve the static maps, and we have special logic for static maps that allows us to generate them more efficiently, also it support load balance and server cache that David had mentioned in the post.
Currently WMS Server doesn’t support markers if you want the Popups, Dragging and ContextMenu capabilities; I think WebEdition can implement it. Just as you said, performance is all about your objectives. We have done many optimizations for Markers system, such as adding Cluster strategy and show the dive of Popups dynamically in order to decrease the number of divs representing the content of popups.
In a typical scenario, for the best performance I suggest that you set all of the base layers into one plugin and not allow users to turn off or turn on its sub-layers. This will reduce the server load and user just need one layer or overlay to request these base map images on the client side also it decrease the size of DOM.
In addition we do support dynamic maps, just as the “SimulateVehicleTracking” sample you mentioned. Upon receiving one request from client-side on the WMS Server and it will call the plugin you requested and return the map image you want, so that you can return what you want and you just need to override the GetMapCore method. Please see it for full detail in the SimulateVehicleTrackingWmsLayerPlugin class.
For MEF, you can run WMS Server very well even though you know nothing about it. We have prepared everything you need.
If you still have any questions about it please let me know.
Thanks,
Khalil

Hi Khalil, 



Thanks for the advice. 



I will keep my static layers to a minimum and use the Web Edition to create the In Memory Marker Overlays and retain that functionality where I currently have it.  



This means as I understand it that the marker overlay will be served by our web app IIS instance as opposed to the WMS.  



Noted on the optimizations on the marker overlays. Its about fine tuning it on our end really. 



I have a rough sample of our maps running through WMS and need to add caching [client and server] to it so will investigate that today. 

Have you any advice on this. I not that in some cases you mentioned that the tiles are pre-rendered.  

I assume that there is a choice between caching on the first call or a tile or having them pre-cached at the server end. How do you typically implement that. [I plan to investigate this properly today anyway] 



I have mailed support about my login issue and its resolved thanks. I have got the latest development build.


--------------------------------------------------------------------------------


Update


I have set up the WMS server product on a separate machine and am serving our own base overlays from that location. I have noted a little improvement, however I guess that further gains can be achieved when we look at caching properly [server and client] and aslo when I further fine tune the code. 


As requested above, any advice on this would be helpful. Its late here and I did not get a chance to investigate it properly so I'll leave it until tomorrow.




Rgds, 

Liam



Hi, Liam


You can add caching to every plug-in between the client-side and server-side with ease in the WMS Server product.
Just as you have mentioned that there are two ways to generate caching, one is pre-generate and another is generate it on the fly upon receiving the requests from client.
Let’s take DisplayASimpleMapWmsLayerPlugin as an example; you can add the codes below to MapConfiguration object in the GetMapConfigurationCore method that is in charge of creating layers and specify the tile caching.
MapConfiguration mapConfiguration = new MapConfiguration();
          
FileBitmapTileCache tileCache = new FileBitmapTileCache(@"c:\temp", "DisplayASimpleMapWmsLayerPlugin");
mapConfiguration.TileCache = tileCache;
 
mapConfiguration.Layers.Add("WorldLayer", worldLayer);
 
On the Winforms desktop client-side for DisplayASimpleMapWmsLayerPlugin, and add caching to it in the same way:
TiledWmsOverlay tiledWmsOverlay = new TiledWmsOverlay(new Uri("localhost:62626/WmsHandler.axd"));
tiledWmsOverlay.TileCache = null;
tiledWmsOverlay.ActiveLayerNames.Add("Display A Simple Map");
tiledWmsOverlay.ActiveStyleNames.Add("DEFAULT");
 
FileBitmapTileCache tileCache = new FileBitmapTileCache(@"c:\temp", "DisplayASimpleMapWmsLayerPlugin");
tiledWmsOverlay.TileCache = tileCache;
 
FileBitmapTileCache object contains two important properties, and they are CacheDirectory and CachId respectively. CacheDirectory specifies the cache directory of the tile cache, and CachId specifies the cache id of the tile cache and it’s more like a sub folder under the CacheDirectory.
If you have pre-generated the entire tile images caching for one plug-in and you just need to specify the cache directory and the cache id for TiledWmsOverlay on the client-side, it won’t send requests to server and so it can reduce the load of WMS Servers.
If you still have any questions about it please let me know.
Thanks,
Khalil

 



Hi Khalil, 
  
 Thanks for that. So if the TiledWmsOverlay TileCache.TileCacheID matches the server CacheID then it will pull the images from that store.  
 It seems simple enough in that case. If the cache is not present then I suppose that it will retrieve the shape file and add it to the cache for later reuse. 
  
 We are using the Web Editions so we can then set up the client side caching also. 
  
 I guess the next step for me is to pre-render the shape files into the server cache for most [if not all] zoom levels.  
  
 I have seen some samples on the forum as follows . [Is this the recommended way or are there utilities for this,] 
  
 
            for (int i = 0; i < zoomLevels.Count; i++)
            {
                MapSuiteTileMatrix tileMatrix = new MapSuiteTileMatrix(zoomLevels[i].Scale, tileWidth, tileHeight, mapUnit);
                BitmapTileCache tileCache = new FileBitmapTileCache(txtCachePath.Text, string.Empty, TileImageFormat.Png, tileMatrix);
                RowColumnRange rowColumnRange = tileCache.TileMatrix.GetIntersectingRowColumnRange(cacheExtent);

                Bitmap bitmap = null;
                RectangleShape tileExtent = tileCache.TileMatrix.GetCell(0, 0).BoundingBox;

                //if (tileExtent.Width > cacheExtent.Width || tileExtent.Height > cacheExtent.Height)
                if (rowColumnRange.MaxColumnIndex == 0 && rowColumnRange.MaxRowIndex == 0)
                {
                    mapEngine.CurrentExtent = tileExtent;
                    bitmap = new Bitmap(tileWidth, tileHeight);
                }
                else
                {
                    int bitMapWidth = tileWidth * ((int)(rowColumnRange.MaxColumnIndex - rowColumnRange.MinColumnIndex) + 1);
                    int bitMapHeight = tileHeight * ((int)(rowColumnRange.MaxRowIndex - rowColumnRange.MinRowIndex) + 1);
                    mapEngine.CurrentExtent = cacheExtent;
                    // if the bitmapWidth is very big, you need to optimize the way generating tiles, otherwise the bitmap will be too big for the memory.
                    // For example, you can always generate a 1024 * 1024 bitmap and then split it into 4 512*512 tiles
                    bitmap = new Bitmap(bitMapWidth, bitMapHeight);
                }

                mapEngine.OpenAllLayers();
                mapEngine.DrawStaticLayers(bitmap, mapUnit);
                mapEngine.DrawDynamicLayers(bitmap, mapUnit);
                mapEngine.CloseAllLayers();

                tileCache.SaveTiles((Bitmap)bitmap.Clone(), tileCache.TileMatrix.BoundingBox);
                bitmap.Dispose();
            }
 
  
  
  
 Rgds, 
 Liam

Hi, Liam


Yes, you are correct. That’s the way we recommend. But the codes you posted have some problem. The bitMapWidth or bitMapHeight maybe exceed the critical value of Bitmap’s constructor, so “Parameter is not valid” exception will throw.
So you can generate one tile image whose dimension is 256*256 in general and avoid this exception. Please refer to sample codes for full detail. I have added some necessary comments to help you understand how it works.
If you have any additional questions, please let us know.
 
Thanks,
 
Khalil

001_GenerateTiling.zip (125 KB)

 Hi Khalil,


Thats great, thanks for your assistance.


Best Regards,


Liam



Liam, 
  
 You are welcome. 
  
 Any questions about it please let us know. 
  
 Thank, 
  
 Khalil

Hi Khalil,


Your example worked perfectly for a large shape file at world extent such as the countries map.


We have numerous shape files that are of higher detail and they appear from Zoom Level 8 to Zoom Level 20.


When I precompiled the cache for this. [Adding the CacheID also]. 


BitmapTileCache tileCache = new FileBitmapTileCache(cachePath, clienCacheID, TileImageFormat.Png, tileMatrix);


4 hours later PC was happily churning its way through the first out of 114 shape files Zoom Levels 8 to 19 and has [so far] created an impressive 140,000 files with zoom level 19 containing 100,000 files. [I'm resisting the temptation to open this folder in Windows Explorer in thumbnail view! :)]


Anyway, unless there is something inherently wrong I think that the caching strategy needs to be revised a little on my side.


Zoom levels 6-18 contain 41,000 files per tile * 114 tiles = 4,674,000 files


Zoom levels 6-17 contain 10,800 files per tile * 114 tiles = 1,231,200 files 


Are these typical numbers for precompiled files in a WMS server. I note the specs for the machines and how long it takes to generate the tiles here in Davids response here gis.thinkgeo.com/Support/Dis...aspx#19489 so it may not be far off but I'm just checking.


The area of interest that we require high performance and high detail on is approx a 100 mile square area. 


Would an amended strategy be to precompile up to level 17 and cache on demand the remaining zoom levels at the server. We would also use client caching. [My plan at the minute is to stick to one base overlay and not allow the user to switch between static overlays at all.].


Given time and resources we could go up to zoom level 18 but may run into deployment issues unless we can agree to precompile the maps on a customer site.


In the web app for zoom levels up to 17 we will point to the cache and for 17 to 20 I assume if it does not see the cached image it will generate it and then use it the next time on the server or do I need to set the TileAccessMode accordingly.


 


Implementation


------------------


The File Structure is as follows MapCache\ShapeFileName\9011.7155456543\10393\ etc as I have set the CacheID to the Shape file name when creating the Cache. [Each shape filename is unique]


On the Web client end I had assume that consuming the tiles would be as simple as creating a FileBitmapTileCache, setting the cache folder once and for each of the 114 ShapeFileFeatureLayer objects that I load I set the CacheId to the file name


mapConfiguration.TileCache.CacheId = shapeFileName   // this will ensure a differnt cacheID for each ShapeFileFeatureLaer


This I assume will point to the correct cache store.


Are the above assumptions correct or have I lost my way [Its late and likely that I may have. :)]


In the event that the cache is not there I assume that the shape file will be rendered and that this implies that I set the required properties for the ShapeFileFeatureLayer regardless of whether I believe it to be cached or not. i.e. Properties such as IndexPathFileName , Zoom levels


[code]


FileBitmapTileCache tileCache = new FileBitmapTileCache(mapCacheFolder);


 


// I'm loading the shape files from a XML file


foreach (XElement level1Element in XElement.Load(mapConfigFile).Elements("Layers").Elements("Layer"))


{


shapeFileName = mapFolderLocation + level1Element.Element("ShapeFile").Attribute("Value").Value;


mapConfiguration.TileCache.CacheId = Path.GetFileName(shapeFileName);


 // set the remaining properties of the shape file even if the cache is expected ....


 


}


 


Is there an easy way to tell at the server when a cache tile was used or if a cache tile was deemed to be unavailable and a shape file was used to render the image required


 


Further Clarification


---------------------------


There another thing I'm slightly unclear of also.


A static layer may consist of multiple Shape File Feature Layers in the one location.


ShapeFileFeatureLayer 1 = rivers


ShapeFileFeatureLayer 2 = county boundary


ShapeFileFeatureLayer 3 = Road + streets


When rendered without caching the 3 ShapeFileFeatureLayer above overlap as expected to form the full picture of an area with a river, county boundary and a road.


When caching is enabled it seems that each ShapeFileFeatureLayer has its own set of tiles at a particular zoom level. 


ShapeFileFeatureLayer 1 = set of images


ShapeFileFeatureLayer 2 = separate set of images 


ShapeFileFeatureLayer 3 = separate set of images again


Is there a way when caching to somehow indicate that those 3 layers are in the same region and can be covered by 1 set of images which make up an image of the 3 layers combined instead of 3 separate sets of images from each layer.


It would cut down on the amount of images required and the amount of requests made. Just wondering, maybe the gains made are not as big as I imagine them to be or maybe its a difficult thing to do properly.


Rgds,


Liam


 


 



Hi, Liam


As for David’s advice about the time consumed by generating tiles images and I think David means for WorldMapKit Data which is 24.8 gig and include 899 shape files. For the first 14 zoom levels it takes us a few days, on a few machines, and about 3-5 gig of space to cache all of the tiles. I guess the number of tiles you have generated is so huge, and you don’t need to generate the tiles at world extent. Our strategy is that for lower zoom levels (start from zoom level 11) we cache tiles that are within US urban areas and towns. If you have some any questions about it, please let us know and we can provide you tools we use.
Just as you have mentioned, if one requests comes and it can’t find the cache images, and then it will generate it on the fly depend on the request, and then the following request can use it. And you don’t need to set the TileAccessMode property which is used to control access mode for tiles images. For example, if you have set TileAccessMode for one TileCache object as ReadOnly that means you can’t store any cache images in the cache directory.
If you want to identify whether we have generated the tile cache and the GetTiles method of BitmapTileCache can help you, and you just need to pass the BoundingBox which comes from GetMapRequest into it, this method will return tile collection. And then loop it and judge whether the Bitmap property of every tile is null. If it’s null that means this tile cache is unavailable and it needs to be rendered, and if it’s not null that means this tile cache is available.
For your last question about caching strategy, for the best performance I would suggest you generate one set of images for all the base layers (that always are static )including rivers, county boundary, road, streets layers, etc. This is the way we always do. This allows only one layer of base images to be requested and lowers the load on the server. As you know, it will cut down the amount of images and also reduce the requests. Anyway, you can easily get it. You just need to add any other ShapeFileFeatureLayers you want to combine into StaticLayers collection of MapEngine and it will render all the static layers depend on the sequence you added. Please see if for full detail in the attached code.
If you have additional questions, please let us know.
Thanks,
Khalil

GeneratingTileCacheForBaseLayers.txt (4.87 KB)

Hi Khalil,


Thanks for your help on this. I would be interested in looking at the caching tools you use if they are available.


The attached code is also very helpful. 


Also, if we cache the tiles from the shape files do we loose any feature layer functionality for querying later shapefile feature layers once they are loaded.


If we have numerous shape files [in our case 114] and a user uses a polygon to select a region on a map and need to return the features from the shape files contained in the polygon - is it easy to do that. i.e. does pre-caching shape files mean that functionality is lost.


Liam


 


 



Hi, Liam


As for the caching tool, it’s so big so that I extract the necessary codes for your scenario. Please see it for full detail in the attached solution. If you don’t have the urban_dtl shape file please let us know and we can send you that.
In fact, WMS Server product is mainly used to serve static maps, but it also support dynamic functionality. Currently, if you want to query features from specified shape files included in the selected polygon, please add the dynamic layers into client-side that means at the code behind of page. Web Edition did a great job in the section and it provided many features to support that. If you don’t familiar with that, please check out the samples which located at “Samples\MapShapes” folder in the installed samples.
For WMS Server and it also support GetFeatureInfo request except that GetMapRequest, now we are enhancing this functionality. I guess it will release soon, by then you can directly query features from plug-in.
Thanks,
Khalil

002_001_GenerateTiling.zip (8.95 KB)

Hi Khalil,


Thanks for the response and the code.


The caching is working perfectly up to zoom level 17 and is creating server cached images from zoom levels 18 to 20 as required and reusing the cached images at those levels as expected.


Wrt to the previous response I guess ideally I would like to serve static maps fully from WMS Server fully for performance reasons


When a user selects a region on the map then the Web UI code behind is aware that in this area the following x shape files are involved. The code behind could then load those those shape files and perform the relevant spatial query.


i.e. 



        
  1. WMS Server serves static maps fully

  2.     
  3. User selects a region on the map

  4.     
  5. In the Web UI code behind the TrackShapeFinished event [or other wise] the size of the polygon can be retrieved from the EditOverlay.Features

  6.     
  7. The polygon size is converted to a valid extent and a separate WMS server plugin is called to temporarily add the required shape files into the dynamic map overlays of the Map in the Code behind for that extent where they can then be queried in the code behind of the Web Edition.

  8.     
  9. Alternatively I could make use of the GetFeatureInfo Method but I need to investigate further.
        

     


        



There is a performance hit I guess with the above but its a trade off between that and quick loading of cached maps from a WMS server and slower shape file querying.


The above process may work for use as the performance is key on rendering the maps and we could take a hit on the querying performance. Most of our queries would probably be on marker overlays and they will come from the WebEdition part anyway.


Is the above even possible or a valid approach. I don't want to load the shape files into the WebEdition Product at all if I can help it unless its required.


I'm unsure if thats what you were aiming for in your reply or if you proposed that the layers that would be queried are loaded in the WebEdition on page load from the start. 


To add another challenge to the mix we are soon to evaluate the Routing and Geocoding products and so we need to be aware of any implications or knock on effect also.


In summary it would be great of we would use WMS to serve the cached maps and dynamically load the shape files required for any spatial query or subsequently routing or geocoding operations.


Regards,


Liam


 


 



Hi, Liam


I am not sure of your requirements, and so I need to confirm with you.
What do you mean by say that “and a separate WMS server plugin is called to temporarily add the required shape files into the dynamic map overlays of the Map in the Code behind”. How do you get the shape files from one WMS Server plug-in, and if you use the TiledWmsLayer on the client-side, of course you can get layer names, style names and crss etc.
In the previous reply, what I mean is that you can add the ShapeFileFeatureLayers that you need to do query into one LayerOverlay , for this LayerOverlay, and you can set its IsVisible property as false, so it won’t send requests to server and it’s just used to do query. Next you can query that in the TrackShapeFinished event handler base on the region that you have selected.
If I misunderstand your meaning, please correct me.
Thanks,
Khalil

 Hi Khalil,


Thanks for you clarification. I was overcomplicating it by a long shot.


Adding the ShapeFileFeature layers and setting its Visible property to false sounds like what we need. By the sounds of it there is no load put on IIS then as no requests are sent to the server to render it. Makes sense.


Thanks again,


Liam



Hi, Liam


That’s right. You are correct. This is what I've been trying to say for your solution.
If you have any questions, please let us know.
Thanks,
Khalil