Adding Ellipse Geometry to ESRI Silverlight GraphicsLayer

If you are developing a web application with Esri Silverlight Api, you will probably need to display ellipse elements with given center x, y, radius-1 and radius-2 values. Unfortunately, Esri Silverlight API hasn’t provided ellipse and circle geometry types yet but we can easily display ellipse geometries in forms of polygons. All we need to do is to split the ellipse geometry into a reasonable number of points (the more point you specify, the more accurate ellipse you will gain, which comes with a considerable performance penalty), calculate the coordinates of each point and add the point collection to a polygon’s ring property. I’ve implemented a very simple application which receives center-x, center-y, radius-1 and radius-2 values from the user and displays an ellipse on the map.

This is the control where user enters information to display an ellipse

And below is how the given ellipse looks like

Now let’s dig into the source code

 public partial class MainPage : UserControl
    {
        GraphicsLayer gl = new GraphicsLayer();

        public MainPage()
        {
            InitializeComponent();
            map1.Layers.Add(gl);
        }

        private void btnAddEllipse_Click(object sender, RoutedEventArgs e)
        {
            double radius1 = nudRadius1.Value;
            double radius2 = nudRadius2.Value;
            double centerX = nudCenterX.Value;
            double centerY = nudCenterY.Value;

            ESRI.ArcGIS.Client.Geometry.Geometry polygon = GeometryConverter.ConvertEllipse(radius1, radius2, centerX, centerY);
            SimpleFillSymbol sym = new SimpleFillSymbol();
            sym.Fill =
            new SolidColorBrush(Color.FromArgb(170, 51, 153, 51));
            sym.BorderThickness = 2;
            sym.BorderBrush =
            new SolidColorBrush(Colors.LightGray);
            Graphic gr = new Graphic();
            gr.Symbol = sym;
            gr.Geometry = polygon;
            gl.Graphics.Add(gr);

            map1.Extent = gl.FullExtent;
        }

        private void btnZoomToGraphics_Click(object sender, RoutedEventArgs e)
        {
            if (gl.Graphics.Count > 0)
                map1.Extent = gl.FullExtent;
        }
    }

And this is the converter class

In this part, in order to reduce the performance cost that is caused by calculating each coordinate by use of Math.Cos() and Math.Sin() methods, we can think the ellipse is on a coordinate system where center-x and center-y point is the origin. This way, while ellipse consists of 4 symetric parts, we can calculate cos and sin values for only one part of the ellipse, put the values in an array with associated angles, and use these values to calculate the other points in other parts but with related signs.

  public static class GeometryConverter
    {
        public static Geometry ConvertEllipse(double a, double b, double cx, double cy)
        {
            var mercator = new WebMercator();
            var mercatorPoint = mercator.FromGeographic(new MapPoint(cx, cy)) as MapPoint;

            if (mercatorPoint != null)
            {
                var mercatorX = mercatorPoint.X;
                var mercatorY = mercatorPoint.Y;
                var collection = new PointCollection();
                double x = 0;
                double y = 0;
                double rad = 0;
                double cos = 0;
                double sin = 0;
                List<Point> radiusColl = new List<Point>();
                int delta = 5;
                for (int i = 0; i <= 90; i = i + delta)
                {
                    rad = i *
                    Math.PI / 180;
                    cos =
                    Math.Cos(rad);
                    sin =
                    Math.Sin(rad);
                    x = a *
                    Math.Cos(rad);
                    y = b *
                    Math.Sin(rad);
                    var pnt = new Point(x, y);
                    radiusColl.Add(pnt);
                }
                for (int i = 0; i < radiusColl.Count; i++)
                {
                    x =mercatorX+(  radiusColl[i].X);
                    y =mercatorY+(  radiusColl[i].Y);
                    var pnt = new MapPoint(x, y);
                    collection.Add(pnt);
                }
                for (int i = radiusColl.Count - 1; i > 0; i--)
                {
                    x =mercatorX+(   -1 * radiusColl[i].X);
                    y =mercatorY+(   radiusColl[i].Y);
                    var pnt = new MapPoint(x, y);
                    collection.Add(pnt);
                }
                for (int i = 0; i < radiusColl.Count; i++)
                {
                    x =mercatorX+ ( -1 * radiusColl[i].X);
                    y =mercatorY+ ( -1 * radiusColl[i].Y);
                    var pnt = new MapPoint(x, y);
                    collection.Add(pnt);
                }
                for (int i = radiusColl.Count - 1; i >= 0; i--)
                {
                    x =mercatorX+(  radiusColl[i].X);
                    y =mercatorY+ ( -1 * radiusColl[i].Y);
                    var pnt = new MapPoint(x, y);
                    collection.Add(pnt);
                }

                var polygon = new Polygon();
                polygon.Rings =
                new ObservableCollection<PointCollection>();

                polygon.Rings.Add(collection);

                Geometry geoPolygon = mercator.ToGeographic(polygon);
                return geoPolygon;
            }
            return null;
        }
    }

PS: Not worth telling but specifying the same radius-1 and radius-2 will give you the circle geometry.

Enjoy coding

Leave a comment