ThinkGeo.com    |     Documentation    |     Premium Support

Deserialized Rasters looking for filename

 I am serializing GdiPlusRasterLayers to file and then deserializing them on different machines.  The problem I am experiencing is this:


When I take the deserialized raster, add it to a Layer, add the layer to an Overlay and add the overlay to the Map, everything seems to work fine. There is a hidden issue however; there is a bug in the .NET deserialization method that causes waaaay too much memory to be allocated for the deserialized class.  A 6MB bitmap winds up with a 120MB allocation. This eats up memory pretty fast in out application.


To get around the issue, I make a new GdiPlusRasterLayer instance and DeepClone the giant instance delivered by the .NET deserializer into the new instance, then just let the giant go out of scope and the GC takes care of getting the memory back. Then I add the new instance to a Layer, add the layer to an Overlay and add the overlay to the Map. This time however, the Map complains that the file listed in the PathFilename can't be found.  (again, if I use the original Layer instance, it doesn't seem to care that the file isn't there).


How can I tell the new Layer not to look for the filename and just render the image it already has? (like the giant one does)


Dave



 BTW, I thought maybe setting the PathFilename to "" would work, but it does not. That gets you a 'file type is not supported' error.


Setting PathFilename to null results in a 'not set to instance of an object' error, probably because the set accessor isn't protected againt nulls and throws an exception rather than accepting the null as a valid option (which it can be since the constuctor returns a new GdiPlusRasterLayer with it set to null.


Creating a new GdiPlusRasterLayer does create the PathLayer to null, but thus far I have not been able to figure out how to move the original instance's ImageSource (nor a copy of it) over to the instance I created.  Trying something like this:


newRaster.ImageSource = (RasterSource)originalRaster.ImageSource.CloneDeep();


gets an error saying that 'ImageSource cannot be used in this coontext because the set accessor is inaccessible'


Dave



Hi David, 



Thanks for your post. 

This is a very interesting and complex issue. 

Can you please attach a sample that reproduces this problem to your next post? 

That'll be very helpful for us. 

And if you don't have a simple sample, then can you send us the raster file that you were using? 



And two other things: 

1. How did you measure the memory allocation for one instance? 

2. You said that "How can I tell the new Layer not to look for the filename and just render the image it already has?" 

I don't really understand what "the image it already has" means, can you shed more light on that? 



Hope we can get this problem fixed soon. 



Thanks, 

Cui



 Hi Cui,  these projects never seem to be small enough to attach, so I sent ot to support@thinkgeo.com with instructions to forward to you.


I am going to turn this question around and just assume I am doing something incorrect in my sample project and ask you to tell me where I am going wrong.


The sample app has a number of menu items left over from other thinkGeo issues it was the sample application for. We are only interested in the first 3 right now.


The 'Make A Tab' selection makes a new tab to display a new View instance of the View class using the LargeTestFile.png as the raster layer. The View class in the example application is very simple, but in our application is contains many other details as well as the FeatureLayers of a map.


The 'Save Tab To File' selection serializes the View class to a file. The raster layers are included in the serialization to provide portability to the View file in that the original png does not need to be present on the client using the view file. Clicking this overwrites the existing ViewFile.fvw


The 'Load Tab From File' selection creates a new tab, puts a Map in it, deserializes the View class and adds the reconstituted raster layers to the new Map.  Or at least that is what it is supposed to do.  I must be doing something wrong, because the raster is not coming back at the end of method UnpackageViewFromStorage.


Please look at the way I am doing this and let me know where I am going wrong.


Thank you for your time,

Dave



 David,


 
I got the sample you sent to our support. It's easy to help us recreate your issue and I found the problem is that the CurrentExtent of map didn't be set when "Load tab from file", I try to add following code, I can see the map again after load the tab.
 
                        newRaster.Open();
                        RectangleShape rect = newRaster.GetBoundingBox();
                        newRaster.Close();

                        mt.Map.CurrentExtent = rect;

 
Let me know if you have more questions.
 
Thanks,
Li Fan
 

 That seems to have done the trick! 


Thank you very much,

Dave



David, 
  
 You’re welcome, just let us know if you have more questions in the furture. 
  
 James

 I spoke too soon...  I just noticed that in the example application you have does not really show the issue as well as it could.  I am sending an other version to you via email.


In the example we are serializing a class we call a 'View' to a file. The view class contains a collection of GdiPlusRasterLayers and a collection of InMemoryFeatureLayers (tho we are not using that collection in this example application).  You can see how we do this in the methods PackageViewForStorage() and SerializeToFile().


Later we de-serialize the view into a WinformsMap when the 'Load Tab from File' menu item is clicked using the method UnpackageViewFromStorage().  This is where we have trouble... the deserialized raster is insisting on finding the original file on the original path even tho it already contains the bitmap and really should not need the original file.


This is pretty urgent for us at this point (please notice when this thread was opened) and extra attention would be greatly appriciated.


Thank you for your time,

Dave



 David,


 
I got your new version of sample, but it still doesn't set CurrentExtent of map after load deserialized layer, you should set CurrentExtent at UnpackageViewFromStorage method.
 
When you first load layer you set it as code below at LoadRaster method, but when you load layer again, you doesn't do anything or CurrentExtent.
 
            RasterLayer.Open();
            RectangleShape rect = RasterLayer.GetBoundingBox();
            RasterLayer.Close();

            Map.CurrentExtent = DoubleSize(rect);

 
 
Let me know if you still has problem.
 
Thanks,
James

 I tried this earlier:



r.Close();
GdiPlusRasterLayer newRaster = (GdiPlusRasterLayer)r.CloneDeep();

newRaster.Open();
RectangleShape rect = newRaster.GetBoundingBox();
newRaster.Close();

mt.Map.CurrentExtent = rect;

LayerOverlay imageOverlay = new LayerOverlay();

It didn't fix anything so I took it back out...


Dave



David, 
  
 It’s wired, do you add that code the line 113 of Form1.cs which inside a foreach block, I upload my screenshot of my result. 
  
 I only did this change, if possible you can let our support to put the updated sample to you on FTP and you can take a look. 
  
 Thanks, 
 James

 Here is my entire foreach loop which does not work:



//now we need to unpack the Map with the rasters
foreach (GdiPlusRasterLayer r in _view.Rasters)
{
    // When the rasters come back from Microsoft's deserializer routine they are enourmously bloated, this is a known bug
    // To solve the bloat, a deep clone is used and the original raster is allowed to go out of scope and be garbage collected
    // You have to close the raster before you can clone it
    r.Close();
    GdiPlusRasterLayer newRaster = (GdiPlusRasterLayer)r.CloneDeep();

    newRaster.Open();  //Fails on this line with error 'The file specified does not exist.'
    RectangleShape rect = newRaster.GetBoundingBox();
    newRaster.Close();

    mt.Map.CurrentExtent = rect;

    LayerOverlay imageOverlay = new LayerOverlay();
    if (newRaster.Name == null) newRaster.Name = "Raster";
    imageOverlay.Layers.Add(newRaster.Name, newRaster);
    mt.Map.Overlays.Add(imageOverlay);                        
}

Dave



David, 
  
 That’s what I did changes, however I realized that we may use different MapSuite dll to test, the 5.5.98.0 is working in my machine, so if yours are very old version, please get the latest version assemblies and have another try. 
  
 Thanks, 
 James

 Ok, I pulled down the daily Production Build (5.5.0.103) and tried that in the example program.  Now I get this error on the very first line inside the foreach loop  (r.Close())



   at ThinkGeo.MapSuite.Core.RasterLayer.get_IsOpenCore()
   at ThinkGeo.MapSuite.Core.Layer.get_IsOpen()
   at ThinkGeo.MapSuite.Core.Layer.Close()
   at MapMemTest.Form1.UnpackageViewFromStorage(View _view) in C:\Users\Dave\Documents\Visual Studio 2010\Projects\MapMemTest\MapMemTest\Form1.cs:line 106

Dave



Hello David, 
  
 Thanks for your further informatin, could you please provide the full exception? 
  
 Regards, 
  
 Gary

What I pasted before, plus this is the full exception:


System.NullReferenceException: Object reference not set to an instance of an object.


Dave


 



 It has been over a week since I supplied the requested information.  Can I get a status please?


Dave



Hello David, 
  
 Sorry for wait, I think you use the wrong dlls to test, 5.5.0.103 is release branch, could you please use 5.5.103.0(development branch) or later and have a try? 
  
 Regards, 
  
 Gary

Same error with 5.5.139.0...


Dave



Hi David, 
  
 The version 6.0 will be released soon, please keep an eye on newsletter, since you got the new dll you can try again and see if the issue is fixed. 
  
 Thanks, 
 James