ThinkGeo.com    |     Documentation    |     Premium Support

Generate Tiles on Request

i am not sure if this is the reight forum or i should ask this in the services edition forum:


we are developing a j2me application (java application on a mobile). the application uses some sdk to display maps. 

maps are displayed using the 3785 projection (like google and others). the sdk uses scales 0..19 where the assumtion is that on scale 0 the map is 1 tile, on scale 1 it is 2x2 tiles and so on (each scale multipliues the rows and columns by 2)


this sdk loads tiled maps from a map server using http and requesting each tile generally looks like this (we add some more params but they are not relevant):


server/Page?Row=x&Col=y&Scale=z

where x, y and z are changing for each tile. the requests are made to a page i am writing on the server which makes some validations and then renders the tile and returns the tile as the requested content type and bytes to the j2me application.


when looking on the requests made by the map suite web edition i see that unlike other web map sites where they request tiles by row,column,scale, the map suite requests tiles by defining the bounding box of the tile. from experience with other web mapping sdk's i noticed that generating tiles based on the bounding box sometimes resulted in tiles not joining correctly one to another.


my questions:

1. just for understanding, why is Map Suite using bbox as the way to render tiles unlike all other mapping solutions out there and unlike google, microsoft and others?

2. is there some code that will allow me to convert from tile row,column,scale to a bbox? can i be sure that the tiles generated will fit one next to the other exactly?


 



Dan, 
  
   This is a good question and I will be my best to explain. 
  
   The Web Edition was designed around the concept used by WMS servers.  The WMS standard has been around for a long time and is very good for general display when the scales being requested are unknown.  WMS is based off of a bounding box and requests are drawn in real-time in most cases.  When Microsoft, Google and alike entered the scene their offering didn’t fit this model very well.  They had large pre-generated data sets where the scales were set and to support scales in-between or outside of their standards was impractical.  This is when you started to see in GIS the concept of scale snapping.  Before this you could zoom into any scale and in most all GIS tools this is still the case.  The advantage with the limited scales model was that the data could be pre-generated and distributes more easily and with less overhead.  The Microsoft & Googles needed is as their solutions would need to be consumed by millions so that was their trade off.  Generating tiles on the fly and not caching them can really tax a web server especially under heavy load. 
  
   In the GIS community proper things have started to trend this way but in general change happens more slowly.  There has been an initiative for a WMTS (Web Map Tile Service) specification which would be like WMS except the server would publish a set list of scales for the consumer.  At the same time the scales can be defined by the server and do not have to adhere to the same scale sets Microsoft and Google use.  In this system you request the tiles lin the row and column fashion along with the scale.  One drawback of this is that you have to do calculation on the client part to know what you need.  For varying sets of scales it can be more difficult.  Many junior developers like the bounding box approach much better.  In the end things will need to go this route.  As I said change is sometimes slow in GIS at large, just look at the spherical Mercator projection, it was known as 90013 for the longest time (spelling google) and has just recently been given a proper number by the EPSG. 
  
   In our particular case we saw the bounding box to be much more flexible as we have to support very specialized implementation.  Our tools were designed to be flexible and we had offering out before this became the fad.   It is possible with our Desktop Edition for example to not have snapped scales and to zoom in with any granularity.  On the Web side it is very similar, you can easily change the pre-set scales in the zoom bar to be whatever you desire.  What we decided to do was to allow granular scales but at the same time offer a set of tiling and caching classes that would give us the benefits of set scales if the user chose.  If you want speed then pre-set the scales and enable caching and if you did not then choose not to and have each map be rendered on the fly.  The way we do this is for each particular scale we setup a grid of 256x256 bounding boxes that we generate our tiles on.  For that scale the grid will always be the same with a starting point way outside of the world.  When a request comes in we determine what grid cells overlap the bounding box passed in and we generate the combination of those.  We later split them into the smaller tiles and same them to disk.  This works very well when you people snap to scales. 
  
   The classes we use are the TileCache class and it has been inherited to form things like BitmapTileCache and FileBitmapTile cache etc.  You can pass in a bounding box and the size of the image requested and it will return a collection of tiles.  Each tile contains its own bounding box and some additional information such as the row and column.  Using the FileBitmapTileCache class you can save images to the tiles.  If you were to look at the underlying directory structure you would see something like CachId\Scale\Column\Row.jpg.  We like to think we have the best of both worlds. 
  
   It is unclear where the future will be in general.  For the giants like Microsoft and Google who have to serve to millions and who need to have data pre-cached I suspect they are locked into their solution.  For other vendors they need to cater to all of the niche industries where one size doesn’t fit all.   
  
 One thing I am not sure on is the issue you brought up with the tiles not fitting together.  As long as the scale is the same and the grid you create is deterministic based on scale there are no issues, we use it all the time.  If you have a situation where you have tiles cached at one scale and you want to support in-between scales you can stretch or condense the tiles and this does ok but the effects of stretching look less then great.  We have considered this from time to time on some projects but found it better to have the customers choose their own scale set to fit their needs. 
  
 David

Hi David,


thanks for the quick and very informative response!


few questions:

1. does the FileBitmapTileCache has the same directory structure as the cache used by Map Suite Web? since my web site serves both web users and the j2me application i want to use the same tile cache for both.

2. do you have some sample code showing how to take a row,column,scale and convert it to a rectangle that needs to be rendered and then using cache to retrieve the tile and/or generate it if the file does not exist?

 


 



Dan, 
  
   I am almost 100% it uses the same tile structure.  The Web Edition came first and I am not sure we had the TileCache system then but I will have to check.  I am pretty sure we made it compatible though. 
  
 The second question is a bit trickier.  When you pass scale is is 1-19 etc or the real scale like 150000?  Are the scales you are expecting the exact same as Google Maps?  When you deal in images like Google they deal with resolution. Doubling of resolution does not exactly equate to halving or doubling the real scale.  Resolution conversion is involved and there is a subtle and tricky relationship there.  They might not be exactly the same but really really close.  It in 99% of the cases would not make an noticeable difference.  We have the Google scale set and can support it as we do for many clients.  I will need to figure out the exact API and sequence you need to call them.   
  
   I am up late working on some stuff and happened to see your posts but I need to get some other people involved and bounce this off them.  I will most likely not be in tomorrow but will pass this on.  I will talk with some others and see what we can do.  The APIs are pretty flexible and we built a number of classes to support the grids that we use in the TileCache system. 
  
 Can you reply to this post to help keep it on our radar.  When a support person is the last to post it is removed off the support list. 
  
 David

David, 
  
 i am using the exact same scales as google does (591657550.5,295828775.3,147914387.6,73957193.82,36978596.91,18489298.45,9244649.227,4622324.614,2311162.307,1155581.153,577790.5767,288895.2884,144447.6442,72223.82209,36111.91104,18055.95552,9027.977761,4513.98888,2256.99444,1128.49722) 
  
 and i am working in the same projection as they do (3785) although i would like the solution to be as generic as possible to support other projections as well if possible 
  
 thanks

Dan,


Here is a sample for you showing how to Generate Tiling images and how to consume it in Web Edition.



When clicking Generate Tiling, it will generate the tiling images for top 5 GoogleMap zoomLevels to the path you specified. There is only one line of code:



Map1.StaticOverlay.ServerCache.CacheDirectory = txtCachePath.Text;



in LoadMap.Click event to consume the generate tilings.


Note: it only generates the tilings for top 5 ZoomLevels. To generate for the others, you need to optimize the code to avoid generating a very huge bitmap. please see the comments in the code.


Thanks,


Ben


 



1223-GenerateTiling.zip (8.08 KB)

Ben, 
  
 thanks for the sample. few questions: 
 1.  isn’t there a way to generate just the tile i need that the j2me application requested? 
 2. i see that you are using the FileBitmapTileCache class but i do not see that tiles that where generated are actually used when a new request comes in. 
  
 my basic idea was: 
 1. the j2me app needs a tile on Row=R, Col=C, Scale=S. it requests this from the server using server/GetTile.aspx?Row=R&Col=C&Scale=S 
 2. the page converts R,C,S into a bounding box rectangle 
 3. the page looks on the cache for the tile image. if it is found it uses it. if not the tile is generated and returned as the response (content-type: image/png) 
  
  
 maybe i am missing something here but i think that is the basic idea. i do not want to generate tiles for the whole world if the j2me app will only look on one country 
  
 thanks for all your help!

Dan, 
  
 Sure you can generate the tiles on request. Here I write some quick pseudocode for this which I think is easy to understand. If you still have some problem let us know. 
  
 
public void GenerateTilesOnRequest(parameter)
{
        // 1, 
        // Let’s say the parameter is what user requests, it includes the extent, the image height and image width.
        // Get a proper scale from the request. Here we need to snap to one of the scale we supported(do the caching) 
        double scale = GetScale(parameter.Extent, parameter.Width, parameter.Height, mapUnit);
        
        // 2, 
        // Get the Tiling objects
        MapSuiteTileMatrix tileMatrix = new MapSuiteTileMatrix(scale, tileWidth, tileHeight, mapUnit);
        BitmapTileCache tileCache = new FileBitmapTileCache(cacheDirectory, string.Empty, TileImageFormat.Jpeg, tileMatrix);

        // 3, 
        // Here we get the tiles which covers the request extent. Some of the tiles may already be cached on disk and some others may not. 
        Collection<BitmapTile> tiles = tileCache.GetTiles(parameter.Extent);

        // 4, 
        // Get the TilesWithoutBitmap, and then get the DrawingExtent we need to draw on the fly.
        // You can check tile.Bitmap == null to see if the tile has been cached.
        Collection<Tile> tilesWithoutBitmap = GetTilesWithoutBitmap(tiles);
        RectangleShape drawingExtent = GetDrawingExtent(tilesWithoutBitmap);

        // 5,
        // Here if not all the tiles have already been saved to disk, we need to draw the image on the fly. 
        // Every tile has a bounding box so you can easily get the extent to draw, 
        bitmap = GetBitMapCoveringUncachingTiles(tileCache, drawingExtent, mapUnit);

        // 6, 
        // save the tiles to disk
        tileCache.SaveTiles(bitmap, extent);
}
 
  
 Thanks, 
  
 Ben.

how generate tiles automation? 
 Do you understend,becose I little speak english?

Ivan, 
  
 I guess I know what you mean. If you want to generate the cache for map images on the fly, and here are two properties of LayerOverlay can help you. 
  
 You could set them like the code below, please give a try. 
                 overlay.ServerCache.CacheDirectory = "c:\temp"; // Set the cache directory 
                 overlay.ServerCache.CacheId = "CacheID"; //Like the sub-folder for the cache diretory 
  
 And if you want to generate cache in advance, and please refer to the attached GenerateTiling zip file in the post ahead. 
  
 Thanks, 
  
 Khalil 


Khalil 
  
 Thanks for your help

Ivan, 
  
 You’re welcome. Thanks for you feedback. 
  
 Thanks, 
  
 Khalil

How to generate a map consisting of several layers? 
 On example : 
  
                 ShapeFileFeatureLayer orderLabelLayer = new ShapeFileFeatureLayer(@"C:\files\Order.shp"); 
  
                 ShapeFileFeatureLayer networkLabelLayer = new ShapeFileFeatureLayer(@"C:\files\Network.shp"); 
                 ShapeFileFeatureLayer waterwaysLabelLayer = new ShapeFileFeatureLayer(@"C:\files\Waterways.shp"); 
  
 Would have to work for any particular or is there some way that is all done automatically? 
  
 Thakns for help 


Ivan, 
  
 What do you mean by "How to generate a map consisting of several layers? ". 
 Do you mean how to add these FeatureLayer to Map control or generate cache for all of these layers automatically? 
  
 I suggest that you could refer to the "LoadAShapeFileFeatureLayer" sample in our installed samples which you can find its source code at "Sampels\DataProviders\LoadAShapeFileFeatureLayer.aspx". 
  
 Also, if you want to add cache for all of these layers, and add the CacheDirectory and CacheId property for LayerOverlay which is used to load these FeatureLayers. 
  
 Thanks, 
  
 Khalil

Khalil 
 plaese attach me example? 
 Thanks

Sorry 
 I understand that. 
 but you do not understand me. 
 I have already set up a map that works and is loaded, but I do not know how to do tile because there are several layers of maps that I made in prior post

Whether you are dealing with one layer or several layers in your map, generating the tiles is exactely the same operation. You do not have to worry about that. Now, if you already have generated the tiles and you want to update them because you have new layers or different layers you want to add, you need to clear the cache and regenerate the tiles so that they are up to date. You should see an API such as Map1.StaticOverlay.ServerCache.Clear. I hope we understood you this time.

Thanks Val for help me

Do you have some other example of how to do Tiles, except that here? 
 Tiles for web application

when inplementiram example you gave, in my. SHP file command line gives a negative result … 
  
  RowColumnRange rowColumnRange = tileCache.TileMatrix.GetIntersectingRowColumnRange(cacheExtent); 
  
 and never passes through part of the code 
  
  if (rowColumnRange.MaxColumnIndex == 0 && rowColumnRange.MaxRowIndex == 0) 
                 { 
                     mapEngine.CurrentExtent = tileExtent; 
                     bitmap = new Bitmap(tileWidth, tileHeight); 
                 } 
  
 Whether the problem is. Ifile or SHP in the loop?