7

Using ArcObjects (C#) I am trying to build a tool that will extract the Z value from a point in a LAS Dataset when the user clicks on it (then use that value elsewhere). My intention was to use the IIdentify2 interface to extract the Z value, but that doesn't seem to support LASDatasetLayer's

I have looked at a couple of other interfaces that might be of some use like ILasPointInfo or even an event. I feel like there should be one for clicking on a point in a LAS Dataset.

Unfortunately the documentation on this whole area of LASDatasets is pretty limited. Also I am pretty new to C# and ArcObjects so its pretty hard to work out how this all fits together.


I've rewritten the code and now have a path to the desired outcome. However there is a new problem. I have tried to use the 'DisplayTransformation.ToMapPoint()' method on the arg.X and arg.Y values from the MouseDownEvent (see code below). This simply inverts the Y value of the 'device coordinates'. Because of this by the time that I try to select a LAS point, the IGeometry object that is required in the GetLasPointInfo method is still in 'device coordinates' and is being used to search for a LAS point that is in 'real-world coordinates' - therefore the error below is returned. Are there other interfaces I should be using or is there a completely different way to do this?

enter image description here

As you can see I've tried to set the spatial reference to that of the Scene (GDA_1994_MGA_Zone_55). I have also tried to use the Project method on the point. Most likely in vein.

If you think of anything I've left out, let me know.

Environment: ArcScene 10.3.1, ArcObjects .NET

New code:

protected override void OnMouseDown(ESRI.ArcGIS.Desktop.AddIns.Tool.MouseEventArgs arg)
    {
        try
        {
            ITrackCancel tCancel = new CancelTrackerClass();

            ISxDocument sxdoc = ArcScene.Application.Document as ISxDocument;
            IScene scene = sxdoc.Scene as IScene;

            ILayer layer = scene.Layer[0] as ILayer;
            MessageBox.Show("Layer: " + layer.Name);

            IPoint clickPoint = new Point();
            clickPoint.PutCoords((double)arg.X, (double)arg.Y);
            MessageBox.Show(arg.X + ", " + arg.Y);

            ISpatialReference sRef = scene.SpatialReference;

            ISceneGraph sceneGraph = scene.SceneGraph;
            IScreenDisplay screenDisplay = sceneGraph as IScreenDisplay;
            IPoint scenePoint = screenDisplay.DisplayTransformation.ToMapPoint((int)clickPoint.X, (int)clickPoint.Y) as IPoint;
            MessageBox.Show("New coord sys for scene point: " + scenePoint.SpatialReference.Name);
            IPoint projScenePoint = new Point();
            scenePoint.Project(sRef);


            //IEnvelope projEnjGeoClick = geomClickPoint.Envelope as IEnvelope;
            MessageBox.Show("Projected coords: "+ scenePoint.X + ", " + scenePoint.Y);


            //ILayer layer = scene.Layer[0] as ILayer;
            MessageBox.Show("Layer: " + layer.Name);
            ILasDatasetLayer lasLayer = layer as ILasDatasetLayer;


            ILasDataset lasDataset = lasLayer as ILasDataset;
            ILasPointCloud lasPC = lasDataset as ILasPointCloud;
            IArray lasArray = lasPC.GetLasPointInfo(tCancel, scenePoint, 0) as IArray;

            ILasPointInfo lasPtInfo = lasArray.Element[0] as ILasPointInfo;
            WKSPointZ wksPtZ = lasPtInfo.get_WksLocation();
            double zValue = wksPtZ.Z;
            MessageBox.Show("Z Value: " + zValue);
        }

        catch (Exception ex)
        {
            MessageBox.Show(ex.Source + "\n\n" + ex.Message + "\n\n" + ex.InnerException + "\n\n" + ex.StackTrace);
        }

    }
  • 1
    I go straight to the LAS records, but that's using unsafe code in C# (so I use C++ for LAS) as you need to recast bytes to a structure. There must be a way to interrogate a LASdataset object but as this datasource is developing it's crucial to know what version of ArcGis you're coding for. The closest I can see is ILasDatasetLayer.GetCurrentSurface => ISurface then IFunctionalSurface.Z, this doesn't get a point but rather the interpolated Z at a given X,Y based on the LAS files which is in the v10 ArcObjects. – Michael Stimson May 12 '16 at 22:44
  • Im using ArcGIS 10.3.1. I have had issues using the 'interpolate shape' tool in ArcGIS. The LAS file is through a rail corridor so the Z value used isnt always the one i want. Eg. Two Z values at the same xy location that are separated by 10+ meters. – TheLastGIS May 12 '16 at 22:50
  • 1
    Rasterize the las dataset if you're only interested in the correct elevation, use the Natural Neighbors method. Sorry, but I think this interface is too new to be fully fleshed out. If you're over a corridor then play with your output extent environment to make boxes over the whole length then create a mosaic dataset.. to save space and process faster. – Michael Stimson May 12 '16 at 23:16
  • 1
    If you want to get a single point (rather an array of LAS points) you could use (YourLasDS as ILasPointCloud).GetLasPointInfo(null,YourClickedPoint,null) but I'm not sure if that searches with a buffer (you might need to buffer yourself) and what the array elements are, it says point info objects but what's that? Possilby ILasPointInfo but that's not stated implicitly. When you get the answer can you post as an answer to your own question; I'd be interested in what you came up with. – Michael Stimson May 13 '16 at 0:20
  • 2
    Did you try ISceneGraph.Locate, lamely documented here? It's also used in this github sample – Kirk Kuykendall Aug 17 '16 at 23:25
3
+50

This works at 10.2.2. It uses an envelope (constructed around a mouse-click point) to locate the LAS points. (I have it set as a small square based on map units for demo purposes, but something based on pixels may be better.) And instead of DisplayTransformation to get the map coordinates, I used ISceneGraph.Locate. Also, to get the LAS dataset that you need for the ILasPointCloud cast, you need the ILasDatasetLayer.Dataset.

protected override void OnMouseDown(ESRI.ArcGIS.Desktop.AddIns.Tool.MouseEventArgs arg)
{       
   IScene pScene = ArcScene.Scene;
   ISceneGraph pSceneGraph = pScene.SceneGraph;

   IPoint pPoint;
   object pOwner, pObject;
   pSceneGraph.Locate(pSceneGraph.ActiveViewer, arg.X, arg.Y, esriScenePickMode.esriScenePickGeography, true, out pPoint, out pOwner, out pObject);

   if (pPoint != null)
   {
       IEnvelope pEnv = new Envelope() as IEnvelope;
       pEnv.XMin = pPoint.X - 1;
       pEnv.XMax = pPoint.X + 1;
       pEnv.YMin = pPoint.Y - 1;
       pEnv.YMax = pPoint.Y + 1;

       ILasDatasetLayer pLas = pScene.Layer[0] as ILasDatasetLayer;
       ILasPointCloud pLasPointCloud = pLas.Dataset as ILasPointCloud;

       IArray pArray = pLasPointCloud.GetLasPointInfo(null, pEnv, 0);
       for (int i = 0; i < pArray.Count; i++)
       {
           ILasPointInfo pLasPointInfo = pArray.Element[i] as ILasPointInfo;
           var elevation = pLasPointInfo.get_AttributeValue(esriLasAttributeType.esriLasZ);
           MessageBox.Show(elevation.ToString());
       }  
   }
}

http://resources.arcgis.com/en/help/arcobjects-net/componenthelp/index.html#/ISceneGraph_Interface/000900000100000000/

  • 1
    Since this solution returns an IArray of points, the OP could modify to choose the max elevation, or the lowest point number, or the point with only certain other parameters. (ie first, highest, strongest, etc.) – JasonInVegas Aug 23 '16 at 17:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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