/GPX.Firemap.Desktop/GPX.Firemap.Desktop.CustomLayers/GraticuleCustomLayers/GraticuleCustomLayers/CustomLayers/MGA94GraticuleCustomLayer.cs

https://bitbucket.org/shope/dfu · C# · 740 lines · 487 code · 90 blank · 163 comment · 66 complexity · bddc1424172e3589638b95538f6ff0c7 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.InteropServices;
  5. using ESRI.ArcGIS.ADF;
  6. using ESRI.ArcGIS.ArcMapUI;
  7. using ESRI.ArcGIS.Carto;
  8. using ESRI.ArcGIS.Display;
  9. using ESRI.ArcGIS.esriSystem;
  10. using ESRI.ArcGIS.Geometry;
  11. using ESRI.ArcGIS.Geodatabase;
  12. namespace GraticuleCustomLayers
  13. {
  14. /// <summary>
  15. ///
  16. /// </summary>
  17. [Guid("b4244d63-a4f0-4649-bd96-88238b63b0f3")]
  18. [ClassInterface(ClassInterfaceType.None)]
  19. [ProgId("GraticuleCustomLayers.MGA94GraticuleCustomLayer")]
  20. public class MGA94GraticuleCustomLayer : ILayer, IGeoDataset, IPersistVariant
  21. {
  22. #region Module Level Variables and Constants
  23. // The default densify value used when densifying lines prior to projecting a line
  24. private const int DensifySegmentCount = 50;
  25. // MGA Grid spacing
  26. private const int MGAGridSpacing = 1000;
  27. // MGA Grid max scale (the MGA grid will be shown below this scale)
  28. private const double MGAGridMaxScale = 101000;
  29. // MGA Grid label scale boundary (the MGA grid will have additional labels shown below this scale)
  30. private const double MGAGridLabelScaleBoundary = 50000.5;
  31. /// <summary>
  32. /// Name of the layer
  33. /// </summary>
  34. public static readonly string LayerName = "MGA94 Graticule";
  35. /// <summary>
  36. /// Indicates if the layer needs its own display cache.
  37. /// </summary>
  38. private bool m_isCached = false;
  39. /// <summary>
  40. /// Indicates if the layer is currently visible.
  41. /// </summary>
  42. private bool m_visible = true;
  43. /// <summary>
  44. /// Describes the area of interest of the layer.
  45. /// </summary>
  46. private IEnvelope m_areaOfInterest = SpatialReferenceStore.Instance.ExtentMGAZone55.Envelope;
  47. #endregion
  48. #region Constructors
  49. // None
  50. #endregion
  51. #region ILayer Members
  52. /// <summary>
  53. /// The default area of interest for the layer.
  54. /// </summary>
  55. public IEnvelope AreaOfInterest
  56. {
  57. get
  58. {
  59. return m_areaOfInterest;
  60. }
  61. }
  62. /// <summary>
  63. /// Indicates if the layer needs its own display cache.
  64. /// </summary>
  65. public bool Cached
  66. {
  67. get
  68. {
  69. return m_isCached;
  70. }
  71. set
  72. {
  73. m_isCached = value;
  74. }
  75. }
  76. /// <summary>
  77. /// Draws the layer to the specified display for the given draw phase.
  78. /// </summary>
  79. /// <param name="DrawPhase"></param>
  80. /// <param name="Display"></param>
  81. /// <param name="TrackCancel"></param>
  82. public void Draw(esriDrawPhase DrawPhase, IDisplay Display, ITrackCancel TrackCancel)
  83. {
  84. System.Diagnostics.Debug.WriteLine("GraticuleDrawEvent");
  85. using (ComReleaser comReleaser = new ComReleaser())
  86. {
  87. // Make sure we can proceed, if not exit
  88. if (!Valid || !Visible) { return; }
  89. // Get the display transformation and spatial reference
  90. IDisplayTransformation displayTranformation = Display.DisplayTransformation;
  91. ISpatialReference displaySpatialReference = displayTranformation.SpatialReference;
  92. comReleaser.ManageLifetime(displaySpatialReference);
  93. if (SpatialReferenceStore.IsUnknownSpatialReference(displaySpatialReference)) { return; }
  94. // Get the bounds of the map.
  95. IEnvelope mapBounds = displayTranformation.FittedBounds;
  96. // Check if we should be drawing anything at all based on scale
  97. if (displayTranformation.ScaleRatio > MGA94GraticuleCustomLayer.MGAGridMaxScale) { return; }
  98. // Get the list of lines and label points to be displayed (calculated in graticule coords, returned in display coords)
  99. List<GraticuleLine> graticuleLines54 = CalculateGraticuleLines(mapBounds, displaySpatialReference, SpatialReferenceStore.Instance.ExtentMGAZone54);
  100. List<GraticuleLine> graticuleLines55 = CalculateGraticuleLines(mapBounds, displaySpatialReference, SpatialReferenceStore.Instance.ExtentMGAZone55);
  101. List<GraticuleLine> graticuleLines56 = CalculateGraticuleLines(mapBounds, displaySpatialReference, SpatialReferenceStore.Instance.ExtentMGAZone56);
  102. List<GraticuleLine> graticuleLines = new List<GraticuleLine>();
  103. if (graticuleLines54 != null) { graticuleLines.AddRange(graticuleLines54); }
  104. if (graticuleLines55 != null) { graticuleLines.AddRange(graticuleLines55); }
  105. if (graticuleLines56 != null) { graticuleLines.AddRange(graticuleLines56); }
  106. // Make sure we have something to draw
  107. if (graticuleLines.Count == 0) { return; }
  108. // Draw the lines
  109. Display.SetSymbol((ISymbol)GraticuleGraphics.MGA94GraticuleLineSymbol);
  110. foreach (GraticuleLine graticuleLine in graticuleLines)
  111. {
  112. Display.DrawPolyline((IGeometry)graticuleLine.Line);
  113. }
  114. // Hold some x values to help with label collision calculations
  115. double topBoundsCheck = 0;
  116. double bottomBoundsCheck = 0;
  117. int topCount = 0;
  118. int bottomCount = 0;
  119. int leftCount = 0;
  120. int rightCount = 0;
  121. // Draw the labels
  122. ITextSymbol textSymbol = GraticuleGraphics.MGA94GraticuleTextSymbol;
  123. foreach (GraticuleLine graticuleLine in graticuleLines)
  124. {
  125. foreach (GraticuleLabelPoint graticuleLabelPoint in graticuleLine.LabelPoints)
  126. {
  127. // Set the properties of the text symbol based on the properties of the label point
  128. textSymbol.HorizontalAlignment = graticuleLabelPoint.HorizontalAlignment;
  129. textSymbol.VerticalAlignment = graticuleLabelPoint.VerticalAlignment;
  130. textSymbol.Text = graticuleLabelPoint.Text;
  131. // Work out the extent of the label to be drawn
  132. IEnvelope boundsEnv = new EnvelopeClass();
  133. comReleaser.ManageLifetime(boundsEnv);
  134. ((IQueryGeometry)textSymbol).QueryEnvelope(Display.hDC, (ITransformation)displayTranformation, (IGeometry)graticuleLabelPoint.AnchorPoint, boundsEnv);
  135. // Do some calcs to make sure we only draw lables that not too close to the corners of the display.
  136. bool draw = false;
  137. if (textSymbol.VerticalAlignment == esriTextVerticalAlignment.esriTVATop) // Top labels
  138. {
  139. if (boundsEnv.XMin > (mapBounds.XMin + (boundsEnv.Width / 2D)) && boundsEnv.XMax < (mapBounds.XMax - (boundsEnv.Width / 2D)) && boundsEnv.XMin > topBoundsCheck)
  140. {
  141. if (displayTranformation.ScaleRatio > MGA94GraticuleCustomLayer.MGAGridLabelScaleBoundary)
  142. {
  143. topCount += 1;
  144. if (IsOdd(topCount))
  145. {
  146. topBoundsCheck = boundsEnv.XMax;
  147. draw = true;
  148. }
  149. }
  150. else
  151. {
  152. topBoundsCheck = boundsEnv.XMax;
  153. draw = true;
  154. }
  155. }
  156. }
  157. else if (textSymbol.VerticalAlignment == esriTextVerticalAlignment.esriTVABottom) // Bottom labels
  158. {
  159. if (boundsEnv.XMin > (mapBounds.XMin + (boundsEnv.Width / 2D)) && boundsEnv.XMax < (mapBounds.XMax - (boundsEnv.Width / 2D)) && boundsEnv.XMin > bottomBoundsCheck)
  160. {
  161. if (displayTranformation.ScaleRatio > MGA94GraticuleCustomLayer.MGAGridLabelScaleBoundary)
  162. {
  163. bottomCount += 1;
  164. if (IsOdd(bottomCount))
  165. {
  166. bottomBoundsCheck = boundsEnv.XMax;
  167. draw = true;
  168. }
  169. }
  170. else
  171. {
  172. bottomBoundsCheck = boundsEnv.XMax;
  173. draw = true;
  174. }
  175. }
  176. }
  177. else if (textSymbol.HorizontalAlignment == esriTextHorizontalAlignment.esriTHALeft) // Left labels
  178. {
  179. if ((mapBounds.YMax - boundsEnv.YMax) > boundsEnv.Height &&
  180. (boundsEnv.YMin - mapBounds.YMin) > boundsEnv.Height)
  181. {
  182. if (displayTranformation.ScaleRatio > MGA94GraticuleCustomLayer.MGAGridLabelScaleBoundary)
  183. {
  184. leftCount += 1;
  185. if (IsOdd(leftCount))
  186. {
  187. draw = true;
  188. }
  189. }
  190. else
  191. {
  192. draw = true;
  193. }
  194. }
  195. }
  196. else // Right labels
  197. {
  198. if ((mapBounds.YMax - boundsEnv.YMax) > boundsEnv.Height &&
  199. (boundsEnv.YMin - mapBounds.YMin) > boundsEnv.Height)
  200. {
  201. if (displayTranformation.ScaleRatio > MGA94GraticuleCustomLayer.MGAGridLabelScaleBoundary)
  202. {
  203. rightCount += 1;
  204. if (IsOdd(rightCount))
  205. {
  206. draw = true;
  207. }
  208. }
  209. else
  210. {
  211. draw = true;
  212. }
  213. }
  214. }
  215. // If the label is to be drawn, the draw it
  216. if (draw)
  217. {
  218. Display.SetSymbol((ISymbol)textSymbol);
  219. Display.DrawText((IGeometry)graticuleLabelPoint.AnchorPoint, graticuleLabelPoint.Text);
  220. }
  221. ComReleaser.ReleaseCOMObject(graticuleLabelPoint.AnchorPoint);
  222. }
  223. ComReleaser.ReleaseCOMObject(graticuleLine.Line);
  224. }
  225. }
  226. }
  227. /// <summary>
  228. /// Maximum scale (representative fraction) at which the layer will display.
  229. /// </summary>
  230. public double MaximumScale
  231. {
  232. get
  233. {
  234. return default(double);
  235. }
  236. set
  237. {
  238. // Setting MaximumScale does nothing
  239. }
  240. }
  241. /// <summary>
  242. /// Minimum scale (representative fraction) at which the layer will display.
  243. /// </summary>
  244. public double MinimumScale
  245. {
  246. get
  247. {
  248. return default(double);
  249. }
  250. set
  251. {
  252. // Setting MinimumScale does nothing
  253. }
  254. }
  255. /// <summary>
  256. /// Layer name.
  257. /// </summary>
  258. public string Name
  259. {
  260. get
  261. {
  262. return LayerName;
  263. }
  264. set
  265. {
  266. // Setting Name does nothing
  267. }
  268. }
  269. /// <summary>
  270. /// Indicates if the layer shows map tips.
  271. /// </summary>
  272. public bool ShowTips
  273. {
  274. get
  275. {
  276. return default(bool);
  277. }
  278. set
  279. {
  280. // Setting ShowTips does nothing
  281. }
  282. }
  283. /// <summary>
  284. /// Spatial reference for the layer.
  285. /// </summary>
  286. public ISpatialReference SpatialReference
  287. {
  288. set
  289. {
  290. // Setting SpatialReference does nothing
  291. }
  292. }
  293. /// <summary>
  294. /// Supported draw phases.
  295. /// </summary>
  296. public int SupportedDrawPhases
  297. {
  298. get
  299. {
  300. return (int)esriDrawPhase.esriDPGeography;
  301. }
  302. }
  303. /// <summary>
  304. /// Indicates if the layer is currently valid.
  305. /// </summary>
  306. public bool Valid
  307. {
  308. get
  309. {
  310. return true;
  311. }
  312. }
  313. /// <summary>
  314. /// Indicates if the layer is currently visible.
  315. /// </summary>
  316. public bool Visible
  317. {
  318. get
  319. {
  320. return m_visible;
  321. }
  322. set
  323. {
  324. m_visible = value;
  325. }
  326. }
  327. /// <summary>
  328. /// Get_s the tip text.
  329. /// </summary>
  330. /// <param name="x">The x.</param>
  331. /// <param name="y">The y.</param>
  332. /// <param name="Tolerance">The tolerance.</param>
  333. public string get_TipText(double x, double y, double Tolerance)
  334. {
  335. return default(string);
  336. }
  337. #endregion
  338. #region IGeoDataset Members
  339. IEnvelope IGeoDataset.Extent
  340. {
  341. get { return m_areaOfInterest; }
  342. }
  343. ISpatialReference IGeoDataset.SpatialReference
  344. {
  345. get { return m_areaOfInterest.SpatialReference; }
  346. }
  347. #endregion
  348. #region IPersistVariant Members
  349. public ESRI.ArcGIS.esriSystem.UID ID
  350. {
  351. get
  352. {
  353. UID typeID = new UIDClass();
  354. typeID.Value = GetType().GUID.ToString("B");
  355. return typeID;
  356. }
  357. }
  358. public void Load(ESRI.ArcGIS.esriSystem.IVariantStream Stream)
  359. {
  360. // TODO: Add ArcGISClass1.Load implementation
  361. }
  362. public void Save(ESRI.ArcGIS.esriSystem.IVariantStream Stream)
  363. {
  364. // TODO: Add ArcGISClass1.Save implementation
  365. }
  366. #endregion
  367. #region Static Helper Methods
  368. /// <summary>
  369. /// Calculates the graticule lines.
  370. /// </summary>
  371. /// <param name="displayBounds">The display bounds.</param>
  372. /// <param name="displaySpatialReference">The display spatial reference.</param>
  373. /// <param name="mgaZoneExtent">The mga zone extent.</param>
  374. /// <returns>
  375. /// A list of GraticuleLine DTOs
  376. /// </returns>
  377. private static List<GraticuleLine> CalculateGraticuleLines(IEnvelope displayBounds, ISpatialReference displaySpatialReference, IPolygon mgaZoneExtent)
  378. {
  379. using (ComReleaser comReleaser = new ComReleaser())
  380. {
  381. // Turn the display bounds in a polygon, densify it and project it to same spatial reference as the mga zone we are dealing with
  382. IPolygon displayBoundsPolyMga = new PolygonClass();
  383. comReleaser.ManageLifetime(displayBoundsPolyMga);
  384. ((IPointCollection)displayBoundsPolyMga).AddPoint(displayBounds.UpperLeft);
  385. ((IPointCollection)displayBoundsPolyMga).AddPoint(displayBounds.UpperRight);
  386. ((IPointCollection)displayBoundsPolyMga).AddPoint(displayBounds.LowerRight);
  387. ((IPointCollection)displayBoundsPolyMga).AddPoint(displayBounds.LowerLeft);
  388. displayBoundsPolyMga.Close();
  389. displayBoundsPolyMga.SpatialReference = displayBounds.SpatialReference;
  390. ((IPolycurve)displayBoundsPolyMga).Densify(displayBoundsPolyMga.Length / Convert.ToDouble(MGA94GraticuleCustomLayer.DensifySegmentCount), 0);
  391. displayBoundsPolyMga = (IPolygon)ProjectionMethods.Project(displayBoundsPolyMga, mgaZoneExtent.SpatialReference);
  392. // Determine the extent that is common between the zone extent and the display bounds
  393. IPolygon graticuleBoundsPolyMga = (IPolygon)((ITopologicalOperator)mgaZoneExtent).Intersect(displayBoundsPolyMga, esriGeometryDimension.esriGeometry2Dimension);
  394. comReleaser.ManageLifetime(graticuleBoundsPolyMga);
  395. // Make sure we have at least some common area
  396. if (graticuleBoundsPolyMga.IsEmpty) { return null; }
  397. // Make an envelope that covers the graticule polygon extent
  398. IEnvelope graticuleBoundsMga = graticuleBoundsPolyMga.Envelope;
  399. comReleaser.ManageLifetime(graticuleBoundsMga);
  400. graticuleBoundsMga.Expand(1.25, 1.25, true);
  401. // Work out how many vertical and horizontal grid lines are going to have
  402. // Make an envelope slightly smaller than the display bounds to use for calculating labels
  403. IEnvelope displayBoundsReduced = (IEnvelope)((IClone)displayBounds).Clone();
  404. comReleaser.ManageLifetime(displayBoundsReduced);
  405. displayBoundsReduced.Expand(0.9975, 0.9975, true);
  406. // Get the edges of the reduced display bounds in display coordinates
  407. object missing = Type.Missing;
  408. IPointCollection displayBoundsTopEdge = new PolylineClass();
  409. comReleaser.ManageLifetime(displayBoundsTopEdge);
  410. ((IPolyline)displayBoundsTopEdge).SpatialReference = displaySpatialReference;
  411. displayBoundsTopEdge.AddPoint(displayBoundsReduced.UpperLeft, ref missing, ref missing);
  412. displayBoundsTopEdge.AddPoint(displayBoundsReduced.UpperRight, ref missing, ref missing);
  413. IPointCollection displayBoundsBottomEdge = new PolylineClass();
  414. comReleaser.ManageLifetime(displayBoundsBottomEdge);
  415. ((IPolyline)displayBoundsBottomEdge).SpatialReference = displaySpatialReference;
  416. displayBoundsBottomEdge.AddPoint(displayBoundsReduced.LowerLeft, ref missing, ref missing);
  417. displayBoundsBottomEdge.AddPoint(displayBoundsReduced.LowerRight, ref missing, ref missing);
  418. IPointCollection displayBoundsLeftEdge = new PolylineClass();
  419. comReleaser.ManageLifetime(displayBoundsLeftEdge);
  420. ((IPolyline)displayBoundsLeftEdge).SpatialReference = displaySpatialReference;
  421. displayBoundsLeftEdge.AddPoint(displayBoundsReduced.LowerLeft, ref missing, ref missing);
  422. displayBoundsLeftEdge.AddPoint(displayBoundsReduced.UpperLeft, ref missing, ref missing);
  423. IPointCollection displayBoundsRightEdge = new PolylineClass();
  424. comReleaser.ManageLifetime(displayBoundsRightEdge);
  425. ((IPolyline)displayBoundsRightEdge).SpatialReference = displaySpatialReference;
  426. displayBoundsRightEdge.AddPoint(displayBoundsReduced.LowerRight, ref missing, ref missing);
  427. displayBoundsRightEdge.AddPoint(displayBoundsReduced.UpperRight, ref missing, ref missing);
  428. // Calc the starting cooordinates to build the grid lines
  429. int remainder = 0;
  430. int eastingLeft = Math.DivRem(Convert.ToInt32(graticuleBoundsMga.XMin), MGA94GraticuleCustomLayer.MGAGridSpacing, out remainder) * MGA94GraticuleCustomLayer.MGAGridSpacing;
  431. int northingBottom = Math.DivRem(Convert.ToInt32(graticuleBoundsMga.YMin), MGA94GraticuleCustomLayer.MGAGridSpacing, out remainder) * MGA94GraticuleCustomLayer.MGAGridSpacing;
  432. // Create the graticule lines (extend to expanded bounds), project back to the display coordinates and calculate label points
  433. List<GraticuleLine> graticuleLines = new List<GraticuleLine>();
  434. for (int i = eastingLeft; i <= graticuleBoundsMga.XMax; i += MGA94GraticuleCustomLayer.MGAGridSpacing)
  435. {
  436. IPoint lineStart = new PointClass();
  437. comReleaser.ManageLifetime(lineStart);
  438. IPoint lineEnd = new PointClass();
  439. comReleaser.ManageLifetime(lineEnd);
  440. lineStart.PutCoords(i, graticuleBoundsMga.YMin);
  441. lineEnd.PutCoords(i, graticuleBoundsMga.YMax);
  442. IPolyline verticalLineFull = new PolylineClass();
  443. comReleaser.ManageLifetime(verticalLineFull);
  444. verticalLineFull.SpatialReference = mgaZoneExtent.SpatialReference;
  445. verticalLineFull.FromPoint = lineStart;
  446. verticalLineFull.ToPoint = lineEnd;
  447. IPolyline verticalLine = (IPolyline)((ITopologicalOperator)verticalLineFull).Intersect((IGeometry)graticuleBoundsPolyMga, esriGeometryDimension.esriGeometry1Dimension);
  448. if (verticalLine != null && verticalLine.IsEmpty == false)
  449. {
  450. ((IPolycurve)verticalLine).Densify(verticalLine.Length / Convert.ToDouble(MGA94GraticuleCustomLayer.DensifySegmentCount), 0);
  451. verticalLine = (IPolyline)ProjectionMethods.Project((IGeometry)verticalLine, displaySpatialReference);
  452. // Intersect the lines with the display bounds (top and bottom)
  453. List<GraticuleLabelPoint> graticuleLabelPoints = new List<GraticuleLabelPoint>();
  454. IGeometry topLabelPoint = ((ITopologicalOperator)verticalLine).Intersect((IGeometry)displayBoundsTopEdge, esriGeometryDimension.esriGeometry0Dimension);
  455. if ((topLabelPoint as IMultipoint) != null && ((IPointCollection)topLabelPoint).PointCount == 1)
  456. {
  457. graticuleLabelPoints.Add(new GraticuleLabelPoint(((IPointCollection)topLabelPoint).get_Point(0),
  458. esriTextHorizontalAlignment.esriTHACenter, esriTextVerticalAlignment.esriTVATop, "<FNT scale='80'>" + mgaPrefix(i, MGA94GraticuleCustomLayer.MGAGridSpacing) + "</FNT>" + "<FNT scale='60'> </FNT>" + mgaSuffix(i, MGA94GraticuleCustomLayer.MGAGridSpacing) +
  459. System.Environment.NewLine + "<FNT scale='80'>" + "z" + mgaZoneExtent.SpatialReference.Name.Substring(mgaZoneExtent.SpatialReference.Name.IndexOf("_Zone_") + 6, 2) + "</FNT>"));
  460. }
  461. IGeometry bottomLabelPoint = ((ITopologicalOperator)verticalLine).Intersect((IGeometry)displayBoundsBottomEdge, esriGeometryDimension.esriGeometry0Dimension);
  462. if ((bottomLabelPoint as IMultipoint) != null && ((IPointCollection)bottomLabelPoint).PointCount == 1)
  463. {
  464. graticuleLabelPoints.Add(new GraticuleLabelPoint(((IPointCollection)bottomLabelPoint).get_Point(0),
  465. esriTextHorizontalAlignment.esriTHACenter, esriTextVerticalAlignment.esriTVABottom, "<FNT scale='80'>" + mgaPrefix(i, MGA94GraticuleCustomLayer.MGAGridSpacing) + "</FNT>" + "<FNT scale='60'> </FNT>" + mgaSuffix(i, MGA94GraticuleCustomLayer.MGAGridSpacing)));
  466. }
  467. graticuleLines.Add(new GraticuleLine(verticalLine, graticuleLabelPoints));
  468. }
  469. }
  470. for (int i = northingBottom; i <= graticuleBoundsMga.YMax; i += MGA94GraticuleCustomLayer.MGAGridSpacing)
  471. {
  472. IPoint lineStart = new PointClass();
  473. comReleaser.ManageLifetime(lineStart);
  474. IPoint lineEnd = new PointClass();
  475. comReleaser.ManageLifetime(lineEnd);
  476. lineStart.PutCoords(graticuleBoundsMga.XMin, i);
  477. lineEnd.PutCoords(graticuleBoundsMga.XMax, i);
  478. IPolyline horizontalLineFull = new PolylineClass();
  479. comReleaser.ManageLifetime(horizontalLineFull);
  480. horizontalLineFull.SpatialReference = mgaZoneExtent.SpatialReference;
  481. horizontalLineFull.FromPoint = lineStart;
  482. horizontalLineFull.ToPoint = lineEnd;
  483. IPolyline horizontalLine = (IPolyline)((ITopologicalOperator)horizontalLineFull).Intersect((IGeometry)graticuleBoundsPolyMga, esriGeometryDimension.esriGeometry1Dimension);
  484. if (horizontalLine != null && horizontalLine.IsEmpty == false)
  485. {
  486. ((IPolycurve)horizontalLine).Densify(horizontalLine.Length / Convert.ToDouble(MGA94GraticuleCustomLayer.DensifySegmentCount), 0);
  487. horizontalLine = (IPolyline)ProjectionMethods.Project((IGeometry)horizontalLine, displaySpatialReference);
  488. // Intersect the lines with the display bounds (top and bottom)
  489. List<GraticuleLabelPoint> graticuleLabelPoints = new List<GraticuleLabelPoint>();
  490. IGeometry leftLabelPoint = ((ITopologicalOperator)horizontalLine).Intersect((IGeometry)displayBoundsLeftEdge, esriGeometryDimension.esriGeometry0Dimension);
  491. if ((leftLabelPoint as IMultipoint) != null && ((IPointCollection)leftLabelPoint).PointCount == 1)
  492. {
  493. graticuleLabelPoints.Add(new GraticuleLabelPoint(((IPointCollection)leftLabelPoint).get_Point(0),
  494. esriTextHorizontalAlignment.esriTHALeft, esriTextVerticalAlignment.esriTVACenter, "<FNT scale='80'>" + mgaPrefix(i, MGA94GraticuleCustomLayer.MGAGridSpacing) + "</FNT>" + "<FNT scale='60'> </FNT>" + mgaSuffix(i, MGA94GraticuleCustomLayer.MGAGridSpacing)));
  495. }
  496. IGeometry rightLabelPoint = ((ITopologicalOperator)horizontalLine).Intersect((IGeometry)displayBoundsRightEdge, esriGeometryDimension.esriGeometry0Dimension);
  497. if ((rightLabelPoint as IMultipoint) != null && ((IPointCollection)rightLabelPoint).PointCount == 1)
  498. {
  499. graticuleLabelPoints.Add(new GraticuleLabelPoint(((IPointCollection)rightLabelPoint).get_Point(0),
  500. esriTextHorizontalAlignment.esriTHARight, esriTextVerticalAlignment.esriTVACenter, "<FNT scale='80'>" + mgaPrefix(i, MGA94GraticuleCustomLayer.MGAGridSpacing) + "</FNT>" + "<FNT scale='60'> </FNT>" + mgaSuffix(i, MGA94GraticuleCustomLayer.MGAGridSpacing)));
  501. }
  502. graticuleLines.Add(new GraticuleLine(horizontalLine, graticuleLabelPoints));
  503. }
  504. }
  505. return graticuleLines;
  506. }
  507. }
  508. /// <summary>
  509. /// Calculates the mag prefix for a label based on grid spacing.
  510. /// </summary>
  511. /// <param name="mgaValue">The mga value.</param>
  512. /// <param name="gridSpacing">The grid spacing.</param>
  513. /// <returns>The prefix string.</returns>
  514. private static string mgaPrefix(int mgaValue, int gridSpacing)
  515. {
  516. int result;
  517. string wholeValue = Math.DivRem(mgaValue, gridSpacing, out result).ToString();
  518. return wholeValue.Substring(0, (wholeValue.Length - 2));
  519. }
  520. /// <summary>
  521. /// Calculates the mag suffix for a label based on grid spacing.
  522. /// </summary>
  523. /// <param name="mgaValue">The mga value.</param>
  524. /// <param name="gridSpacing">The grid spacing.</param>
  525. /// <returns>The prefix string.</returns>
  526. private static string mgaSuffix(int mgaValue, int gridSpacing)
  527. {
  528. int result;
  529. string wholeValue = Math.DivRem(mgaValue, gridSpacing, out result).ToString();
  530. return wholeValue.Substring((wholeValue.Length - 2), 2);
  531. }
  532. /// <summary>
  533. /// Determines whether the specified int value is odd.
  534. /// </summary>
  535. /// <param name="intValue">The int value.</param>
  536. /// <returns>
  537. /// <c>true</c> if the specified int value is odd; otherwise, <c>false</c>.
  538. /// </returns>
  539. public static bool IsOdd(int intValue)
  540. {
  541. return ((intValue & 1) == 1);
  542. }
  543. #endregion
  544. #region Data Transfer Objects
  545. /// <summary>
  546. /// Class to hold details of a graticule line
  547. /// </summary>
  548. private class GraticuleLine
  549. {
  550. #region Module Level Variables
  551. private IPolyline _line;
  552. private List<GraticuleLabelPoint> _labelPoints;
  553. #endregion
  554. #region Constructors
  555. /// <summary>
  556. /// Initializes a new instance of the <see cref="GraticuleLine"/> class.
  557. /// </summary>
  558. /// <param name="line">The line.</param>
  559. /// <param name="labelPoints">The label points.</param>
  560. public GraticuleLine(IPolyline line, List<GraticuleLabelPoint> labelPoints)
  561. {
  562. _line = line;
  563. _labelPoints = labelPoints;
  564. }
  565. #endregion
  566. #region Properties
  567. /// <summary>
  568. /// Gets the line.
  569. /// </summary>
  570. /// <value>The line.</value>
  571. public IPolyline Line
  572. {
  573. get { return _line; }
  574. }
  575. /// <summary>
  576. /// Gets the label points.
  577. /// </summary>
  578. /// <value>The label points.</value>
  579. public List<GraticuleLabelPoint> LabelPoints
  580. {
  581. get { return _labelPoints; }
  582. }
  583. #endregion
  584. }
  585. /// <summary>
  586. /// Class to hold details of a graticule label point
  587. /// </summary>
  588. private class GraticuleLabelPoint
  589. {
  590. #region Module Level Variables
  591. private IPoint _anchorPoint;
  592. private esriTextHorizontalAlignment _horizontalAlignment;
  593. private esriTextVerticalAlignment _verticalAlignment;
  594. private string _text;
  595. #endregion
  596. #region Constructors
  597. /// <summary>
  598. /// Initializes a new instance of the <see cref="GraticuleLabelPoint"/> class.
  599. /// </summary>
  600. /// <param name="anchorPoint">The anchor point.</param>
  601. /// <param name="horizontalAlignment">The horizontal alignment.</param>
  602. /// <param name="verticalAlignment">The vertical alignment.</param>
  603. /// <param name="text">The text.</param>
  604. public GraticuleLabelPoint(IPoint anchorPoint, esriTextHorizontalAlignment horizontalAlignment,
  605. esriTextVerticalAlignment verticalAlignment, string text)
  606. {
  607. _anchorPoint = anchorPoint;
  608. _horizontalAlignment = horizontalAlignment;
  609. _verticalAlignment = verticalAlignment;
  610. _text = text;
  611. }
  612. #endregion
  613. #region Properties
  614. /// <summary>
  615. /// Gets the anchor point.
  616. /// </summary>
  617. /// <value>The anchor point.</value>
  618. public IPoint AnchorPoint
  619. {
  620. get { return _anchorPoint; }
  621. }
  622. /// <summary>
  623. /// Gets the horizontal alignment.
  624. /// </summary>
  625. /// <value>The horizontal alignment.</value>
  626. public esriTextHorizontalAlignment HorizontalAlignment
  627. {
  628. get { return _horizontalAlignment; }
  629. }
  630. /// <summary>
  631. /// Gets the vertical alignment.
  632. /// </summary>
  633. /// <value>The vertical alignment.</value>
  634. public esriTextVerticalAlignment VerticalAlignment
  635. {
  636. get { return _verticalAlignment; }
  637. }
  638. /// <summary>
  639. /// Gets the text.
  640. /// </summary>
  641. /// <value>The text.</value>
  642. public string Text
  643. {
  644. get { return _text; }
  645. }
  646. #endregion
  647. }
  648. #endregion
  649. }
  650. }