ThinkGeo.com    |     Documentation    |     Premium Support

How to start for good performance with WMS server?

Hello,


 


I developped a prototype with MapSuite Services a few month ago, and now I'm back for the real thing (no more a prototype, but the first version.)


I have downloaded the latest release, and now I'm trying to build a WMS server. This server will deal with 5Go Tiff Geofiles, and 5Go shapes files + read data in database.


In the prototype, a bad point was really performance, so I will begin dealing with that.


My question is easy : I would like to know was are my solutions (server caching ? client caching ? file spliting ? ...) for a good approch of this development.


If you have documentation or samples code for dealing with big files, I would appreciate that too !


Tanks you very much for your answer,

Guillaume.



Guillaume, 
  
   This is a pretty complex answer and depends on a number of factors.  Having said that I can give you a high level of what I know about it. 
  
 Questions: 
  
 1.What are you using to consume the WMS content.  While it could technically be anything if you know 100% it is the Desktop or Web Editions then we can do some extra stuff to get good speed. 
  
 2.What do you mean by 5Go Tiff Geofiles and 5Go shape files.  I mean I am not familiar with the term “Go”, I think it might mean Gig but I want to clarify. 
  
  
 Suggestions: (random thoughts) 
  
 1.I would sort out all of your data into what is going to be static and what is going to be very dynamic. For example the shape files may only change once a month or year and some of the data from the database might change very often.  Lump the stuff into categories of cachability. 
  
 2.If you have allot of GeoTiff files I suggest if possible that you compress them into a format like Ecw, Jpeg2000, or Mr Sid.  They will read faster, take up less space and by overall better. 
  
 3.For data sets that are very static including the raster stuff I suggest you implement some kind of server side cache based on a tile size like 256x256.  If the client requests a larger area you can first see if it is in the cache and if so you load the tiles and stitch them together.  If they are not then you generate the image on the fly and then split them into tiles and save them on the disk. I think in the latest version we have some classes to help you do this like FileBitmapTileCahe. This encapsulates this kind of thing very well. 
  
 4.For the shape files make sure you have your spatial indexes built.  The size of the shape file doesn’t effect its performance with a spatial index.  One thing you do want to consider is to try and not have any value or class break renderes.  If you do then we need to index intot he dbf and this is a slow process.  If you have things that need to draw differently based on this then I suggest you create different spatial indexes with just those attributes.  This filters by the index instead of looking into the dbf.   For example if you have a shape file that has road and in there you have three types of roads.  In the dbf there is a column that says what type it is, if you used a value rendered then it will render slowly.  The best approach would be to build a custom spatial index where spatial index 1 for example just contained road type one and the second index road type two etc.  In this way you can load the layer multiple times but specify a different spatial index.  This will make the speed much higher because the decision making is in the spatial index which is very fast. 
  
 5.Make sure to have generalized high level shape files.  When you have very detailed shape files with many lines and want to display them at higher extents then you need to have alternate shape files that have fewer lines.  Make sure you have these. 
  
 6.If you know you will access the WMS from the Web Edition or Desktop edition where you have snapping to zoom levels then server side caching is a great choice as the amount of re-use is high as there are only a fixed number of scales the user can get too. 
  
 7.Inside of your WMS server I suggest you create as many MapEngines as you have cores on your machine.  Load all of they layers in them and then place them in the Application state.  Do not close them between requests.  Put locking around their use to make sure other threads that call them do not get one that is currently working.   
  
 8.Along with number 7 I suggest you create a HttpHandeler to server the images and not a web page.  I also suggest for the handeler that you include the IhttpAsyncHandler and the IreadOnlySessionState to enure that your HttpHandeler will be multi threaded.  Or another option that may be faster is to dump all session state. 
  
 9.I suggest that you add Trace statement into your handeler to make 100% sure it is processing requests in a multithreaded way. 
  
 10.I suggest that you create a set of WMS servers that is load balanced for higher throughput.  When you load balance them I suggest you do not simple use one URL to balance to multiple servers but you use three to four load balanced URLs to balance to multiple servers.  The reason is IE has a connection limit of three or four to one URL.  If you have multiple load balancer servers you are going to see much better response. 
  
 11.I suggest if you use the Web Edition you consume the WMS service through the WmsOverlay. It supports specifying more than one server to make the request from as it request more than one tile at a time. 
  
 We have designed a pretty fairly tuned WMS server setup that we are going to launch soon.  This service serves up the World Map Kit data set for all of our samples and we have been working very hard to make it lightning fast.  We have it hosted on the Amazon cloud it is working great with elastic load balancers etc…  We have spent all of of time though to make sure we are getting the most out of the machines. 
  
 If you get in touch with your sales contact I think we may be able to arrange some consultation or maybe access to the source we have. 
  
 David

Hello, 
  
 First thank you for your quick and very usefull reply ! 
  
 Answers : 
  
 1. We will use the MapSuite Desktop Client and the Silverlight Client to consume this WMS Server. The WMS server must however be accessible via others clients (for example with a simple web browser), but this will not be very often, and in that case performance is not a requirement. 
  
 2. I should have written “Gb” for Gigabytes, obviously :) Actually, Giga-bytes (Gb) in english is Giga-octet (Go) in french, so I mistaked, sorry for that ! 
  
  
 Thank you very much for all your suggestions, I will go through them, and first see what I can do with caching, index and compression. 
 Furthermore, your WMS server on Amazon seems to be a really good thing, I’ll probably ask for further informations later, but for the moment I first have to deal with technical stuff. 
  
 Thanks again, 
 Guillaume.

Guillaume, 
  
   I have tried to bring up at least all the topics I can think of but I am sure I am missing some things. 
  
   Considering you are using it in the Desktop and the Silverlight I suggest you do heavy caching on the pre-set scales we provide.  Then of course you may have clients that are at different scales but they won’t generate too many more tiles.  Your main clients will get a big boost from having the tiles pre-created.  You can also consider pre-creating about 10 zoom levels automaticly.  There are also some other ways you can pre-cache image and be smart about it.  What are the maps of?  Depending on that you can be tricky with how you cache. 
  
   I understand, Giga-octet, how precise, I should only expect something from the country to give us the wonderful metric system. :-)   
  
   The WMS server we designed for the cloud can also be run from any set of server and not just the cloud.  I think you may be able to save allot of time by re-using some of our stuff or getting some consulting.  I’m not in sales but throwing that out because we just came of a big project building a high performance WMS server.  
  
   In any event good luck and we will try and help you however we can here on the forums. 
  
 David

Hello, 
  
 I’ve been through your advises. I worked on Shapes Files with custom indexes, I’ve implemented someprototypes, and it seems to work well, with very better performances. 
 Now I’m working on GeoTiff files. I tested the server cache methods (FileBitmapTileCache), but ther some things I really don’t understand, and I don’t find any informtion neither on the website, nor in the forum, nor in the sample applications. 
 Here are my questions : 
  
 - When I execute this kind of code : var data = bitmapCache.GetTile(parameter.BoundingBox); , the BoudingBox returned in data is not equal to the boudingbox in parameter.BoudingBox. Is that normal ? 
  
 - When I use the SaveTile() method in order to save my tile on harddrive, I can’t understand the folders and files names… I would like please some explanations about this folder hierarchy, because I would like to be available to search direct through the directories with the explorer. 
  
 - I don’t find how to cut my pictures in 256x256 pictures, and then how to save them in the cache system. I tried to use MapSuiteTileMatrix, but I didn’t find out a way to use this class properly. If you have some sample code for this operation, I would be really gratefull. 
  
 Thank you ! 
 Guillaume.

Guillaume, 
  
   The API you mentioed will only return one tile that is in the bounding box, you can also get it by row & column.  I think that API is when you know the tile extent.  If you have an extent that is not perfectly the extent of a tile you should instead call the GetTiles API and it will return you all of the tiles that overlay the extent.  Of course when you combine the tiles they may be larger than the extent.  Also make sure to check the bitmap property as maybe on of the bitmaps may not be cached. 
  
 The directory Structure is made up as follows  [FileBitmapTileCache.CacheDirectory][FileBitmapTileCache.CacheId][Scale][TileRow][Column].jpg 
  
 I might have the row and column mixed up at the end but that is the basic jist. 
  
 To save the tile you can call the .SaveTiles and pass in the bitmap and the extent of the bitmap.  If the bitmap is larger than a 256x256 tile then we will split it for you.  If it is the size of four tiles we will split it into four tiles and save it.  If there is some space around the tile that does not make up a full tile them we ignore the extra and just save the tile. 
  
 The MapSuiteTileMatrix is just a way to specify a scale and tile size and for it to create an in-memeory grid for you.  We use it internally and have used it to do things like cluster maps but I think it is not necessary for what you are trying to do.  I think the SaveTiles will do everything for you. 
  
 David

David, 
  
 I’ve tried to use GetTiles() and SaveTiles() methods instead of GetTile() and SaveTile(), but I have other problems : 
 - Some times I get the folowwing exception : InvalidOperationException : This method will return too many cells that might cause performance problem. Please using GetIntersectionRowColumnRange instead.  
 - When calling SaveTiles with an image from 1024x1024, only one picture of 256x256 was saved in cache (the bottom left picture), and nothing else. Furthermore this picture is named “0.png”, and is in a directory named “0”. 
  
 Here is my code () : 
  
   
 FileBitmapTileCache bitmapCache = new FileBitmapTileCache(@“C:\Temp\GIS\Maps\Cache\GeoTiff”, “GeoTiffCache”); 
 private MemoryStream GetMapInMemory(WmsRequest parameter) 
         { 
             var ms = new MemoryStream(); 
             var data = bitmapCache.GetTiles(parameter.BoundingBox); 
             bool alreadyLoaded = (data.Count <= 0) ? false : true; 
             foreach (BitmapTile tile in data) 
             { 
                 if (tile.Bitmap == null) 
                     alreadyLoaded = false; 
             } 
  
             if (alreadyLoaded) 
             { 
                 // Image already cached 
                 // TODO : Create complete image from tiles 
                 // Yet we send only the first tile 
                 data[0].Bitmap.Save(ms, parameter.ImageFormat); 
             } 
             else 
             { 
                 // New image to create 
                 GeoImage geo = GdiPlusGeoCanvas.CreateGeoImage(parameter.Width, parameter.Height); 
                 GdiPlusGeoCanvas.FillBackground(geo, new GeoSolidBrush(GeoColor.FromHtml("#FFFFFF"))); 
                 var rect = new RectangleShape(parameter.BoundingBox.UpperLeftPoint, parameter.BoundingBox.LowerRightPoint); 
                 map.CurrentExtent = ExtentHelper.GetDrawingExtent(rect, parameter.Width, parameter.Height); 
  
                 OpenLayers(parameter.Scale, parameter.Layers); // This method open the right layers, depending on the scale we asked for (I have different picture quality for different scales.) 
  
                 map.DrawStaticLayers(geo, GeographyUnit.Meter); 
  
                 CloseLayers(); 
  
                 ms = GdiPlusGeoCanvas.ConvertGeoImageToMemoryStream(geo, parameter.ImageFormat); 
                 var bmp = new Bitmap(ms); 
                 bitmapCache.SaveTiles(bmp, rect); 
             } 
  
             return ms; 
         } 
  
 
 
  
 I’m probably doing something wrong, but I can’t figure out what. Please can you give me some further advices ? 
 Many thanks, 
 Guillaume.

Guillaume, 
  
   I think the problem is that you need to set the scale.  I don’t have the API in front of me at the moment but I am pretty sure we need the scale.  The tile cache create a matrix of cells but to do that it needs to know the current scale so it can calculate how big the cells need to be.  The reason i think you are getting the error is because the scale must be set really small and maybe 0 by default.  When you call the GetTiles there are so many that we raise the exception.  We did that because if we didn’t then it might take a very long time to return.  I think by setting the scale it will fix it.  The reason the scale is not in the constructor as maybe you might think it should is because we built it to use as the caching system and in the map control the scale changes all the time so setting it once doesn’t really buy us much.  For people using it outside of the internals of the map control this might be a good thing we can add in the next release.  This way you would at least get a hint.   
  
   If you are not sure how to calculate the scale you can use and API like ExtentHelper.GetScale or something like that.  I am away from the API right now but I think that is close.  ExtentHelper is a static class with a bunch of helpful function on it dealing with extents and scales.  i hope this help you out.  Once you get this in place then i think the save will also work well. 
  
   Worst case let me know if this doesn’t work and we can get a sample together to show this.  When we built the tile cache stuff we intended it at first to be internal but then we wanted to expose it as we thought it might be helpful.  
  
 David

David, 
  
 Ther isn’t any Scale property in the FileBitmapTileCache, so I created a MapSuiteTileMatrix, and then passed this matrix to the FileBitmapTileCache constructor. 
   
         var matrix = new MapSuiteTileMatrix(parameter.Scale, 256, 256, GeographyUnit.Meter); 
         var bitmapCache = new FileBitmapTileCache(@“C:\Temp\GIS\Maps\Cache\GeoTiff”, “GeoTiffCache”, TileImageFormat.Png, matrix); 
 
 
  
 For the GetTiles method, it seems that it works better. For a 1024x768 picture, I get a collection of 12 tiles (that seems correct). 
 But then, when I call the SaveTiles method, I get a NullReferenceException. 
 Am I missing something else ? 
  
 Thanks, 
 Guillaume. 


Guillaume,


two things need to pay attention to:
1) The second parameter should stands for the bitmap extent (probably it takes up 3*4 tiles).
 
2) The matrix scale should be consistent with the target bitmap scale.
 
We have a post contains a small sample using this, take a look if you are interested.
gis.thinkgeo.com/Support/Dis...fault.aspx
 
If you still have problem, could you send us a small application to show your problem?
 
Any more questions please feel free to let me know.
 
Thanks.
 
Yale

Guillaume, 
  
 There is another post related:),just let you know. 
 gis.thinkgeo.com/Support/DiscussionForums/tabid/143/aff/12/aft/6274/afv/topic/afpgj/1/Default.aspx#11328 
  
 Thanks. 
  
 Yale

David, Yale, 
  
 I figured out that I hadn’t the latest version of the MapSuireCore.dll file (actually your sample didn’t work, so I thought there was a problem with my version). I had the 397. Now I have downloaded the RC2, and it seems to work properly. 
  
 Thanks for your time, 
 Guillaume.

Guillaume, 
  
   Glad things are working now. 
  
 David

David, 
  
 I noticed that there was some debug output in the Output window, while executing the SaveTiles() method. I have many lines with an "ExtractBitmap problem" message. Nevertheles everything seems to work correctly so far. Is it important ? 
  
 Thanks, 
 Guillaume.

Guillaume, 
  
 Sorry for misguiding, this is some debug information we did when developing, please ignore it. 
  
 Any more questions please let me know. 
  
 Thanks. 
  
 Yale 


Ok, thank you Yale. 
  
 Guillaume.

Guillaume, 
  
 You are welcome. 
  
 Yale