WPF绘制深圳地铁路线图

  • WPF绘制深圳地铁路线图已关闭评论
  • 222 次浏览
  • A+
所属分类:.NET技术
摘要

经常坐地铁,却不知道地铁多少条线路?哪个站下车?今天就带领大家熟悉并绘制深圳地铁路线图。

经常坐地铁,却不知道地铁多少条线路?哪个站下车?今天就带领大家熟悉并绘制深圳地铁路线图。

WPF在绘制矢量图方面有非常强大的优势,利用WPF可以绘制出各种矢量图形,如线,圆,多边形,矩形,及组合图形。今天以绘制深圳地铁路线图为例,简述WPF在图形绘制方面的一些知识,仅供学习分享使用,如有不足之处,还请指正。

 

WPF图形概述

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

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

 

地铁官网效果

 

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

WPF绘制深圳地铁路线图

 

获取地铁路线数据

 

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

WPF绘制深圳地铁路线图

 

构建地铁数据模型

 

在得到shentie.json文件后,通过分析,构建模型类,如下所示:

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

 

解析数据源

 

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

WPF绘制深圳地铁路线图

 

绘制地铁路线图

 

地铁路线图在WPF主页面显示,用Grid作为容器【subwayBox】,如下所示:

 1 <Window x:Class="DemoSubway.MainWindow"  2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  4         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  5         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  6         xmlns:local="clr-namespace:DemoSubway"  7         mc:Ignorable="d"  8         Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded">  9  10     <Grid> 11         <Grid.RowDefinitions> 12             <RowDefinition Height="Auto"></RowDefinition> 13             <RowDefinition Height="*"></RowDefinition> 14         </Grid.RowDefinitions> 15         <StackPanel Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Center"> 16             <TextBlock x:Name="tbTitle" FontSize="30" HorizontalAlignment="Center"></TextBlock> 17         </StackPanel> 18         <Viewbox Stretch="Fill" Grid.Row="1"> 19             <Grid x:Name="subwayBox"> 20  21             </Grid> 22         </Viewbox> 23     </Grid> 24 </Window>

ShenTie对象创建成功后,就可以获取路线数据,然后创建地铁路线元素,如下所示:

  1 private void Window_Loaded(object sender, RoutedEventArgs e)   2 {   3     string jsonFile = "shentie.json";   4     JsonHelper jsonHelper = new JsonHelper();   5     var shentie = jsonHelper.Deserialize<ShenTie>(jsonFile);   6     this.tbTitle.Text = shentie.Name;   7     List<string> lstSites = new List<string>();   8     for(int i = 0; i < shentie.SubwayLines?.Length; i++)   9     {  10         var subwayLine= shentie.SubwayLines[i];  11         if(subwayLine != null)  12         {  13             //地铁线路  14             var color = ColorTranslator.FromHtml($"#{subwayLine.Color}");//线路颜色  15             var circles = subwayLine.Circles;//线路节点  16             Path line = new Path();  17             PathFigureCollection lineFigures = new PathFigureCollection();  18             PathFigure lineFigure = new PathFigure();  19             lineFigure.IsClosed= false;  20             var start = circles?[0].Split(" ");//线路起始位置  21             lineFigure.StartPoint = new System.Windows.Point(int.Parse(start[0]), int.Parse(start[1]));  22   23             for (int j= 0;j< circles?.Length;j++)  24             {  25                 var circle= circles[j].Split(" ");  26                 LineSegment lineSegment = new LineSegment(new System.Windows.Point(int.Parse(circle[0]), int.Parse(circle[1])),true);  27                 lineFigure.Segments.Add(lineSegment);  28             }  29             lineFigures.Add(lineFigure);  30             line.Data = new PathGeometry(lineFigures, FillRule.Nonzero, null);  31             line.Stroke = new SolidColorBrush(System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B));  32             line.StrokeThickness = 4;  33             this.subwayBox.Children.Add(line);  34             //地铁站点  35             for (int j = 0; j < subwayLine.Sites?.Length; j++)  36             {  37                 var site = subwayLine.Sites[j];  38                 if (site != null)  39                 {  40                       41                     //站点标识,圆圈  42                     Path siteCirclePath = new Path();  43                     var sitePosition = site?.Position?.Split(" ");  44                     EllipseGeometry ellipse = new EllipseGeometry();  45                     ellipse.Center = new System.Windows.Point(int.Parse(sitePosition[0]), int.Parse(sitePosition[1]));  46                     ellipse.RadiusX = 4;   47                     ellipse.RadiusY=4;  48                     siteCirclePath.Data=ellipse;  49                     siteCirclePath.Fill = Brushes.White;  50                     siteCirclePath.Stroke = new SolidColorBrush(System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B));  51                     siteCirclePath.Cursor= Cursors.Hand;  52                     siteCirclePath.Focusable = true;  53                     siteCirclePath.Tag = site?.Name;  54                     siteCirclePath.MouseDown += SiteCirclePath_MouseDown;  55                     this.subwayBox.Children.Add(siteCirclePath);  56                     //站点名字  57                     if (lstSites.Contains(site?.Name))  58                     {  59                         continue;//对于交汇站点,只绘制一次  60                     }  61                     //站点名称  62                     Path siteTextPath = new Path();  63                     FormattedText siteContent = new FormattedText(site?.Name,CultureInfo.CurrentCulture,FlowDirection.LeftToRight,new Typeface("Arial"),14,Brushes.Black, 1.25);  64                     var x = int.Parse(sitePosition[0]);  65                     var y = int.Parse(sitePosition[1]);  66                     if (j + 1 < subwayLine.Sites?.Length)  67                     {  68                         //站点位置适当偏移  69                         var next = subwayLine.Sites[j + 1]?.Position?.Split(" ");  70                         var nextx = int.Parse(next[0]);  71                         var nexty = int.Parse(next[1]);  72                         if (x == nextx)  73                         {  74                             x = x + 6;  75                         }  76                         else if (y == nexty)  77                         {  78                             y = y + 6;  79                         }  80                         else  81                         {  82                             x = x + 1;  83                             y = y + 1;  84                         }  85                     }  86                     Geometry geometry = siteContent.BuildGeometry(new System.Windows.Point(x, y));  87                     siteTextPath.Data = geometry;  88                     siteTextPath.Stroke = Brushes.Black;  89                     siteTextPath.Focusable = true;  90                     siteTextPath.Cursor = Cursors.Hand;  91                     siteTextPath.MouseDown += SiteTextPath_MouseDown;  92                     siteTextPath.Tag = site?.Name;  93                     this.subwayBox.Children.Add(siteTextPath);  94                     lstSites.Add(site?.Name);  95                 }  96             }  97   98             var kName = subwayLine.KName;//线路名称  99             var linePosition= subwayLine.LinePosition?[0].Split(" "); 100             if(kName != null) 101             { 102                 Path lineNamePath = new Path(); 103                 FormattedText lineNameText = new FormattedText(kName, CultureInfo.CurrentCulture,FlowDirection.LeftToRight,new Typeface("Arial"),16,Brushes.Black,1.25); 104                 var lineX = int.Parse(linePosition[0]); 105                 var lineY = int.Parse(linePosition[1]); 106                 if (subwayLine.LineNumber == "1") 107                 { 108                     lineX = lineX - 10; 109                     lineY = lineY + 20; 110                 } 111                 Geometry geometry = lineNameText.BuildGeometry(new System.Windows.Point(lineX, lineY)); 112                 lineNamePath.Data=geometry; 113                 lineNamePath.Stroke = new SolidColorBrush(System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B)); 114                 this.subwayBox.Children.Add(lineNamePath); 115             } 116         } 117     } 118 }

 

效果展示

 

本示例效果图如下所示:

WPF绘制深圳地铁路线图

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

 以上就是本篇文章的全部内容,旨在学习分享,传播知识。