Displaying Images

Editor
May 24, 2013 at 9:04 AM
Hi,

Is it possible to just load a normal jpg or tif image onto sharpmap?

We have users that need to be able to plot some points over an image (e.g a tube map or building plan). These images will not be georeferenced or have a world file, but we would still like the option of using them alongside real maps.

I had a quick look at the GdalRasterLayer, but that is probably a bit complex for just displaying an Image. Is there any other layers that can handle this type of data?

Would it be fairly easy to put together a new layerType if I implemented ILayer? and just supported the standard GDI+ supported image types?

Thanks,

Rob Smart
Editor
May 24, 2013 at 2:06 PM
I tried creating a new layer by inheriting from Layer and Implementing ILayer, but it doesn't draw. It basically never hits the layers render function. Have I missed something? I know the actual code inside the render loop is probably wrong, but I thought it should at least get hit?

This is the new layer:

public class GDIRasterLayer : Layer, SharpMap.Layers.ILayer
    {

        private bool _enabled;
        private Image _image;
        private GeoAPI.Geometries.Envelope _envelope;
        private string _layerName;


        public GDIRasterLayer(string layerName, string fileName)
        {
            _image = Image.FromFile(fileName);
            _layerName = layerName;
        }

        public Image Image
        {
            get { return _image; }
            set
            {
                _image = value;
                _envelope = new Envelope(0,_image.Width,0,_image.Height);
            }
        }

        public bool Enabled
        {
            get
            {
                return _enabled;

                //throw new NotImplementedException();
            }
            set
            {
                _enabled = value;
                //throw new NotImplementedException();
            }
        }

        public override GeoAPI.Geometries.Envelope Envelope
        {
            get { return _envelope; }
        }

        public string LayerName
        {
            get { return _layerName; }
            set { _layerName = value;}
        }

        public double MaxVisible
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public double MinVisible
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        /// <summary>
        /// No Proj needed
        /// </summary>
        public string Proj4Projection
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public void Render(Graphics g, SharpMap.Map map)
        {
            if (map.Center == null)
                throw (new ApplicationException("Cannot render map. View center not specified"));

            var envelope = map.Envelope; //View to render

            Coordinate worldPos = map.ImageToWorld(new Point(0,0));
            g.DrawImage(_image,(float)worldPos.X,(float)worldPos.Y);


        }

        /// <summary>
        /// No SRID needed
        /// </summary>
        public int SRID
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public int TargetSRID
        {
            get { throw new NotImplementedException(); }
        }
    }

and this is the code I'm using to load;

var MyRasterLayer = new GDIRasterLayer("Myraster2", "C:\Users\roberts\Downloads\GIS Data\standard-tube-map.tif");
        mapBox1.Map.BackgroundLayer.Add(MyRasterLayer);

        mapBox1.Map.ZoomToBox(MyRasterLayer.Envelope);
Coordinator
May 24, 2013 at 3:45 PM
From looking at the code:
  • Envelope is null because only set when Image property is set, the constructor assigns _image
  • Min-/MaxVisible values should not throw exceptions on getters, use double.Epsilon and double.MaxValue instead
  • I'm not sure when layers in BackgroundLayerCollection are actually drawn, maybe you need to place the layer in the default LayerCollection.
Hth FObermaier
Editor
May 24, 2013 at 4:05 PM
Good spot re Image vs _image

However it still isn't working. It's not hitting the render func at all.

This is how the layer looks now

public class GDIRasterLayer : Layer
    {

        private bool _enabled;
        private Image _image;
        private GeoAPI.Geometries.Envelope _envelope;
        private string _layerName;


        public GDIRasterLayer(string layerName, string fileName)
        {
            Image = Image.FromFile(fileName);
            _layerName = layerName;
        }

        public Image Image
        {
            get { return _image; }
            set
            {
                _image = value;
                _envelope = new Envelope(0,_image.Width,0,_image.Height);
            }
        }

        public bool Enabled
        {
            get
            {
                return _enabled;

                //throw new NotImplementedException();
            }
            set
            {
                _enabled = value;
                //throw new NotImplementedException();
            }
        }

        public override GeoAPI.Geometries.Envelope Envelope
        {
            get { return _envelope; }
        }

        public string LayerName
        {
            get { return _layerName; }
            set { _layerName = value;}
        }

        public double MaxVisible
        {
            get { return double.MaxValue; }
            set
            {

            }
        }

        public double MinVisible
        {
            get { return double.Epsilon; }
            set { }
        }

        /// <summary>
        /// No Proj needed
        /// </summary>
        public string Proj4Projection
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public void Render(Graphics g, SharpMap.Map map)
        {
            if (map.Center == null)
                throw (new ApplicationException("Cannot render map. View center not specified"));

            var envelope = map.Envelope; //View to render

            Coordinate worldPos = map.ImageToWorld(new Point(0,0));
            g.DrawImage(_image,(float)worldPos.X,(float)worldPos.Y);
            base.Render(g,map);

        }

        /// <summary>
        /// No SRID needed
        /// </summary>
        public int SRID
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public int TargetSRID
        {
            get { throw new NotImplementedException(); }
        }
    }
Coordinator
May 24, 2013 at 4:35 PM
You switched from ILayer to Layer, therefor you need to override the Render (and other) methods.
Editor
May 24, 2013 at 5:00 PM
Hi,

Thanks, that got it.

I just changed it so that it is always rendering it at 0,0 so it at least shows.

Whats the best way of handling the zoom in and out? don't worry about answering if its to complex as I'll figure it out when I get time, but if you happen to know off the top of your head then it'll be a quick win.

Thanks for your help.
Coordinator
May 27, 2013 at 7:57 AM
I'd say you have to associate your image with some Envelope, that defines where you want it to be drawn.
When it comes to rendering, you have to
  • transform the envelopes upperleft point to a PointF using Map.Transform.WorldToImage and
  • calculate new width and hight in map units.
Then you should have everything you need to call graphics.DrawImage function.
Hth FObermaier
Jul 10, 2013 at 8:27 AM
was this future worked for you? Can you share your GDIRasterLayer here?

This future is very useful but I can not find it in the newest SharpMap, Hope it will be included.
Editor
Jul 10, 2013 at 8:37 AM
Hi,

yes I got this working. It draws that image and you can zoom in and out etc. I'm not entirely sure that I have the corner coordinates correct, but it really was just thrown together to see if it was possible. Please feel free to modify and upload any improvements you make.

Thanks

public class GDIRasterLayer : Layer
    {

        private bool _enabled;
        private Image _image;
        private GeoAPI.Geometries.Envelope _envelope;
        private string _layerName;


        public GDIRasterLayer(string layerName, string fileName)
        {
            Image = Image.FromFile(fileName);
            _layerName = layerName;
        }

        public Image Image
        {
            get { return _image; }
            set
            {
                _image = value;
                _envelope = new Envelope(0,_image.Width,0,_image.Height);
            }
        }

        public bool Enabled
        {
            get
            {
                return _enabled;

                //throw new NotImplementedException();
            }
            set
            {
                _enabled = value;
                //throw new NotImplementedException();
            }
        }

        public override GeoAPI.Geometries.Envelope Envelope
        {
            get { return _envelope; }
        }

        public string LayerName
        {
            get { return _layerName; }
            set { _layerName = value;}
        }

        public double MaxVisible
        {
            get { return double.MaxValue; }
            set
            {

            }
        }

        public double MinVisible
        {
            get { return double.Epsilon; }
            set { }
        }

        /// <summary>
        /// No Proj needed
        /// </summary>
        public string Proj4Projection
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override void Render(Graphics g, SharpMap.Map map)
        {
            if (map.Center == null)
                throw (new ApplicationException("Cannot render map. View center not specified"));

            var envelope = map.Envelope; //View to render

           // Coordinate worldPos = map.ImageToWorld(new PointF((float)this.Envelope.MinX,(float)this.Envelope.MinY));
            var worldPos1 = map.WorldToImage(this.Envelope.BottomLeft());
            var worldPos2 = map.WorldToImage(this.Envelope.TopRight());
           // Console.WriteLine(worldPos1.ToString());

            g.DrawImage(_image, worldPos1.X,worldPos1.Y, (float)(worldPos2.X - worldPos1.X),(float)( worldPos1.Y - worldPos2.Y));

            base.Render(g,map);

        }

        /// <summary>
        /// No SRID needed
        /// </summary>
        public int SRID
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public int TargetSRID
        {
            get { throw new NotImplementedException(); }
        }
    }
Jul 15, 2013 at 8:25 AM
Thanks for your work. I also tried "GdalRasterLayer" of "SharpMap.Extensions" Project. It work with no errors and when I add a ".jpg" file within GdalRasterLayer, It shows in the map but there is a problem the image looks strange(be out of shape). you can try this one. if there is any more I got I will update soon.
Editor
Jul 15, 2013 at 4:47 PM
Hi,

yes it just needs little work figuring out the corner coordinates. Shouldn't be very hard, I've just had no time to look at it.


the advantage with GDILayer over GDALlayer is that you don't have to have GDAL installed to run it.
Coordinator
Jul 16, 2013 at 10:14 AM
This information might help to position the images correctly:
http://en.wikipedia.org/wiki/World_file
Editor
Jan 28, 2014 at 4:15 PM
I have successfully created a GDI+ layer which works OK.

The only problem is that .net is not very good at handling larger images. So for these I thought I could use the Gdal raster layer.

When I try and load my jpeg or png into this, I just get a cropped off part of the bottom of the image showing. I think this might be something to do with the envelope being calculated wrongly or something, as the envelope values are completely wrong. The envelope is protected so I cannot modify it after load to test this theory.

I'm using the extensions project which I downloaded in October 2013. Should this work? have any changed been made recently to address this?
the image is just an ordinary png.

my code is simply:
         var myLayer = new GdalRasterLayer("bigRaster", @"Q:\USER_BACKUPS\chenai.png");

        map.Layers.Add(myLayer);

        map.ZoomToBox(myLayer.Envelope);
Thanks
Coordinator
Jan 28, 2014 at 7:03 PM
Do you have a world file for your image? If you have that, the bounds should be ok.
Editor
Jan 28, 2014 at 9:21 PM
No world file as it is a floor plan rendered to a png so doesn't really have a world file. Ideally i'd like to be able to provide an extent in the constructor or something. The extent of this image should be the same as its height and width.
Coordinator
Jan 28, 2014 at 9:38 PM
We could create a layer that fulfills your requirement
.ctor- parameters: path to image-file and extent + CRS of it
It could be implemented to use GDALRasterlayer behind the scenes for rendering...
Coordinator
Jan 28, 2014 at 10:36 PM
Robert_Smart wrote:
No world file as it is a floor plan rendered to a png so doesn't really have a world file. Ideally i'd like to be able to provide an extent in the constructor or something. The extent of this image should be the same as its height and width.
World file is a big word. It just references your image in regard to other sources you have. It is a text file with 6 double values. See its definition. For your requirement its content would be

1.0
0.0
0.0
-1.0
0.0
<height>
Editor
Jan 28, 2014 at 10:49 PM
Hi, yes i know about world files, its just that these may be getting read directly from client network paths, so there might not be an option to add a world file to there directory. If i could just stream a generic world file to the gdal layer or provide the extents/pixel sizes manually this would work great.

I'll try the world file idea first, but it would be good to be able to this through code without needing to write a world file.

I'll also send you my gdiRasterlayer as it is a nice lightweight layer for getting simple images loaded into sharpmap.

Thanks
Editor
Jan 31, 2014 at 11:37 AM
I tried it with a world file with these parameters.

1.0
0.0
0.0
-1.0
0.0
6336

but it did the same thing. Just an small section showing in the map window.

for png I gave it the same name as the image with an extension of wpg. I also tried a jpeg verison with extension wjg.

Any more suggestions?
Coordinator
Jan 31, 2014 at 11:42 AM
For a png file the extension should be pgw, for jpg it should be jgw.
Editor
Jan 31, 2014 at 11:50 AM
D'oh...I got my w's round the wrong way!

that fixed it. Thanks Felix.

Rob
Feb 15, 2014 at 9:01 AM
Edited Feb 15, 2014 at 9:02 AM
Hi!

I'm using Robert's GDIRasterLayer to display a png image over OSM.
I've created pgw file with same name in same folder.
Image is expected to change position on the map and scale, but nothing changes.

Layer creation looks like this:
GDIRasterLayer myLayer = new GDIRasterLayer("bigRaster", @"C:\Users\...\bin\Debug\plan.png");
Contents of world file (\Debug\plan.pgw):
0.0558
0.0
0.0
-0.0558
4211099.8061753744
7503537.89936463
SharpMap version (displayed in VS) 1.0.4709.40104.
Coordinator
Feb 15, 2014 at 10:25 AM
The code that handles worldfile is in Branches/1.0/SharpMap/Layers/GdiImageLayer.cs

Or you update to version 1.1