ThinkGeo.com    |     Documentation    |     Premium Support

How to plotting the point and display distance between two point

Hi

I already manage to display the raster map and read the elevation from Dted file. Now I need to plotting the point on the raster map , then it will display the distance between point just like popup. How can I achieve that?

Regards,

Fathin

Hi Fathin,

You can create a InmemoryFeatureLayer and add the points into it. Then set point style for them.

But if that’s elevation data, the distance between them is in the plane or space?

I think you can show the distance as label of the line between points.

You can calculate the lines, add the distance as column value of line feature. Then when you render the map, only render the text style but not assign line style, so the distance value will be render on map directly.

About the popup you mentioned I don’t think it’s a good choice for distance, because without the line you don’t know what it point to.

Regards,

Ethan

Hi Ethan,

I don’t quite understand about InmemoryFeatureLayer features. Did you have any simple example for that?
My elevation data work just like converter, it will display the elevation when the lat long on raster map are same with lat long on Dted file. I don’t know what you mean by is in the plane or space.

CODE

namespace RasterMapViewer
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window

    {   Proj4Projection proj4Projection;
        GeoTiffRasterLayer tiffLayer;      

        public MainWindow()
        {
            InitializeComponent();                       
        }

        private void Map_Loaded(object sender, RoutedEventArgs e)
        {                                     
            Map1.MapUnit = GeographyUnit.Meter;
            LayerOverlay myOverlay = new LayerOverlay();
            Map1.Overlays.Add(myOverlay);
            myOverlay.Open();

            myOverlay.TileCache = new FileBitmapTileCache(@"C:\Users\User\Documents\Visual Studio 2012\Projects\RasterMapViewer\RasterMapViewer\Cache\");

            string[] files = System.IO.Directory.GetFiles(@"C:\Users\User\Desktop\DSI REFERENCE\Map Data\Raster");

            foreach (string file in files)
            {
                GeoTiffRasterLayer tiffLayer = new GeoTiffRasterLayer(file);  
          
                myOverlay.Layers.Add(tiffLayer);
            }          
            //Apply the projection 
            proj4Projection = new Proj4Projection();
            proj4Projection.InternalProjectionParametersString = "+proj=omerc +lat_0=4 +lonc=102.25 +alpha=323.0257905 +k=0.99984 +x_0=804670.24 +y_0=0 +no_uoff +gamma=323.1301023611111 +a=6377295.664 +b=6356094.667915204 +units=m +no_defs"; //Current Proj in meter
            proj4Projection.ExternalProjectionParametersString = Proj4Projection.GetEpsgParametersString(4326); //Change to the proj with decimal degree
            proj4Projection.Open();

           PopupOverlay popupOverlay = new PopupOverlay();
     
            Map1.CurrentExtent = myOverlay.GetBoundingBox();
            Map1.TrackOverlay.TrackMode = TrackMode.Line;
            Map1.Refresh();
                 
        }

        private void Map1_MouseMove(object sender, MouseEventArgs e)
        {

            Point point = e.MouseDevice.GetPosition(Map1);

            ScreenPointF screenPointF = new ScreenPointF((float)point.X, (float)point.Y);
            PointShape pointShape = ExtentHelper.ToWorldCoordinate(Map1.CurrentExtent, screenPointF, (float)Map1.ActualWidth, (float)Map1.ActualHeight);

            string x = pointShape.X.ToString("f6", CultureInfo.InvariantCulture);
            string y = pointShape.Y.ToString("f6", CultureInfo.InvariantCulture);

            if (proj4Projection != null)
            {
                //PointShape p = new PointShape(605770.08015, 388756.038425);
                PointShape p2 = proj4Projection.ConvertToExternalProjection(pointShape) as PointShape;

                Gdal.AllRegister(); //Register driver(s).    

                //string Dted = @"C:\N03.dt2";
                double[] gGeoTrans = new double[6];
                Dataset ds = Gdal.Open(@"C:\N03.dt2", Access.GA_ReadOnly); //Open Dataset

                ds.GetGeoTransform(gGeoTrans);
                OSGeo.GDAL.Band gThisBand = ds.GetRasterBand(1);

                double X = Convert.ToDouble(p2.X);

                double Y = Convert.ToDouble(p2.Y);

                // Your query location is X, Y (both doubles), convert your location into
                // local row, col addressing to specify which cell to read
                int row = (int)((gGeoTrans[3] - Y) / -gGeoTrans[5]);// ULY - xcoord / -N-S CellSize;
                int col = (int)((X - gGeoTrans[0]) / gGeoTrans[1]);  // xcoord - ULX / E-W CellSize

                // check here that the row and col are >= 0 and < gDataset.RasterXSize and 
                // gDataset.RasterYSize or you will get a read error.

                // read the value.. reading is done to an array but as we want only one cell
                // it needs to be an array of 1 float.
                float[] buff = new float[1];

                if ((col <= ds.RasterXSize) && col >= 0)
                {
                    if ((row <= ds.RasterYSize) && row >= 0)
                    {
                        gThisBand.ReadRaster(col, row, 1, 1, buff, 1, 1, 0, 0); // read the cell value
                    }
                }
                else
                {
                    return;
                }

                float ThisCellValue = buff[0];

                TextBoxSatu.Text = "RSO_WEST(KERTAU)" + "(" + (y) + "," + " " + (x) + ")" + " " + " Lat: " + DecimalDegreesHelper.GetDegreesMinutesSecondsStringFromDecimalDegree(p2.Y) + "Long: " + DecimalDegreesHelper.GetDegreesMinutesSecondsStringFromDecimalDegree(p2.X) + " " + "Height :" + ThisCellValue + "m";
    
            }
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            Button button = sender as Button;
            if (button != null)
            {
                switch (button.Name)
                {
                    case "TrackPolygon":
                        Map1.TrackOverlay.TrackMode = TrackMode.Line;
                        break;

                    case "TrackNormal":
                    default:
                        Map1.TrackOverlay.TrackMode = TrackMode.None;
                        break;

                    case "TrackDelete":
                        Map1.TrackOverlay.TrackShapeLayer.InternalFeatures.Clear();
                        tiffLayer = null;
                        Map1.Refresh();
                        break;
                }
            }
        }
    }
}       

Regards,

Fathin

Hi Fathin,

Thanks for your code.

It looks the elevation value here is “ThisCellValue” and it’s related with X and Y which is mouse coordinates.

You know our layer is raster layer, it don’t read feature from data but read the image, so the points you saw on map is not point shape in layer.

So I want to double check your requirement here, what’s the scenario now?

  1. Your code pass in a group of points, then shows their elevation and shows the distance between them.
  2. You click on map to set many points, when you click, the distance of them will be rendered on map.

If you want to implement #1, you can just use InmemoryFeaturelayer and render it like I mentioned in last reply.
If you want to implement #2, you can simply use TrackOverlay for it.

So what’s the mainly problem you faced here?

Regards,

Ethan

Hi Ethan,

My first problem is how can i plotting the point on map plane by using mouse click.

  1. first i plot one point then a second point.
  2. make like ruler application, that when i click the 2 point it will show straight line and view distance.

You have a simple example for it and what the difference of InFeatureMemoryLayer and SimpleMarkerOverlay for the point plot?

Hi Fathin,

If you want to plot points, you have to use track overlay.

After vertex added each time or track complete, you can move to points into InmemoryFeatureLayer or SimpleMarkerOverlay.

SimpleMarkerOverlay have some special feature for example popup for marker, and the InmemoryFeatureLayer is just a standard layer which is saved in memory.

About how to use the track for your scenario, I created a simple sample here:
9408.zip (102.7 KB)

You can view it and modify it for your scenario, and you can refer the other posts about it here:

Wish that’s helpful.

Regards,

Ethan

Hi Ethan,

Thank for your sample. So if i want to save the plot point i need to use InmemoryFeatureLayer right? Then how i can implement the ruler application on the point that use InmemoryFeatureLayer? My target is so that i can select an existing two point and get their distance.

Regards,

Fathin

Hi Fathin,

Yes you should want to plot points into InmemoryFeatureLayer.

If your target is select two existing points, you should want to implement this logic in your mouse click or mouse down event. You can did spatial query based on your clicked point, the nearest one point is the selected point. About how to find nearest feature based on clicked point please refer the sample here: https://github.com/ThinkGeo/GetFeaturesClickedOnSample-ForWinForms

Then you can call the spatial query API to get distance and render it on map just like my sample shows.

Wish that’s helpful.

Regards,

Ethan

Hi Ethan,

Did you have any example for the wpf ? My trial for winform already expired. May i know is there any dedicated spatial query API for mapsuite?

Regards,

Fathin

Hi Fathin,

The API for wpf and winforms is nearly the same, you can directly copy the code to wpf.

And you can find the spatial query API in QueryTools like this:

InMemoryFeatureLayer layer;
layer.QueryTools.xxx

Regards,

Ethan

Hi Ethan,

I can’t seem to create a InmemoryFeatureLayer on mouse click. It will throws me error when i start clicking the mouse on map. I want to plot a point on mouse click so that i can save it.

public partial class gdalElevation : Window

{   
    Proj4Projection proj4Projection;
    GeoTiffRasterLayer tiffLayer = new GeoTiffRasterLayer();
    LayerOverlay myOverlay = new LayerOverlay();
    SimpleMarkerOverlay markerOverlay = new SimpleMarkerOverlay();
    PointShape p2 = new PointShape();
    PointShape makePoint = new PointShape();
  
 
    public gdalElevation()
    {
       
        InitializeComponent();                       
    }

    private void Map_Loaded(object sender, RoutedEventArgs e)
    {                                     
        Map1.MapUnit = GeographyUnit.Meter;
        //LayerOverlay myOverlay = new LayerOverlay();
        Map1.Overlays.Add(myOverlay);
              
        myOverlay.TileCache = new FileBitmapTileCache(@"C:\Users\User\Documents\Visual Studio 2012\Projects\RasterMapViewer\RasterMapViewer\Cache\");

        string[] files = System.IO.Directory.GetFiles(@"C:\Users\User\Desktop\DSI REFERENCE\Map Data\Raster");

        foreach (string file in files)
        {
            GeoTiffRasterLayer tiffLayer = new GeoTiffRasterLayer(file);  
      
            myOverlay.Layers.Add(tiffLayer);
        }

        //Map1.TrackOverlay = new RulerTrackInteractiveOverlay();    

        myOverlay.Open();
        Map1.CurrentExtent = myOverlay.GetBoundingBox();

        //InMemoryFeature to show the selected feature (the feature clicked on).
        InMemoryFeatureLayer selectLayer = new InMemoryFeatureLayer();
        selectLayer.Open();         
        selectLayer.Close();
        selectLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle.PointType = PointType.Bitmap;
        selectLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle.Image = new GeoImage(@"C:\Users\User\Documents\Visual Studio 2012\Projects\RasterMapViewer\RasterMapViewer\Icon\Point.png");
        selectLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
              
        //Apply the projection 
        proj4Projection = new Proj4Projection();
        proj4Projection.InternalProjectionParametersString = "+proj=omerc +lat_0=4 +lonc=102.25 +alpha=323.0257905 +k=0.99984 +x_0=804670.24 +y_0=0 +no_uoff +gamma=323.1301023611111 +a=6377295.664 +b=6356094.667915204 +units=m +no_defs"; //Current Proj in meter
        proj4Projection.ExternalProjectionParametersString = Proj4Projection.GetEpsgParametersString(4326); //Change to the proj with decimal degree
        proj4Projection.Open();

        myOverlay.Layers.Add(selectLayer);
        myOverlay.TileType = TileType.MultipleTile;
        myOverlay.IsBase = false;
       
        Map1.Refresh();
             
    }
 
    private void Map1_MouseMove(object sender, MouseEventArgs e)
    {

        Point point = e.MouseDevice.GetPosition(Map1);

        ScreenPointF screenPointF = new ScreenPointF((float)point.X, (float)point.Y);
        PointShape pointShape = ExtentHelper.ToWorldCoordinate(Map1.CurrentExtent, screenPointF, (float)Map1.ActualWidth, (float)Map1.ActualHeight);

        string x = pointShape.X.ToString("f6", CultureInfo.InvariantCulture);
        string y = pointShape.Y.ToString("f6", CultureInfo.InvariantCulture);

        if (proj4Projection != null)
        {
            //PointShape p = new PointShape(605770.08015, 388756.038425);
            PointShape p2 = new PointShape();
            p2 = proj4Projection.ConvertToExternalProjection(pointShape) as PointShape;

            /********GDAL DTED CONVERSION*******/
         
            Gdal.AllRegister(); //Register driver(s).    

            //string Dted = @"C:\N03.dt2";
            double[] gGeoTrans = new double[6];
            Dataset ds = Gdal.Open(@"C:\N03.dt2", Access.GA_ReadOnly); //Open Dataset

            ds.GetGeoTransform(gGeoTrans);
            OSGeo.GDAL.Band gThisBand = ds.GetRasterBand(1);


            double X = Convert.ToDouble(p2.X);

            double Y = Convert.ToDouble(p2.Y);

            // Your query location is X, Y (both doubles), convert your location into
            // local row, col addressing to specify which cell to read
            int row = (int)((gGeoTrans[3] - Y) / -gGeoTrans[5]);// ULY - xcoord / -N-S CellSize;
            int col = (int)((X - gGeoTrans[0]) / gGeoTrans[1]);  // xcoord - ULX / E-W CellSize

            // check here that the row and col are >= 0 and < gDataset.RasterXSize and 
            // gDataset.RasterYSize or you will get a read error.

            // read the value.. reading is done to an array but as we want only one cell
            // it needs to be an array of 1 float.
            float[] buff = new float[1];

            if ((col <= ds.RasterXSize) && col >= 0)
            {
                if ((row <= ds.RasterYSize) && row >= 0)
                {
                    gThisBand.ReadRaster(col, row, 1, 1, buff, 1, 1, 0, 0); // read the cell value
                }
            }
            else
            {
                return;
            }

            float ThisCellValue = buff[0];

            TextBoxSatu.Text = "RSO_WEST(KERTAU)" + "(" + (y) + "," + " " + (x) + ")" + " " + " Lat: " + DecimalDegreesHelper.GetDegreesMinutesSecondsStringFromDecimalDegree(p2.Y) + "Long: " + DecimalDegreesHelper.GetDegreesMinutesSecondsStringFromDecimalDegree(p2.X) + " " + "Height :" + ThisCellValue + "m";

        }
    }

    private void button_Click(object sender, RoutedEventArgs e)
    {
                  
        Button button = sender as Button;
        if (button != null)
        {
            switch (button.Name)
            {
                case "TrackNormal":
                default:
                    Map1.TrackOverlay.TrackMode = TrackMode.None;
                    break;

                case "TrackDelete":
                    Map1.TrackOverlay.TrackShapeLayer.InternalFeatures.Clear();
                    tiffLayer = null;
                    Map1.Refresh();                         
                    break;

                case "TrackLine":
                    Map1.TrackOverlay.TrackMode = TrackMode.Line;
                    break;
                               
                case "Marker":
                    Map1.TrackOverlay = new RulerTrackInteractiveOverlay();
                    break;

                case "Point":
                    Map1.TrackOverlay.TrackMode = TrackMode.Point;
                    break;             
            }
        }
    }

    private void Map1_MapClick(object sender, MapClickWpfMapEventArgs e)
    {
        //Here we use a buffer of 15 in screen coordinate. This means that regardless of the zoom level, we will always find the nearest feature
        //within 15 pixels to where we clicked.
        int screenBuffer = 10;

        //Logic for converting screen coordinate values to world coordinate for the spatial query. Notice that the distance buffer for the spatial query
        //will change according to the zoom level while it remains the same for the screen buffer distance.
        ScreenPointF clickedPointF = new ScreenPointF(e.ScreenX, e.ScreenY);
        ScreenPointF bufferPointF = new ScreenPointF(clickedPointF.X + screenBuffer, clickedPointF.Y);

        double distanceBuffer = ExtentHelper.GetWorldDistanceBetweenTwoScreenPoints(Map1.CurrentExtent, clickedPointF, bufferPointF,
                                                            (float)Map1.Width,(float)Map1.Height, Map1.MapUnit, DistanceUnit.Meter);

       

        //Adds the feature clicked on to the selected layer to be displayed as highlighed and with the name labeled.
        InMemoryFeatureLayer selectLayer = (InMemoryFeatureLayer)Map1.FindFeatureLayer("SelectLayer");

       

        //Refreshes only the select layer.
        Map1.Refresh(Map1.Overlays["SelectOverlay"]);

    }
    
  
}

Regards,

Fathin

Hi Fathin,

You should want to use Map1.ActualWidth and Map1.ActualHeight instead Map1.Width and Map1.Height in function Map1_MapClick.

And I suggest you build a simple sample with your code, so our developer can reproduce your exception quickly. You can build the sample based on my sample project.

Regards,

Ethan

HI Ethan,

Thank you for a reply. I will try to construct a simple sample code for the InmemoryFeatureLayer that i need to use on my project.

Regards,

Fathin

Hi Fathin,

That’s great, so we can modify based on the sample and save our communication time.

Just like I mentioned, it should be simple enough and shows the problem clearly, I think our template is a good choice for create a sample quickly: http://wiki.thinkgeo.com/wiki/map_suite_project_template_guide

Regards,

Ethan

Hi Ethan

This is a sample i just construct. I managed to plot the map using InMemoryFeatureLayer. I have this problem:

  1. Adjust the icon with the tooltip of mouse.
  2. How can implement button for the InMemoryFeatureLayer to enabled it, so when i start the run the it not immediately execute the InMemoryFeatureLayer for plot.
  3. How can i delete the specific plot.

My code below throw me an exception:

KeyNotFoundException was unhandled

with remarks :

The given key was not present in the dictionary.

CODE:

 namespace PointPlot
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        InMemoryFeatureLayer inmemoryFeatureLayer = new InMemoryFeatureLayer();
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Map1_Loaded(object sender, RoutedEventArgs e)
        {
            Map1.MapUnit = GeographyUnit.Meter;
            LayerOverlay myOverlay = new LayerOverlay();
            Map1.Overlays.Add(myOverlay);

            myOverlay.TileCache = new FileBitmapTileCache(@"C:\Users\User\Documents\Visual Studio 2012\Projects\RasterMapViewer\RasterMapViewer\Cache\");

            string[] files = System.IO.Directory.GetFiles(@"C:\Users\User\Desktop\DSI REFERENCE\Map Data\Raster");

            foreach (string file in files)
            {
                GeoTiffRasterLayer tiffLayer = new GeoTiffRasterLayer(file);

                myOverlay.Layers.Add(tiffLayer);
            }
            myOverlay.Open();
            Map1.CurrentExtent = myOverlay.GetBoundingBox();

            InMemoryFeatureLayer inmemoryFeatureLayer = new InMemoryFeatureLayer();
            inmemoryFeatureLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle = new PointStyle(new GeoImage(@"C:\Users\User\Documents\Visual Studio 2012\Projects\PointPlot\PointPlot\Icon\Point.png"));
            //inmemoryFeatureLayer.InternalFeatures.Add("point", new Feature(@"C:\Users\User\Documents\Visual Studio 2012\Projects\PointPlot\PointPlot\Icon\Point.png"));           
           
            inmemoryFeatureLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

            LayerOverlay markerOverlay = new LayerOverlay();
            markerOverlay.Layers.Add(inmemoryFeatureLayer);
            Map1.Overlays.Add("MarkerOverlay", markerOverlay);
           
            Map1.Overlays.Add("MarkerOverlay", markerOverlay);

          
            Map1.Refresh();

        }

        private void Map1_MapClick(object sender, MapClickWpfMapEventArgs e)
        {
            
            LayerOverlay markerOverlay = (LayerOverlay)Map1.Overlays["MarkerOverlay"];
            InMemoryFeatureLayer inmemoryFeatureLayer = markerOverlay.Layers[0] as InMemoryFeatureLayer;

            MultipolygonShape buffer = e.WorldLocation.Buffer(350, GeographyUnit.Meter, DistanceUnit.Kilometer);

                inmemoryFeatureLayer.InternalFeatures.Add(new Feature(e.WorldLocation));

            //Collection<Feature> deleteMarkers = inmemoryFeatureLayer.QueryTools.GetFeaturesWithin(buffer, ReturningColumnsType.NoColumns);
            //if (deleteMarkers.Count > 0)
            //{
            //    inmemoryFeatureLayer.InternalFeatures.Remove(deleteMarkers[0]);
            //}
            //else
            //{
            //    inmemoryFeatureLayer.InternalFeatures.Add(new Feature(e.WorldLocation));
            //}

           Map1.Refresh();
        }

        private void Plot_ButtonClick(object sender, RoutedEventArgs e)
        {
            InMemoryFeatureLayer inMemoryLayer = (InMemoryFeatureLayer)Map1.FindFeatureLayer("InMemoryFeatureLayer");

            inMemoryLayer.Open();
            inMemoryLayer.EditTools.BeginTransaction();
            inMemoryLayer.EditTools.Delete("MarkerOverlay");
            inMemoryLayer.EditTools.CommitTransaction();
            inMemoryLayer.Close();

            Map1.Refresh(Map1.Overlays["InmemoryOverlay"]);   
        }
    }
}

Regards,

Fathin

Hi Ethan,

If let say i create a point using marker just like in HowDoI sample, is that any a way for me to select the point so that i can edit the point or maybe get a distance in line from one marker point to another>

Regards,

Fathin

Hi Fathin,

  1. Adjust the icon with the tooltip of mouse.
    I hadn’t notice your code have related logic, do you means change the mouse cursor? or the tooltip of marker which is clicked by mouse?

  2. How can implement button for the InMemoryFeatureLayer to enabled it, so when i start the run the it not immediately execute the InMemoryFeatureLayer for plot.
    Add a bool type variable, set it’s value by a button. If it’s true just return in Map1_MapClick event, if it’s false, run your logic.

  3. How can i delete the specific plot.

This logic should be helpful.

            Collection<Feature> neartestFeatures = inmemoryFeatureLayer.QueryTools.GetFeaturesNearestTo(e.WorldLocation, GeographyUnit.Meter, 1, ReturningColumnsType.AllColumns);
        if (neartestFeatures.Count > 0)
        {
            inmemoryFeatureLayer.InternalFeatures.Remove(neartestFeatures[0]);
        }
  1. is that any a way for me to select the point so that i can edit the point or maybe get a distance in line from one marker point to another
    Use the query logic for item 3, then add the point into a special layer, you can call it highlight layer. It can be thought selected point. You can make this layer contains two points, then create a line between them(start point and end point), then show the line.

Here is a sample project based on your code, please modify based on it, and you can upload it again to show your question instead of paste code.

15floors.zip (11.7 KB)

Regards,

Ethan

Hi Ethan,

Sorry for not clear statement regarding question (1). I mean how can i adjust so that the tooltip of icon near the tooltip of mouse cursor. Just like in using SimpleMarkerOverlay by adjusting the Yoffset, but i need to implement it on InMemoryFeatureLayer.

Sorry for paste the code i will modify the sample and upload it if i have issues with it.

Regards,

Fathin

Hi Fathin,

That related with how you implement the tooltip. I think our InmemoryFeatureLayer don’t support tooltip by default, it’s not the same like MarkerOverlay.

If you still hadn’t found the way to implement that, please refer this topic: OpenStreet Maps and WGS84 map

It contains a custom tooltip which works for winform, but I think you can try to use it in WPF and see whether it works for your requirement.

Regards,

Ethan

HI Ethan,

May i know how can i make a selection for the inmemoryfeaturelayer? What i mean i can select the point that just been created using InmemoryFeatureLayer. In addition, can you check this code? I try copy exactly the same code from HowDoI sample for delete the feature in InmemoryFeatureLayer. It does not delete the rectangle feature.

Regards,

Fathin