3

I have a raster derived from a .png which has been drawn into a geodatabase. I also have the spatial reference and extent coordinates where I want to georeference the raster.

Is there a way to accomplish it with ArcObjects (10.4.1 SDK) ?

Here is the code I've got so far:

            IEnvelope envelope = ArcMap.Document.ActiveView.Extent;

            byte[] image = await GetImageAsync(endpointUri, accessToken);

            MemoryStream ms = new MemoryStream(image);
            Image i = Image.FromStream(ms);
            i.Save("demo.png");

            IRasterLayer rasterLayer = new RasterLayerClass();
            rasterLayer.CreateFromFilePath("demo.png");

            // Need to georeference rasterLayer

            foreach (ILayer layer in layers)
            {
               // Get a random feature spatial reference
                rasterLayer.SpatialReference = randomFeature.Shape.SpatialReference;

                break;
            }
            IDisplay display = ArcMap.Document.ActiveView.ScreenDisplay;

            (ArcMap.Document as ESRI.ArcGIS.ArcMapUI.IMxDocument).FocusMap.AddLayer(rasterLayer);
            rasterLayer.Draw(ESRI.ArcGIS.esriSystem.esriDrawPhase.esriDPGeography, display, null);
            ArcMap.Document.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null);

UPDATE:

Obtaining this result after adding world file (.pnw) creation, as @MichaelStimson suggested. Way closer, but still doesn't draw as expected. Do I have it right?

            IEnvelope envelope = ArcMap.Document.ActiveView.Extent;

            byte[] image = await GetImageAsync(endpointUri, accessToken);
            MemoryStream ms = new MemoryStream(image);
            Image i = Image.FromStream(ms);

            i.Save("demo.png");

            // Generate demo.pnw file to georeference .png
            if (!File.Exists("demo.pnw"))
            {
                File.Create("demo.pnw").Dispose();
                StreamWriter streamWriter = File.AppendText("demo.pnw");

                double A = envelope.Width / i.Height;
                double D = 0d;
                double B = 0d;
                double E = -(envelope.Height / i.Width);
                double C = envelope.UpperLeft.X;
                double F = envelope.UpperLeft.Y;

                CultureInfo cultureInfo = new CultureInfo("en-US");
                streamWriter.Write(string.Format(cultureInfo, "{0}\n{1}\n{2}\n{3}\n{4}\n{5}", A, D, B, E, C, F));
                streamWriter.Dispose();
            }
            IRasterLayer rasterLayer = new RasterLayerClass();
            rasterLayer.CreateFromFilePath("demo.png");

            IQueryFilter queryFilterClass = new QueryFilterClass();
            foreach (ILayer layer in layers)
            {
                if (!layer.Valid) continue;

                rasterLayer.SpatialReference = ((IGeoDataset)layer).SpatialReference;

                break;
            }
            IDisplay display = ArcMap.Document.ActiveView.ScreenDisplay;

            (ArcMap.Document as ESRI.ArcGIS.ArcMapUI.IMxDocument).FocusMap.AddLayer(rasterLayer);
            rasterLayer.Draw(ESRI.ArcGIS.esriSystem.esriDrawPhase.esriDPGeography, display, null);
            ArcMap.Document.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null);

UPDATE 2:

After correcting the extension to .pgw and tweaking a bit with the x and y scale it does seem that it is scaling properly but it is not translating where it should (it doesn't even overlap with the envelope), even though I'm just setting in the file the same envelope coordinates used to originally request the image.

Here's the code so far:

            IEnvelope envelope = ArcMap.Document.ActiveView.Extent;

            byte[] image = await GetImageAsync(endpointUri, accessToken);
            MemoryStream ms = new MemoryStream(image);
            Image i = Image.FromStream(ms);

            i.Save("demo.png");

            // Generate demo.pgw file to georeference .png
            if (!File.Exists("demo.pgw"))
            {
                File.Create("demo.pgw").Dispose();
                StreamWriter streamWriter = File.AppendText("demo.pgw");

                double A = envelope.Width / i.Width;
                const double D = 0d;
                const double B = 0d;
                double E = -(envelope.Height / i.Height);
                double C = envelope.UpperLeft.X;
                double F = envelope.UpperLeft.Y;

                CultureInfo cultureInfo = new CultureInfo("en-US");
                streamWriter.Write(string.Format(cultureInfo, "{0}\n{1}\n{2}\n{3}\n{4}\n{5}", A, D, B, E, C, F));
                streamWriter.Dispose();
            }

            IRasterLayer rasterLayer = new RasterLayerClass();
            rasterLayer.CreateFromFilePath("demo.png");
            rasterLayer.SpatialReference = envelope.SpatialReference;

            (ArcMap.Document as ESRI.ArcGIS.ArcMapUI.IMxDocument).FocusMap.AddLayer(rasterLayer);

            IDisplay display = ArcMap.Document.ActiveView.ScreenDisplay;
            rasterLayer.Draw(ESRI.ArcGIS.esriSystem.esriDrawPhase.esriDPGeography, display, null);

            ArcMap.Document.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null);
10
  • 1
    Sure, the easiest way is to create a pgw file and write the world file webhelp.esri.com/arcims/9.3/General/topics/… then open the raster as an IRasterLayer. Note the cell size is the width of the envelope divided by the png columns for X and height of the envelope divided by the png rows as a negative number for the Y as the hook point is the top left of your envelope. Commented Jun 12, 2019 at 3:17
  • 1
    Note your spatial reference can be obtained by an IGeoDataset resources.arcgis.com/en/help/arcobjects-net/componenthelp/… which implements FeatureLayer and RasterLayer so instead of getting the spatial reference from a random feature (which may have a Null geometry or no features in the layer) you can grab your reference from (layer as IGeoDataset).SpatialReference but first check layer.Valid resources.arcgis.com/en/help/arcobjects-net/componenthelp/… to ensure it's not broken already. Commented Jun 12, 2019 at 3:21
  • 1
    It's pgw not pnw, the first letter, last letter and w. Thus for a bitmap (bmp) it's bpw, for a tif it's tfw etc. Your calculations look correct for the parameters. Commented Jun 12, 2019 at 22:56
  • 1
    Another comment about spatial reference, it seems to me that you're grabbing the spatial reference of just any layer which may be different to the map, I think it would be safer to use rasterLayer.SpatialReference = ArcMap.Document.ActiveView.Extent.SpatialReference; or more simply rasterLayer.SpatialReference = envelope.SpatialReference; to ensure the raster is in the same CRS as the current display. Commented Jun 13, 2019 at 0:23
  • 1
    Thank you very much @MichaelStimson , I'm still having trouble with the translation part... I've updated my question. About the Spatial Reference, you couldn't be righter, code makes much more sense right now.
    – Jose
    Commented Jun 13, 2019 at 10:07

1 Answer 1

2

I have it working. My solution was to generate a world file (.pgw in my case as my image was a PNG) Link and invaluable assistance provided by @MichaelStimson.

Here it is the working code:

            IEnvelope envelope = ArcMap.Document.ActiveView.Extent;

            byte[] image = await GetImageAsync(endpointUri, accessToken);
            MemoryStream ms = new MemoryStream(image);
            Image i = Image.FromStream(ms);

            i.Save("demo.png");

            // Generate demo.pgw file to georeference .png
            if (!File.Exists("demo.pgw"))
            {
                File.Create("demo.pgw").Dispose();
                StreamWriter streamWriter = File.AppendText("demo.pgw");

                double A = envelope.Width / i.Width;
                const double B = 0d;
                const double C = 0d;
                double D = -(envelope.Height / i.Height);
                double E = envelope.UpperLeft.X;
                double F = envelope.UpperLeft.Y;

                CultureInfo cultureInfo = new CultureInfo("en-US");
                streamWriter.Write(string.Format(cultureInfo, "{0}\n{1}\n{2}\n{3}\n{4}\n{5}", A, B, C, D, E, F));
                streamWriter.Dispose();
            }

            IRasterLayer rasterLayer = new RasterLayerClass();
            rasterLayer.CreateFromFilePath("demo.png");
            rasterLayer.SpatialReference = envelope.SpatialReference;

            ILayerEffects layerEffects = (ILayerEffects)rasterLayer;
            layerEffects.Transparency = 50;

            (ArcMap.Document as ESRI.ArcGIS.ArcMapUI.IMxDocument).FocusMap.AddLayer(rasterLayer);

            IDisplay display = ArcMap.Document.ActiveView.ScreenDisplay;
            rasterLayer.Draw(ESRI.ArcGIS.esriSystem.esriDrawPhase.esriDPGeography, display, null);

            ArcMap.Document.ActiveView.PartialRefresh(esriViewDrawPhase.esriViewGeography, null, null);

And here is the final result:

RasterLayer overlapping envelope

1
  • 1
    Excellent! Glad to help. +1 from me. Commented Jun 14, 2019 at 0:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.