ThinkGeo.com    |     Documentation    |     Premium Support

Custom AreaStyles

Greetings,


I am looking to create a custom AreaStyle where the border of the AreaShape is a line with dashed lines perpendicular to that line, where the perpendicular lines go from the line in a few pixels, however I need those perpendicular lines to always go on the inside of the AreaShape.  This is a quick sample I drew up:



What would be the best way to go about creating this style?  I think it would be relativly easy to do for rectangles, but I must be able to use this style for any polygon.


Any help would be appreciated.  Thank you for your time and help.


 


.Ryan.



Ryan, 
  
 I think the best way is create your custom style, override DrawCore. 
 I hope you can make it, and then put your code here, share to everybody. 
  
 James

Ryan,


Thanks for your post. 


First of all, I would say this problem is some advanced usage. I tried to create some CustomAreaStyle, but I give it up for following reason: In the draw core, the features are all Polygon type no matter original it is a really PolygonShape or Ellipse/ Rectangle type. So it is impossible to make a difference between Polygon and Rectangle/ Ellipse Here.
 

protected override void DrawCore(IEnumerable<Feature> features, GeoCanvas canvas, Collection<SimpleCandidate> labelsInThisLayer,Collection<SimpleCandidate> labelsInAllLayers)

 
Following is the codes which can implement the functionality as you describled. Personally, I do not think it is the best way.
 

namespace Post5813
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            winformsMap1.MapUnit = GeographyUnit.DecimalDegree;

            winformsMap1.CurrentExtent = new RectangleShape(0, 100, 100, 0);
            winformsMap1.BackgroundOverlay.BackgroundBrush = new GeoSolidBrush(GeoColor.StandardColors.White);

            RectangleShape rectangleShape = new RectangleShape(65, 30, 95, 15);
            EllipseShape ellipseShape = new EllipseShape(new PointShape(30, 30), 20, 5);
            EllipseShape circleShape = new EllipseShape(new PointShape(60, 10), 10, 10);
            PolygonShape polygonShape = (PolygonShape)BaseShape.CreateShapeFromWellKnownData("POLYGON((10 60,40 70,30 85, 10 60))");
            InMemoryFeatureLayer inMemoryLayer = new InMemoryFeatureLayer();
            inMemoryLayer.InternalFeatures.Add("Polygon", new Feature(polygonShape));
            inMemoryLayer.InternalFeatures.Add("Rectangle", new Feature(rectangleShape));
            inMemoryLayer.InternalFeatures.Add("Ellipse", new Feature(ellipseShape));
            inMemoryLayer.InternalFeatures.Add("Circle", new Feature(circleShape));

            Collection<Feature> lineFeaturesForRectangle = GetLineFeatures(ellipseShape);
            foreach (Feature feature in lineFeaturesForRectangle)
            {
                inMemoryLayer.InternalFeatures.Add(feature);
            }

            lineFeaturesForRectangle = GetLineFeatures(rectangleShape);
            foreach (Feature feature in lineFeaturesForRectangle)
            {
                inMemoryLayer.InternalFeatures.Add(feature);
            }

            lineFeaturesForRectangle = GetLineFeatures(circleShape);
            foreach (Feature feature in lineFeaturesForRectangle)
            {
                inMemoryLayer.InternalFeatures.Add(feature);
            }

            lineFeaturesForRectangle = GetLineFeatures(polygonShape);
            foreach (Feature feature in lineFeaturesForRectangle)
            {
                inMemoryLayer.InternalFeatures.Add(feature);
            }

            inMemoryLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = AreaStyles.Country1;
            inMemoryLayer.ZoomLevelSet.ZoomLevel01.DefaultLineStyle.OuterPen.Color = GeoColor.SimpleColors.Red;
            inMemoryLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

            LayerOverlay staticOverlay = new LayerOverlay();
            staticOverlay.Layers.Add("InMemoryFeatureLayer", inMemoryLayer);
            winformsMap1.Overlays.Add(staticOverlay);

            winformsMap1.Refresh();
        }

        private Collection<Feature> GetLineFeatures(AreaBaseShape areaShape)
        {
            Collection<Feature> returnFeatures = new Collection<Feature>();

            if (areaShape is RectangleShape)
            {
                RectangleShape rectangleShape = (RectangleShape)areaShape;
                returnFeatures = GetLineFeaturesForRectangleShape(rectangleShape);
            }

            if (areaShape is EllipseShape)
            {
                EllipseShape ellipseShape = (EllipseShape)areaShape;
                returnFeatures = GetLineFeaturesForEllipse(ellipseShape);
            }

            if (areaShape is PolygonShape)
            {
                PolygonShape polygonShape = (PolygonShape)areaShape;
                returnFeatures = GetLineFeaturesForPolygon(polygonShape);
            }

            return returnFeatures;
        }

        private Collection<Feature> GetLineFeaturesForRectangleShape(RectangleShape rectangle)
        {
            Collection<Feature> returnFeatures = new Collection<Feature>();

            int horzCount = 5;
            int vertiCount = 5;
            double pencetage = 0.05;

            for (int i = 0; i < horzCount; i++)
            {
                Vertex vertex1 = new Vertex(rectangle.UpperLeftPoint.X + (i + 1.0) * rectangle.Width / (horzCount + 1.0), rectangle.UpperLeftPoint.Y);
                Vertex vertex2 = new Vertex(rectangle.UpperLeftPoint.X + (i + 1.0) * rectangle.Width / (horzCount + 1.0), rectangle.UpperLeftPoint.Y - rectangle.Height * pencetage);

                LineShape lineShape = new LineShape();
                lineShape.Vertices.Add(vertex1);
                lineShape.Vertices.Add(vertex2);
                returnFeatures.Add(new Feature(lineShape));
            }

            for (int i = 0; i < horzCount; i++)
            {
                Vertex vertex1 = new Vertex(rectangle.LowerLeftPoint.X + (i + 1.0) * rectangle.Width / (horzCount + 1.0), rectangle.LowerLeftPoint.Y);
                Vertex vertex2 = new Vertex(rectangle.LowerLeftPoint.X + (i + 1.0) * rectangle.Width / (horzCount + 1.0), rectangle.LowerLeftPoint.Y + rectangle.Height * pencetage);

                LineShape lineShape = new LineShape();
                lineShape.Vertices.Add(vertex1);
                lineShape.Vertices.Add(vertex2);
                returnFeatures.Add(new Feature(lineShape));
            }

            for (int i = 0; i < vertiCount; i++)
            {
                Vertex vertex1 = new Vertex(rectangle.LowerLeftPoint.X, rectangle.LowerLeftPoint.Y + (i + 1.0) * rectangle.Height / (vertiCount + 1.0));
                Vertex vertex2 = new Vertex(rectangle.LowerLeftPoint.X + rectangle.Width * pencetage, rectangle.LowerLeftPoint.Y + (i + 1.0) * rectangle.Height / (vertiCount + 1.0));

                LineShape lineShape = new LineShape();
                lineShape.Vertices.Add(vertex1);
                lineShape.Vertices.Add(vertex2);
                returnFeatures.Add(new Feature(lineShape));
            }

            for (int i = 0; i < vertiCount; i++)
            {
                Vertex vertex1 = new Vertex(rectangle.LowerRightPoint.X, rectangle.LowerLeftPoint.Y + (i + 1.0) * rectangle.Height / (vertiCount + 1.0));
                Vertex vertex2 = new Vertex(rectangle.LowerRightPoint.X - rectangle.Width * pencetage, rectangle.LowerRightPoint.Y + (i + 1.0) * rectangle.Height / (vertiCount + 1.0));

                LineShape lineShape = new LineShape();
                lineShape.Vertices.Add(vertex1);
                lineShape.Vertices.Add(vertex2);
                returnFeatures.Add(new Feature(lineShape));
            }

            return returnFeatures;
        }

        private Collection<Feature> GetLineFeaturesForEllipse(EllipseShape ellipse)
        {
            Collection<Feature> returnFeatures = new Collection<Feature>();
            double length = 1.0;
            double pi2 = Math.PI / 2;

            RingShape ringShape = ellipse.ToPolygon().OuterRing;
            for (int i = 0; i < ringShape.Vertices.Count - 1; i++)
            {
                Vertex vertex1 = ringShape.Vertices<i>;
                Vertex vertex2 = ringShape.Vertices[i + 1];

                double middleX = 0.5 * (vertex1.X + vertex2.X);
                double middleY = 0.5 * (vertex1.Y + vertex2.Y);

                double angle = Math.Atan((vertex1.Y - vertex2.Y) / (vertex1.X - vertex2.X));

                double x = 0;
                double y = 0;
                if (angle > 0 && angle < pi2)
                {
                    if (vertex1.X > vertex2.X)
                    {
                        x = middleX + Math.Sin(angle) * length;
                        y = middleY - Math.Cos(angle) * length;
                    }
                    else
                    {
                        x = middleX - Math.Sin(angle) * length;
                        y = middleY + Math.Cos(angle) * length;
                    }
                }
                else
                {
                    if (vertex1.X > vertex2.X)
                    {
                        x = middleX + Math.Sin(angle) * length;
                        y = middleY - Math.Cos(angle) * length;
                    }
                    else
                    {
                        x = middleX - Math.Sin(angle) * length;
                        y = middleY + Math.Cos(angle) * length;
                    }
                }

                LineShape line = new LineShape(new Vertex[] { new Vertex(middleX, middleY), new Vertex(x, y) });
                returnFeatures.Add(new Feature(line));
            }

            return returnFeatures;
        }

        private Collection<Feature> GetLineFeaturesForPolygon(PolygonShape polygonShape)
        {
            Collection<Feature> returnFeatures = new Collection<Feature>();
            double length = 1.0;
            double pi2 = Math.PI / 2;
            float percentage = 5f;

            RingShape ringShape = polygonShape.OuterRing;
            for (int i = 0; i < ringShape.Vertices.Count - 1; i++)
            {
                Vertex vertex1 = ringShape.Vertices<i>;
                Vertex vertex2 = ringShape.Vertices[i + 1];

                for (int k = 1; k < 20; k++)
                {
                    PointShape rootPoint = new LineShape(new Vertex[] { vertex1, vertex2 }).GetPointOnALine(StartingPoint.FirstPoint, percentage * k);
                    double middleX = rootPoint.X;
                    double middleY = rootPoint.Y;

                    double angle = Math.Atan((vertex1.Y - vertex2.Y) / (vertex1.X - vertex2.X));

                    double x = 0;
                    double y = 0;
                    if (angle > 0 && angle < pi2)
                    {
                        if (vertex1.X > vertex2.X)
                        {
                            x = middleX + Math.Sin(angle) * length;
                            y = middleY - Math.Cos(angle) * length;
                        }
                        else
                        {
                            x = middleX - Math.Sin(angle) * length;
                            y = middleY + Math.Cos(angle) * length;
                        }
                    }
                    else
                    {
                        if (vertex1.X > vertex2.X)
                        {
                            x = middleX + Math.Sin(angle) * length;
                            y = middleY - Math.Cos(angle) * length;
                        }
                        else
                        {
                            x = middleX - Math.Sin(angle) * length;
                            y = middleY + Math.Cos(angle) * length;
                        }
                    }

                    LineShape line = new LineShape(new Vertex[] { new Vertex(middleX, middleY), new Vertex(x, y) });
                    returnFeatures.Add(new Feature(line));
                }
            }

            return returnFeatures;
        }
    }
}

 

Any questions just let me know.
 
Thanks.
 
Yale

 

Ryan, I am very interested in this style.   We need it for defining surface ponds and knobs.   Seems like a good style to have built-in, but in the abscence of that, I would be intersted in helping with the custom style.    I’m not sure I understand the code above, though.   I don’t understand Yale’s implementation that uses a different class for these different polygon shapes confuses me, though.   That doesn’t seem necessary.   And it looks like this implementation is doing 5 points per line segment… that would create different tick spacing on each segment, right?   Don’t we want to have even tick spacing?   So we need a routine to get a point at every 50 meters (or whatever) along a line, right?   Then Yale’s code is good to go?

Ryan & Ted,


I think probably I understood wrong way what kinds of requirements need to be.
 
But if we implement all kinds of Area type shape tick the same way, the Polygon and RectangShape seems OK while the Ellipse and Circle seems too clustered(if you don’t care, I think have your own custom area style is a very good solution) .
 
Below is the custom style codes, you can try it with the latest build Desktop 3.0.307 RC.

public class CustomAreaStyle : AreaStyle
    {
        public CustomAreaStyle()
            : this(new GeoPen(), new GeoSolidBrush(), PenBrushDrawingOrder.BrushFirst)
        {
        }

        public CustomAreaStyle(GeoSolidBrush fillSolidBrush)
            : this(new GeoPen(), fillSolidBrush, PenBrushDrawingOrder.BrushFirst)
        {
        }

        public CustomAreaStyle(GeoPen outlinePen)
            : this(outlinePen, new GeoSolidBrush(), PenBrushDrawingOrder.BrushFirst)
        {
        }

        public CustomAreaStyle(GeoPen outlinePen, GeoSolidBrush fillSolidBrush)
            : this(outlinePen, fillSolidBrush, PenBrushDrawingOrder.BrushFirst)
        {
        }

        public CustomAreaStyle(GeoPen outlinePen, GeoSolidBrush fillSolidBrush, PenBrushDrawingOrder penBrushDrawingOrder)
            : base(outlinePen, fillSolidBrush, penBrushDrawingOrder)
        { }

        protected override void DrawCore(IEnumerable<Feature> features, GeoCanvas canvas, Collection<SimpleCandidate> labelsInThisLayer, Collection<SimpleCandidate> labelsInAllLayers)
        {
            base.DrawCore(features, canvas, labelsInThisLayer, labelsInAllLayers);

            LineStyle lineStyle = new LineStyle(OutlinePen);
            Collection<Feature> lineFeatures = new Collection<Feature>();

            foreach (Feature feature in features)
            {
                AreaBaseShape baseShape = (AreaBaseShape)feature.GetShape();

                Collection<Feature> lineFeaturesForThisFeature = GetLineFeatures(baseShape);

                foreach (Feature lineFeature in lineFeaturesForThisFeature)
                {
                    lineFeatures.Add(lineFeature);
                }
            }

            lineStyle.Draw(lineFeatures, canvas, labelsInThisLayer, labelsInAllLayers);
        }

        private Collection<Feature> GetLineFeatures(AreaBaseShape areaShape)
        {
            Collection<Feature> returnFeatures = new Collection<Feature>();

            if (areaShape is PolygonShape)
            {
                PolygonShape polygonShape = (PolygonShape)areaShape;
                returnFeatures = GetLineFeaturesForPolygon(polygonShape);
            }

            return returnFeatures;
        }

        private Collection<Feature> GetLineFeaturesForPolygon(PolygonShape polygonShape)
        {
            Collection<Feature> returnFeatures = new Collection<Feature>();
            double length = 0.5;
            float percentage = 5f;

            RingShape ringShape = polygonShape.OuterRing;
            for (int i = 0; i < ringShape.Vertices.Count - 1; i++)
            {
                Vertex vertex1 = ringShape.Vertices[i];
                Vertex vertex2 = ringShape.Vertices[i + 1];

                for (int k = 1; k < 20; k++)
                {
                    PointShape rootPoint = new LineShape(new Vertex[] { vertex1, vertex2 }).GetPointOnALine(StartingPoint.FirstPoint, percentage * k);
                    double middleX = rootPoint.X;
                    double middleY = rootPoint.Y;

                    double angle = Math.Atan((vertex1.Y - vertex2.Y) / (vertex1.X - vertex2.X));

                    double x = 0;
                    double y = 0;
                    
                    if (vertex1.X > vertex2.X)
                    {
                        x = middleX + Math.Sin(angle) * length;
                        y = middleY - Math.Cos(angle) * length;
                    }
                    else
                    {
                        x = middleX - Math.Sin(angle) * length;
                        y = middleY + Math.Cos(angle) * length;
                    }

                    if (vertex1.Y == vertex2.Y)
                    {
                        if (vertex1.X > vertex2.X)
                        {
                            x = middleX - Math.Sin(angle) * length;
                            y = middleY + Math.Cos(angle) * length;
                        }
                        else
                        {
                            x = middleX + Math.Sin(angle) * length;
                            y = middleY - Math.Cos(angle) * length;
                        }
                    }

                    LineShape line = new LineShape(new Vertex[] { new Vertex(middleX, middleY), new Vertex(x, y) });
                    returnFeatures.Add(new Feature(line));
                }
            }

            return returnFeatures;
        }
    }

Test codes as below:

winformsMap1.MapUnit = GeographyUnit.DecimalDegree;

            winformsMap1.CurrentExtent = new RectangleShape(0, 100, 100, 0);
            winformsMap1.BackgroundOverlay.BackgroundBrush = new GeoSolidBrush(GeoColor.StandardColors.White);

            RectangleShape rectangleShape = new RectangleShape(65, 30, 95, 15);
            EllipseShape ellipseShape = new EllipseShape(new PointShape(30, 30), 20, 5);
            EllipseShape circleShape = new EllipseShape(new PointShape(60, 10), 10, 10);
            PolygonShape polygonShape = (PolygonShape)BaseShape.CreateShapeFromWellKnownData("POLYGON((10 60,40 70,30 85, 10 60))");
            InMemoryFeatureLayer inMemoryLayer = new InMemoryFeatureLayer();
            inMemoryLayer.InternalFeatures.Add("Polygon", new Feature(polygonShape));
            inMemoryLayer.InternalFeatures.Add("Rectangle", new Feature(rectangleShape));
            inMemoryLayer.InternalFeatures.Add("Ellipse", new Feature(ellipseShape));
            inMemoryLayer.InternalFeatures.Add("Circle", new Feature(circleShape));

CustomAreaStyle curstomerAreaStyle = new CustomAreaStyle(new GeoPen( GeoColor.SimpleColors.Red,1), new GeoSolidBrush( GeoColor.SimpleColors.LightBlue), PenBrushDrawingOrder.BrushFirst);
            inMemoryLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = curstomerAreaStyle;
            inMemoryLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;

            LayerOverlay staticOverlay = new LayerOverlay();
            staticOverlay.Layers.Add("InMemoryFeatureLayer", inMemoryLayer);
            winformsMap1.Overlays.Add(staticOverlay);

            winformsMap1.Refresh();

Any more questions just let me know.


Thanks.
Yale

Ted, 

Hope following can answer your questions. 



In the code above(custom area style one), we will set 5 ticks for each segment in the Polygon, which will make different tick spacing on each segment. I am not clear what Ryan need, if you need even-spacing tick, just try to change the logic( I guess we have to make some decision when you implement the new logic codes). 



If you want to do the 50-meter even ticking logic, I guess the MapUnit stored in the Canvas which give you help to determine the total length of the OutRing of the Polygon. 



Good luck and if any more questions just let me know. 

Thanks.


Yale



Thanks Yale, 
  
 Although my requirements were a little different in that I wanted a tick every x pixels rather than every x meters or x number of ticks, I was write an AreaStyle based off your suggestions.  Thank you very much for your help. 
  
 .Ryan.

Ryan, Welcome. 
  
 Any more questions just let me know. 
  
 Thanks. 
  
 Yale