本文内容:时态数据的概念,ArcGIS API中符号的运用
本文来完成按时间对超市营业额信息查看的功能。拖动屏幕上方的滑块,当前日期会随着变化,而地图上显示的内容则是当前日期(某一天)每个店面的营业额状况:符号大小直观的表示了每个店面营业额的大体情况,当地图放大到一定程度时,显示出具体的数字,代表该店面当天营业额的多少。选中“连续查看”后,则可以以动画的形式对一段时间内的营业额进行连续播放。
ArcGIS 10中强调了时态数据的概念。可以通过矢量或影像属性表中一个(某一个时刻)或两个字段(某一时间段),来表示该行数据发生或持续的时间。时态数据通常分为两类,一类是随着时间的变化,要素的图形或位置发生了变化(shape字段的变化),比如随着时间的变化,台风中心位置(点)进行了移动,或者火灾中的过火面积(面)发生了改变;另一类是随着时间的变化,要素的属性值发生了变化(非shape字段的变化),比如本例中每个超市的营业额。ArcGIS中,不同时刻或时段的数据用表中不同的行来存储,为了减少数据冗余,通常也采取多表关联的形式来管理时态数据。
本例中为了数据存储简单起见,将每天的营业额按列存储,也就是每天的营业额存储在了每个超市属性的不同字段中。这样我们就需要自己通过代码来完成时间展示的功能。当然也可以用ArcGIS提供的Transpose Fields工具,轻松将数据转换为ArcGIS能识别的分行存储的时态格式,从而利用API中Map/FeatureLayer的TimeExtent属性来完成我们的功能。
先来看一下我们的TimeQuery工具初始化完成的工作:
1: private bool _isActivated;
2: DispatcherTimer _dTimer;//use to control autoplay
3: private Border _Border;//stackpanel used to hold the slider and textblock
4: private CheckBox _chkboxAutoPlay;//auto paly animation?
5: private Slider _slider;//time slider
6: private TextBlock _textblock;//date textblock
7: private FeatureLayer _FLayer;//business layer
8: private GraphicsLayer _GLayerSymbol;//do nothing with Business FeatureLayer, using another graphicslayer to display symobls
9: private GraphicsLayer _GLayerText;//becuase the binding didn't work well in wp7 api, so using another textsymbol in a graphicslayer
10: //instead of a custom markersymbol with a text in it(using binding).
11: private double _salesmin, _salesmax;//use to determine symbol size
12: private enum SymbolSize
13: {
14: small=20,
15: middle=40,
16: large=60
17: }
18: public Map map1 { get; set; }
19: public bool IsActivated
20: {
21: get { return _isActivated; }
22: set
23: {
24: if (_isActivated != value)
25: {
26: _isActivated = value;
27: if (value)
28: {
29: SlidePanel(Visibility.Visible, TimeSpan.FromMilliseconds(300));
30: _FLayer.Visible = false;
31: map1.Layers.Add(_GLayerSymbol);
32: map1.Layers.Add(_GLayerText);
33: InitSymbols();
34: _chkboxAutoPlay.Visibility = Visibility.Visible;
35: _slider.ValueChanged += _slider_ValueChanged;
36: map1.ExtentChanged += map1_ExtentChanged;
37: }
38: else
39: {
40: SlidePanel(Visibility.Collapsed, TimeSpan.FromMilliseconds(300));
41: _FLayer.Visible = true;
42: map1.Layers.Remove(_GLayerText);
43: map1.Layers.Remove(_GLayerSymbol);
44: UnInitSymbols();
45: _chkboxAutoPlay.Visibility = Visibility.Collapsed;
46: map1.ExtentChanged -= map1_ExtentChanged;
47:
48: _slider.ValueChanged -= _slider_ValueChanged;
49: _chkboxAutoPlay.IsChecked = false;
50: _dTimer.Stop();
51: }
52: }
53: }
54: }
55:
56: /// <summary>
57: /// display or hide the time panel with a slide animation
58: /// </summary>
59: /// <param name="visible"></param>
60: /// <param name="duration"></param>
61: private void SlidePanel(Visibility visible, TimeSpan duration)
62: {
63: DoubleAnimation da = new DoubleAnimation();
64: da.Duration = duration;
65: if (visible == Visibility.Visible)
66: {
67: //_Border.Visibility = Visibility.Visible;
68: da.From = 0;
69: da.To = 100;
70: }
71: else
72: {
73: //_Border.Visibility = Visibility.Collapsed;
74: da.From = 100;
75: da.To = 0;
76: }
77: Storyboard sb = new Storyboard();
78: Storyboard.SetTarget(da, _Border);
79: Storyboard.SetTargetProperty(da, new PropertyPath("Height"));
80: sb.Children.Add(da);
81: sb.Begin();
82: }
83:
84: private void InitSymbols()
85: {
86: foreach (Graphic g in _FLayer.Graphics)
87: {
88: //determin symbol size
89: double size = -1;
90: if ((double)g.Attributes["D1101"] <= (_salesmax + _salesmin)/2 * 1 / 3)
91: size = (double)SymbolSize.small;
92: else if ((double)g.Attributes["D1101"] > (_salesmax + _salesmin) / 2 * 1 / 3 && (double)g.Attributes["D1101"] <= (_salesmax + _salesmin) / 2 * 2 / 3)
93: size = (double)SymbolSize.middle;
94: else
95: size = (double)SymbolSize.large;
96:
97: //change Businesslayer symbol to custom symbols and display in _GLayerSymbol for each store
98: Graphic gsymbol = new Graphic()
99: {
100: Geometry = g.Geometry,
101: Symbol = new PictureMarkerSymbol()
102: {
103: Source = new BitmapImage(new Uri("../Images/Dollar.png", UriKind.Relative)),
104: }
105: };
106:
107: (gsymbol.Symbol as PictureMarkerSymbol).Height = (gsymbol.Symbol as PictureMarkerSymbol).Width = size;
108: (gsymbol.Symbol as PictureMarkerSymbol).OffsetX = (gsymbol.Symbol as PictureMarkerSymbol).OffsetY = size / 2;
109: g.Attributes.Add("symbol", gsymbol);
110:
111: //add a graphic with textsymbol, which display sales, to _GLayerText for each store
112: Graphic gtext = new Graphic()
113: {
114: Geometry = g.Geometry,
115: Symbol = new TextSymbol()
116: {
117: Foreground = new SolidColorBrush(Color.FromArgb(225,255,255,255)),
118: Text = double.Parse(g.Attributes["D1101"].ToString()).ToString("###,###"),
119: OffsetX = -size / 2+15,
120: OffsetY = -size / 2+15,
121: FontSize=30
122: }
123: };
124: gtext.Symbol.ControlTemplate = (App.Current.Resources["LegendTextSymbol"] as TextSymbol).ControlTemplate;
125: g.Attributes.Add("text", gtext);
126:
127: _GLayerText.Graphics.Add(gtext);
128: _GLayerSymbol.Graphics.Add(gsymbol);
129: }
130: _GLayerText.Visible = false;
131: }
在这个工具类的构造函数中,添加了一些控件,包括一个控制时间的slider,一个自动播放的checkbox,一个显示当前日期的textblock。另外找出了所有店面所有时间范围内营业额的最大值和最小值,这样有助于对我们的营业额进行“归一化”,从而决定地图符号的大小。
1: public TimeQuery(Map map)
2: {
3: map1 = map;
4: _GLayerText = new GraphicsLayer();
5: _GLayerSymbol= new GraphicsLayer();
6: _FLayer = map1.Layers["BusinessLayer"] as FeatureLayer;
7: _slider = new Slider()
8: {
9: Height=84,
10: Width=360,
11: Minimum=1,
12: Maximum=30,
13: SmallChange=1,
14: LargeChange=5,
15: Margin=new Thickness(0,0,0,-30)
16: };
17:
18: _textblock = new TextBlock()
19: {
20: Text = "时间:11月1日",
21: Foreground=new SolidColorBrush(Colors.White),
22: FontSize=20,
23: FontWeight=FontWeights.Bold,
24: Margin=new Thickness(20,0,0,0),
25: };
26: StackPanel sp = new StackPanel()
27: {
28: HorizontalAlignment=HorizontalAlignment.Center,
29: VerticalAlignment=VerticalAlignment.Center,
30: };
31: sp.Children.Add(_slider);
32:
33: //auto play box
34: _chkboxAutoPlay = new CheckBox()
35: {
36: Content = "连续查看",
37: FontSize = 20,
38: FontWeight=FontWeights.Bold,
39: Foreground = new SolidColorBrush(Colors.White),
40: Visibility = Visibility.Collapsed,
41: Margin = new Thickness(90, -22, 0, 0),
42: };
43:
44: _dTimer = new DispatcherTimer()
45: {
46: Interval = TimeSpan.FromMilliseconds(500)
47: };
48: _dTimer.Tick += (sender, args) =>
49: {
50: if (_slider.Value == 30)
51: _slider.Value = 1;
52: else
53: _slider.Value += 1;
54: };
55:
56: _chkboxAutoPlay.Click += (s, a) =>
57: {
58: CheckBox chkbox = s as CheckBox;
59: if (chkbox.IsChecked == true)
60: {
61: _dTimer.Start();
62: }
63: else
64: {
65: _dTimer.Stop();
66: }
67: };
68: StackPanel sp1 = new StackPanel()
69: {
70: Orientation = Orientation.Horizontal
71: };
72: sp1.Children.Add(_textblock);
73: sp1.Children.Add(_chkboxAutoPlay);
74: sp.Children.Add(sp1);
75:
76: _Border = new Border()
77: {
78: Background=new SolidColorBrush(Color.FromArgb(225,0,0,0)),
79: Width=400,
80: Height=0,
81: CornerRadius= new CornerRadius(10),
82: BorderBrush=new SolidColorBrush(Colors.White),
83: BorderThickness=new Thickness(5),
84: //Visibility=Visibility.Collapsed,
85: VerticalAlignment=VerticalAlignment.Top,
86: };
87: _Border.Child = sp;
88:
89: (map1.Parent as Grid).Children.Add(_Border);
90:
91:
92: _salesmin = _salesmax = 0;
93: //find day minsales and maxsales in all graphics and all days
94: for (int i = 1; i <= 30; i++)
95: {
96: string strDay = string.Empty;
97: if (i < 10)
98: strDay = "0" + i.ToString();
99: else
100: strDay = i.ToString();
101: double fieldmax = (from graphic in _FLayer.Graphics
102: select graphic).Max(a => (double)a.Attributes["D11" + strDay]);
103: _salesmax = _salesmax > fieldmax ? _salesmax : fieldmax;
104: double fieldmin = (from graphic in _FLayer.Graphics
105: select graphic).Min(a => (double)a.Attributes["D11" + strDay]);
106: _salesmin = _salesmin < fieldmin ? _salesmin : fieldmin;
107: }
108: }
当控制时间的slider发生变化时,我们就根据当前日期的营业额,计算出每个超市店面符号的大小,从而使用新的符号来显示。
1: void _slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
2: {
3: int day = (int)e.NewValue;
4: //change text
5: _textblock.Text = string.Format("时º¡À间?:11月?{0}日¨?", day.ToString());
6:
7: ChangeSymbolAndText(day);
8: }
9:
10: /// <summary>
11: /// change symbols and text according to current day
12: /// </summary>
13: /// <param name="day"></param>
14: private void ChangeSymbolAndText(int day)
15: {
16: string strDay = string.Empty;
17: if (day < 10)
18: strDay = "D11"+"0" + day.ToString();
19: else
20: strDay = "D11" + day.ToString();
21:
22: foreach (Graphic g in _FLayer.Graphics)
23: {
24: //determin symbol size
25: double size = -1;
26: if ((double)g.Attributes[strDay] <= (_salesmax - _salesmin) * 1 / 3)
27: size = (double)SymbolSize.small;
28: else if ((double)g.Attributes[strDay] > (_salesmax - _salesmin) * 1 / 3 && (double)g.Attributes[strDay] <= (_salesmax - _salesmin) * 2 / 3)
29: size = (double)SymbolSize.middle;
30: else
31: size = (double)SymbolSize.large;
32:
33: PictureMarkerSymbol symbol = (g.Attributes["symbol"] as Graphic).Symbol as PictureMarkerSymbol;
34: symbol.Height = symbol.Width = size;
35: symbol.OffsetX = symbol.OffsetY = size / 2;
36:
37: TextSymbol text = (g.Attributes["text"] as Graphic).Symbol as TextSymbol;
38: text.Text = double.Parse(g.Attributes[strDay].ToString()).ToString("###,###");
39: text.OffsetX = -size / 2 + 10;
40: text.OffsetY = -size / 2 + 10;
41: //text.FontSize = size == (double)SymbolSize.small ? size : size - 10;
42: }
43: }
最后,还要控制营业额数字的显示范围。当比例尺较小时,超市图标分布较为密集,具体营业额数据不予显示;当比例尺较大时,才显示每个超市的具体营业额数字。
1: void map1_ExtentChanged(object sender, ExtentEventArgs e)
2: {
3: if (map1.Resolution > 50.2185141425366)//38
4: _GLayerText.Visible = false;
5: else
6: _GLayerText.Visible = true;
7: }
至此,我们的第三个功能也完成了。在这里请各位朋友思考一下,如果使用ArcGIS格式的时态数据,我们按时间查看超市营业额的功能该怎么做?相较于本文中的方法,各自的优缺点是什么?
参考资料:
ArcGIS中的时态数据:http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/What_is_temporal_data/005z00000001000000/
一直看老王的文章,我顶啊…
最近在搞一个兴趣点数据采集的小应用,用的也是arcgis api for wp,现在的问题是修改属性数据后,如何post给FeatureLayer,老王给指条路吧…
顺便给留个联系方式呗,神马邮箱、MSN、QQ,通吃啊…
用FeatureLayer来加载可编辑的FeatureService,对FeatureLayer的内容修改过之后,调用它的saveedits方法,就可以自动把修改提交到数据库中。具体问题的话可以上论坛提问吧
我一直在想作为Esri Map在商业上的应用有多大呢?
@robertvvv
不同用户实现的价值不一样么。