context: // ********************************************************************************************************
  // Product Name: DotSpatial.Controls.dll
  // Description:  The Windows Forms user interface controls like the map, legend, toolbox, ribbon and others.
  // ********************************************************************************************************
  // 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 7/25/2008 9:37:01 AM
  //
  // Contributor(s): (Open source contributors should list themselves and their modifications here).
  //
  // Name               |   Date             |         Comments
  //--------------------|--------------------|--------------------------------------------------------
  // Jiri Kadlec        |  2/18/2010         |  Added zoom out button and custom mouse cursors
  // Kyle Ellison       | 12/15/2010         |  Fixed Issue #190 (Deactivated MapFunctions still involved)
  // Eric Hullinger     | 12/28/2012         |  Resolved Issue (Unrestricted Zoom Out)
  // ********************************************************************************************************
  
  using System;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Diagnostics;
  using System.Drawing;
  using System.Drawing.Drawing2D;
  using System.Drawing.Imaging;
  using System.IO;
  using System.Linq;
  using System.Windows.Forms;
  using DotSpatial.Data;
  using DotSpatial.Data.Forms;
  using DotSpatial.Projections;
  using DotSpatial.Serialization;
  using DotSpatial.Symbology;
  using DotSpatial.Topology;
  using Point = System.Drawing.Point;
  using SelectionMode = DotSpatial.Symbology.SelectionMode;
  
  namespace DotSpatial.Controls
  {
      /// <summary>
      /// The Map Control for 2D applications.
      /// </summary>
      public partial class Map : UserControl, IMap, IMessageFilter
      {
          #region Events
  
          /// <summary>
          /// Occurs after the map refreshes the image
          /// </summary>
          public event EventHandler FinishedRefresh;
  
          /// <summary>
          /// Occurs when the map function mode has changed
          /// </summary>
          /// <remarks>Example of changing the function mode
          /// is changing from zoom mode to select mode.</remarks>
          public event EventHandler FunctionModeChanged;
  
          /// <summary>
          /// Occurs after a resize event
          /// </summary>
          public event EventHandler Resized;
  
          /// <summary>
          /// Occurs after the selection has changed for all the layers
          /// </summary>
          public event EventHandler SelectionChanged;
  
          /// <summary>
          /// Occurs after a layer has been added to the mapframe, or any of the child groups of that mapframe.
          /// </summary>
          public event EventHandler<LayerEventArgs> LayerAdded;
  
          /// <summary>
          /// Public event advertising the mouse movement
          /// </summary>
          public event EventHandler<GeoMouseArgs> GeoMouseMove;
  
          /// <summary>
          /// Fires after the view extents have been altered and the map has redrawn to the new extents.
          /// This is an echo of the MapFrame.ViewExtentsChanged, so you only want one handler.
          /// </summary>
          public event EventHandler<ExtentArgs> ViewExtentsChanged;
  
          /// <summary>
          /// Occurs after the projection of the map has been changed
          /// </summary>
          public event EventHandler ProjectionChanged;
  
          #endregion
  
          #region Private Variables
  
          private Dictionary<FunctionMode, IMapFunction> _functionLookup;
          private FunctionMode _functionMode;
          private IMapFrame _geoMapFrame;
          private int _isBusyIndex;
          private ILegend _legend;
          private IProgressHandler _progressHandler;
          private Size _oldSize;
  
          #endregion
  
          #region Constructors
  
          /// <summary>
          /// Creates a new instance of a map component that can be dropped on a form.
          /// </summary>
          public Map()
          {
              InitializeComponent();
              Configure();
              Application.AddMessageFilter(this);
          }
  
          /// <summary>
          /// Handles the resizing in the case where the map uses docking, and therefore
          /// needs to be updated whenever the form changes size.
          /// </summary>
          /// <param name="e"></param>
          protected override void OnLoad(EventArgs e)
          {
              KeyUp += Map_KeyUp;
              KeyDown += Map_KeyDown;
  
              SizeChanged += OnSizeChanged;
              _oldSize = Size;
          }
  
          public bool PreFilterMessage(ref Message m) 
          {
              if (m.Msg == (int)0x0100)
              {
                  if(this.ContainsFocus)
                      OnKeyDown(new KeyEventArgs((Keys)m.WParam.ToInt32()));
              }
              else if (m.Msg == (int)0x0101)
              {
                  if (this.ContainsFocus)
                      OnKeyUp(new KeyEventArgs((Keys)m.WParam.ToInt32()));
              }
              return false;
          }
  
          private void Map_KeyUp(object sender, KeyEventArgs e)
          {
  
              foreach (IMapFunction tool in MapFunctions)
              {
                  if (tool.Enabled) tool.DoKeyUp(e);
              }
          }
  
          private void Map_KeyDown(object sender, KeyEventArgs e)
          {
  
              foreach (IMapFunction tool in MapFunctions)
              {
                  if (tool.Enabled) tool.DoKeyDown(e);
              }
          }
         
          private void Configure()
          {
              MapFrame = new MapFrame(this, new Extent(0, 0, 0, 0));
  
              //_resizeEndTimer = new Timer {Interval = 100};
              //_resizeEndTimer.Tick += _resizeEndTimer_Tick;
  
              IMapFunction info = new MapFunctionIdentify(this);
              IMapFunction pan = new MapFunctionPan(this);
              IMapFunction label = new MapFunctionLabelSelect(this);
              IMapFunction select = new MapFunctionSelect(this);
              IMapFunction zoomIn = new MapFunctionClickZoom(this);
              IMapFunction zoomOut = new MapFunctionZoomOut(this);
              MapFunctions = new List<IMapFunction>
                                 {
                                     new MapFunctionZoom(this),
                                     new MapFunctionKeyNavigation(this),
                                     pan,
                                     select,
                                     zoomIn,
                                     zoomOut,
                                     label,
                                     info,
                                 };
              _functionLookup = new Dictionary<FunctionMode, IMapFunction>
                                    {
                                        {FunctionMode.Pan, pan},
                                        {FunctionMode.Info, info},
                                        {FunctionMode.Label, label},
                                        {FunctionMode.Select, select},
                                        {FunctionMode.ZoomIn, zoomIn},
                                        {FunctionMode.ZoomOut, zoomOut}
                                    };
  
              CollisionDetection = false;
  
              IMapFunction KeyNavigation = MapFunctions.Find(f => f.GetType() == typeof(MapFunctionKeyNavigation));
              ActivateMapFunction(KeyNavigation);
              //changed by Jiri Kadlec - default function mode is none
              FunctionMode = FunctionMode.None;
          }
  
          private void MapFrameItemChanged(object sender, EventArgs e)
          {
              Invalidate();
          }
  
          private void MapFrameUpdateMap(object sender, EventArgs e)
          {
              Invalidate();
          }
  
          private void MapFrameScreenUpdated(object sender, EventArgs e)
          {
              Invalidate();
          }
  
          /// <inheritdoc />
          protected override void OnDragEnter(DragEventArgs drgevent)
          {
              drgevent.Effect = drgevent.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Copy : DragDropEffects.None;
              base.OnDragEnter(drgevent);
          }
  
          /// <inheritdoc />
          protected override void OnDragDrop(DragEventArgs drgevent)
          {
              string[] s = (string[])drgevent.Data.GetData(DataFormats.FileDrop, false);
              if (s == null)
              {
                  base.OnDragDrop(drgevent);
                  return;
              }
              int i;
              bool failed = false;
              for (i = 0; i < s.Length; i++)
              {
                  try
                  {
                      AddLayer(s[i]);
                  }
                  catch (Exception ex)
                  {
                      Debug.WriteLine(ex.Message);
                      // failed at least one effort
                      failed = true;
                  }
              }
              if (failed)
              {
                  MessageBox.Show(MessageStrings.Map_OnDragDrop_Invalid);
              }
          }
  
          #endregion
  
          #region Methods
  
          /// <summary>
          /// This will add a new label category that will only apply to the specified filter expression.
          /// This will not remove any existing categories.
          /// </summary>
          /// <param name="featureLayer">The feature layer that the labels should be applied to</param>
          /// <param name="expression">The string expression where field names are in square brackets</param>
          /// <param name="filterExpression">The string filter expression that controls which features are labeled.
          /// Field names are in square brackets, strings in single quotes.</param>
          /// <param name="symbolizer">The label symbolizer that controls the basic appearance of the labels in this
          /// category.</param>
          /// <param name="name">The name of the category.</param>
          public void AddLabels(IFeatureLayer featureLayer, string expression, string filterExpression,
                                ILabelSymbolizer symbolizer, string name)
          {
              if (featureLayer.LabelLayer == null) featureLayer.LabelLayer = new MapLabelLayer();
              featureLayer.ShowLabels = true;
              ILabelCategory lc = new LabelCategory
                                      {
                                          Expression = expression,
                                          FilterExpression = filterExpression,
                                          Symbolizer = symbolizer,
                                          Name = name,
                                      };
              featureLayer.LabelLayer.Symbology.Categories.Add(lc);
              featureLayer.LabelLayer.CreateLabels();
          }
  
          /// <summary>
          /// This will add a new label category that will only apply to the specified filter expression.  This will
          /// not remove any existing categories.
          /// </summary>
          /// <param name="featureLayer">The feature layer that the labels should be applied to</param>
          /// <param name="expression">The string expression where field names are in square brackets</param>
          /// <param name="filterExpression">The string filter expression that controls which features are labeled.
          /// Field names are in square brackets, strings in single quotes.</param>
          /// <param name="symbolizer">The label symbolizer that controls the basic appearance of the labels in this
          ///  category.</param>
          /// <param name="width">A geographic width, so that if the map is zoomed to a geographic width smaller than
          /// this value, labels should appear.</param>
          public void AddLabels(IFeatureLayer featureLayer, string expression, string filterExpression,
                                ILabelSymbolizer symbolizer, double width)
          {
              if (featureLayer.LabelLayer == null) featureLayer.LabelLayer = new MapLabelLayer();
              featureLayer.ShowLabels = true;
              ILabelCategory lc = new LabelCategory
                                      {
                                          Expression = expression,
                                          FilterExpression = filterExpression,
                                          Symbolizer = symbolizer
                                      };
              featureLayer.LabelLayer.UseDynamicVisibility = true;
              featureLayer.LabelLayer.DynamicVisibilityWidth = width;
              featureLayer.LabelLayer.Symbology.Categories.Add(lc);
              featureLayer.LabelLayer.CreateLabels();
          }
  
          /// <summary>
          /// Gets the subset of layers that are specifically raster layers, allowing
          /// you to control their symbology.
          /// </summary>
          /// <returns></returns>
          public IMapImageLayer[] GetImageLayers()
          {
              return MapFrame.Layers.OfType<IMapImageLayer>().ToArray();
          }
  
          /// <summary>
          /// Gets the subset of layers that are specifically raster layers, allowing
          /// you to control their symbology.
          /// </summary>
          /// <returns></returns>
          public IMapRasterLayer[] GetRasterLayers()
          {
              return MapFrame.Layers.OfType<IMapRasterLayer>().ToArray();
          }
  
          /// <summary>
          /// Gets a list of just the line layers (and not the general layers)
          /// </summary>
          /// <returns></returns>
          public IMapLineLayer[] GetLineLayers()
          {
              return MapFrame.Layers.OfType<IMapLineLayer>().ToArray();
          }
  
          /// <summary>
          /// Gets a list of just the line layers (and not the general layers)
          /// </summary>
          /// <returns></returns>
          public IMapPolygonLayer[] GetPolygonLayers()
          {
              return MapFrame.Layers.OfType<IMapPolygonLayer>().ToArray();
          }
  
          /// <summary>
          /// Gets a list of just the line layers (and not the general layers)
          /// </summary>
          /// <returns></returns>
          public IMapPointLayer[] GetPointLayers()
          {
              return MapFrame.Layers.OfType<IMapPointLayer>().ToArray();
          }
  
          /// <summary>
          /// Gets a list of just the feature layers regardless of whether they are lines, points, or polygons
          /// </summary>
          /// <returns>An array of IMapFeatureLayers</returns>
          public IMapFeatureLayer[] GetFeatureLayers()
          {
              return MapFrame.Layers.OfType<IMapFeatureLayer>().ToArray();
          }
  
          /// <summary>
          /// Gets the MapFunction based on the string name
          /// </summary>
          /// <param name="name">The string name to find</param>
          /// <returns>The MapFunction with the specified name</returns>
          public IMapFunction GetMapFunction(string name)
          {
              return MapFunctions.First(f => f.Name == name);
          }
  
          /// <summary>
          /// Removes any members from existing in the selected state
          /// </summary>
          public bool ClearSelection(out IEnvelope affectedArea)
          {
              affectedArea = new Envelope();
              if (MapFrame == null) return false;
              return MapFrame.ClearSelection(out affectedArea);
          }
  
          /// <summary>
          /// Adds any members found in the specified region to the selected state as long as SelectionEnabled is set to true.
          /// </summary>
          /// <param name="tolerant">The geographic region where selection occurs that is tolerant for point or linestrings.</param>
          /// <param name="strict">The tight envelope to use for polygons.</param>
          /// <param name="mode">The selection mode.</param>
          /// <param name="affectedArea">The envelope affected area.</param>
          /// <returns>Boolean, true if any members were added to the selection.</returns>
          public bool Select(IEnvelope tolerant, IEnvelope strict, SelectionMode mode, out IEnvelope affectedArea)
          {
              affectedArea = new Envelope();
              if (MapFrame == null) return false;
              return MapFrame.Select(tolerant, strict, mode, out affectedArea);
          }
  
          /// <summary>
          /// Inverts the selected state of any members in the specified region.
          /// </summary>
          /// <param name="tolerant">The geographic region where selection occurs that is tolerant for point or linestrings.</param>
          /// <param name="strict">The tight envelope to use for polygons.</param>
          /// <param name="mode">The selection mode determining how to test for intersection.</param>
          /// <param name="affectedArea">The geographic region encapsulating the changed members.</param>
          /// <returns>boolean, true if members were changed by the selection process.</returns>
          public bool InvertSelection(IEnvelope tolerant, IEnvelope strict, SelectionMode mode, out IEnvelope affectedArea)
          {
              affectedArea = new Envelope();
              if (MapFrame == null) return false;
              return MapFrame.InvertSelection(tolerant, strict, mode, out affectedArea);
          }
  
          /// <summary>
          /// Adds any members found in the specified region to the selected state as long as SelectionEnabled is set to true.
          /// </summary>
          /// <param name="tolerant">The geographic region where selection occurs that is tolerant for point or linestrings.</param>
          /// <param name="strict">The tight envelope to use for polygons.</param>
          /// <param name="mode">The selection mode.</param>
          /// <param name="affectedArea">The envelope affected area.</param>
          /// <returns>Boolean, true if any members were added to the selection.</returns>
          public bool UnSelect(IEnvelope tolerant, IEnvelope strict, SelectionMode mode, out IEnvelope affectedArea)
          {
              affectedArea = new Envelope();
              if (MapFrame == null) return false;
              return MapFrame.UnSelect(tolerant, strict, mode, out affectedArea);
          }
  
          /// <summary>
          /// Allows the user to add a new layer to the map using an open file dialog to choose a layer file.
          /// Multi-select is an option, so this return a list with all the layers.
          /// </summary>
          public virtual List<IMapLayer> AddLayers()
          {
              List<IDataSet> sets = DataManager.DefaultDataManager.OpenFiles();
              if (sets == null || sets.Count == 0) return null;
              List<IMapLayer> results = new List<IMapLayer>();
              foreach (IDataSet set in sets)
              {
                  if (set == null) return null;
  
                  IFeatureSet fs = set as IFeatureSet;
                  if (fs != null)
                  {
                      results.Add(Layers.Add(fs));
                      continue;
                  }
                  IImageData id = set as IImageData;
                  if (id != null)
                  {
                      results.Add(Layers.Add(id));
                      continue;
                  }
                  IRaster r = set as IRaster;
                  if (r != null)
                  {
                      results.Add(Layers.Add(r));
                      continue;
                  }
              }
              return results;
          }
  
          /// <summary>
          /// Adds the fileName as a new layer to the map, returning the new layer.
          /// </summary>
          /// <param name="fileName">The string fileName of the layer to add</param>
          /// <returns>The IMapLayer added to the file.</returns>
          public virtual IMapLayer AddLayer(string fileName)
          {
              return Layers.Add(fileName);
          }
  
          /// <summary>
          /// This is so that if you have a basic map interface you can still prompt
          /// to add a layer, you just won't get an IMapLayer back.
          /// </summary>
          ILayer IBasicMap.AddLayer()
          {
              return AddLayer();
          }
  
          /// <summary>
          /// Uses the file dialog to allow selection of a fileName for opening the
          /// new layer, but does not allow multiple files to be added at once.
          /// </summary>
          /// <returns>The newly opened IMapLayer</returns>
          public virtual IMapLayer AddLayer()
          {
              if (DataManager.DefaultDataManager.ProgressHandler == null)
              {
                  if (ProgressHandler != null)
                  {
                      DataManager.DefaultDataManager.ProgressHandler = ProgressHandler;
                  }
              }
              try
              {
                  return Layers.Add(DataManager.DefaultDataManager.OpenFile());
              }
              catch (Exception ex)
              {
                  MessageBox.Show(ex.Message);
              }
              return null;
          }
  
          /// <summary>
          /// Allows a multi-select file dialog to add raster layers, applying a
          /// filter so that only supported raster formats will appear.
          /// </summary>
          /// <returns>A list of the IMapRasterLayers that were opened.</returns>
          public virtual List<IMapRasterLayer> AddRasterLayers()
          {
              List<IRaster> sets = DataManager.DefaultDataManager.OpenRasters();
              return sets.Select(raster => Layers.Add(raster)).ToList();
          }
  
          /// <summary>
          /// Allows an open file dialog without multi-select enabled to add a single
          /// raster to the map as a layer, and returns the added layer.
          /// </summary>
          /// <returns>The IMapRasterLayer that was added</returns>
          public virtual IMapRasterLayer AddRasterLayer()
          {
              return Layers.Add(DataManager.DefaultDataManager.OpenRaster());
          }
  
          /// <summary>
          /// Allows a mult-select open file dialog to specify several fileNames to add.
          /// Only files with supported vector extensions will be shown.
          /// </summary>
          /// <returns>The list of added MapFeatureLayers</returns>
          public virtual List<IMapFeatureLayer> AddFeatureLayers()
          {
              List<IFeatureSet> sets = DataManager.DefaultDataManager.OpenVectors();
              return sets.Select(featureSet => Layers.Add(featureSet)).ToList();
          }
  
          /// <summary>
          /// Allows an open file dialog without multi-select enabled to add a single
          /// raster tot he map as a layer, and returns the added layer.
          /// </summary>
          /// <returns></returns>
          public virtual IMapFeatureLayer AddFeatureLayer()
          {
              return Layers.Add(DataManager.DefaultDataManager.OpenVector());
          }
  
          /// <summary>
          /// Allows a mult-select open file dialog to specify several fileNames to add.
          /// Only files with supported image extensions will be shown.
          /// </summary>
          /// <returns>The list of added MapImageLayers</returns>
          public virtual List<IMapImageLayer> AddImageLayers()
          {
              List<IImageData> sets = DataManager.DefaultDataManager.OpenImages();
              return sets.Select(imageData => Layers.Add(imageData)).ToList();
          }
  
          /// <summary>
          /// Allows an open dialog without multi-select to specify a single fileName
          /// to be added to the map as a new layer and returns the newly added layer.
          /// </summary>
          /// <returns>The layer that was added to the map.</returns>
          public virtual IMapImageLayer AddImageLayer()
          {
              return Layers.Add(DataManager.DefaultDataManager.OpenImage());
          }
  
          /// <summary>
          /// This can be called any time, and is currently being used to capture
          /// the end of a resize event when the actual data should be updated.
          /// </summary>
          public virtual void ResetBuffer()
          {
              if (_geoMapFrame != null)
              {
                  _geoMapFrame.ResetBuffer();
              }
          }
  
          /// <summary>
          /// Saves the dataset belonging to the layer.
          /// </summary>
          public virtual void SaveLayer()
          {
              SaveFileDialog sfd = new SaveFileDialog();
              IMapLayer layer = _geoMapFrame.Layers[0];
              IMapFeatureLayer mfl = layer as IMapFeatureLayer;
              if (mfl != null)
              {
                  sfd.Filter = DataManager.DefaultDataManager.VectorWriteFilter;
                  if (sfd.ShowDialog() != DialogResult.OK) { return; }
                  mfl.DataSet.SaveAs(sfd.FileName, true);
                  return;
              }
              IMapRasterLayer mrl = layer as IMapRasterLayer;
              if (mrl != null)
              {
                  sfd.Filter = DataManager.DefaultDataManager.RasterWriteFilter;
                  if (sfd.ShowDialog() != DialogResult.OK) { return; }
                  mrl.DataSet.SaveAs(sfd.FileName);
                  return;
              }
              IMapImageLayer mil = layer as IMapImageLayer;
              if (mil != null)
              {
                  sfd.Filter = DataManager.DefaultDataManager.ImageWriteFilter;
                  if (sfd.ShowDialog() != DialogResult.OK) { return; }
                  mil.DataSet.SaveAs(sfd.FileName);
                  return;
              }
              throw new ArgumentException("The layer chosen did not have a raster, vector or image dataset to save.");
          }
  
          /// <summary>
          /// Instructs the map to change the perspective to include the entire drawing content, and
          /// in the case of 3D maps, changes the perspective to look from directly overhead.
          /// </summary>
          public void ZoomToMaxExtent()
          {
              // to prevent exception when zoom to map with one layer with one point
              const double eps = 1e-7;
              if (Extent.Width < eps || Extent.Height < eps)
              {
                  Extent newExtent = new Extent(Extent.MinX - eps, Extent.MinY - eps, Extent.MaxX + eps, Extent.MaxY + eps);
                  ViewExtents = newExtent;
              }
              else
              {
                  ViewExtents = Extent;
              }
  
              this.IsZoomedToMaxExtent = true;
          }
  
          //  Added by Eric Hullinger 12/28/2012 for use in preventing zooming out too far.
          /// <summary> 
          /// Gets the MaxExtent Window of the current Map
          /// </summary>
          public Extent GetMaxExtent()
          {
              Extent MaxExtent;
              // to prevent exception when zoom to map with one layer with one point
              const double eps = 1e-7;
              if (Extent.Width < eps || Extent.Height < eps)
              {
                  MaxExtent = new Extent(Extent.MinX - eps, Extent.MinY - eps, Extent.MaxX + eps, Extent.MaxY + eps);
              }
              else
              {
                  MaxExtent = Extent;
              }
              return MaxExtent;
          }
  
  
          /// <summary>
          /// This activates the labels for the specified feature layer that will be the specified expression
          /// where field names are in square brackets like "[Name]: [Value]".  This will label all the features,
          /// and remove any previous labeling.
          /// </summary>
          /// <param name="featureLayer">The FeatureLayer to apply the labels to.</param>
          /// <param name="expression">The string label expression to use where field names are in square brackets like
          /// [Name]</param>
          /// <param name="font">The font to use for these labels</param>
          /// <param name="fontColor">The color for the labels</param>
          public void AddLabels(IFeatureLayer featureLayer, string expression, Font font, Color fontColor)
          {
              featureLayer.ShowLabels = true;
  
              MapLabelLayer ll = new MapLabelLayer();
              ll.Symbology.Categories.Clear();
              LabelCategory lc = new LabelCategory { Expression = expression };
              ll.Symbology.Categories.Add(lc);
  
              ILabelSymbolizer ls = ll.Symbolizer;
              ls.Orientation = ContentAlignment.MiddleCenter;
              ls.BackColorEnabled = false;
              ls.BackColorEnabled = false;
              ls.FontColor = fontColor;
              ls.FontFamily = font.FontFamily.ToString();
              ls.FontSize = font.Size;
              ls.FontStyle = font.Style;
              ls.PartsLabelingMethod = PartLabelingMethod.LabelLargestPart;
              featureLayer.LabelLayer = ll;
          }
  
          /// <summary>
          /// Removes any existing label categories
          /// </summary>
          public void ClearLabels(IFeatureLayer featureLayer)
          {
              featureLayer.ShowLabels = false;
              if (featureLayer.LabelLayer == null) return;
          }
  
          /// <summary>
          /// Opens a new layer and automatically adds it to the map.
          /// </summary>
          [Obsolete("Use AddLayers() with no parameters")]
          public virtual void OpenLayer()
          {
              AddLayers();
          }
  
          /// <summary>
          /// Instead of using the usual buffers, this bypasses any buffering and instructs the layers
          /// to draw directly to the specified target rectangle on the graphics object.  This is useful
          /// for doing vector drawing on much larger pages.  The result will be centered in the
          /// specified target rectangle bounds.
          /// </summary>
          /// <param name="device">The graphics device to print to</param>
          /// <param name="targetRectangle">the rectangle where the map content should be drawn.</param>
          public void Print(Graphics device, Rectangle targetRectangle)
          {
              MapFrame.Print(device, targetRectangle);
          }
  
          /// <summary>
          /// Instead of using the usual buffers, this bypasses any buffering and instructs the layers
          /// to draw directly to the specified target rectangle on the graphics object.  This is useful
          /// for doing vector drawing on much larger pages.  The result will be centered in the
          /// specified target rectangle bounds.
          /// </summary>
          /// <param name="device">The graphics device to print to</param>
          /// <param name="targetRectangle">the rectangle where the map content should be drawn.</param>
          /// <param name="targetEnvelope">the extents to print in the target rectangle</param>
          public void Print(Graphics device, Rectangle targetRectangle, Extent targetEnvelope)
          {
              MapFrame.Print(device, targetRectangle, targetEnvelope);
          }
  
          #endregion
  
          #region Properties
  
          /// <summary>
          /// Gets or sets a boolean that indicates whether or not
          /// the drawing layers should cache off-screen data to
          /// the buffer.  Panning will be much more elegant,
          /// but zooming, selecting and resizing will take a
          /// performance penalty.
          /// </summary>
          [Category("Behavior"), Description("Gets or sets a boolean that indicates whether or not zooming will also retrieve content immediately around the map so that panning pulls new content onto the screen.")]
          public bool ExtendBuffer
          {
              get { return MapFrame.ExtendBuffer; }
              set { MapFrame.ExtendBuffer = value; }
          }
  
          /// <summary>
          /// Gets or sets the Projection Esri string of the map. This property is used for serializing
          /// the projection string to the project file.
          /// </summary>
          [Serialize("ProjectionEsriString")]
          [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)]
          public string ProjectionEsriString
          {
              get
              {
                  return Projection != null ? Projection.ToEsriString() : null;
              }
              set
              {
                  if (Projection != null)
                  {
                      if (string.IsNullOrEmpty(value))
                      {
                          Projection = null;
                      }
                      else
                      {
                          if (Projection.ToEsriString() != value)
                          {
                              Projection = ProjectionInfo.FromEsriString(value);
                              OnProjectionChanged();
                          }
                      }
                  }
              }
          }
  
          /// <summary>
          /// Gets or sets a PromptMode enumeration that controls how users are prompted before adding layers
          /// that have a coordinate system that is different from the map.
          /// </summary>
          public ActionMode ProjectionModeReproject
          {
              get
              {
                  if (_geoMapFrame != null) return MapFrame.ProjectionModeReproject;
                  return ActionMode.Never;
              }
              set
              {
                  if (_geoMapFrame != null) _geoMapFrame.ProjectionModeReproject = value;
              }
          }
  
          /// <summary>
          /// Gets or sets a PromptMode enumeration that controls how users are prompted before adding layers
          /// that have a coordinate system that is different from the map.
          /// </summary>
          public ActionMode ProjectionModeDefine
          {
              get
              {
                  if (_geoMapFrame != null) return MapFrame.ProjectionModeDefine;
                  return ActionMode.Never;
              }
              set
              {
                  if (_geoMapFrame != null) _geoMapFrame.ProjectionModeDefine = value;
              }
          }
  
          /// <summary>
          /// Gets or sets a value indicating whether layers should draw during the actual resize itself.  The
          /// normal behavior is to draw the existing image buffer in the new size and position which is much
          /// faster for large datasets, but is not as visually appealing if you only work with small datasets.
          /// </summary>
          public bool RedrawLayersWhileResizing { get; set; }
  
          /// <summary>
          /// Cursor hiding from designer
          /// </summary>
          [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
          public override Cursor Cursor
          {
              get
              {
                  return base.Cursor;
              }
              set
              {
                  base.Cursor = value;
              }
          }
  
          /// <summary>
          /// Gets or sets a boolean indicating whether or not the sel
          /// </summary>
          public bool SelectionEnabled
          {
              get
              {
                  if (MapFrame == null) return false;
                  return MapFrame.SelectionEnabled;
              }
              set
              {
                  if (MapFrame == null) return;
                  MapFrame.SelectionEnabled = value;
              }
          }
  
          /// <summary>
          /// Instructs the map to clear the layers.
          /// </summary>
          public void ClearLayers()
          {
              if (MapFrame == null) return;
              MapFrame.Layers.Clear();
          }
  
          /// <summary>
          /// Gets or sets a boolean that indicates whether the Garbage collector should collect after drawing.
          /// This can be disabled for fast-action panning, but should normally be enabled.
          /// </summary>
          public bool CollectAfterDraw { get; set; }
  
          /// <summary>
          /// Gets or sets the dictionary of tools built into this project
          /// </summary>
          [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
          public List<IMapFunction> MapFunctions { get; set; }
  
          /// <summary>
          /// Gets or sets the current tool mode.  This rapidly enables or disables specific tools to give
          /// a combination of functionality.  Selecting None will disable all the tools, which can be
          /// enabled manually by enabling the specific tool in the GeoTools dictionary.
          /// </summary>
          public FunctionMode FunctionMode
          {
              get { return _functionMode; }
              set
              {
                  _functionMode = value;
                  switch (_functionMode)
                  {
                      case FunctionMode.ZoomIn:
                          try
                          {
                              MemoryStream ms = new MemoryStream(Images.cursorZoomIn);
  
gt:                             Cursor = new Cursor(ms);