TriangleElementClass.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.Generic; using System.Drawing; using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters.Binary; using System.IO; using ESRI.ArcGIS.ADF; using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.Display; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.esriSystem; namespace TriangleElement { [ComVisible(true)] [Guid("DC8482C9-5DD6-44dc-BF3C-54B18AB813C9")] public interface ITriangleElement { ISimpleFillSymbol FillSymbol { get; set;} double Size { get; set;} double Angle { get; set;} } [Guid(TriangleElementClass.CLASSGUID)] [ClassInterface(ClassInterfaceType.None)] [ProgId("TriangleElement.TriangleElementClass")] public sealed class TriangleElementClass : ITriangleElement, IElement, IElementProperties, IElementProperties2, IElementProperties3, IBoundsProperties, ITransform2D, IGraphicElement, IPersistVariant, IClone, IDocumentVersionSupportGEN { #region class members //some win32 imports and constants [System.Runtime.InteropServices.DllImport("gdi32", EntryPoint = "GetDeviceCaps", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)] public static extern int GetDeviceCaps(int hDC, int nIndex); private const double c_Cosine30 = 0.866025403784439; private const double c_Deg2Rad = (Math.PI / 180.0); private const double c_Rad2Deg = (180.0 / Math.PI); private const int c_Version = 2; public const string CLASSGUID = "cbf943e2-ce6d-49f4-a4a7-ce16f02379ad"; public const int LOGPIXELSX = 88; public const int LOGPIXELSY = 90; private IPolygon m_triangle = null; private IPoint m_pointGeometry = null; private ISimpleFillSymbol m_fillSymbol = null; private double m_rotation = 0.0; private double m_size = 20.0; private ISelectionTracker m_selectionTracker = null; private IDisplay m_cachedDisplay = null; private ISpatialReference m_nativeSR = null; private string m_elementName = string.Empty; private string m_elementType = "TriangleElement"; private object m_customProperty = null; private bool m_autoTrans = true; private double m_scaleRef = 0.0; private esriAnchorPointEnum m_anchorPointType = esriAnchorPointEnum.esriCenterPoint; private double m_dDeviceRatio = 0; #endregion #region class constructor public TriangleElementClass() { //initialize the element's geometry m_triangle = new PolygonClass(); m_triangle.SetEmpty(); InitMembers(); } #endregion #region ITriangleElement Members public ISimpleFillSymbol FillSymbol { get { return m_fillSymbol; } set { m_fillSymbol = value; } } public double Size { get { return m_size; } set { m_size = value; } } public double Angle { get { return m_rotation; } set { m_rotation = value; } } #endregion #region IElement Members public void Activate(IDisplay Display) { //cache the display m_cachedDisplay = Display; SetupDeviceRatio(Display.hDC, Display); //need to calculate the points of the triangle polygon if(m_triangle.IsEmpty) BuildTriangleGeometry(m_pointGeometry); //need to refresh the element's tracker RefreshTracker(); } public void Deactivate() { m_cachedDisplay = null; } public void Draw(IDisplay Display, ITrackCancel TrackCancel) { if (null != m_triangle && null != m_fillSymbol) { Display.SetSymbol((ISymbol)m_fillSymbol); Display.DrawPolygon(m_triangle); } } public IGeometry Geometry { get { return Clone(m_pointGeometry) as IGeometry; } set { try { m_pointGeometry = Clone(value) as IPoint; UpdateElementSpatialRef(); } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.Message); } } } public bool HitTest(double x, double y, double Tolerance) { if (null == m_cachedDisplay) return false; IPoint point = new PointClass(); point.PutCoords(x,y); return ((IRelationalOperator)m_triangle).Contains((IGeometry)point); } public bool Locked { get { return false; } set { } } public void QueryBounds(IDisplay Display, IEnvelope Bounds) { //return a bounding envelope IPolygon polygon = new PolygonClass(); polygon.SetEmpty(); ((ISymbol)m_fillSymbol).QueryBoundary(Display.hDC, Display.DisplayTransformation, m_triangle, polygon); Bounds.XMin = polygon.Envelope.XMin; Bounds.XMax = polygon.Envelope.XMax; Bounds.YMin = polygon.Envelope.YMin; Bounds.YMax = polygon.Envelope.YMax; Bounds.SpatialReference = polygon.Envelope.SpatialReference; } public void QueryOutline(IDisplay Display, IPolygon Outline) { //return a polygon which is the outline of the element IPolygon polygon = new PolygonClass(); polygon.SetEmpty(); ((ISymbol)m_fillSymbol).QueryBoundary(Display.hDC, Display.DisplayTransformation, m_triangle, polygon); ((IPointCollection)Outline).AddPointCollection((IPointCollection)polygon); } public ISelectionTracker SelectionTracker { get { return m_selectionTracker; } } #endregion #region IElementProperties Members /// <summary> /// Indicates if transform is applied to symbols and other parts of element. /// False = only apply transform to geometry. /// Update font size in ITransform2D routines /// </summary> public bool AutoTransform { get { return m_autoTrans; } set { m_autoTrans = value; } } public object CustomProperty { get { return m_customProperty; } set { m_customProperty = value; } } public string Name { get { return m_elementName; } set { m_elementName = value; } } public string Type { get { return m_elementType; } set { m_elementType = value; } } #endregion #region IElementProperties2 Members public bool CanRotate() { return true; } public double ReferenceScale { get { return m_scaleRef; } set { m_scaleRef = value; } } #endregion #region IElementProperties3 Members public esriAnchorPointEnum AnchorPoint { get { return m_anchorPointType; } set { m_anchorPointType = value; } } #endregion #region IBoundsProperties Members public bool FixedAspectRatio { get { return true; } set { throw new Exception("The method or operation is not implemented."); } } public bool FixedSize { get { return true; } } #endregion #region ITransform2D Members public void Move(double dx, double dy) { if (null == m_triangle) return; ((ITransform2D)m_triangle).Move(dx, dy); ((ITransform2D)m_pointGeometry).Move(dx, dy); RefreshTracker(); } public void MoveVector(ILine v) { if (null == m_triangle) return; ((ITransform2D)m_triangle).MoveVector(v); ((ITransform2D)m_pointGeometry).MoveVector(v); RefreshTracker(); } public void Rotate(IPoint Origin, double rotationAngle) { if (null == m_triangle) return; ((ITransform2D)m_triangle).Rotate(Origin, rotationAngle); ((ITransform2D)m_pointGeometry).Rotate(Origin, rotationAngle); m_rotation = rotationAngle * c_Rad2Deg; RefreshTracker(); } public void Scale(IPoint Origin, double sx, double sy) { if (null == m_triangle) return; ((ITransform2D)m_triangle).Scale(Origin, sx, sy); ((ITransform2D)m_pointGeometry).Scale(Origin, sx, sy); if (m_autoTrans) { m_size *= Math.Max(sx, sy); } RefreshTracker(); } public void Transform(esriTransformDirection direction, ITransformation transformation) { if (null == m_triangle) return; //Geometry ((ITransform2D)m_triangle).Transform(direction, transformation); IAffineTransformation2D affineTrans = (IAffineTransformation2D)transformation; if (affineTrans.YScale != 1.0) m_size *= Math.Max(affineTrans.YScale, affineTrans.XScale); RefreshTracker(); } #endregion #region IGraphicElement Members public ISpatialReference SpatialReference { get { return m_nativeSR; } set { m_nativeSR = value; UpdateElementSpatialRef(); } } #endregion #region IPersistVariant Members public UID ID { get { UID uid = new UIDClass(); uid.Value = "{" + TriangleElementClass.CLASSGUID + "}"; return uid; } } public void Load(IVariantStream Stream) { int ver = (int)Stream.Read(); if (ver > c_Version || ver <= 0) throw new Exception("Wrong version!"); InitMembers(); m_size = (double)Stream.Read(); m_scaleRef = (double)Stream.Read(); m_anchorPointType = (esriAnchorPointEnum)Stream.Read(); m_autoTrans = (bool)Stream.Read(); m_elementType = (string)Stream.Read(); m_elementName = (string)Stream.Read(); m_nativeSR = Stream.Read() as ISpatialReference; m_fillSymbol = Stream.Read() as ISimpleFillSymbol; m_pointGeometry = Stream.Read() as IPoint; m_triangle = Stream.Read() as IPolygon; if (ver == 2) { m_rotation = (double)Stream.Read(); } } public void Save(IVariantStream Stream) { Stream.Write(c_Version); Stream.Write(m_size); Stream.Write(m_scaleRef); Stream.Write(m_anchorPointType); Stream.Write(m_autoTrans); Stream.Write(m_elementType); Stream.Write(m_elementName); Stream.Write(m_nativeSR); Stream.Write(m_fillSymbol); Stream.Write(m_pointGeometry); Stream.Write(m_triangle); Stream.Write(m_rotation); } #endregion #region IClone Members public void Assign(IClone src) { //1. make sure that src is pointing to a valid object if (null == src) { throw new COMException("Invalid object."); } //2. make sure that the type of src is of type 'TriangleElementClass' if (!(src is TriangleElementClass)) { throw new COMException("Bad object type."); } //3. assign the properties of src to the current instance TriangleElementClass srcTriangle = (TriangleElementClass)src; m_elementName = srcTriangle.Name; m_elementType = srcTriangle.Type; m_autoTrans = srcTriangle.AutoTransform; m_scaleRef = srcTriangle.ReferenceScale; m_rotation = srcTriangle.Angle; m_size = srcTriangle.Size; m_anchorPointType = srcTriangle.AnchorPoint; IObjectCopy objCopy = new ObjectCopyClass(); //take care of the custom property if (null != srcTriangle.CustomProperty) { if (srcTriangle.CustomProperty is IClone) m_customProperty = (object)((IClone)srcTriangle.CustomProperty).Clone(); else if (srcTriangle.CustomProperty is IPersistStream) { m_customProperty = objCopy.Copy((object)srcTriangle.CustomProperty); } else if (srcTriangle.CustomProperty.GetType().IsSerializable) { //serialize to a memory stream MemoryStream memoryStream = new MemoryStream(); BinaryFormatter binaryFormatter = new BinaryFormatter(); binaryFormatter.Serialize(memoryStream, srcTriangle.CustomProperty); byte[] bytes = memoryStream.ToArray(); memoryStream = new MemoryStream(bytes); m_customProperty = binaryFormatter.Deserialize(memoryStream); } } if (null != srcTriangle.SpatialReference) m_nativeSR = objCopy.Copy(srcTriangle.SpatialReference) as ISpatialReference; else m_nativeSR = null; if (null != srcTriangle.FillSymbol) { m_fillSymbol = objCopy.Copy(srcTriangle.FillSymbol) as ISimpleFillSymbol; } else m_fillSymbol = null; if (null != srcTriangle.Geometry) { m_triangle = objCopy.Copy(srcTriangle.Geometry) as IPolygon; m_pointGeometry = objCopy.Copy(((IArea)m_triangle).Centroid) as IPoint; } else { m_triangle = null; m_pointGeometry = null; } } public IClone Clone() { TriangleElementClass triangle = new TriangleElementClass(); triangle.Assign((IClone)this); return (IClone)triangle; } public bool IsEqual(IClone other) { //1. make sure that the 'other' object is pointing to a valid object if (null == other) throw new COMException("Invalid object."); //2. verify the type of 'other' if (!(other is TriangleElementClass)) throw new COMException("Bad object type."); TriangleElementClass otherTriangle = (TriangleElementClass)other; //test that all of the object's properties are the same. //please note the usage of IsEqual when using ArcObjects components that //supports cloning if (otherTriangle.Name == m_elementName && otherTriangle.Type == m_elementType && otherTriangle.AutoTransform == m_autoTrans && otherTriangle.ReferenceScale == m_scaleRef && otherTriangle.Angle == m_rotation && otherTriangle.Size == m_size && otherTriangle.AnchorPoint == m_anchorPointType && ((IClone)otherTriangle.Geometry).IsEqual((IClone)m_triangle) && ((IClone)otherTriangle.FillSymbol).IsEqual((IClone)m_fillSymbol) && ((IClone)otherTriangle.SpatialReference).IsEqual((IClone)m_nativeSR)) return true; return false; } public bool IsIdentical(IClone other) { //1. make sure that the 'other' object is pointing to a valid object if (null == other) throw new COMException("Invalid object."); //2. verify the type of 'other' if (!(other is TriangleElementClass)) throw new COMException("Bad object type."); //3. test if the other is the 'this' if ((TriangleElementClass)other == this) return true; return false; } #endregion #region IDocumentVersionSupportGEN Members public object ConvertToSupportedObject(esriArcGISVersion docVersion) { //in case of 8.3, create a character marker element and use a triangle marker... ICharacterMarkerSymbol charMarkerSymbol = new CharacterMarkerSymbolClass(); charMarkerSymbol.Color = m_fillSymbol.Color; charMarkerSymbol.Angle = m_rotation; charMarkerSymbol.Size = m_size; charMarkerSymbol.Font = ESRI.ArcGIS.ADF.Connection.Local.Converter.ToStdFont(new Font("ESRI Default Marker", (float)m_size, FontStyle.Regular)); charMarkerSymbol.CharacterIndex = 184; IMarkerElement markerElement = new MarkerElementClass(); markerElement.Symbol = (IMarkerSymbol)charMarkerSymbol; IPoint point = ((IClone)m_pointGeometry).Clone() as IPoint; IElement element = (IElement)markerElement; element.Geometry = (IGeometry)point; return element; } public bool IsSupportedAtVersion(esriArcGISVersion docVersion) { //support all versions except 8.3 if (esriArcGISVersion.esriArcGISVersion83 == docVersion) return false; else return true; } #endregion #region private methods private IClone Clone(object obj) { if (null == obj || !(obj is IClone)) return null; return ((IClone)obj).Clone(); } private int TwipsPerPixelX() { return 16; } private int TwipsPerPixelY() { return 16; } private void SetupDeviceRatio(int hDC, ESRI.ArcGIS.Display.IDisplay display) { if (display.DisplayTransformation != null) { if (display.DisplayTransformation.Resolution != 0) { m_dDeviceRatio = display.DisplayTransformation.Resolution / 72; // Check the ReferenceScale of the display transformation. If not zero, we need to // adjust the Size, XOffset and YOffset of the Symbol we hold internally before drawing. if (display.DisplayTransformation.ReferenceScale != 0) m_dDeviceRatio = m_dDeviceRatio * display.DisplayTransformation.ReferenceScale / display.DisplayTransformation.ScaleRatio; } } else { // If we don't have a display transformation, calculate the resolution // from the actual device. if (display.hDC != 0) { // Get the resolution from the device context hDC. m_dDeviceRatio = System.Convert.ToDouble(GetDeviceCaps(hDC, LOGPIXELSX)) / 72; } else { // If invalid hDC assume we're drawing to the screen. m_dDeviceRatio = 1 / (TwipsPerPixelX() / 20); // 1 Point = 20 Twips. } } } private double PointsToMap(IDisplayTransformation displayTransform, double dPointSize) { double tempPointsToMap = 0; if (displayTransform == null) tempPointsToMap = dPointSize * m_dDeviceRatio; else { tempPointsToMap = displayTransform.FromPoints(dPointSize); } return tempPointsToMap; } private void BuildTriangleGeometry(IPoint pointGeometry) { try { if (null == m_triangle || null == pointGeometry || null == m_cachedDisplay) return; m_triangle.SpatialReference = pointGeometry.SpatialReference; m_triangle.SetEmpty(); object missing = System.Reflection.Missing.Value; IPointCollection pointCollection = (IPointCollection)m_triangle; double radius = PointsToMap(m_cachedDisplay.DisplayTransformation, m_size); double X = pointGeometry.X; double Y = pointGeometry.Y; IPoint point = new PointClass(); point.X = X + radius * c_Cosine30; point.Y = Y - 0.5 * radius; pointCollection.AddPoint(point, ref missing, ref missing); point = new PointClass(); point.X = X; point.Y = Y + radius; pointCollection.AddPoint(point, ref missing, ref missing); point = new PointClass(); point.X = X - radius * c_Cosine30; point.Y = Y - 0.5 * radius; pointCollection.AddPoint(point, ref missing, ref missing); m_triangle.Close(); if (m_rotation != 0.0) { ((ITransform2D)pointCollection).Rotate(pointGeometry, m_rotation * c_Deg2Rad); } return; } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.Message); } } private void SetDefaultDymbol() { IColor color = (IColor)ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.Black); ISimpleLineSymbol lineSymbol = new SimpleLineSymbolClass(); lineSymbol.Style = esriSimpleLineStyle.esriSLSSolid; lineSymbol.Width = 1.0; lineSymbol.Color = color; color = (IColor)ESRI.ArcGIS.ADF.Connection.Local.Converter.ToRGBColor(Color.Navy); if (null == m_fillSymbol) m_fillSymbol = new SimpleFillSymbolClass(); m_fillSymbol.Color = color; m_fillSymbol.Style = esriSimpleFillStyle.esriSFSSolid; m_fillSymbol.Outline = (ILineSymbol)lineSymbol; } /// <summary> /// assign the triangle's geometry to the selection tracker /// </summary> private void RefreshTracker() { if (null == m_cachedDisplay) return; m_selectionTracker.Display = (IScreenDisplay)m_cachedDisplay; IPolygon outline = new PolygonClass(); this.QueryOutline(m_cachedDisplay, outline); m_selectionTracker.Geometry = (IGeometry)outline; } private void UpdateElementSpatialRef() { if (null == m_cachedDisplay || null == m_nativeSR || null == m_triangle || null == m_cachedDisplay.DisplayTransformation.SpatialReference) return; if (null == m_triangle.SpatialReference) m_triangle.SpatialReference = m_cachedDisplay.DisplayTransformation.SpatialReference; m_triangle.Project(m_nativeSR); RefreshTracker(); } private void InitMembers() { //initialize the selection tracker m_selectionTracker = new PolygonTrackerClass(); m_selectionTracker.Locked = false; m_selectionTracker.ShowHandles = true; //set a default symbol SetDefaultDymbol(); } #endregion } }