ThinkGeo.com    |     Documentation    |     Premium Support

Loading images... 101

Ok... we have finally gotten to the point where we are ready to load raster images from our database.   We have these stored as blobs, and can readily turn them into binary streams.    But I'm at a bit of a loss on how to proceed, depending upon the type of imagery.


We have some images that are small .bmp or .png images, with world files.   I can create a stream for the image portion, and I know the world file (or bounding box) information.    Looks like I create a GDIPlusRasterSource and provide the stream on the StreamLoading event?   But, will I get the loading event twice... once for the image and once for the world file?   Or do I need to set the image extent in the constructor.    If the latter, do you treat the image extent as the outer edge of the pixel (as would be implied), or the center of the pixel, as defined in the world file spec?


Then, we also have some imagery that is ECW, JPG2000, and SID.   These have embedded georeferencing.  However, I don't see a StreamLoading event on the matching raster data source objects.    To load this imagery, will I need to write the data I extract from our database out to a temp file?


I've searched on StreamLoading, and did not find any answer to these questions.   And the HowDoI samples don't show loading anything from a stream.   I'll be glad to look in other locations for documentation, if you can give me a nudge.


Thanks!



Ted, 
  
 The good news and bad news is that the streaming works well for all types except MrSid & ECW.  The reason is that the libraries the vendors provide us do not have a way to load from a stream.  Their API requires a filename so I think in that case you need to write it out to a temp file. 
  
 When you set the path and file name for the image file you can use whatever string you want.  The stream loading event will fire and pass you back the string you passed in.  In the case where you have a world file it will be called twice, once for the image and once for the world file.  I wouldn’t bother using the extent in the constructor, I would use the stream for the world file as well. 
  
   We will create a code community sample to show how to load from a stream.   
  
 David

 Ted,


  Here is an example if the stream loading.  We have a code community project that is going to be posted soon but wanted to share the code with you first.




        private void TestForm_Load(object sender, EventArgs e)
        {
            winformsMap1.MapUnit = GeographyUnit.DecimalDegree;
            winformsMap1.CurrentExtent = new RectangleShape(-180, 90, 180, -90);

            GdiPlusRasterLayer worldImageLayer = new GdiPlusRasterLayer(@"world.tif");
            ((GdiPlusRasterSource)(worldImageLayer.ImageSource)).StreamLoading += new EventHandler<StreamLoadingEventArgs>(MainForm_StreamLoading);
            worldImageLayer.UpperThreshold = double.MaxValue;
            worldImageLayer.LowerThreshold = 0;
            worldImageLayer.IsGrayscale = false;

            LayerOverlay testOverlay = new LayerOverlay();
            testOverlay.Layers.Add("worldImageLayer", worldImageLayer);
            winformsMap1.Overlays.Add("testOverlay", testOverlay);

            winformsMap1.Refresh();
        }

        void MainForm_StreamLoading(object sender, StreamLoadingEventArgs e)
        {
            if (e.AlternateStreamName.Contains(".TIF"))
            {
                Stream stream = new FileStream(@"..\..\Data\world.tif", FileMode.Open, FileAccess.Read);
                e.AlternateStream = stream;
            }

            if (e.AlternateStreamName.Contains(".TFW"))
            {
                Stream stream = new FileStream(@"..\..\Data\world.tfw", FileMode.Open, FileAccess.Read);
                e.AlternateStream = stream;
            }
        }


David



Ted, 
  
   We also added this as a code community item.  You can find it at the link below. 
  
 code.thinkgeo.com/projects/show/imagestreamloading 
  
 David

Thank you, David.  But, as we let the user load imagery of their choice, we can’t be sure that it won’t be ECW, JPG2000, or SID.   So, we are going to follow your recommendation to extract two blobs from the database for each image… the image,  and our own structure that contains the requisite georeferencing information (as a world file has no embedded projection info). 
  
 And that leads back to the first question.   You recommended against constructing the image with an extent rectangle.   Can you elaborate on that?  And, if we did decide to do that, can you answer the question about whether the rectangle should extent to the edges of the pixels, or the middles? 
  
 Thanks!

Ted, 
  
   How did I know you wanted answers to those questions.  The reason I led you away from that was I am not sure about the pixel stuff relating to the bounding box.  I just figured that if you had the world file information handy it was easier to just create that file.  I will find out about exactly how the rectangle bounding box relates to the pixel width etc…  Dig, dig… 
  
 David

Ted, 
  
 I checked in the code and I checked visually on the map using the World.tiff. The result is that the RectangleShape passed in the constructor needs to be for the overall bounding box of the image. This means that the upper left point of the RectangleShape is for the upper left part of the upper left pixel of the image. And the lower right point of the rectangleShape is for the lower right part of the lower right part of the image.  
  
 For example:  
  
 GdiPlusRasterLayer gdiPlusRasterLayer = new GdiPlusRasterLayer(@"C:\ThinkGeo\Support\MapData\World.tif", new RectangleShape(-180,90,180,-90)); 
  
 Is the same as: 
  
 GdiPlusRasterLayer gdiPlusRasterLayer = new GdiPlusRasterLayer(@"C:\ThinkGeo\Support\MapData\World.tif"); 
  
 With the world fie World.tfw: 
  
 0.36000 
 0 
 0 
 -0.36000 
 -179.82000 
 89.8199999999999872 
  
 I get exactly the same result from the GetBoundingBox, although there seem to be some precision issues: 
  
  double ulX = gdiPlusRasterLayer.GetBoundingBox().UpperLeftPoint.X; //-180.00000733137131 
  double ulY = gdiPlusRasterLayer.GetBoundingBox().UpperLeftPoint.Y;//89.999999701976776 
  double lrX = gdiPlusRasterLayer.GetBoundingBox().LowerRightPoint.X;//180.00000697374344 
  double lrY = gdiPlusRasterLayer.GetBoundingBox().LowerRightPoint.Y;//-90.0000074505806 
  
 I hope that helps. 
  
 David

That's perfect, David.   Exactly how I hoped it worked.


Thank you.



Ted, 
  
   Excellent.  Let me know if you have any other issues. 
  
 David