Plotting Latitude, Longitude, and Elevation


https://www.nevron.com/Forum/Topic9581.aspx
Print Topic | Close Window

By Sean Dorsett - 4 Years Ago
I am having some confusion with plotting latitude, longitude, and elevation via the NMeshSurfaceSeries. I see that we have to provide indexX, and indexZ values. I am not entirely clear on why. From what I gather, the values are for an array of sorts? The data I am using is  a comma delimited file containing latitude, longitude, and elevation. How would the data be translated in the method provided below? Thanks in advance for your help.

Latitude, Longitude, Depth
3357.424,-8648.6006,1
3357.4242,-8648.6004,1
3357.4245,-8648.6001,1.1
3357.4242,-8648.6004,1.2
3357.4248,-8648.6,1.3
3357.4248,-8648.6,0.6
3357.4254,-8648.5997,0.6
3357.4254,-8648.5997,0.6
3357.4254,-8648.5997,0.6
3357.4254,-8648.5997,0.6
3357.4254,-8648.5997,0.6
3357.4265,-8648.5976,0.6
3357.4266,-8648.5971,0.7
3357.4268,-8648.5965,0.7
3357.4268,-8648.5965,0.7
3357.4268,-8648.5965,0.7
3357.427,-8648.5955,0.7
3357.427,-8648.5955,0.7
3357.4273,-8648.5943,0.8
3357.4273,-8648.5943,0.8


        private void FillData(NMeshSurfaceSeries surface)
        {
            //double x, y, z;
            //int nCountX = surface.Data.GridSizeX;
            //int nCountZ = surface.Data.GridSizeZ;

            //for (int j = 0; j < nCountZ; j++)
            //{
            //    for (int i = 0; i < nCountX; i++)
            //    {
            //        x = 2 + i + Math.Sin(j / 4.0) * 2;
            //        z = 1 + j + Math.Cos(i / 4.0);

            //        y = Math.Sin(i / 3.0) * Math.Sin(j / 3.0);

            //        if (y < 0)
            //        {
            //            y = Math.Abs(y / 2.0);
            //        }

            //        System.Diagnostics.Debug.WriteLine("{0},{1},{2},{3}", j, i, y, x, z);
            //        surface.Data.SetValue(j, i, y, x, z);
            //    }
            //}

            surface.Data.SetValue(j, i, y, x, z);

        }
By Sean Dorsett - 4 Years Ago
So I was able to determine that the data values were in degrees and minutes. So the first point 3357.424,-8648.6006,1 translate into latitude = 33.9570666, longitude = -86.81001, elevation = 1m. I was able to convert the latitude/longitude to cartesian coordinates for plotting a surface chart. Unfortunately, the data does not plot a smooth mesh surface series. I have attached my surface plot and the data used.https://www.nevron.com/forum/uploads/images/5b6a0a46-3489-46bc-9d69-863c.png
By Nevron Support - 4 Years Ago
Hi Sean,
Do you have an image of how this data should look like? If you send the coordinate transformation you work with we can also check what is the best way to present this data - it looks like that the data does not have an order so you should probably use the triangulated surface series (which accepts a set of 3D points and creates a surface by connecting them).
By Sean Dorsett - 4 Years Ago
Hello and thanks for the response. I figured out that I need to use a triangulated surface. I am including the code I wrote to generate the contour plot. At this point, I have two questions for you.

1) How can I display a horizontal plane at the 0th elevation. If you look at my code, I attempted to display a plane by simply obtaining the max and min X and Z values and setting their depth to zero. The results were not favorable. In one of the images provided by way of another plotting application, you can see the horizontal plane.

2) Is there a limitation on the number of data points to plot? When attempting to plot the data in the attached file LargeNumberOfData.csv, I get a Stack Overflow error. "An unhandled exception of type 'System.StackOverflowException' occurred in Nevron.Presentation.dll"

Lastly, I should point out that in my could I have added "Convert.ToDouble(point[2]) <= 5)" to deal with the data outlier issue. I suspect that I will have to add some logic to clean my data of outliers before plotting.. Smile  

using Nevron.Chart;
using Nevron.Chart.Windows;
using Nevron.GraphicsCore;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

namespace WindowsFormsApplication4
{
  public partial class Form1 : Form
  {
   public Form1()
   {
    InitializeComponent();
    nChartControl1.Settings.ShapeRenderingMode = ShapeRenderingMode.None;
    nChartControl1.Controller.Tools.Add(new NSelectorTool());
    nChartControl1.Controller.Tools.Add(new NTrackballTool());

    // set a chart title
    NLabel title = nChartControl1.Labels.AddHeader("Mesh Surface Chart");
    title.TextStyle.FontStyle = new NFontStyle("Times New Roman", 18, FontStyle.Italic);
    title.TextStyle.FillStyle = new NColorFillStyle(Color.AliceBlue);

    // setup chart
    NChart chart = nChartControl1.Charts[0];
    chart.Enable3D = true;
    chart.Width = 70.0f;
    chart.Depth = 70.0f;
    chart.Height = 30.0f;
    chart.Projection.SetPredefinedProjection(PredefinedProjection.PerspectiveTilted);

    // setup axes
    NLinearScaleConfigurator linearScale = new NLinearScaleConfigurator();
    linearScale.MajorGridStyle.SetShowAtWall(ChartWallType.Floor, true);
    linearScale.MajorGridStyle.SetShowAtWall(ChartWallType.Back, true);
    linearScale.RoundToTickMax = false;
    linearScale.RoundToTickMin = false;
    chart.Axis(StandardAxis.PrimaryX).ScaleConfigurator = linearScale;

    linearScale = new NLinearScaleConfigurator();
    linearScale.MajorGridStyle.SetShowAtWall(ChartWallType.Floor, true);
    linearScale.MajorGridStyle.SetShowAtWall(ChartWallType.Left, true);
    linearScale.RoundToTickMax = false;
    linearScale.RoundToTickMin = false;
    chart.Axis(StandardAxis.Depth).ScaleConfigurator = linearScale;

    // setup surface series
    NTriangulatedSurfaceSeries surface = new NTriangulatedSurfaceSeries();
    surface.Name = "Surface";
    surface.Legend.Mode = SeriesLegendMode.SeriesLogic;
    surface.FillMode = SurfaceFillMode.ZoneTexture;
    surface.ShadingMode = ShadingMode.Smooth;
    surface.FrameMode = SurfaceFrameMode.None;
    surface.FrameColorMode = SurfaceFrameColorMode.Zone;
    surface.DrawFlat = false;
    surface.PositionValue = 0.5;
    //surface.Data.SetGridSize(20, 20);
    surface.SyncPaletteWithAxisScale = false;
    surface.PaletteSteps = 8;
    surface.ValueFormatter.FormatSpecifier = "0.00";
    surface.FillStyle = new NColorFillStyle(Color.YellowGreen);

    chart.Series.Add(surface);


    List<GeospatialPoint> geospatialPoints = GetSurveyData();

    for (int i = 0; i < geospatialPoints.Count; i++)
    {
      surface.Values.Add(geospatialPoints[i].y);
      surface.XValues.Add(geospatialPoints[i].x);
      surface.ZValues.Add(geospatialPoints[i].z);   
    }

    nChartControl1.Refresh();
   }
  

   private List<GeospatialPoint> GetSurveyData()
   {
   
    List<GeospatialPoint> geospatialPoints = new List<GeospatialPoint>();

    try
    { 
      // Open the text file using a stream reader.
      using (StreamReader sr = new StreamReader("TestData.csv"))
      {
       string headerLine = sr.ReadLine();      
       string line;

       double minX = 0;
       double maxX = 0;
       double minZ = 0;
       double maxZ = 0;

       // Read the stream to a string.
       while ((line = sr.ReadLine()) != null)
       {
        if (line.Contains(","))
        {
          string[] point = line.Split(',');
          if (IsValidGeospatialPoint(point) && Convert.ToDouble(point[2]) <= 5)
          {
           double latitude = ConvertDegreeAngleToDouble(point[0]);
           double longitude = ConvertDegreeAngleToDouble(point[1]);
           double elevation = Convert.ToDouble(point[2]);

           double cosLat = Math.Cos(latitude * Math.PI / 180.0);
           double sinLat = Math.Sin(latitude * Math.PI / 180.0);
           double cosLon = Math.Cos(longitude * Math.PI / 180.0);
           double sinLon = Math.Sin(longitude * Math.PI / 180.0);
           double rad = 6378137.0;
           double f = 1.0 / 298.257224;
           double C = 1.0 / Math.Sqrt(cosLat * cosLat + (1 - f) * (1 - f) * sinLat * sinLat);
           double S = (1.0 - f) * (1.0 - f) * C;
           double h = 0;

           double cartesianX = ((rad * C + h) * cosLat * cosLon);
           double cartesianY = -elevation;
           double cartesianZ = ((rad * S + h) * sinLat);

           if (geospatialPoints.Find(geoPoint => (geoPoint.x == cartesianX && geoPoint.y == cartesianY && geoPoint.z == cartesianZ)) == null)
           {
            System.Diagnostics.Debug.WriteLine("{0}, {1}, {2}", cartesianX, cartesianZ, cartesianY);
            geospatialPoints.Add(new GeospatialPoint()
            {
              x = cartesianX,
              y = cartesianY,
              z = cartesianZ
            });
           }
          }
        }
       }
      
       //minX = (from p in geospatialPoints select p.x).Min();
       //minZ = (from p in geospatialPoints select p.z).Min();
       //maxX = (from p in geospatialPoints select p.x).Max();
       //maxZ = (from p in geospatialPoints select p.z).Max();

       //geospatialPoints.Add(new GeospatialPoint() { x = minX, z = minZ, y = 0 });
       //geospatialPoints.Add(new GeospatialPoint() { x = maxX, z = minZ, y = 0 });
       //geospatialPoints.Add(new GeospatialPoint() { x = maxX, z = maxZ, y = 0 });
       //geospatialPoints.Add(new GeospatialPoint() { x = minX, z = maxZ, y = 0 });
      }
    }
    catch (Exception e)
    {
      Console.WriteLine("The file could not be read:");
      Console.WriteLine(e.Message);
    }

    return geospatialPoints;
   }

   private double ConvertDegreeAngleToDouble(string degreesMinutes)
   {
    // Decimal degrees =
    // whole number of degrees,
    // plus minutes divided by 60,
    // plus seconds divided by 3600

    // 3357.4196
    // degrees = 33
    // minutes = 57.4196

    string[] degreesMinutesDivided = degreesMinutes.Split('.');
    string degrees = degreesMinutesDivided[0].Substring(0, degreesMinutesDivided[0].Length - 2);
    string minutes = degreesMinutesDivided[0].Substring(degrees.Length ) + "." + degreesMinutesDivided[1];
    string seconds = "0";

    return Convert.ToDouble(degrees) + (Convert.ToDouble(minutes) / 60) + (Convert.ToDouble(seconds) / 3600);
   }

   private bool IsValidGeospatialPoint(string[] point)
   {

    if (point.Length >= 3)
    {
      double latitude;
      double longitude;
      double elevation;

      if (Double.TryParse(point[0], out latitude) && Double.TryParse(point[1], out longitude) && Double.TryParse(point[2], out elevation))
      {
       return true;
      }

      return false;
    }

    return false;
   }
  }
}
By Sean Dorsett - 4 Years Ago
I am attaching a copy of what the plot should look like. I am also including images of what I rendered via the Nevron interface. The data for the plot is from the TestData.csv file.



By Nevron Support - 4 Years Ago
Hi Sean,

We think that the transformation produces incorrect points that's why you get such results. Please find attached a sample app that shows how to plot the same surface using grid and triangulated surface series. The reference image you attached is most likely displayed with grid surface equivalent as the x/z coordinates look equally spaced. Regarding your question:

1) The sample app shows that there is nothing special about the flat surface - simply the y coordinates of adjacent points are equal.

2) There is no internal limitation to the number of data points in the surface - some of our users use the control with millions of data points. The exception is very strange given the fact that VS does not show the stack trace and only appears when running the app in debug mode so it may be related to a .NET bug and not the control.

Hope this helps - let us know if you meet any problems or have any questions.
By Sean Dorsett - 4 Years Ago
Thanks for the sample application. I was hoping for some sort of example that used the data I provided.

Your answer for issue #2 is not going to fly for me. The error clearly states that there is a problem in the Nevron.Presentation.dll. Am I missing something here? I cannot have my client purchase your software if I can expect a stack overflow exception when plotting data. The file I provided has no more than 2400 data points. I will need a more concrete answer here.

System.StackOverflowException was unhandled
Message: An unhandled exception of type 'System.StackOverflowException' occurred in Nevron.Presentation.dll