ScaleBar.cs
上传用户:huazai0421
上传日期:2008-05-30
资源大小:405k
文件大小:10k
源码类别:

SilverLight

开发平台:

C#

  1. using System;
  2. using System.Windows;
  3. using System.Windows.Controls;
  4. using System.Windows.Media;
  5. using ESRI.ArcGIS.Client;
  6. using ESRI.ArcGIS.Client.Geometry;
  7. namespace ESRI.ArcGIS.Samples
  8. {
  9. [TemplatePart(Name = "ScaleBarBlock", Type = typeof(FrameworkElement))]
  10. [TemplatePart(Name = "PaddingLeftForScaleBarTextMeters", Type = typeof(FrameworkElement))]
  11. [TemplatePart(Name = "PaddingLeftTopNotch", Type = typeof(FrameworkElement))]
  12. [TemplatePart(Name = "PaddingLeftForScaleBarTextMiles", Type = typeof(FrameworkElement))]
  13. [TemplatePart(Name = "PaddingLeftBottomNotch", Type = typeof(FrameworkElement))]
  14. [TemplatePart(Name = "ScaleBarTextForMeters", Type = typeof(TextBlock))]
  15. [TemplatePart(Name = "ScaleBarTextForMiles", Type = typeof(TextBlock))]
  16. public class ScaleBar : Control
  17. {
  18. FrameworkElement ScaleBarBlock;
  19. FrameworkElement PaddingLeftForScaleBarTextMeters;
  20. TextBlock ScaleBarTextForMeters;
  21. FrameworkElement PaddingLeftTopNotch;
  22. FrameworkElement PaddingLeftForScaleBarTextMiles;
  23. FrameworkElement PaddingLeftBottomNotch;
  24. TextBlock ScaleBarTextForMiles;
  25. public ScaleBar()
  26. {
  27. DefaultStyleKey = typeof(ScaleBar);
  28. }
  29. public override void OnApplyTemplate()
  30. {
  31. ScaleBarBlock = this.GetTemplateChild("ScaleBarBlock") as FrameworkElement;
  32. PaddingLeftForScaleBarTextMeters = this.GetTemplateChild("PaddingLeftForScaleBarTextMeters") as FrameworkElement;
  33. PaddingLeftTopNotch = this.GetTemplateChild("PaddingLeftTopNotch") as FrameworkElement;
  34. PaddingLeftForScaleBarTextMiles = this.GetTemplateChild("PaddingLeftForScaleBarTextMiles") as FrameworkElement;
  35. PaddingLeftBottomNotch = this.GetTemplateChild("PaddingLeftBottomNotch") as FrameworkElement;
  36. ScaleBarTextForMeters = this.GetTemplateChild("ScaleBarTextForMeters") as TextBlock;
  37. ScaleBarTextForMiles = this.GetTemplateChild("ScaleBarTextForMiles") as TextBlock;
  38. refreshScalebar();
  39. base.OnApplyTemplate();
  40. }
  41. #region Helper Functions
  42. private void map_ExtentChanged(object sender, ESRI.ArcGIS.Client.ExtentEventArgs args)
  43. {
  44. refreshScalebar();
  45. }
  46. private void refreshScalebar()
  47. {
  48. if (Map == null || double.IsNaN(Map.Resolution))
  49. {
  50. this.Visibility = Visibility.Collapsed;
  51. return;
  52. }
  53. ScaleBarUnit outUnit = ScaleBarUnit.Undefined;
  54. double outResolution;
  55. #region KiloMeters/Meters
  56. double roundedKiloMeters = getBestEstimateOfValue(Map.Resolution, ScaleBarUnit.Kilometers, out outUnit, out outResolution);
  57. double widthMeters = roundedKiloMeters / outResolution;
  58. bool inMeters = outUnit == ScaleBarUnit.Meters;
  59. if(PaddingLeftForScaleBarTextMeters!=null)
  60. PaddingLeftForScaleBarTextMeters.Width = widthMeters;
  61. if(PaddingLeftTopNotch!=null)
  62. PaddingLeftTopNotch.Width = widthMeters;
  63. if (ScaleBarTextForMeters != null)
  64. {
  65. ScaleBarTextForMeters.Text = string.Format("{0}{1}", roundedKiloMeters, (inMeters ? "m" : "km"));
  66. ScaleBarTextForMeters.Width = widthMeters;
  67. }
  68. #endregion
  69. #region Miles
  70. double roundedMiles = getBestEstimateOfValue(Map.Resolution, ScaleBarUnit.Miles, out outUnit, out outResolution);
  71. double widthMiles = roundedMiles / outResolution;
  72. bool inFeet = outUnit == ScaleBarUnit.Feet;
  73. if (PaddingLeftForScaleBarTextMiles != null)
  74. PaddingLeftForScaleBarTextMiles.Width = widthMiles;
  75. if (PaddingLeftBottomNotch != null)
  76. PaddingLeftBottomNotch.Width = widthMiles;
  77. if (ScaleBarTextForMiles != null)
  78. {
  79. ScaleBarTextForMiles.Text = string.Format("{0}{1}", roundedMiles, inFeet ? "ft" : "mi");
  80. ScaleBarTextForMiles.Width = widthMiles;
  81. }
  82. #endregion
  83. double widthOfNotches = 4; // 2 for left notch, 2 for right notch
  84. double scaleBarBlockWidth= (widthMiles > widthMeters) ? widthMiles : widthMeters;
  85. scaleBarBlockWidth += widthOfNotches;
  86. this.Visibility = roundedMiles == double.NaN || roundedKiloMeters == double.NaN ? Visibility.Collapsed : Visibility.Visible;
  87. if (!double.IsNaN(scaleBarBlockWidth) && ScaleBarBlock != null)
  88. ScaleBarBlock.Width = scaleBarBlockWidth;
  89. }
  90. private double getBestEstimateOfValue(double resolution, ScaleBarUnit displayUnit, out ScaleBarUnit unit, out double outResolution)
  91. {
  92. unit = displayUnit;
  93. double rounded = 0;
  94. double originalRes = resolution;
  95. while (rounded < 0.5)
  96. {
  97. resolution = originalRes;
  98. if (MapUnit == ScaleBarUnit.DecimalDegrees) 
  99. {
  100. resolution = getResolutionForGeographic(Map.Extent, resolution);
  101. resolution = resolution * (int)ScaleBarUnit.Meters/(int)unit;                
  102. }
  103. else if (MapUnit != ScaleBarUnit.Undefined) 
  104. {
  105. resolution = resolution * (int)MapUnit / (int)unit;
  106. }
  107. double val = TargetWidth * resolution;
  108. val = roundToSignificant(val, resolution);
  109. double noFrac = Math.Round(val); // to get rid of the fraction
  110. if (val < 0.5)
  111. {
  112. ScaleBarUnit newUnit = ScaleBarUnit.Undefined; 
  113. // Automatically switch unit to a lower one
  114. if (unit == ScaleBarUnit.Kilometers)
  115. newUnit = ScaleBarUnit.Meters;
  116. else if(unit == ScaleBarUnit.Miles)
  117. newUnit = ScaleBarUnit.Feet;
  118. if (newUnit == ScaleBarUnit.Undefined) { break; } //no lower unit
  119. unit = newUnit;
  120. }
  121. else if (noFrac > 1)
  122. {
  123. rounded = noFrac;
  124. var len = noFrac.ToString().Length;
  125. if (len <= 2)
  126. {
  127. // single/double digits ... make it a multiple of 5 ..or 1,2,3,4
  128. if (noFrac > 5)
  129. {
  130. rounded -= noFrac % 5;
  131. }
  132. while (rounded > 1 && (rounded / resolution) > TargetWidth)
  133. {
  134. // exceeded maxWidth .. decrement by 1 or by 5
  135. double decr = noFrac > 5 ? 5 : 1;
  136. rounded = rounded - decr;
  137. }
  138. }
  139. else if (len > 2)
  140. {
  141. rounded = Math.Round(noFrac / Math.Pow(10, len - 1)) * Math.Pow(10, len - 1);
  142. if ((rounded / resolution) > TargetWidth)
  143. {
  144. // exceeded maxWidth .. use the lower bound instead
  145. rounded = Math.Floor(noFrac / Math.Pow(10, len - 1)) * Math.Pow(10, len - 1);
  146. }
  147. }
  148. }
  149. else
  150. { // anything between 0.5 and 1
  151. rounded = Math.Floor(val);
  152. if (rounded == 0)
  153. {
  154. //val >= 0.5 but < 1 so round up
  155. rounded = (val == 0.5) ? 0.5 : 1;
  156. if ((rounded / resolution) > TargetWidth)
  157. {
  158. // exceeded maxWidth .. re-try by switching to lower unit 
  159. rounded = 0;
  160. ScaleBarUnit newUnit = ScaleBarUnit.Undefined;
  161. // Automatically switch unit to a lower one
  162. if (unit == ScaleBarUnit.Kilometers)
  163. newUnit = ScaleBarUnit.Meters;
  164. else if (unit == ScaleBarUnit.Miles)
  165. newUnit = ScaleBarUnit.Feet;
  166. if (newUnit == ScaleBarUnit.Undefined) { break; } //no lower unit
  167. unit = newUnit;
  168. }
  169. }
  170. }
  171. }
  172. outResolution = resolution;
  173. return rounded;
  174. }
  175. double roundToSignificant(double value, double resolution) 
  176. {
  177. var round = Math.Floor(-Math.Log(resolution));
  178. if (round > 0) {
  179. round = Math.Pow(10, round);
  180. return Math.Round(value * round) / round;
  181. }
  182. else { return Math.Round(value); }
  183. }
  184. /// <summary>
  185. /// Calculates horizontal scale at center of extent
  186. /// for geographic / Plate Carrée projection.
  187. /// Horizontal scale is 0 at the poles.
  188. /// </summary>
  189. double toRadians = 0.017453292519943295769236907684886;
  190. double earthRadius = 6378137; //Earth radius in meters (defaults to WGS84 / GRS80)
  191. double degreeDist;
  192. private double getResolutionForGeographic(ESRI.ArcGIS.Client.Geometry.Envelope extent, double resolution)
  193. {
  194. degreeDist = earthRadius * toRadians;
  195. MapPoint center = extent.GetCenter();
  196. double y = center.Y;
  197. if (Math.Abs(y) > 90) { return 0; }
  198. return Math.Cos(y * toRadians) * resolution * degreeDist;
  199. }
  200. #endregion
  201. #region Properties
  202. /// <summary>
  203. /// Identifies the Map dependency property.
  204. /// </summary>
  205. public static readonly DependencyProperty MapProperty =
  206. DependencyProperty.Register(
  207.   "Map",
  208.   typeof(Map),
  209.   typeof(ScaleBar),
  210.   new PropertyMetadata(OnMapPropertyChanged));
  211. private static void OnMapPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  212. {
  213. Map oldMap = e.OldValue as Map;
  214. Map newMap = e.NewValue as Map;
  215. ScaleBar bar = d as ScaleBar;
  216. if (bar != null)
  217. {
  218. if (oldMap != null)
  219. {
  220. oldMap.ExtentChanged -= bar.map_ExtentChanged;
  221. oldMap.ExtentChanging -= bar.map_ExtentChanged;
  222. }
  223. if (newMap != null)
  224. {
  225. newMap.ExtentChanged += bar.map_ExtentChanged;
  226. newMap.ExtentChanging += bar.map_ExtentChanged;
  227. }
  228. bar.refreshScalebar();
  229. }
  230. }
  231. /// <summary>
  232. /// Gets or sets the map that the scale bar is buddied to.
  233. /// </summary>
  234. public ESRI.ArcGIS.Client.Map Map
  235. {
  236. get { return GetValue(MapProperty) as Map; }
  237. set { SetValue(MapProperty, value); }
  238. }
  239. /// <summary>
  240. /// Identifies the <see cref="FillColor1"/> dependency property.
  241. /// </summary>
  242. public static readonly DependencyProperty TargetWidthProperty = DependencyProperty.Register("TargetWidth", typeof(double), typeof(ScaleBar), new PropertyMetadata(150.0));
  243. /// <summary>
  244. /// Gets or sets the target width of the scale bar.
  245. /// </summary>
  246. /// <remarks>The actual width of the scale bar changes when values are rounded.</remarks>
  247. public double TargetWidth
  248. {
  249. get { return (double)GetValue(TargetWidthProperty); }
  250. set { SetValue(TargetWidthProperty, value); }
  251. }
  252. /// <summary>
  253. /// Gets or sets the map unit.
  254. /// </summary>
  255. public ESRI.ArcGIS.Client.ScaleBarUnit MapUnit { get; set; }
  256. /// <summary>
  257. /// Identifies the <see cref="Fill"/> dependency property.
  258. /// </summary>
  259. public static readonly DependencyProperty FillProperty =
  260. DependencyProperty.Register("Fill", typeof(Brush), typeof(ScaleBar),
  261. new PropertyMetadata(new SolidColorBrush(Colors.Black)));
  262. /// <summary>
  263. /// Gets or sets the color of the scalebar.
  264. /// </summary>
  265. public Brush Fill
  266. {
  267. get { return (Brush)GetValue(FillProperty); }
  268. set { SetValue(FillProperty, value); }
  269. }
  270. #endregion
  271. }
  272. }