翼度科技»论坛 编程开发 .net 查看内容

WPF绘制深圳地铁路线图

2

主题

2

帖子

6

积分

新手上路

Rank: 1

积分
6
经常坐地铁,却不知道地铁多少条线路?哪个站下车?今天就带领大家熟悉并绘制深圳地铁路线图。
WPF在绘制矢量图方面有非常强大的优势,利用WPF可以绘制出各种矢量图形,如线,圆,多边形,矩形,及组合图形。今天以绘制深圳地铁路线图为例,简述WPF在图形绘制方面的一些知识,仅供学习分享使用,如有不足之处,还请指正。
 
WPF图形概述


与传统的.NET开发使用GDI+进行绘图不同,WPF拥有自己的一套图形API,绘图为矢量图。绘图可以在任何一种布局控件中完成,wpf会根据容器计算相应坐标。最常用的是Canvas和Grid。基本图形包括以下几个,都是Shaper类的派生类。

  • Line,直线段,可以设置Stroke
  • Rectangle,有Stroke也有Fill
  • Ellipse,椭圆,同上
  • Polygon,多边形。由多条直线线段围成的闭合区域,同上。
  • Polyline,折线,不闭合,由多条首尾相接的直线段组成
  • Path,路径,闭合。可以由若干直线、圆弧、贝塞尔曲线(由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋)组成。很强大。
 
地铁官网效果

 
首先打开深圳地铁官网【https://www.szmc.net/map/】,可查看深圳地铁的路线图,如下所示:

 
获取地铁路线数据

 
通过对地铁官网的网络接口接收数据分析,可以获取地铁数据的原始JSON文件,将原始JSON文件保存到本地,在程序中进行引用,如下所示:

 
构建地铁数据模型

 
在得到shentie.json文件后,通过分析,构建模型类,如下所示:
  1.   1 namespace DemoSubway.Models
  2.   2 {
  3.   3     /// <summary>
  4.   4     /// 深圳地铁模型
  5.   5     /// </summary>
  6.   6     public class ShenTie
  7.   7     {
  8.   8         [JsonProperty("s")]
  9.   9         public string? Name { get; set; }
  10. 10
  11. 11         [JsonProperty("i")]
  12. 12         public string? Index { get; set; }
  13. 13
  14. 14         [JsonProperty("l")]
  15. 15         public SubwayLine[]? SubwayLines { get; set; }
  16. 16
  17. 17         [JsonProperty("o")]
  18. 18         public string? Obj { get;set; }
  19. 19     }
  20. 20
  21. 21     public class SubwayLine
  22. 22     {
  23. 23         [JsonProperty("st")]
  24. 24         public St[]? Sites { get; set; }
  25. 25
  26. 26         [JsonProperty("ln")]
  27. 27         public string? LineNumber { get; set; }
  28. 28
  29. 29         [JsonProperty("su")]
  30. 30         public string? Su { get; set; }
  31. 31
  32. 32         [JsonProperty("kn")]
  33. 33         public string? KName { get; set; }
  34. 34
  35. 35         [JsonProperty("c")]
  36. 36         public string[]? Circles { get;set; }
  37. 37
  38. 38         [JsonProperty("lo")]
  39. 39         public string? Lo { get; set; }
  40. 40
  41. 41         [JsonProperty("lp")]
  42. 42         public string[]? LinePosition { get; set; }
  43. 43
  44. 44         [JsonProperty("ls")]
  45. 45         public string? Ls { get; set; }
  46. 46
  47. 47         [JsonProperty("cl")]
  48. 48         public string? Color { get; set; }
  49. 49
  50. 50         [JsonProperty("la")]
  51. 51         public string? La { get; set; }
  52. 52
  53. 53         [JsonProperty("x")]
  54. 54         public string? X { get; set; }
  55. 55
  56. 56         [JsonProperty("li")]
  57. 57         public string? Li { get; set; }
  58. 58     }
  59. 59
  60. 60     public class St
  61. 61     {
  62. 62         [JsonProperty("rs")]
  63. 63         public string? Rs { get; set; }
  64. 64
  65. 65         [JsonProperty("udpx")]
  66. 66         public string? Udpx { get; set; }
  67. 67
  68. 68         [JsonProperty("su")]
  69. 69         public string? Su { get; set; }
  70. 70
  71. 71         [JsonProperty("udsu")]
  72. 72         public string? Udsu { get; set;}
  73. 73
  74. 74         [JsonProperty("n")]
  75. 75         public string? Name { get; set;}
  76. 76
  77. 77         [JsonProperty("en")]
  78. 78         public string? En { get; set; }
  79. 79
  80. 80         [JsonProperty("sid")]
  81. 81         public string? Sid { get; set; }
  82. 82
  83. 83         [JsonProperty("p")]
  84. 84         public string? Position { get; set; }
  85. 85
  86. 86         [JsonProperty("r")]
  87. 87         public string? R { get; set; }
  88. 88
  89. 89         [JsonProperty("udsi")]
  90. 90         public string? Udsi { get; set; }
  91. 91
  92. 92         [JsonProperty("t")]
  93. 93         public string? T { get; set;}
  94. 94
  95. 95         [JsonProperty("si")]
  96. 96         public string? Si { get; set; }
  97. 97
  98. 98         [JsonProperty("sl")]
  99. 99         public string? Sl { get; set;}
  100. 100
  101. 101         [JsonProperty("udli")]
  102. 102         public string? Udli { get; set; }
  103. 103
  104. 104         [JsonProperty("poiid")]
  105. 105         public string? Poiid { get; set; }
  106. 106
  107. 107         [JsonProperty("lg")]
  108. 108         public string? Lg { get; set; }
  109. 109
  110. 110         [JsonProperty("sp")]
  111. 111         public string? Sp { get; set; }
  112. 112     }
  113. 113 }
复制代码
 
解析数据源

 
通过反序列化,将shentie.json文件内容,加载到内存并实例化为ShenTie对象,在此需要用到第三方库【Newtonsoft.Json】,如下所示:

 
绘制地铁路线图

 
地铁路线图在WPF主页面显示,用Grid作为容器【subwayBox】,如下所示:
  1. 1 <Window x:Class="DemoSubway.MainWindow"
  2. 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. 4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  5. 5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  6. 6         xmlns:local="clr-namespace:DemoSubway"
  7. 7         mc:Ignorable="d"
  8. 8         Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded">
  9. 9
  10. 10     <Grid>
  11. 11         <Grid.RowDefinitions>
  12. 12             <RowDefinition Height="Auto"></RowDefinition>
  13. 13             <RowDefinition Height="*"></RowDefinition>
  14. 14         </Grid.RowDefinitions>
  15. 15         <StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center">
  16. 16             <TextBlock x:Name="tbTitle" FontSize="30" HorizontalAlignment="Center"></TextBlock>
  17. 17         </StackPanel>
  18. 18         <Viewbox Stretch="Fill" Grid.Row="1">
  19. 19             <Grid x:Name="subwayBox">
  20. 20
  21. 21             </Grid>
  22. 22         </Viewbox>
  23. 23     </Grid>
  24. 24 </Window>
复制代码
ShenTie对象创建成功后,就可以获取路线数据,然后创建地铁路线元素,如下所示:
  1.   1 private void Window_Loaded(object sender, RoutedEventArgs e)
  2.   2 {
  3.   3     string jsonFile = "shentie.json";
  4.   4     JsonHelper jsonHelper = new JsonHelper();
  5.   5     var shentie = jsonHelper.Deserialize<ShenTie>(jsonFile);
  6.   6     this.tbTitle.Text = shentie.Name;
  7.   7     List<string> lstSites = new List<string>();
  8.   8     for(int i = 0; i < shentie.SubwayLines?.Length; i++)
  9.   9     {
  10. 10         var subwayLine= shentie.SubwayLines[i];
  11. 11         if(subwayLine != null)
  12. 12         {
  13. 13             //地铁线路
  14. 14             var color = ColorTranslator.FromHtml($"#{subwayLine.Color}");//线路颜色
  15. 15             var circles = subwayLine.Circles;//线路节点
  16. 16             Path line = new Path();
  17. 17             PathFigureCollection lineFigures = new PathFigureCollection();
  18. 18             PathFigure lineFigure = new PathFigure();
  19. 19             lineFigure.IsClosed= false;
  20. 20             var start = circles?[0].Split(" ");//线路起始位置
  21. 21             lineFigure.StartPoint = new System.Windows.Point(int.Parse(start[0]), int.Parse(start[1]));
  22. 22
  23. 23             for (int j= 0;j< circles?.Length;j++)
  24. 24             {
  25. 25                 var circle= circles[j].Split(" ");
  26. 26                 LineSegment lineSegment = new LineSegment(new System.Windows.Point(int.Parse(circle[0]), int.Parse(circle[1])),true);
  27. 27                 lineFigure.Segments.Add(lineSegment);
  28. 28             }
  29. 29             lineFigures.Add(lineFigure);
  30. 30             line.Data = new PathGeometry(lineFigures, FillRule.Nonzero, null);
  31. 31             line.Stroke = new SolidColorBrush(System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B));
  32. 32             line.StrokeThickness = 4;
  33. 33             this.subwayBox.Children.Add(line);
  34. 34             //地铁站点
  35. 35             for (int j = 0; j < subwayLine.Sites?.Length; j++)
  36. 36             {
  37. 37                 var site = subwayLine.Sites[j];
  38. 38                 if (site != null)
  39. 39                 {
  40. 40                     
  41. 41                     //站点标识,圆圈
  42. 42                     Path siteCirclePath = new Path();
  43. 43                     var sitePosition = site?.Position?.Split(" ");
  44. 44                     EllipseGeometry ellipse = new EllipseGeometry();
  45. 45                     ellipse.Center = new System.Windows.Point(int.Parse(sitePosition[0]), int.Parse(sitePosition[1]));
  46. 46                     ellipse.RadiusX = 4;
  47. 47                     ellipse.RadiusY=4;
  48. 48                     siteCirclePath.Data=ellipse;
  49. 49                     siteCirclePath.Fill = Brushes.White;
  50. 50                     siteCirclePath.Stroke = new SolidColorBrush(System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B));
  51. 51                     siteCirclePath.Cursor= Cursors.Hand;
  52. 52                     siteCirclePath.Focusable = true;
  53. 53                     siteCirclePath.Tag = site?.Name;
  54. 54                     siteCirclePath.MouseDown += SiteCirclePath_MouseDown;
  55. 55                     this.subwayBox.Children.Add(siteCirclePath);
  56. 56                     //站点名字
  57. 57                     if (lstSites.Contains(site?.Name))
  58. 58                     {
  59. 59                         continue;//对于交汇站点,只绘制一次
  60. 60                     }
  61. 61                     //站点名称
  62. 62                     Path siteTextPath = new Path();
  63. 63                     FormattedText siteContent = new FormattedText(site?.Name,CultureInfo.CurrentCulture,FlowDirection.LeftToRight,new Typeface("Arial"),14,Brushes.Black, 1.25);
  64. 64                     var x = int.Parse(sitePosition[0]);
  65. 65                     var y = int.Parse(sitePosition[1]);
  66. 66                     if (j + 1 < subwayLine.Sites?.Length)
  67. 67                     {
  68. 68                         //站点位置适当偏移
  69. 69                         var next = subwayLine.Sites[j + 1]?.Position?.Split(" ");
  70. 70                         var nextx = int.Parse(next[0]);
  71. 71                         var nexty = int.Parse(next[1]);
  72. 72                         if (x == nextx)
  73. 73                         {
  74. 74                             x = x + 6;
  75. 75                         }
  76. 76                         else if (y == nexty)
  77. 77                         {
  78. 78                             y = y + 6;
  79. 79                         }
  80. 80                         else
  81. 81                         {
  82. 82                             x = x + 1;
  83. 83                             y = y + 1;
  84. 84                         }
  85. 85                     }
  86. 86                     Geometry geometry = siteContent.BuildGeometry(new System.Windows.Point(x, y));
  87. 87                     siteTextPath.Data = geometry;
  88. 88                     siteTextPath.Stroke = Brushes.Black;
  89. 89                     siteTextPath.Focusable = true;
  90. 90                     siteTextPath.Cursor = Cursors.Hand;
  91. 91                     siteTextPath.MouseDown += SiteTextPath_MouseDown;
  92. 92                     siteTextPath.Tag = site?.Name;
  93. 93                     this.subwayBox.Children.Add(siteTextPath);
  94. 94                     lstSites.Add(site?.Name);
  95. 95                 }
  96. 96             }
  97. 97
  98. 98             var kName = subwayLine.KName;//线路名称
  99. 99             var linePosition= subwayLine.LinePosition?[0].Split(" ");
  100. 100             if(kName != null)
  101. 101             {
  102. 102                 Path lineNamePath = new Path();
  103. 103                 FormattedText lineNameText = new FormattedText(kName, CultureInfo.CurrentCulture,FlowDirection.LeftToRight,new Typeface("Arial"),16,Brushes.Black,1.25);
  104. 104                 var lineX = int.Parse(linePosition[0]);
  105. 105                 var lineY = int.Parse(linePosition[1]);
  106. 106                 if (subwayLine.LineNumber == "1")
  107. 107                 {
  108. 108                     lineX = lineX - 10;
  109. 109                     lineY = lineY + 20;
  110. 110                 }
  111. 111                 Geometry geometry = lineNameText.BuildGeometry(new System.Windows.Point(lineX, lineY));
  112. 112                 lineNamePath.Data=geometry;
  113. 113                 lineNamePath.Stroke = new SolidColorBrush(System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B));
  114. 114                 this.subwayBox.Children.Add(lineNamePath);
  115. 115             }
  116. 116         }
  117. 117     }
  118. 118 }
复制代码
 
效果展示

 
本示例效果图如下所示:

 在获取的JSON文件中,有些属性名都是简写,所以在编写示例代码过程中,对有些属性的理解并不准确,需要不断测试优化,绘制出的地铁路线图可能与实际存在稍微的差异,如站点名称,路线名称等内容的位置。
 以上就是本篇文章的全部内容,旨在学习分享,传播知识。

来源:https://www.cnblogs.com/hsiang/archive/2023/06/01/17447750.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具