Sharpmap bar charteveryone

Topics: SharpMap Project, SharpMap v0.9 / v1.x, Web Controls
Dec 8, 2008 at 2:54 PM
Hi everyone

I have been using SharpMap with vb asp.net to create a map over the web. Previously, I was using Mapobjects together with VB and ArcIMS to do this. So far I have been able to do everything that I could with MapObjects, but am now faced with a problem that I can't solve. I use a CustomTheme to return a bitmap bar chart of the number of accidents that occur at points on the roads. The bar chart centres on the point and I need the bottom of the bar chart to be on the point. This is my code

  Dim AccsChartTheme As SharpMap.Rendering.Thematics.CustomTheme
  AccsChartTheme = New SharpMap.Rendering.Thematics.CustomTheme(AddressOf GetAccsChartStyle)
  With LyrAccsChart
            .DataSource = dtp
            .Theme = AccsChartTheme
  End With
  Map.Layers.Add(LyrAccsChart)

......
......

   Private Function GetAccsChartStyle(ByVal row As SharpMap.Data.FeatureDataRow) As SharpMap.Styles.IStyle

        Dim s As New SharpMap.Styles.VectorStyle()
        s.Symbol = CreateAccsChart(row)
        Return s

    End Function

    Private Function CreateAccsChart(ByVal row As SharpMap.Data.FeatureDataRow) As Bitmap

        Dim width As Integer
        Dim height As Integer

        width = 8
        height = row("NoOfAccs") * 3

        Dim b As System.Drawing.Bitmap = New Bitmap(width, height)
        Dim rect As New Rectangle(0, 0, width, height)

        Dim g As Graphics = Graphics.FromImage(b)

        g.FillRectangle(Brushes.Navy, rect)
        g.Dispose()

        Return b

    End Function


Can anyone help

Thanks

Coordinator
Dec 8, 2008 at 3:46 PM
I don't think that there is an anchor/reference you can set in v0.9.
You could use the SymbolOffset property of the VectorStyle.
But you would have to use a fixed bitmap size and scale the bar
to the maximum value of accidents in order to adjust it right.
hth
FObermaier

Coordinator
Dec 8, 2008 at 3:48 PM
You can measure the returned symbol and use that to come up with the SymbolOffset..
Dec 9, 2008 at 8:37 AM
Hi

Fobermaier and Johndiss, thanks for this info. I am quite new to .net and SharpMap and dont really know what I am doing. Would it be possible to give me an example of this. I have tried creating a new pointF called pt, using the X and Y coords of the FeatureDataRow and then manipulating the Y coord. I then set the s.SymbolOffset = p in the GetAccsChartStyle function, but then no charts appear on the Map at all. Please help.

Thanks
Coordinator
Dec 9, 2008 at 9:07 AM
Look at definition of SymbolOffset:
/// <summary>
/// Gets or sets the offset in pixels of the symbol.
/// </summary>
/// <remarks>
/// The symbol offset is scaled with the <see cref="SymbolScale"/> property and refers to the offset af <see cref="SymbolScale"/>=1.0.
/// </remarks>
All you need to know is the size of the bitmap to set the symbol offset accordingly.
FObermaier
Dec 9, 2008 at 10:29 AM
OK, thanks I have sorted it out. I thought the x/y coords of the pointf for the symboloffset were the x/y coords of the symbol on the Map. I just halved the height of the bitmap and make it negative for the y coord. Thanks for the help
Coordinator
Dec 9, 2008 at 10:37 AM
Hi CJC, it is important to remember that the x and y from the FeatureDataRow are in real world coordinates wheras the symbol offset is in 'image' /pixel  coordinates
so you want to offset the point by half the height of the symbol 

so something like:

Private Function GetAccsChartStyle(ByVal row As SharpMap.Data.FeatureDataRow) As SharpMap.Styles.IStyle

        Dim s As New SharpMap.Styles.VectorStyle()
        s.Symbol = CreateAccsChart(row)
        s.SymbolOffset =  new SizeF(0,  - (s.Symbol.Height/2)) 'note that even though the SymbolOffset is a SizeF, it is treated as a vector rather than a size by the renderer
        Return s

    End Function

    Private Function CreateAccsChart(ByVal row As SharpMap.Data.FeatureDataRow) As Bitmap

        Dim width As Integer
        Dim height As Integer

        width = 8
        height = row("NoOfAccs") * 3

        Dim b As System.Drawing.Bitmap = New Bitmap(width, height)
        Dim rect As New Rectangle(0, 0, width, height)

        Dim g As Graphics = Graphics.FromImage(b)

        g.FillRectangle(Brushes.Navy, rect)
        g.Dispose()

        Return b

    End Function
 
Dec 9, 2008 at 3:35 PM
Thank you - all working perfectly now
Dec 28, 2010 at 7:46 AM

Hi Johndiss

Thanks for your help, but now i want to ham 2 or more bar chart  near the each other.

my code is hear : 

   public static Bitmap CreateAccsChart(FeatureDataRow row)
    {
        // Replace polygon with a center point (this is where we place the symbol
        row.Geometry = row.Geometry.GetBoundingBox().GetCentroid();

        int width = 0;
        int height = 0;

        width = 8;
        System.Drawing.Bitmap b = new Bitmap(3 * width, 35);


        if (Convert.ToInt32(row.ItemArray.GetValue(7)) == 0)
            height = 1;
        else
            height = Convert.ToInt32(row.ItemArray.GetValue(7)) * 2;
        Rectangle rect1 = new Rectangle(0, 0, width, height);

        if (Convert.ToInt32(row.ItemArray.GetValue(8)) == 0)
            height = 1;
        else
            height = Convert.ToInt32(row.ItemArray.GetValue(8)) * 2;
        Rectangle rect2 = new Rectangle(0, 0, width, height);

        if (Convert.ToInt32(row.ItemArray.GetValue(9)) == 0)
            height = 1;
        else
            height = Convert.ToInt32(row.ItemArray.GetValue(9)) * 2;
        Rectangle rect3 = new Rectangle(0, 0, width, height);

        Graphics g = Graphics.FromImage(b);
        g.FillRectangle(Brushes.Navy, rect1);
        g.FillRectangle(Brushes.Aqua, rect2);
        g.FillRectangle(Brushes.Red, rect3);

        g.Dispose();

        return b;
    }

but when i run it, show a bar chart On each other in each geom.
how can i fix it?
sorry for bad english...

Dec 28, 2010 at 6:43 PM

i fix it with this code,

but this code is just for my shape data and is static code.

if any one fix it to work properly with any data, please put the code here please : 

   public static Bitmap CreateAccsChart(FeatureDataRow row)
    {
        // Replace polygon with a center point (this is where we place the symbol)
        SharpMap.Geometries.Point p= row.Geometry.GetBoundingBox().GetCentroid();
        row.Geometry = p;

        int width = 0;
        int height = 0;

        width = 11;
        System.Drawing.Bitmap b = new Bitmap((3 * width ) + 3, 35);

        int BigI = 0;
        if (Convert.ToInt32(row.ItemArray.GetValue(7)) > Convert.ToInt32(row.ItemArray.GetValue(8)))
            BigI = Convert.ToInt32(row.ItemArray.GetValue(7));
        else
            BigI = Convert.ToInt32(row.ItemArray.GetValue(8));
        if (Convert.ToInt32(row.ItemArray.GetValue(9)) > BigI)
            BigI = Convert.ToInt32(row.ItemArray.GetValue(9));


        if (Convert.ToInt32(row.ItemArray.GetValue(7)) == 0)
            height = 1;
        else
            height = Convert.ToInt32(row.ItemArray.GetValue(7)) ;
        Rectangle rect1 = new Rectangle(0, 0, width, height);

        if (Convert.ToInt32(row.ItemArray.GetValue(8)) == 0)
            height = 1;
        else
            height = Convert.ToInt32(row.ItemArray.GetValue(8)) ;
        Rectangle rect2 = new Rectangle(12, BigI - height, width, height);

        if (Convert.ToInt32(row.ItemArray.GetValue(9)) == 0)
            height = 1;
        else
            height = Convert.ToInt32(row.ItemArray.GetValue(9)) ;
        Rectangle rect3 = new Rectangle(24, BigI - height, width, height);

        Graphics g = Graphics.FromImage(b);
        g.FillRectangle(Brushes.Navy, rect1);
        g.FillRectangle(Brushes.Aqua, rect2);
        g.FillRectangle(Brushes.Red, rect3);

        g.Dispose();

        return b;
    }

Coordinator
Jan 4, 2011 at 11:55 AM

Hello livici,

this is a small example of how you could do it:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using SharpMap.Data;
using SharpMap.Styles;
using GeoPoint = SharpMap.Geometries.Point;

namespace SharpMap.Rendering.Thematics
{
    public class BarChartTheme : ITheme
    {
        public struct BarChartItem
        {
            /// <summary>
            /// The Name of the column
            /// </summary>
            public readonly string ColumnName;
            /// <summary>
            /// The brush to fill the column
            /// </summary>
            public readonly Brush ColumnBrush;

            public BarChartItem(string columnName, Brush columnBrush)
            {
                ColumnName = columnName;
                ColumnBrush = columnBrush;
            }
        }

        #region private fields

        /// <summary>
        /// The background brush for the chart
        /// </summary>
        public Brush Background = Brushes.Transparent;

        /// <summary>
        /// The Pen to outline the Bars
        /// </summary>
        public Pen OutlineColumn = Pens.Black;

        /// <summary>
        /// The Pen draw the Axis
        /// </summary>
        public Pen Axis = Pens.Black;

        /// <summary>
        /// The width of the column
        /// </summary>
        public Int32 ColumnWidth;
        
        /// <summary>
        /// The spacing between columns, 
        /// </summary>
        public Int32 ColumnSpacing;

        /// <summary>
        /// The value to scale the bars
        /// </summary>
        public Double ScaleValue = 1d;

        /// <summary>
        /// Unit
        /// </summary>
        public Double PixelsPerUnit = Double.NaN;

        private readonly List<BarChartItem> _chartColumns = new List<BarChartItem>();

        #endregion


        /// <summary>
        /// List of column names that make up the chart
        /// </summary>
        public List<BarChartItem> ChartColumns { get { return _chartColumns; } }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="row"></param>
        /// <returns></returns>
        private Image CreateBarChart(FeatureDataRow row)
        {
            // Ensure that we have a 
            if (row.Geometry == null)
                return null;

            if (!(row.Geometry is GeoPoint))
                row.Geometry = row.Geometry.GetBoundingBox().GetCentroid();

            double[] values = new double[_chartColumns.Count];
            Brush[] brushes = new Brush[_chartColumns.Count];
            int i = 0;
            double max = double.MinValue;
            double min = double.MaxValue;
            foreach (BarChartItem barChartItem in _chartColumns)
            {
                values[i] = Convert.ToDouble(row[barChartItem.ColumnName]);
                min = Math.Min(min, values[i]);
                max = Math.Max(max, values[i]);
                brushes[i++] = barChartItem.ColumnBrush;
            }

            if (min > 0d) min = 0d;
            if (max < 0d) max = 0d;

            int width = ColumnSpacing + _chartColumns.Count* (ColumnWidth + ColumnSpacing); 
            int height = (int)((max - min) * ScaleValue * PixelsPerUnit);

            Bitmap b = new Bitmap( width, height + 1);
            Graphics g = Graphics.FromImage(b);
            g.FillRectangle(Background, 0, 0, width, height);
            Matrix m = new Matrix(1, 0, 0, (float)(min-max) / height, 0, (float)max);
            g.Transform = m;
            g.DrawLine(Axis, 0, (float)min, 0, (float)max);
            g.DrawLine(Axis, 0, 0, width, 0);

            for (i = 0; i < values.Length; i++)
            {
                Rectangle r = new Rectangle(ColumnSpacing + i * (ColumnWidth + ColumnSpacing), 0, ColumnWidth, (int)values[i]);
                g.FillRectangle(brushes[i], r);
                g.DrawRectangle(OutlineColumn, r);
            }

            g.Dispose();

            return b;
        }

        /// <summary>
        /// Returns the style based on a feature
        /// </summary>
        /// <param name="attribute">Attribute to calculate color from</param>
        /// <returns>Color</returns>
        public IStyle GetStyle(FeatureDataRow attribute)
        {
            VectorStyle v = new VectorStyle {Symbol = CreateBarChart(attribute)};
            return v;
        }
    }
}

Here is a simple test method that shows the usage:

       public void TestBarChartTheming()
        {
            //Create a map
            SharpMap.Map map = new SharpMap.Map(new System.Drawing.Size(720, 360));

            //Create some random sample data
            CreatingData cd = new CreatingData();
            SharpMap.Data.FeatureDataTable fdt =
                cd.CreatePointFeatureDataTableFromArrays(GetRandomOrdinates(5, -180, 180),
                                                         GetRandomOrdinates(5, -90, 90), null);

            //Add rotation column and fill with random rotation values
            fdt.Columns.Add("Male", typeof(float));
            fdt.Columns.Add("Female", typeof(float));
            System.Random random = new System.Random();
            foreach (SharpMap.Data.FeatureDataRow row in fdt.Rows)
            {
                float male = random.Next(100);
                row["Male"] = male;
                row["Female"] = 100 - male;
            }

            //Create layer and datasource
            SharpMap.Layers.VectorLayer vl = new SharpMap.Layers.VectorLayer("Points", new SharpMap.Data.Providers.GeometryFeatureProvider(fdt));

            //Create default style
            SharpMap.Styles.VectorStyle defaultStyle = new SharpMap.Styles.VectorStyle();
            defaultStyle.Symbol = new System.Drawing.Bitmap(@"..\..\..\DemoWinForm\Resources\flag.png");
            defaultStyle.SymbolScale = 0.5f;

            //Create theming class and apply to layer
            SharpMap.Rendering.Thematics.BarChartTheme bct = new SharpMap.Rendering.Thematics.BarChartTheme
                                                                 {
                                                                     ColumnSpacing = 3,
                                                                     ColumnWidth = 15,
                                                                     PixelsPerUnit = 1
                                                                 };
            bct.ChartColumns.Add(new BarChartTheme.BarChartItem("Male", System.Drawing.Brushes.Blue));
            bct.ChartColumns.Add(new BarChartTheme.BarChartItem("Female", System.Drawing.Brushes.Pink));

            vl.Theme = bct;

            map.Layers.Add(vl);
            map.ZoomToExtents();
            System.Drawing.Image mapImage = map.GetMap();
            mapImage.Save("BarChartTheme.bmp");
        }

Hth FObermaier

Jan 4, 2011 at 8:58 PM

Thanks FObermaier for this code.