使用Visifire+ArcGIS API for Silverlight实现Graphic信息的动态图表显示

简介: 原文:使用Visifire+ArcGIS API for Silverlight实现Graphic信息的动态图表显示  首先来看一看实现的效果: PS:原始的程序中更新曲线数据时添加了过渡的效果,具体可查看官网的示例: http://www.visifire.com/silverlight_spline_charts_gallery.php 点击其中的一个例子,然后点击Live Updates,就可看到数据更新时的过渡效果。
原文: 使用Visifire+ArcGIS API for Silverlight实现Graphic信息的动态图表显示

  首先来看一看实现的效果:

获取 Microsoft Silverlight

PS:原始的程序中更新曲线数据时添加了过渡的效果,具体可查看官网的示例:

http://www.visifire.com/silverlight_spline_charts_gallery.php

点击其中的一个例子,然后点击Live Updates,就可看到数据更新时的过渡效果。但是蛋疼的博客园,不知道为什么,我插入了我原始的xap文件,过渡效果却没有了,每次更新数据时,曲线直接就出来了(中间没有过渡行为),开始的时候以为是上传后xap文件有可能发生了变化,或者进行压缩什么的,或者浏览器解析什么的,于是我把上传到博客园的xap文件又下载下来,然后嵌入到一个htm页面中,发现还是有过度效果的,但是为什么在博客园的日志中就没有呢?整了好久还是没有整明白,求解释!

 

下面来看看具体实现的过程吧:

首先需要添加SLVisifire.Charts,FJ.Core的引用。然后开始我们正式的工作:

1.定义一个Chart,该Chart即为显示的图表,以及定义一个Timer,用于定时刷新数据。

        Chart chart;                                       
        Random rand = new Random(DateTime.Now.Millisecond);    
        System.Windows.Threading.DispatcherTimer timer = new 
        System.Windows.Threading.DispatcherTimer();

2.定义一个方法:CreateChart实例化Chart,并完成Chart的相关设置(如:高,宽,曲线样式等等)

        /// <summary>
        /// Function to create a Visifire Chart
        /// </summary>
        public void CreateChart()
        {
            try
            {
                // Create a new instance of a Chart
                chart = new Chart();
                //添加X,Y坐标的描述
                Axis axisX = new Axis()
                {
                    Title = "月份",
                    FontSize = 18,
                };
                chart.AxesX.Add(axisX);

                Axis axisY = new Axis()
                {
                    Title = "实时监测值",
                    FontSize = 16,
                };
                chart.AxesY.Add(axisY);

                // 设置图表的高宽
                chart.Width = 640;
                chart.Height = 300;

                //Line数据更新时的过渡效果
                chart.AnimatedUpdate = true;

                // 定义DatatSeries实例,即一条曲线
                DataSeries dataSeries1 = new DataSeries();
                DataSeries dataSeries2 = new DataSeries();
                //只有当Series的个数大于等于2个时Lenged才会生效
                dataSeries1.LegendText = "CO2";
                dataSeries2.LegendText = "SO2";
                // 设置DataSeries样式
                dataSeries1.RenderAs = RenderAs.Spline;
                dataSeries2.RenderAs = RenderAs.Spline;
                // 定义数据点
                DataPoint dataPoint1;
                DataPoint dataPoint2;
                for (int i = 1; i <= 12; i++)
                {
                    // 实例化数据点
                    dataPoint1= new DataPoint();
                    dataPoint2 = new DataPoint();
                    //设置数据值
                    dataPoint1.YValue = rand.Next(0, 99);
                    dataPoint2.YValue = rand.Next(0, 99);
                    //设置X轴显示名称
                    dataPoint1.AxisXLabel = string.Format("{0} 月", i);
                    dataPoint2.AxisXLabel = string.Format("{0} 月", i);
                    // 添加数据点
                    dataSeries1.DataPoints.Add(dataPoint1);
                    dataSeries2.DataPoints.Add(dataPoint2);
                }

                // 将DataSeries(曲线)添加到Chart中
                chart.Series.Add(dataSeries1);
                chart.Series.Add(dataSeries2);
                // 注册Chart Loaded事件,在该事件中设置Timer的间断值及注册Timer Tick事件
                chart.Loaded += new RoutedEventHandler(chart_Loaded);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

3.我们看到在创建Chart中注册了Loaded事件,在该事件的完成函数我们设置Timer的间断值及注册Timer Tick事件

     void chart_Loaded(object sender, RoutedEventArgs e)
        {
            timer.Tick += new EventHandler(timer_Tick);
            timer.Interval = new TimeSpan(0, 0, 0, 0, 2000);
        }
        /// <summary>
        /// Event handler for Tick event of Dispatcher Timer
        /// </summary>
        /// <param name="sender">System.Windows.Threading.DispatcherTimer</param>
        /// <param name="e">EventArgs</param>
        void timer_Tick(object sender, EventArgs e)
        {
            for (Int32 i = 0; i < 12; i++)
            {
                // 更新曲线数据
                chart.Series[0].DataPoints[i].YValue = rand.Next(0, 99); 
                chart.Series[1].DataPoints[i].YValue = rand.Next(20, 80); 
            }
        }

4.这里我们再写一个方法:AddChartToMapLayer,该方法用来将Chart添加到地图中

        /// <summary>
        /// Add Chart to Map
        /// </summary>
        /// <param name="chart">Chart</param>
        /// <param name="position">Position</param>
        public void AddChartToMapLayer(Chart chart, Graphic position)
        {
            ElementLayer chartlayer = new ElementLayer();
            chartlayer.ID = "ChartLayer";
            chartlayer.Opacity = 1;
            if (Map.Layers["ChartLayer"] != null)
            {
                Map.Layers.Remove(Map.Layers["ChartLayer"]);
            }

            chart.Titles.Add(new Title()
            {
                Text=string.Format("City: {0}",position.Attributes["CITY_NAME"].ToString ())
            });

            //获得Graphic的中心坐标
            ESRI.ArcGIS.Client.Geometry.Geometry geometry=position.Geometry;
            MapPoint mapPoint=new MapPoint ((geometry.Extent.XMax+geometry.Extent.XMin)/2,(geometry.Extent.YMax+geometry.Extent.YMin)/2);
          
            //获得图层X,Y方向对应的比例
            double cell_X = (Map.Extent.XMax - Map.Extent.XMin) / Map.ActualWidth;
            double cell_Y = (Map.Extent.YMax - Map.Extent.YMin) / Map.ActualHeight;

            //Chart的宽度为500所以,Chart的中心则位于宽度等于250的位置,将Chart向右移动,以防Chart覆盖Graphic
            Envelope extent = new Envelope(mapPoint.X + (320 + 20) * cell_X, mapPoint.Y, mapPoint.X + (320 + 20) * cell_X, mapPoint.Y);
            //设置ElementLayer的外包范围
            ElementLayer.SetEnvelope(chart, extent);
            chartlayer.Children.Add(chart);
            Map.Layers.Add(chartlayer);
        }

现在准备工作已经完成,下面就调用这些方法来实现上述的功能,我们大体的功能过程是:

当鼠标移入Graphic是就显示其对应的Chart,移除的移除Chart,这里我们注册一下FeatureLayer的MouseEnter和MouseLeave事件就行,我们再后台的XAML中注册这两个事件。

     <esri:FeatureLayer ID="MyFeatureLayer"
                    Url="http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/0" 
                    Where="POP1990 > 500000"
                                       MouseEnter="FeatureLayer_MouseEnter"
                                       MouseLeave="FeatureLayer_MouseLeave"
                                       Renderer="{StaticResource MySimplePointRenderer}"/>

最后在后台添加如下代码:

   private void FeatureLayer_MouseEnter(object sender, GraphicMouseEventArgs e)
        {
            //绘制Chart
            CreateChart();
            //将Chart添加到Map
            AddChartToMapLayer(chart, e.Graphic);
            //启动Timer,定时刷新数据
            timer.Start();
        }

        private void FeatureLayer_MouseLeave(object sender, GraphicMouseEventArgs e)
        {
            //停止Timer
            timer.Stop();
            //移除ElementLayer
            if (Map.Layers["ChartLayer"] != null)
            {
                Map.Layers.Remove(Map.Layers["ChartLayer"]);
            }
        }

后台的XAML代码:

View Code
<UserControl x:Class="SilverlightChartDemo.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   xmlns:vc="clr-namespace:Visifire.Charts;assembly=SLVisifire.Charts"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400" xmlns:esri="http://schemas.esri.com/arcgis/client/2009">

    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.Resources>
            <esri:SimpleRenderer x:Key="MySimplePointRenderer">
                <esri:SimpleRenderer.Symbol>
                    <esri:SimpleMarkerSymbol Size="15" Style="Circle">
                        <esri:SimpleMarkerSymbol.ControlTemplate>
                            <ControlTemplate>
                                <Grid x:Name="RootElement" RenderTransformOrigin="0.5,0.5" Width="15" Height="15">
                                    <Grid.RenderTransform>
                                        <ScaleTransform x:Name="customEnlargeRotatingMarkerSymbolScale" ScaleX="1" ScaleY="1" />
                                    </Grid.RenderTransform>
                                    <Grid.Resources>
                                        <DropShadowEffect x:Key="customEnlargeRotatingMarkerSymbolEffect" />
                                    </Grid.Resources>
                                    <vsm:VisualStateManager.VisualStateGroups>
                                        <vsm:VisualStateGroup x:Name="CommonStates">
                                            <vsm:VisualState x:Name="Normal">
                                                <Storyboard>
                                                    <DoubleAnimation BeginTime="00:00:00" Storyboard.TargetName="customEnlargeRotatingMarkerSymbolScale" Storyboard.TargetProperty="ScaleX" To="1" Duration="0:0:0.2" />
                                                    <DoubleAnimation BeginTime="00:00:00" Storyboard.TargetName="customEnlargeRotatingMarkerSymbolScale" Storyboard.TargetProperty="ScaleY" To="1" Duration="0:0:0.2" />
                                                    <DoubleAnimation BeginTime="00:00:00" Storyboard.TargetName="customEnlargeRotatingMarkerSymbolRotate" Storyboard.TargetProperty="Angle" To="360" Duration="0:0:0.2" />
                                                    <DoubleAnimation BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.ShadowDepth)" To="2" Duration="0:0:0.2" />
                                                </Storyboard>
                                            </vsm:VisualState>
                                            <vsm:VisualState x:Name="MouseOver">
                                                <Storyboard>
                                                    <DoubleAnimation BeginTime="00:00:00" Storyboard.TargetName="customEnlargeRotatingMarkerSymbolScale" Storyboard.TargetProperty="ScaleX" To="2" Duration="0:0:0.2" />
                                                    <DoubleAnimation BeginTime="00:00:00" Storyboard.TargetName="customEnlargeRotatingMarkerSymbolScale" Storyboard.TargetProperty="ScaleY" To="2" Duration="0:0:0.2" />
                                                    <DoubleAnimation BeginTime="00:00:00" Storyboard.TargetName="customEnlargeRotatingMarkerSymbolRotate" Storyboard.TargetProperty="Angle" To="0" Duration="0:0:0.2" />
                                                    <DoubleAnimation BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.ShadowDepth)" To="5" Duration="0:0:0.2" />
                                                </Storyboard>
                                            </vsm:VisualState>
                                        </vsm:VisualStateGroup>
                                    </vsm:VisualStateManager.VisualStateGroups>
                                    <Ellipse x:Name="ellipse" Width="15" Height="15" Fill="Green" Stroke="White" StrokeThickness="2" HorizontalAlignment="Center" VerticalAlignment="Center" Effect="{StaticResource customEnlargeRotatingMarkerSymbolEffect}"></Ellipse>
                                    <Canvas x:Name="RotateCanvas" HorizontalAlignment="Left" VerticalAlignment="Top" Width="15" Height="15" RenderTransformOrigin="0.5,0.5">
                                        <Canvas.Clip>
                                            <EllipseGeometry RadiusX="7.5" RadiusY="7.5" Center="7.5,7.5" />
                                        </Canvas.Clip>
                                        <Canvas.RenderTransform>
                                            <RotateTransform x:Name="customEnlargeRotatingMarkerSymbolRotate" Angle="360" />
                                        </Canvas.RenderTransform>
                                        <Line Stroke="White" StrokeThickness="2" X1="0" Y1="0" X2="15" Y2="15" />
                                        <Line Stroke="White" StrokeThickness="2" X1="0" Y1="15" X2="15" Y2="0" />
                                    </Canvas>
                                </Grid>
                            </ControlTemplate>
                        </esri:SimpleMarkerSymbol.ControlTemplate>
                    </esri:SimpleMarkerSymbol>

                </esri:SimpleRenderer.Symbol>
            </esri:SimpleRenderer>
        </Grid.Resources>
        <esri:Map Background="White" HorizontalAlignment="Stretch" Margin="0" Grid.ColumnSpan="3" Name="Map" VerticalAlignment="Stretch" WrapAround="True" Extent="-15000000,2000000,-7000000,8000000">
            <esri:Map.Layers>
                <esri:LayerCollection>
                    <esri:ArcGISTiledMapServiceLayer Url="http://www.arcgisonline.cn/ArcGIS/rest/services/ChinaOnlineCommunity/MapServer" />
                    <esri:FeatureLayer ID="MyFeatureLayer"
                    Url="http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer/0" 
                    Where="POP1990 > 500000"
                                       MouseEnter="FeatureLayer_MouseEnter"
                                       MouseLeave="FeatureLayer_MouseLeave"
                                       Renderer="{StaticResource MySimplePointRenderer}"/>
                </esri:LayerCollection>
            </esri:Map.Layers>
        </esri:Map>
       
    </Grid>
</UserControl>

这样我们便实现了上述的效果。

 

需要注意的几点:

1.Chart控件第一次加载数据的动画效果(一个点一个点添加),只能使用一次,也就是实例化Chart,加载数据时会有这样的效果,之后更新Chart控件的数据不会有类似第一次加载Chart时显示曲线动画效果,而是上面说的过渡效果。

2.注意设置Chart的AnimatedUpdate属性为True,否则数据更新时绘制曲线没有过渡的效果

3.将Chart作为ElementLayer添加到地图中,注意设置ElementLayer的显示位置。

 

 总结:

 以上的过程数据都是用Timer组件来定时生成的,当然这里也可以扩展,比如你的数据也可以是通过SQL查询得到。此外,本例没有用到数据绑定,这是因为本文的Chart是动态生成的,每次选择Graphic时都会重新生成Chart.所以没有用数据绑定。当然如果你的Chart是写死的(实例化一次),或者定义在后台的xaml中,那么建议您选择数据绑定的方式,数据绑定的方式也很简单,例如在XAML定义如下的Chart(详见Visifire documentation):

<vc:Chart Name="MyChart" Width="500" Height="300" Theme="Theme1">

    <vc:Chart.Series>

        <vc:DataSeries RenderAs="Column" DataSource="{Binding}">

            <vc:DataSeries.DataMappings>
                <vc:DataMapping MemberName="XValue" Path="Key"></vc:DataMapping>
                <vc:DataMapping MemberName="YValue" Path="Value"></vc:DataMapping>
            </vc:DataSeries.DataMappings>

        </vc:DataSeries>

    </vc:Chart.Series>

</vc:Chart>

在后台添加如下代码:

public partial class Page : UserControl
{
    public Page()
    {
        InitializeComponent();
 

        for (int i = 0; i < 10; i++)
        {
            values.Add(new KeyValuePair<double,double>(i, i + 1));
        }
            
        MyChart.DataContext = values;
    }

    Random rand = new Random();
    ObservableCollection<KeyValuePair<Double, Double>> values = new ObservableCollection<KeyValuePair<double, double>>();
}

这样便可以实现Chart曲线的数据绑定。

PS:有那么一段时间没有用Visifire了,很多又忘记了,以上算是自己的一个总结,同时很多人也问个类似的效果怎么做,在此和大家分享一下,鉴于时间和知识的关系,疏漏和错误在所难免,还望各位指正。

 //关于解决地图缩放时Chart偏移的问题。

1.注册Map的ExtentChanging事件,然后在事件完成函数中重新设定Elmentlayer的Envelop,代码如下:

        private void Map_ExtentChanging(object sender, ExtentEventArgs e)
        {
            double cell_X = (Map.Extent.XMax - Map.Extent.XMin) / Map.ActualWidth;
            ESRI.ArcGIS.Client.Geometry.Geometry geometry = selectedGraphic.Geometry;
            MapPoint mapPoint = new MapPoint((geometry.Extent.XMax + geometry.Extent.XMin) / 2, (geometry.Extent.YMax + geometry.Extent.YMin) / 2);
            Envelope extent = new Envelope(mapPoint.X + (320 + 20) * cell_X, mapPoint.Y, mapPoint.X + (320 + 20) * cell_X, mapPoint.Y);
            Chart Mychart = chartlayer.Children[0] as Chart;
            ElementLayer.SetEnvelope(Mychart, extent);
        }

感谢8楼 vergiljzy的建议和提示,有兴趣的不妨试试采用Inforwindow的方式。

目录
相关文章
|
4月前
|
缓存 API
全球最新地震信息免费API接口教程
该接口提供全球最新地震信息,数据源自中国地震台网,每5分钟更新一次。支持POST/GET请求,需提供用户ID和KEY。返回数据包括地震等级、时间、经纬度、深度及位置等详细信息。示例请求和响应详见文档。
|
1月前
|
JSON API UED
商品信息API接口的设计与实现
商品信息API接口的设计与实现,遵循RESTful原则以确保高效、可维护和良好的用户体验。API支持获取、查询、创建、更新和删除商品资源,URL模式直观易懂。请求参数通过查询字符串传递,支持分页和过滤。响应体结构化,包含数据、链接和元数据字段,便于解析。错误处理采用HTTP状态码结合JSON错误描述,提供明确反馈。
|
2月前
|
供应链 API 开发者
解锁电商数据的无限可能:探秘京东商品SKU信息API接口
京东商品SKU信息API接口是电商开发与运营中的重要工具,帮助开发者获取商品的详细属性,如库存、价格、规格等。通过该接口,电商平台可以丰富商品展示页面,提升用户体验;商家能实时掌握库存动态,优化销售策略;数据分析人员可深入洞察市场趋势,实现精准营销。使用前需注册京东开放平台账号、创建应用并获取API权限,同时仔细阅读API文档以确保正确调用。代码示例展示了如何用Python调用该接口,并处理返回数据。未来,该接口将在个性化推荐、智能库存管理和数据分析等领域发挥更大作用,助力电商业务创新与发展。
132 14
|
2月前
|
JSON 监控 API
获取1688商品SKU信息API接口及实战应用
在电商蓬勃发展的今天,数据成为宝贵的财富。1688作为国内知名批发采购平台,提供商品SKU信息API接口,可获取库存、价格、规格等关键数据,助力电商运营、市场分析和价格监控。本文介绍如何注册1688开放平台账号、创建应用并获取AppKey/AppSecret,申请API权限,使用Python实现接口调用,处理响应数据,并注意请求频率限制和错误处理。通过该接口,可为电商运营和数据分析提供有力支持。
127 2
|
2月前
|
供应链 数据挖掘 API
探秘淘宝商品SKU信息API接口
在电子商务中,SKU(库存量单位)用于区分商品的不同规格、颜色、尺寸等属性,是精细化管理的基础。本文深入探讨淘宝商品SKU信息API接口,介绍其功能特点、使用方法及应用场景,并提供Python代码示例。该API支持实时更新、多维度筛选,帮助开发者获取全面的SKU信息,优化库存管理、订单处理和销售数据分析,提升运营效率与市场竞争力。
154 1
|
2月前
|
JSON 缓存 API
深度探索淘宝详情API接口:高效获取商品信息的实践指南
淘宝详情API接口是阿里巴巴开放平台的重要组成部分,帮助开发者通过程序化方式获取淘宝商品的详细信息,如标题、价格、销量等。本文介绍其使用方法,涵盖权限申请、请求构造、异常处理及高级应用,助力开发者优化电商体验和业务决策。
|
3月前
|
JavaScript API C#
【Azure Developer】Python代码调用Graph API将外部用户添加到组,结果无效,也无错误信息
根据Graph API文档,在单个请求中将多个成员添加到组时,Python代码示例中的`members@odata.bind`被错误写为`members@odata_bind`,导致用户未成功添加。
62 10
|
3月前
|
供应链 搜索推荐 API
1688榜单商品详细信息API接口的开发、应用与收益
1688作为全球知名的B2B电商平台,为企业提供丰富的商品信息和交易机会。为满足企业对数据的需求,1688开发了榜单商品详细信息API接口,帮助企业批量获取商品详情,应用于信息采集、校验、同步与数据分析等领域,提升运营效率、优化库存管理、精准推荐、制定市场策略、降低采购成本并提高客户满意度。该接口通过HTTP请求调用,支持多种应用场景,助力企业在电商领域实现可持续发展。
120 4
|
3月前
|
供应链 搜索推荐 API
探索1688榜单商品详细信息API接口:开发、应用与收益
本文深入探讨了1688榜单商品详细信息API接口的开发与应用,涵盖接口概述、开发条件、调用方法及数据处理等内容。该API帮助企业高效获取1688平台商品信息,应用于商品信息采集、校验、同步与数据分析等领域,有效提升了企业的运营效率、库存管理、销售转化率及市场策略制定能力,降低了采购成本,提升了客户满意度。
67 9
|
4月前
|
API
万年历[取当日信息]免费API接口教程
此API提供万年历当天的详细信息,包括农历、星期、宜忌、生肖、星座、节日、五行、星宿等。支持POST和GET请求,需提供用户ID和KEY。返回数据包含阳历、农历、干支、节日列表等多项内容。示例URL:https://cn.apihz.cn/api/time/getday.php?id=88888888&key=88888888。
1095 10

热门文章

最新文章