ThinkGeo.com    |     Documentation    |     Premium Support

Specializing GdiPlusRasterLayer

The GdiPlusRasterLayer class seems very similar to the ImageLayer class in Desktop Edition 2.0 in that you pass it the name of a georeferenced image file on disk. I am working in a more-or-less "diskless" environment, so I cannot save raster imagery to disk. Instead, I have a Bitmap object in memory (as well as a RectangleShape to describe the bounding box). How can I specialize a class similar to GdiPlusRasterLayer so that it will use an in-memory Bitmap object rather than a filename?


Thanks!



Gregory,


Thanks for your post.
 
I think you can achieve this by using the event SteamLoading, we have a HowDoI sample showing how to load the ShapeFileFeatureLayer from a stream, just take a look.
HowDoIsamples\Data Providers\ LoadAMapFromAStream
 
Following shows the way of hooking up the event:

GdiPlusRasterLayer gdiPluseRasterLayer = new GdiPlusRasterLayer(@"C:\DoesNotExistDirectory\Countries02.bmp");
((GdiPlusRasterSource)gdiPluseRasterLayer.ImageSource).StreamLoading += new EventHandler<StreamLoadingEventArgs>(LoadAMapFromStreams_StreamLoading);

 

Any more questions please feel free to let me know.
 
Thanks.
 
Yale


Yale: 
  
   I tried a variation of your sample (you can see the code below) and an exception is thrown. My event is never called.  
  
     class ImageRasterLayer : GdiPlusRasterLayer 
     { 
         #region “Constant declarations…” 
         private const string DOES_NOT_EXIST = @“C:\DoesNotExistDirectory\Countries02.bmp”; 
         #endregion 
         #region “Properties…” 
         private Image _rasterImage; 
         private RectangleShape _rectangleShape; 
         #endregion 
  
         #region “Constructors…” 
         private ImageRasterLayer() 
             : base(DOES_NOT_EXIST) 
         { 
         } 
  
         public ImageRasterLayer(Image rasterImage) 
         { 
             _rasterImage = rasterImage; 
             ((GdiPlusRasterSource)this.ImageSource).StreamLoading += new EventHandler<StreamLoadingEventArgs> 
                   (ImageRasterLayer_StreamLoading); 
         } 
  
         #endregion 
  
         #region “Events…” 
         private void ImageRasterLayer_StreamLoading(object sender, StreamLoadingEventArgs e) 
         { 
              
             MemoryStream memoryStream = new MemoryStream(); 
             _rasterImage.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Bmp); 
             e.AlternateStream = memoryStream; 
         } 
         #endregion 
     } 
  
   and here’s the exception I get: 
  
    at ThinkGeo.MapSuite.Core.Validators.CheckFileIsSupportedByCommonImageSources(String extension) 
    at ThinkGeo.MapSuite.Core.GdiPlusRasterSource.OpenCore() 
    at ThinkGeo.MapSuite.Core.RasterSource.Open() 
    at ThinkGeo.MapSuite.Core.RasterLayer.OpenCore() 
    at ThinkGeo.MapSuite.Core.Layer.Open() 
    at ThinkGeo.MapSuite.DesktopEdition.LayerOverlay.DrawCore(GeoCanvas canvas) 
    at ThinkGeo.MapSuite.DesktopEdition.Overlay.Draw(GeoCanvas canvas) 
    at ThinkGeo.MapSuite.DesktopEdition.WinformsMap.x5e1f8125aa040824(Object x2680f14bfcc5c488) 
    at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state) 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
    at System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(_ThreadPoolWaitCallback tpWaitCallBack) 
    at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state) 
  
 what am I doing incorrectly?

Actually, let me retract that last posting. That’s a big DUH!!! 
  
 Here’s what I had to do to fix: 
  
         public ImageRasterLayer(Image rasterImage) 
             : base(DOES_NOT_EXIST) 
         { 
             _rasterImage = rasterImage; 
             ((GdiPlusRasterSource)this.ImageSource).StreamLoading += new EventHandler<StreamLoadingEventArgs> 
                 (ImageRasterLayer_StreamLoading); 
         } 


Yes, the base function should be called when you write a new class inherited from the GdiPlusRasterSource, because some initial code in the base function should be executed when you create the instance. 
  
 Any more questions please let me know. 
  
 Thanks, 
  
 sun 


Thanks for all the help so far! 



I'm still having an issue with the overidden DrawCore method. When DrawCore is called an exception is thrown: 



protected override void DrawCore(...) 



base.DrawCore(...); 





base.DrawCore throws this exception: 



The ImageSource is not open. Please call the Opn method before calling this method. 



So I added the following: 



protected override void DrawCore(...) 



this.ImageSource.Open(); 

base.DrawCore(...); 

this.ImageSource.Close(); 





however, the exception is still thrown. I inspected this.ImageSource after the Open() call and IsOpen is nonetheless set to false. 



I am uploading my source code and would appreciate your jaundiced eye to look it over and tell me what I'm doing wrong! 



Thanks! 

 



1301-ImageRasterLayer.zip (1.51 KB)

 



Gregory,
 
I can not recreate your problem under the latest version DesktopEdition by using the sample which you supply. Can you tell me which version you are using? 
 
I don’t have jpeg image with world file, so I use tif file which you can find at SampleData folder under DesktopEdition install folder.
 
This is my client side code:


private void Form1_Load(object sender, EventArgs e)

winformsMap1.MapUnit = GeographyUnit.DecimalDegree; 
winformsMap1.BackgroundOverlay.BackgroundBrush = new GeoSolidBrush(GeoColor.GeographicColors.ShallowOcean); 
Bitmap bitmap = new Bitmap(@"..\..\App_Data\world.tif"); 
MemoryStream stream = new MemoryStream(); 
bitmap.Save(stream, ImageFormat.Tiff); 
string worldFile = string.Empty; 
using(StreamReader streamReader = new StreamReader( @"..\..\App_Data\world.tfw")) 

worldFile = streamReader.ReadToEnd(); 

ImageRasterLayer worldImageLayer = new ImageRasterLayer(stream.ToArray(), worldFile); 
worldImageLayer.UpperThreshold = double.MaxValue;  worldImageLayer.LowerThreshold = 0; 
worldImageLayer.IsGrayscale = false; 
LayerOverlay ImageOverlay = new LayerOverlay(); 
ImageOverlay.Layers.Add("WorldImageLayer", worldImageLayer);  winformsMap1.Overlays.Add(ImageOverlay); 
winformsMap1.CurrentExtent = new RectangleShape(-118.098, 84.3, 118.098, -84.3); 
winformsMap1.Refresh(); 


 


As you mentioned you have two kinds of DrawCore methods, both of them will throw exception. 
 
If I use following code it works, not any exception.

protected override void DrawCore(GeoCanvas canvas, Collection<simplecandidate> labelsInAllLayers) 

base.DrawCore(canvas, labelsInAllLayers); 
}
 

 
 
If I following code it works first time call this method, when the second time, it will throw ArgumentException.

 

protected override void DrawCore(GeoCanvas canvas, Collection<simplecandidate> labelsInAllLayers) 

this.ImageSource.Open(); 
base.DrawCore(canvas, labelsInAllLayers); 
this.ImageSource.Close(); 


 



Illegal characters in path.
   at System.IO.Path.CheckInvalidPathChars(String path)
   at System.IO.Path.GetExtension(String path)
 
This exception is because the alternate stream name is wrong; you can update your method added following statement.
 


private void ImageRasterLayer_StreamLoading(object sender, StreamLoadingEventArgs e) 

try 

if (e.AlternateStreamName.IndexOf("JGW") < 0) 

e.AlternateStream = OpenImageStream(); 
// add this code 
e.AlternateStreamName = DOES_NOT_EXIST; 

else 

e.AlternateStream = OpenWorldFileStream();
 } 

catch (Exception ex) 

ExceptionHandler.LogException(ex); 
this.LoadedError = true; 
this.LoadedException = ex; 



 


 

 


Please let me know if you still have problem. 

 


Thanks.

 


James