context: // ********************************************************************************************************
  // Product Name: DotSpatial.Symbology.dll
  // Description:  Contains the business logic for symbology layers and symbol categories.
  // ********************************************************************************************************
  // The contents of this file are subject to the MIT License (MIT)
  // you may not use this file except in compliance with the License. You may obtain a copy of the License at
  // http://dotspatial.codeplex.com/license
  //
  // Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
  // ANY KIND, either expressed or implied. See the License for the specific language governing rights and
  // limitations under the License.
  //
  // The Original Code is from MapWindow.dll version 6.0
  //
  // The Initial Developer of this Original Code is Ted Dunsford. Created 2/20/2009 3:52:22 PM
  //
  // Contributor(s): (Open source contributors should list themselves and their modifications here).
  //
  // ********************************************************************************************************
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Data;
  using System.Drawing;
  using System.Linq;
  using DotSpatial.Data;
  using DotSpatial.Serialization;
  
  namespace DotSpatial.Symbology
  {
      /// <summary>
      /// PointScheme
      /// </summary>
      public class PolygonScheme : FeatureScheme, IPolygonScheme
      {
          #region Private Variables
  
          private PolygonCategoryCollection _categories;
  
          #endregion
  
          #region Constructors
  
          /// <summary>
          /// Creates a new instance of PointScheme with no categories added to the list yet.
          /// </summary>
          public PolygonScheme()
          {
              Configure();
          }
  
          /// <summary>
          /// Initializes a new instance of the PolygonScheme class.
          /// </summary>
          /// <param name="fs">THe featureset with the data Table definition to use for symbolizing.</param>
          /// <param name="uniqueField">The string name of the field to use
          /// when calculating separate color codes.  Unique entries will be
          /// assigned a random color.</param>
          public PolygonScheme(IFeatureSet fs, string uniqueField)
          {
              GenerateUniqueColors(fs, uniqueField);
          }
  
          /// <summary>
          /// Calculates the unique colors as a scheme.
          /// </summary>
          /// <param name="fs">The featureset with the data Table definition.</param>
          /// <param name="uniqueField">The unique field.</param>
          public Hashtable GenerateUniqueColors(IFeatureSet fs, string uniqueField)
          {
              Hashtable result = new Hashtable(); // a hashtable of colors
              DataTable dt = fs.DataTable;
              ArrayList vals = new ArrayList();
              int i = 0;
              foreach (DataRow row in dt.Rows)
              {
                  if (uniqueField != "FID")
                  {
                      if (vals.Contains(row[uniqueField]) == false)
                      {
                          vals.Add(row[uniqueField]);
                      }
                  }
                  else
                  {
                      vals.Add(i);
                      i++;
                  }
              }
  
              Random rnd = new Random();
              foreach (object item in vals)
              {
                  Color c = rnd.NextColor();
                  while (result.ContainsKey(c))
                  {
                      c = rnd.NextColor();
                  }
                  PolygonCategory cat = new PolygonCategory(c, c, 1);
                  string flt = "[" + uniqueField + "] = ";
                  if (uniqueField == "FID")
                  {
                      flt += item;
                  }
                  else
                  {
                      if (dt.Columns[uniqueField].DataType == typeof(string))
                      {
                          flt += "'" + item + "'";
                      }
                      else
                      {
                          flt += item.ToString();
                      }
                  }
                  cat.FilterExpression = flt;
                  Categories.Add(cat);
                  result.Add(c, item);
              }
              return result;
          }
  
          private void Configure()
          {
              _categories = new PolygonCategoryCollection();
              OnIncludeCategories(_categories);
              PolygonCategory def = new PolygonCategory();
              _categories.Add(def);
          }
  
          private void CategoriesItemChanged(object sender, EventArgs e)
          {
              OnItemChanged(sender);
          }
  
          #endregion
  
          #region Methods
  
          /// <summary>
          /// Draws the regular symbolizer for the specified cateogry to the specified graphics
          /// surface in the specified bounding rectangle.
          /// </summary>
          /// <param name="index">The integer index of the feature to draw.</param>
          /// <param name="g">The Graphics object to draw to</param>
          /// <param name="bounds">The rectangular bounds to draw in</param>
          public override void DrawCategory(int index, Graphics g, Rectangle bounds)
          {
              Categories[index].Symbolizer.Draw(g, bounds);
          }
  
          /// <summary>
          /// Adds a new scheme, assuming that the new scheme is the correct type.
          /// </summary>
          /// <param name="category">The category to add</param>
          public override void AddCategory(ICategory category)
          {
              IPolygonCategory pc = category as IPolygonCategory;
              if (pc != null) _categories.Add(pc);
          }
  
          /// <summary>
          /// Reduces the index value of the specified category by 1 by
          /// exchaning it with the category before it.  If there is no
          /// category before it, then this does nothing.
          /// </summary>
          /// <param name="category">The category to decrease the index of</param>
          public override bool DecreaseCategoryIndex(ICategory category)
          {
              IPolygonCategory pc = category as IPolygonCategory;
              return pc != null && Categories.DecreaseIndex(pc);
          }
  
          /// <summary>
          /// Re-orders the specified member by attempting to exchange it with the next higher
          /// index category.  If there is no higher index, this does nothing.
          /// </summary>
          /// <param name="category">The category to increase the index of</param>
          public override bool IncreaseCategoryIndex(ICategory category)
          {
              IPolygonCategory pc = category as IPolygonCategory;
              return pc != null && Categories.IncreaseIndex(pc);
          }
  
          /// <summary>
          /// Inserts the category at the specified index
          /// </summary>
          /// <param name="index">The integer index where the category should be inserted</param>
          /// <param name="category">The category to insert</param>
          public override void InsertCategory(int index, ICategory category)
          {
              IPolygonCategory pc = category as IPolygonCategory;
              if (pc != null) _categories.Insert(index, pc);
          }
  
          /// <summary>
          /// Removes the specified category
          /// </summary>
          /// <param name="category">The category to remove</param>
          public override void RemoveCategory(ICategory category)
          {
              IPolygonCategory pc = category as IPolygonCategory;
  
gt:             if (pc != null) _categories.Remove(pc);