MainForm.cs
// Copyright 2010 ESRI // // All rights reserved under the copyright laws of the United States // and applicable international laws, treaties, and conventions. // // You may freely redistribute and use this sample code, with or // without modification, provided you include the original copyright // notice and use restrictions. // // See the use restrictions. // using System; using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; using System.Runtime.InteropServices; using System.Windows.Forms; using ESRI.ArcGIS.ADF; using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.Controls; using ESRI.ArcGIS.DataSourcesFile; using ESRI.ArcGIS.DefenseSolutions; using ESRI.ArcGIS.Display; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.SystemUI; namespace MoleSymbols { public sealed partial class MainForm : Form { #region class private members private IPoint m_currentMouseLocation = null; private int m_dragStartTime = 0; private bool m_firstTime = true; private IPoint m_lastMouseClick = new PointClass(); private IMapControl3 m_mapControl = null; private IDisplayFeedback m_moveFeedBack = null; private Random m_random = new Random(); private IEnvelope m_selectedBounds = new EnvelopeClass(); private DemoSIC m_sic = new DemoSIC(); private int m_unitCount = 0; #endregion #region Constructor public MainForm() { InitializeComponent(); m_lastMouseClick.PutCoords (0, 0); UpdateStatusBar(); UpdateTitle(); } #endregion #region Tool strip button event handlers private void tsbAdd200_Click (object sender, EventArgs e) { // this one takes a little while, especially when it's the first one chosen Cursor = Cursors.WaitCursor; // create concentric rings of units centered around where the user last clicked double centerLon = m_lastMouseClick.X; double centerLat = m_lastMouseClick.Y; const double circleRadiusInRad = 1.0; const int numberPerCircle = 10; for ( int i = 0; i < 200; ++i ) { // draw a random symbol at the next position in the pattern double currentRadius = (i / numberPerCircle) * circleRadiusInRad + circleRadiusInRad; double currentAngle = (i % numberPerCircle) * 2.0 * 3.1415926536 / (double)numberPerCircle; DrawSymbol ( CreatePoint( centerLon + (currentRadius * Math.Sin(currentAngle)), centerLat + (currentRadius * Math.Cos(currentAngle)) ), m_sic[m_random.Next()], true ); } //refresh the display and restore the cursor axMapControl1.ActiveView.PartialRefresh (esriViewDrawPhase.esriViewGraphics, null, null); Cursor = Cursors.Default; } private void tsbAddArea_Click (object sender, EventArgs e) { // create the symbol using a default symbol ID code (obstacle restricted area) IMoleSymbol moleSymbol = new MoleFillSymbolClass(); moleSymbol.SymbolID = "GUMPOGR-------X"; moleSymbol.TextLabels = GetLabels(); // override the default fill color and outline symbol - these settings are optional //ILineSymbol lineSymbol = new SimpleLineSymbolClass(); //lineSymbol.Color = GetRandomColor(); //lineSymbol.Width = dRandom (1, 5); //IFillSymbol fillSymbol = moleSymbol as IFillSymbol; //fillSymbol.Outline = lineSymbol; //fillSymbol.Color = GetRandomColor(); // create a new polygon geometry for this symbol (four points in this example) IPointCollection pointCollection = new PolygonClass(); // center the polygon somewhere inside the current map extent IEnvelope extent = m_mapControl.ActiveView.Extent; double lat = dRandom (extent.YMin, extent.YMax); double lon = dRandom (extent.XMin, extent.XMax); // place the four corners somewhere within a specified threshold of the center const double threshold = 20; object missing = Type.Missing; pointCollection.AddPoint (CreatePoint(lon, dRandom(lat, lat + threshold)), ref missing, ref missing); pointCollection.AddPoint (CreatePoint(dRandom(lon, lon + threshold), lat), ref missing, ref missing); pointCollection.AddPoint (CreatePoint(lon, dRandom(lat - threshold, lat)), ref missing, ref missing); pointCollection.AddPoint (CreatePoint(dRandom(lon - threshold, lon), lat), ref missing, ref missing); // set up the graphic element with the random geometry IFillShapeElement fillShapeElement = new PolygonElementClass(); fillShapeElement.Symbol = moleSymbol as IFillSymbol; IElement element = fillShapeElement as IElement; element.Geometry = pointCollection as IGeometry; // add the new element to the map and update the user interface m_mapControl.ActiveView.GraphicsContainer.AddElement (element, 0); m_mapControl.ActiveView.PartialRefresh (esriViewDrawPhase.esriViewGraphics, null, null); ++m_unitCount; UpdateTitle(); } private void tsbAddLine_Click (object sender, EventArgs e) { // create the symbol using a default symbol ID code (fix task line) IMoleSymbol moleSymbol = new MoleLineSymbolClass(); moleSymbol.SymbolID = "GUTPF---------X"; moleSymbol.TextLabels = GetLabels(); // override the default line color and width - these settings are optional //ILineSymbol lineSymbol = moleSymbol as ILineSymbol; //lineSymbol.Color = GetRandomColor(); //lineSymbol.Width = dRandom (1, 5); // create a new line geometry for the symbol - this symbol requires two points IPointCollection pointCollection = new PolylineClass(); // place the first endpoint of the line somewhere inside the current map extent IEnvelope ext = m_mapControl.ActiveView.Extent; double lat = dRandom (ext.YMin, ext.YMax); double lon = dRandom (ext.XMin, ext.XMax); object missing = Type.Missing; pointCollection.AddPoint (CreatePoint(lon, lat), ref missing, ref missing); // place the second endpoint somewhere within a specified threshold of the first const double threshold = 20; pointCollection.AddPoint ( CreatePoint( dRandom(lon - threshold, lon + threshold), dRandom(lat - threshold, lat + threshold) ), ref missing, ref missing ); // set up the graphic element with the random geometry ILineElement lineElement = new LineElementClass(); lineElement.Symbol = moleSymbol as ILineSymbol; IElement element = lineElement as IElement; element.Geometry = pointCollection as IGeometry; // add the new element to the map and update the user interface m_mapControl.ActiveView.GraphicsContainer.AddElement (element, 0); m_mapControl.ActiveView.PartialRefresh (esriViewDrawPhase.esriViewGraphics, null, null); ++m_unitCount; UpdateTitle(); } private void tsbAddMoleSymbol_Click (object sender, EventArgs e) { // make this TSB exclusive both in the tool strip and in the AxToolbarControl // the primary logic is in axMapControl1_OnMouseDown if ( tsbAddMoleSymbol.Checked ) { axToolbarControl1.SetBuddyControl (null); axToolbarControl1.Enabled = false; tsbSelect.Checked = false; } else { axToolbarControl1.SetBuddyControl (axMapControl1); axToolbarControl1.Enabled = true; } } private void tsbMoveUnits_Click (object sender, EventArgs e) { // MoveGraphics only applies to units in the selection - this will erase any previous selection IGraphicsContainerSelect graphicsContainerSelect = m_mapControl.ActiveView.GraphicsContainer as IGraphicsContainerSelect; graphicsContainerSelect.SelectAllElements(); MoveGraphics (0.75, 0.75); graphicsContainerSelect.UnselectAllElements(); m_selectedBounds.SetEmpty(); } private void tsbSelect_Click (object sender, EventArgs e) { // make this TSB exclusive both in the tool strip and in the AxToolbarControl // the primary logic is in axMapControl1_OnMouseDown and axMapControl1_OnMouseMove if ( tsbSelect.Checked ) { axToolbarControl1.SetBuddyControl (null); axToolbarControl1.Enabled = false; tsbAddMoleSymbol.Checked = false; } else { axToolbarControl1.SetBuddyControl (axMapControl1); axToolbarControl1.Enabled = true; } } #endregion #region Form event handlers private string GetSdkDataPath() { //get the ArcGIS path from the registry Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\ESRI\ArcGIS_SXS_SDK"); string path = Convert.ToString(key.GetValue("InstallDir")); //set the of the logo string str = System.IO.Path.Combine(path, @"Samples\data\"); if (!System.IO.Directory.Exists(str)) { MessageBox.Show("Path :" + str + " does not exist!"); return string.Empty; } return str; } private void MainForm_Load (object sender, EventArgs e) { // get a reference to the MapControl for local use m_mapControl = axMapControl1.Object as IMapControl3; // get a map from the SDK sample data string dataPath = GetSdkDataPath() + @"MilitaryOverlayEditor\"; string defaultMxDoc = dataPath + "molebasemap.mxd"; if ( m_mapControl.CheckMxFile(defaultMxDoc) ) { // load the map into the map control object missing = Type.Missing; m_mapControl.LoadMxFile (defaultMxDoc, missing, missing); } else { string errorMsg = "Could not load default map document - Application may not work!"; errorMsg += Environment.NewLine + defaultMxDoc; Trace.WriteLine (errorMsg); MessageBox.Show (errorMsg); } } private void MainForm_FormClosing (object sender, FormClosingEventArgs e) { // tell MOLE to save its settings and release its resources (new MoleCoreHelperClass()).ReleaseForceElementRenderer(); } #endregion #region Map control event handlers private void axMapControl1_OnMapReplaced (object sender, IMapControlEvents2_OnMapReplacedEvent e) { // initialize the last mouse click point to the center of the map's extent IEnvelope extent = m_mapControl.ActiveView.Extent; m_lastMouseClick.PutCoords ( extent.XMin + extent.Width * 0.5, extent.YMin + extent.Height * 0.5 ); // update the status bar and the title bar m_unitCount = 0; UpdateStatusBar(); UpdateTitle(); } private void axMapControl1_OnMouseDown (object sender, IMapControlEvents2_OnMouseDownEvent e) { m_lastMouseClick.PutCoords (e.mapX, e.mapY); if ( tsbAddMoleSymbol.Checked ) { // "Add MOLE Symbol" command: draw a symbol at the click point DrawSymbol (m_lastMouseClick, tstSIC.Text, false); tstSIC.Text = m_sic[m_random.Next()]; } else if ( tsbSelect.Checked && SelectElements(m_lastMouseClick, m_mapControl.ActiveView, m_selectedBounds) ) { // "Select & Drag Graphics" command: initialize mouse tracking to move the selected elements Trace.WriteLine ("Start tracking at (" + m_lastMouseClick.X + ", " + m_lastMouseClick.Y + ")"); // the envelope feedback draws a rectangle of the selected elements' extent following the mouse IMoveEnvelopeFeedback moveEnvelopeFeedback = new MoveEnvelopeFeedbackClass(); moveEnvelopeFeedback.Start (m_selectedBounds, m_lastMouseClick); m_moveFeedBack = moveEnvelopeFeedback as IDisplayFeedback; m_moveFeedBack.Display = axMapControl1.ActiveView.ScreenDisplay; // the tick count is used to filter out short, unintentional mouse drags m_dragStartTime = Environment.TickCount; } } private void axMapControl1_OnMouseMove (object sender, IMapControlEvents2_OnMouseMoveEvent e) { // update the current map location of the mouse if ( m_currentMouseLocation == null ) m_currentMouseLocation = new PointClass(); m_currentMouseLocation.PutCoords (e.mapX, e.mapY); UpdateStatusBar(); // "Select & Drag Graphics" command: move the feedback graphic if ( tsbSelect.Checked && e.button == 1 && m_moveFeedBack != null ) m_moveFeedBack.MoveTo (m_currentMouseLocation); } private void axMapControl1_OnMouseUp (object sender, IMapControlEvents2_OnMouseUpEvent e) { if ( m_moveFeedBack != null ) { // stop the feedback graphic and save its geometry for future use m_selectedBounds = ((IMoveEnvelopeFeedback)m_moveFeedBack).Stop(); m_moveFeedBack = null; int endTicks = Environment.TickCount - m_dragStartTime; if ( endTicks > 250 && m_currentMouseLocation != null ) { // only update the graphics if the minimum move time has elapsed Trace.WriteLine ("drag start = (" + m_lastMouseClick.X + ", " + m_lastMouseClick.Y + ")"); Trace.WriteLine ("drag end = (" + m_currentMouseLocation.X + ", " + m_currentMouseLocation.Y + ")"); MoveGraphics ( m_currentMouseLocation.X - m_lastMouseClick.X, m_currentMouseLocation.Y - m_lastMouseClick.Y ); } } } #endregion #region Helper Methods private IPoint CreatePoint (double x, double y) { // create a new point instance and initialize its coordinates IPoint point = new PointClass(); point.PutCoords (x, y); return point; } private void DrawSymbol (IPoint location, string sic, bool suppressRefresh) { // the first time we create a symbol, display the wait cursor while MOLE loads up System.Windows.Forms.Cursor previousCursor = Cursor; if ( m_firstTime ) Cursor = Cursors.WaitCursor; // set up a MOLE symbol for the new graphic; minimally validate the symbol ID code IMoleSymbol moleSymbol = new MoleMarkerSymbolClass(); if ( sic.Length == 15 ) moleSymbol.SymbolID = sic; moleSymbol.TextLabels = GetLabels(); // to remove the symbol's fill, uncomment this code //IMoleMarkerSymbol moleMarkerSymbol = moleSymbol as IMoleMarkerSymbol; //moleMarkerSymbol.ShowFill = false; // initialize the marker symbol properties IMarkerSymbol markerSymbol = moleSymbol as IMarkerSymbol; double size; if ( Double.TryParse(tstSize.Text, out size) ) markerSymbol.Size = size; else markerSymbol.Size = 48; // create the graphic element for the marker symbol and add it to the map IMarkerElement markerElement = new MarkerElementClass(); markerElement.Symbol = markerSymbol; IElement element = markerElement as IElement; element.Geometry = location as IGeometry; m_mapControl.ActiveView.GraphicsContainer.AddElement (element, 0); if ( ! suppressRefresh ) m_mapControl.ActiveView.PartialRefresh (esriViewDrawPhase.esriViewGraphics, null, null); // update the user interface if ( m_firstTime ) { Cursor = previousCursor; m_firstTime = false; } ++m_unitCount; UpdateTitle(); } private double dRandom (double low, double high) { // generate a random floating-point number within the indicated range [low, high) return low + m_random.NextDouble() * (high - low); } private IPropertySet GetLabels() { IPropertySet labelSet = new PropertySetClass(); // all of the below are supported - comment and uncomment to experiment labelSet.SetProperty ("Name", "Name"); labelSet.SetProperty ("Comment", "Comment"); //labelSet.SetProperty ("Parent", "Parent"); //labelSet.SetProperty ("Info", "Info"); //labelSet.SetProperty ("Strength", "Strength"); //labelSet.SetProperty ("EvalRating", "EvalRating"); //labelSet.SetProperty ("Location", "Location"); //labelSet.SetProperty ("Alt_Depth", "Alt_Depth"); //labelSet.SetProperty ("Speed", "Speed"); //labelSet.SetProperty ("DTG", "DTG"); //labelSet.SetProperty ("HQ", "HQ"); //labelSet.SetProperty ("Quantity", "Quantity"); //labelSet.SetProperty ("EType", "EType"); //labelSet.SetProperty ("Effective", "Effective"); //labelSet.SetProperty ("Signature", "Signature"); //labelSet.SetProperty ("IFFSIF", "IFFSIF"); return labelSet; } private IColor GetRandomColor() { // create a random opaque RGB color IRgbColor rgb = new RgbColorClass(); rgb.Red = m_random.Next (0, 255); rgb.Green = m_random.Next (0, 255); rgb.Blue = m_random.Next (0, 255); return rgb as IColor; } private void MoveGraphics (double deltaX, double deltaY) { try { // move all selected graphics along a delta (change) vector Trace.WriteLine ("moving delta = (" + deltaX + ", " + deltaY + ")"); // get reference to graphics container and its selected elements IGraphicsContainer graphicsContainer = axMapControl1.ActiveView.GraphicsContainer; IGraphicsContainerSelect graphicsContainerSelect = graphicsContainer as IGraphicsContainerSelect; IEnumElement enumElement = graphicsContainerSelect.SelectedElements; // iterate through the selected elements enumElement.Reset(); IElement element = enumElement.Next(); while (element != null) { // apply the delta vector to each element's geometry and update it the container IGeometry geometry = element.Geometry; (geometry as ITransform2D).Move (deltaX, deltaY); element.Geometry = geometry; graphicsContainer.UpdateElement (element); element = enumElement.Next(); } // refresh the active view axMapControl1.ActiveView.PartialRefresh (esriViewDrawPhase.esriViewGraphics, null, null); } catch ( Exception ex ) { MessageBox.Show ("Exception: " + ex.GetBaseException().ToString(), "MoveGraphics"); } } private bool SelectElements (IPoint point, IActiveView activeView, IEnvelope selectedBounds) { // this function is written in such a way that it should be pastable Trace.WriteLine ("selecting graphics near (" + point.X + ", " + point.Y + ")"); IGraphicsContainer graphicsContainer = activeView.GraphicsContainer; IGraphicsContainerSelect graphicsContainerSelect = graphicsContainer as IGraphicsContainerSelect; IScreenDisplay screenDisplay = activeView.ScreenDisplay; bool selected = false; bool refreshRequired = false; // start with a precise search, and then widen the tolerance if nothing is found // (you may need to change these tolerances if using this code in your own application) IEnumElement enumElement = graphicsContainer.LocateElements (point, 0.0000001); if ( enumElement == null ) enumElement = graphicsContainer.LocateElements (point, 0.5); // if no elements were selected if ( enumElement == null ) { // if the previous selection is nonempty if ( graphicsContainerSelect.ElementSelectionCount > 0 ) { // clear the selection and refresh the display Trace.WriteLine ("clearing selection"); graphicsContainerSelect.UnselectAllElements(); selectedBounds.SetEmpty(); refreshRequired = true; } // else do nothing } else { // get the extent of the selected elements IEnvelope envelope = new EnvelopeClass(); enumElement.Reset(); IElement element = enumElement.Next(); while ( element != null ) { // establish selectedBounds as the extent of all selected elements element.QueryBounds (screenDisplay, envelope); selectedBounds.Union (envelope); element = enumElement.Next(); } // add all the newly selected elements to the graphics container's selection enumElement.Reset(); graphicsContainerSelect.SelectElements (enumElement); refreshRequired = selected = true; } // refresh the display if anything has changed if ( refreshRequired ) activeView.PartialRefresh (esriViewDrawPhase.esriViewGraphics, null, null); // return true if any elements on the display are currently selected; selectedBounds has their extent return selected; } private void UpdateStatusBar() { if ( m_mapControl != null ) { // put the MXD name and current mouse location in the status bar, if available string documentFileName = System.IO.Path.GetFileName (m_mapControl.DocumentFilename); if ( documentFileName == null || documentFileName == String.Empty ) documentFileName = "<no map>"; if ( m_currentMouseLocation == null ) statusBarXY.Text = documentFileName; else statusBarXY.Text = String.Format ( "{0}: {1}, {2} {3}", documentFileName, m_currentMouseLocation.X.ToString("#######.##"), m_currentMouseLocation.Y.ToString("#######.##"), axMapControl1.MapUnits.ToString().Substring(4) ); } } private void UpdateTitle() { // put the number of symbols in the title bar, when there are any string title = "MOLE Symbols"; if ( m_unitCount == 1 ) title += " (" + m_unitCount + " unit)"; else if ( m_unitCount > 1 ) title += " (" + m_unitCount + " units)"; Text = title; } #endregion } }