ThinkGeo.com    |     Documentation    |     Premium Support

GeoTIFF fails to display to correct extent

Hi Daniel,


Thanks for your test
data.


After we checked properties
of the GeoTIFF files you provided, we found that these test data has very high
resolution and DPI, even its file size is not that big, please check the screen
shot below. We render the tiff files dot by dot, so there will be too much dots
while rendering these files, it’s the reason for pool performance. 

























To resolve this issue,
we can reduce the DPI of these images, but I’m not sure if a little loss of
detail is acceptable for your scenario. If you occur any problem when trying to
reduce the resolution of GeoTIFF files, please feel free to let us know.


Thanks,

Kevin



Hi Kevin,



Thanks for the answer.



I thought that it is a Rendering bug but since the Problem is the high Resolution and dpi, I just have to accept that. The customer provides the Geotiffs so reducing the dpi on our side has no effect for the customer.



In the meantime I activated the FileBitmapTileCache of the Overlays for GeoTiffs, so the customer must only wait once per scale.



Is there some possibility or future Feature for a method like this?    



layerOverlay.TileCache = new FileBitmapTileCache(path, id);

layerOverlay.TileCache.SaveLayerByScale(scale);



So the whole Layer will be saved in the TileCache for the provided scale. It would be easier for us to let the customer wait for 5 minutes creating the TileCache for all scales in the ZoomLevelSet and after that he can work with an excellent Speed.



Alternative:

There is already the method layerOverlay.TileCache.SaveTile(). Can I somehow get all tiles from the layer.GetBoundingBox() area at a certain scale?



Thank you.

Regards

Daniel


Hi Daniel, 
  
 Yes, you can create your custom pre-generating tiles utility by our API. And we have a utility named "Cache generator", you can learn more about it here: 
 thinkgeo.com/forums/MapSuite/tabid/143/aft/10446/Default.aspx 
  
 I think the code of it should be helpful for your requirement. 
  
 Regards, 
  
 Don

Hello Don,



Thanks for pointing me to the Tile Cache Generator. 

It does exactly what I need to do in my own program, but your generator heavily depends on Shape files. I tried to rework it to work with tif-files without a world file (tfw) (see the test files I attached to one of my former posts) but without luck. 

Tifs does not have Features or some other methods I found in the code so I dont know how to compensate that. And unfortunately I can not invest more time to try further reworking your generator. 



My requirement:

As soon as a tif file without a world file (tfw) wants to be added to the map, a message appear that the program is now generating the Cache for this tif. In the background the program starts to generate the Cache tiles for each scale of my zoomlevelset like the normal TileCache would generate if the tif is rendered on the map. After all tiles for all scales has been saved to disk the message dissappear and the tif start to render with the Cache. So now the Rendering is very fast maybe even instant because of the Cache.



Can you provide me with maybe a simple tif generator solution or your Generator reworked for tifs?



Thank you.



Regards

Daniel

Hi Daniel, 
 As known by us, the GeoTiff reader module in Map Suite depends on GeoTiff library trac.osgeo.org/geotiff/ , there it reads the file pixel by pixel, thus a lower performance shown there. I will do more research to see if there is any updating information on office website. Any progress will be updated here. 
  
 Thanks, 
 Casper

Hello Casper,



I am glad to hear that you are researching the low performance of the geotiff rendering. Please post any new news here. I subscribed to this topic so I get all posts directly to my email account.



A better Performance is of course nice but I have still the Problem with the TileCacheGenerator for GeoTiffs. Please read my last post before this one. At the moment I only have the TileCache FileBitmapTileCache activated for GeoTiffs but that is generating while Rendering. I want to generate the tiles before Rendering. Do you have any progress on that?



Edit1: 

I found some code here: thinkgeo.com/forums/MapSuite…fault.aspx

I tested it and it seems to generate the correct tiles, but the Folder and Name of the tiles are incorrect, so as soon as the map starts rendering the normal TileCache generate the tiles again with other names and other Folder names. I will attach my test file for this case and i will post my loadded Event and my generateTiles Method here.



In this sample I only generate the first 2 zoomlevel.

I used the sample program from the start of this thread. 

Here the wpfMap1_Loaded Code:


wpfMap1.MapUnit = GeographyUnit.Meter;
                wpfMap1.GCCollectionMode = GCCollectionMode.Forced;
 
                var allFiles = Directory.GetFiles(@"…\Data");
 
                foreach (var file in allFiles)
                {
                    GeoTiffRasterLayer tiffRasterLayer = new GeoTiffRasterLayer(file);
                    tiffRasterLayer.LibraryType = GeoTiffLibraryType.UnmanagedLibTiff;
                    tiffRasterLayer.LowerThreshold = 0;
 
                    LayerOverlay layerOverlay = new LayerOverlay();
 
                    layerOverlay.TileType = TileType.MultipleTile;
                    layerOverlay.DrawingQuality = DrawingQuality.HighSpeed;
                    layerOverlay.LockLayerMode = LockLayerMode.Lock;
                    layerOverlay.RenderMode = RenderMode.GdiPlus;
                    layerOverlay.TileCache = new FileBitmapTileCache(@"…\DataCache", System.IO.Path.GetFileName(file));
 
                    layerOverlay.Layers.Add(tiffRasterLayer);
                    wpfMap1.Overlays.Add(layerOverlay);
 
Collection
<baseshape> boundingBox = 
new Collection
<baseshape>();

                foreach (var overlay in wpfMap1.Overlays)
                {
                    RectangleShape rect = overlay.GetBoundingBox();
                    if (rect != null)
                    {
                        boundingBox.Add(rect);
                    }
                }
 
                RectangleShape oldtargetExtent = ExtentHelper.GetBoundingBoxOfItems(boundingBox);
 
                RectangleShape newtargetExtent = MapEngine.GetDrawingExtent(oldtargetExtent, (float)wpfMap1.ActualWidth, (float)wpfMap1.ActualHeight);
 
                double targetScale = ExtentHelper.GetScale(newtargetExtent, (float)wpfMap1.ActualWidth, GeographyUnit.Meter);
 
                ZoomLevelSet set new ZoomLevelSet();
                for (int i = 0; i < 20; i++)
                {
                    set.CustomZoomLevels.Add(new ZoomLevel(targetScale));
                    targetScale = targetScale / 1.4;
                }
 
                wpfMap1.ZoomLevelSet = set;
 
                wpfMap1.CurrentExtent = newtargetExtent;
                wpfMap1.RestrictExtent = newtargetExtent;
 
                var counter = 0;
                foreach (var zoomLevel in wpfMap1.ZoomLevelSet.GetZoomLevels())
                {
                    if (counter < 2)
                    {
                        foreach (LayerOverlay overlay in wpfMap1.Overlays)
                        {
                            GeoTiffRasterLayer layer = (GeoTiffRasterLayer)overlay.Layers.First();
 
                            GenerateCacheFiles(layer, zoomLevel.Scale, wpfMap1.CurrentExtent, GeographyUnit.Meter, @"…\DataCache", System.IO.Path.GetFileName(layer.PathFilename));
                            counter++;
                        }
                    }
                }
 
                wpfMap1.Refresh();

And here my GenerateTile Method:




private static void GenerateCacheFiles(Layer layer, double scale, RectangleShape restrictedExtent, GeographyUnit mapUnit, string cacheDirectory, string cacheId)
        {
            // create a MapSuite default tile matrix object.
            MapSuiteTileMatrix matrix = new MapSuiteTileMatrix(scale, 256, 256, mapUnit);
 
            // get cells in the specified extent.
            Collection<tilematrixcell> cells = matrix.GetIntersectingCells(restrictedExtent);
 
            // create a tile cache object with the cache directory and cache id.
            FileBitmapTileCache tileCache = new FileBitmapTileCache(cacheDirectory, cacheId);
 
            foreach (TileMatrixCell cell in cells)
            {
                // create a bitmap whoes size equals to the tile size.
                using (Bitmap bitmap = new Bitmap(256, 256))
                {
                    // create a GeoCanvas which for drawing the passed in layers.
 
                    GdiPlusGeoCanvas geoCanvas = new GdiPlusGeoCanvas();
                    geoCanvas.BeginDrawing(bitmap, cell.BoundingBox, mapUnit);
                    GeoCollection<simplecandidate> candidates = new GeoCollection<simplecandidate>();
                    lock (layer)
                    {
                        if (!layer.IsOpen) { layer.Open(); }
                        layer.Draw(geoCanvas, candidates);
                    }
 
                    // you can commend this line out because it’s just for mark a cache label in order to see if it works with our samples.
                    //geoCanvas.DrawText(“Cache”, new GeoFont(“Arial”, 12), new GeoSolidBrush(GeoColor.SimpleColors.Black), new Collection<screenpointf>() { new ScreenPointF(128f, 128f) }, DrawingLevel.LabelLevel);
                    geoCanvas.EndDrawing();
 
                    // create tile object to maintain current tile information.
                    BitmapTile tile = new BitmapTile(bitmap, cell.BoundingBox, scale);
 
                    // save tile.
                    tileCache.SaveTile(tile);
                }
            }
        }

I also attachted a screenshot of my Folder containing the Cache for the first scale 18992.5741540401.

The Folders with the minus in front of them are generated with the pre-generator the positive Folders from the TileCache of the overlay.

Maybe my restrictedExtent is wrong? I just want to generate all tiles from a certain zoom Level from a certain layer. So that the whole layer is cached and not just a Portion of the layer.



Thanks.



Regards

Daniel

Data.7z (136 KB)

Hi Daniel, 
  
 I think you can get the tile information in event "DrawingTile" of "LayerOverlay" before map rendering like this: 
  
 
LayerOverlay overlay = new LayerOverlay();
overlay.DrawingTile += overlay_DrawingTile;

void overlay_DrawingTile(object sender, DrawingTileTileOverlayEventArgs e)
{
    ThinkGeo.MapSuite.WpfDesktopEdition.Tile tile = e.DrawingTile;
}

 
  
 Thanks,

Hello Don,



thanks for your answer. But if I Cache them shortly before they are drawn to the map I could just use the normal TileCache from the overlay.



My solution posted is the right attempt. I can generate all tiles as soon as someone reads in a tiff-file. After the complete Generation of all tiles for all scales and for all layers, I let the map draw all layers with map.refresh or by refreshing all overlays. This Rendering on the map should be very fast because all tiles are already generated on the hard drive.



So instead of using the normal TileCache generating the tiles while the map very slowly renders all layers I want the user to wait 5-10 min to generate everything once and after that He can work with fast Speed because all tiles are rendered fast/almost instant.



I hope you get my requirement. The code I posted before has a Problem with generating the right Folders and names for the tiles so that they could be used later for the Rendering. I unfortunatly do not know where that Problem is. Again it is generating the tiles correctly but with the wrong Folder names and wrong tile names.



So what is my code doing different/wrong in contrast to the TileCache from the overlay?



Edit1:

After I looked at the properties of the single cells from the TileMatrixCell I saw that the overlay TileCache uses the row and the column for the Folder Name and the tile Name. In my pregenerating sample the row and column of a cell are not wrong they are correct. But as soon as I use tileCache.SaveTile(tile); it generates the Folder and the tile Name different altough the cell has the right row and column properties. Is there a bug in the SaveTile?



Regards

Daniel

Hi Daniel,



Here attached is the “Cache Generator” demo based on “Tiff” file, please check it out.



The only change we did is in “LayerProvider.cs” files and the corresponding changes are:




Map1.MapUnit = GeographyUnit.DecimalDegree;
Map1.CurrentExtent = new RectangleShape(-155.733, 95.60, 104.42, -81.9);
GeoTiffRasterLayer rasterLayer = new GeoTiffRasterLayer(@"…\SampleData\Data\World.tif");
LayerOverlay overlay = new LayerOverlay();
overlay.TileCache = new FileBitmapTileCache(@“C:\Test\ServicesEditionSample_CacheGenerator_CS_120619\CacheFolder”“DecimalDegree”);
overlay.Layers.Add(rasterLayer);
Map1.Overlays.Add(overlay);
Map1.Refresh(); 





Thanks,

Johnny

post11593.zip (22.9 KB)

Hello,



I tried to integrate your tiff TileCacheGenerator into my own application and it generates the Folders correctly but the Bitmaps are totaly wrong. 

Basically I want to do the same as the normal FileBitmapTileCache but instead of generating while I pan and zoom and want to generate all Bitmaps on all scales before the map is even displaying my tiff file. 



Can you please provide me the source code for the FileBitmapTileCache so I can get the generating Bitmap code out of it myself?



Regards

Daniel

Hi Daniel,



The FileBitmapTileCache is just a class used for creating the tile cache, it’s not all, there are several classes related, Could you please provide a test tiff file for us, and the requirement of creating tile cache, such as Projection etc, then we can have a try and provide a correct TileCacheGnerator project for you.



Thanks,

Johnny

Daniel, 
  
 If the file is a bit big, please contact support@thinkgeo.com for a FTP address. 
  
 Thanks, 
 Johnny

Hello,



I uploaded all my tiff files here as a 7z-File. It is only 1,38MB. 



My requirement:



Current steps:

1. Load all tif file into a map as GeoTiffRasterLayer with an active TileCache as FileBitmapTileCache in the Overlay

2. Let the all tif files render in the map --> very slow

3. While Rendering the map the TileCache will create Bitmaps in the Cache Folder for the current scale and visible Tile in the map.



New requirement:

1.As soon as the tif layer & overlay ist created the user will be asked if he wants to pre-generate the Bitmaps on the hard drive.

2. If he click yes the TileCacheGenerator generates ALL Bitmaps for ALL scales. Yes that can take some time but that is ok.

3. After the generating is complete I have exact the same Bitmaps as in the “Current steps”. 

4. Now let the tiffs layers render in the map --> instant loading, not slow



The Pre-Generator must have of course the same CacheId somehow as the normal TileCache in the overlay. So the pre-generated Bitmaps can be loaded.



Basically I want to give the user the choice to pre-generate the Cache Bitmaps. The normal TileCache in the overlay is active as well so that the pre-generated Bitmaps are used. So the user waits 5 minutes maybe more and after that the layers in the single scales will be loaded instant instead of loading slowly and the user have to wait 30 sek per scale until the layers are loaded.



By the way I Need a TileCacheGenerator which I can easily “import” or integrate into my own Project. I can not use a standalone program as your current TileCacheGenerator .



I hope you understand my requirement. If you have any questions please ask me. 



Regards

Daniel

002_001_Tif-Files.7z (1.38 MB)

Hi Daniel,



Our TileCacheGenerator is using another strategy to generate the tiles and that will make the performance much better. Actually, as the TileCacheGenerator tool is open source, you can copy those classes in your application to custom it. The below files are necessary:



TileCacheGenerator.cs

LayerProvider.cs

LayerProvider.cs

CreatingCellsArgument.cs

CacheGeneratorProgressChangedEventArgs.cs



If it is urgent for you, you can also contact with our sales to provide a professional service.

Thanks,

Troy

Hello Troy,



I have already done that. I copied these classes into my Project. But first I only got crashes after deleting all unnecessary stuff like the Events it worked but not in the right way. Please see my previous post. I will try to upload a sample solution in the coming days so you can see my code and maybe you can help me finding the Problem. 



Regards

Daniel

Hi Daniel,



Sorry I lost the context. I checked and tried your codes, the below is what I get:

1. I got an exception with your attached tiff file, the exception shows a memory issue looks like this is related with the high resolution. I test it simply with the below codes:


GeoTiffRasterLayer rasterLayer = new GeoTiffRasterLayer(@“C:\Users\troy\Downloads\002_001_Tif-Files\ALK_5493000_5652600.tif”);
            //GeoTiffRasterLayer rasterLayer = new GeoTiffRasterLayer(tifFile);
            rasterLayer.LibraryType = GeoTiffLibraryType.UnmanagedLibTiff;
            rasterLayer.LowerThreshold = 0;
 
            LayerOverlay layerOverlay = new LayerOverlay();
            layerOverlay.Layers.Add(rasterLayer);
            wpfMap1.Overlays.Add(layerOverlay);
            wpfMap1.CurrentExtent = rasterLayer.GetBoundingBox();

2. As the tiff files doesn’t work in my end, I test the world.tif file with your codes and the cached tiles are matched between the “GenerateCacheFiles” method and add the property "TileCache"on layerovelay. Here is my test codes:

Enable "TileCache"on layerovelay:


wpfMap1.MapUnit = GeographyUnit.DecimalDegree;
           string tifFile = @“D:\TestData\World.tif”;
           GeoTiffRasterLayer rasterLayer = new GeoTiffRasterLayer(tifFile);
           rasterLayer.LibraryType = GeoTiffLibraryType.ManagedLibTiff;
           rasterLayer.LowerThreshold = 0;
           rasterLayer.Open();
 
           LayerOverlay layerOverlay = new LayerOverlay();
           layerOverlay.TileType = TileType.MultipleTile;
           layerOverlay.Layers.Add(rasterLayer);
           layerOverlay.TileCache = new FileBitmapTileCache(@“C:\Users\troy\Downloads\CacheFolder1”, System.IO.Path.GetFileName(tifFile));
           wpfMap1.Overlays.Add(layerOverlay);
           wpfMap1.CurrentExtent = rasterLayer.GetBoundingBox();
 
           double targetScale = wpfMap1.CurrentScale;
           ZoomLevelSet set = new ZoomLevelSet();
           for (int i = 0; i < 20; i++)
           {
               set.CustomZoomLevels.Add(new ZoomLevel(targetScale));
               targetScale = targetScale / 1.4;
           }
 
           wpfMap1.ZoomLevelSet = set;
           wpfMap1.Refresh();

Using GenerateCacheFiles method: 


layerOverlay.Layers.Add(rasterLayer);
            //layerOverlay.TileCache = new FileBitmapTileCache(@“C:\Users\troy\Downloads\CacheFolder1”, System.IO.Path.GetFileName(tifFile));
            wpfMap1.Overlays.Add(layerOverlay);
            wpfMap1.CurrentExtent = rasterLayer.GetBoundingBox();

var counter = 0;
            foreach (var zoomLevel in wpfMap1.ZoomLevelSet.GetZoomLevels())
            {
                if (counter < 4)
                {
                    foreach (LayerOverlay overlay in wpfMap1.Overlays)
                    {
                        GeoTiffRasterLayer layer = (GeoTiffRasterLayer)overlay.Layers.First();
 
                        GenerateCacheFiles(layer, zoomLevel.Scale, wpfMap1.CurrentExtent, wpfMap1.MapUnit, @“C:\Users\troy\Downloads\CacheFolder2”, System.IO.Path.GetFileName(layer.PathFilename));
                        counter++;
                    }
                }
            }
            wpfMap1.Refresh();

Then, I compared with the “CacheFolder1” and “CacheFolder2”, all of the tiles are matched







I think it is better if you can send us a concrete sample with your tif files and that will help us a lot to find out why they are not matched.

Btw, would you please try only one tiff file at first? I suspect if there is an order issue in your provide codes.



Any questions, please feel free to let us know.

Thanks,

Troy

Hello Troy,



I built a simple example program which I upload here. Please check the code and tell me what is wrong.



Short Explanation:

First the DataCache will be generated using the TileCacheGenerator. Second the tif file will be added to the map and rendered so the normal TileCache can do its work. 

Both ways will generate its TileCache in seperate folders so you can compare both results.



Please tell me if you get the right code so both results are exactly the same.



Thank you.



Regards

Daniel

002_001_WpfApplication7.7z (315 KB)

Hi Daniel,



I recreated your issue under meter and found the issue is because we didn’t assign a meter tilematrix(decimal degree by default) to FileBitmapTileCache. Please modify the GenerateCacheFiles method as the below codes:


private static void GenerateCacheFiles(Layer layer, double scale, RectangleShape restrictedExtent, GeographyUnit mapUnit, string cacheDirectory, string cacheId)
        {
            // create a MapSuite default tile matrix object.
            MapSuiteTileMatrix matrix = new MapSuiteTileMatrix(scale, 256, 256, mapUnit);
 
            // get cells in the specified extent.
            Collection<TileMatrixCell> cells = matrix.GetIntersectingCells(restrictedExtent);
 
            // create a tile cache object with the cache directory and cache id.
            FileBitmapTileCache tileCache = new FileBitmapTileCache(cacheDirectory, cacheId);
            tileCache.TileMatrix = matrix;
 
            foreach (TileMatrixCell cell in cells)

I think this is the crime of your issue and if any questions, don’t hesitate to let us know.

Thanks,



Troy

Hello Troy,



your solution fixed the problem. Thank you very much. 



Regards

Daniel

Daniel, 
  
 You are welcome, if any other questions, don’t hesitate to let us know. 
  
 Thanks, 
 Troy